From 301b1a6ca9412576ad3a3aa6598c97ca7909c7b5 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 24 May 2020 19:30:03 +0300 Subject: [PATCH] Improve village state management --- src/Action/ActionController.ts | 14 +-- src/Action/BalanceHeroResourcesAction.ts | 2 +- src/Action/ClickButtonAction.ts | 2 +- src/Action/SendResourcesAction.ts | 4 +- src/Action/UpgradeResourceToLevel.ts | 2 +- src/Container.ts | 40 ++------ src/ControlPanel.ts | 33 +++--- src/DashboardView/Store.ts | 6 +- src/Executor.ts | 15 ++- src/Grabber/BuildingContractGrabber.ts | 2 +- src/Grabber/ForgePageGrabber.ts | 5 +- src/Grabber/Grabber.ts | 11 +- src/Grabber/GrabberManager.ts | 23 +++-- src/Grabber/GuildHallPageGrabber.ts | 3 +- src/Grabber/MarketPageGrabber.ts | 3 +- src/Grabber/VillageOverviewPageGrabber.ts | 7 +- src/Grabber/VillageResourceGrabber.ts | 5 +- src/Scheduler.ts | 19 ++-- src/Storage/LogStorage.ts | 1 - src/Storage/VillageStorage.ts | 36 +------ src/VillageController.ts | 101 ++++--------------- src/VillageControllerFactory.ts | 21 ---- src/VillageFactory.ts | 57 +++++++++++ src/VillageState.ts | 75 +++++++++----- src/VillageTaskCollection.ts | 116 ++++++++++++++++++++++ 25 files changed, 328 insertions(+), 275 deletions(-) delete mode 100644 src/VillageControllerFactory.ts create mode 100644 src/VillageFactory.ts create mode 100644 src/VillageTaskCollection.ts diff --git a/src/Action/ActionController.ts b/src/Action/ActionController.ts index 7c3026c..bcefabe 100644 --- a/src/Action/ActionController.ts +++ b/src/Action/ActionController.ts @@ -5,7 +5,7 @@ import { aroundMinutes } from '../utils'; import { Args } from '../Queue/Args'; import { Task } from '../Queue/TaskProvider'; import { VillageStorage } from '../Storage/VillageStorage'; -import { VillageStateRepository } from '../VillageState'; +import { VillageFactory } from '../VillageFactory'; const actionMap: { [name: string]: Function | undefined } = {}; @@ -16,22 +16,22 @@ export function registerAction(constructor: Function) { export function createActionHandler( name: string, scheduler: Scheduler, - villageStateRepository: VillageStateRepository + villageFactory: VillageFactory ): ActionController | undefined { const storedFunction = actionMap[name]; if (storedFunction === undefined) { return undefined; } const constructor = (storedFunction as unknown) as typeof ActionController; - return new constructor(scheduler, villageStateRepository); + return new constructor(scheduler, villageFactory); } export class ActionController { - protected scheduler: Scheduler; - protected villageStateRepository: VillageStateRepository; - constructor(scheduler: Scheduler, villageStateRepository: VillageStateRepository) { + protected readonly scheduler: Scheduler; + protected readonly villageFactory: VillageFactory; + constructor(scheduler: Scheduler, villageFactory: VillageFactory) { this.scheduler = scheduler; - this.villageStateRepository = villageStateRepository; + this.villageFactory = villageFactory; } async run(args: Args, task: Task) {} diff --git a/src/Action/BalanceHeroResourcesAction.ts b/src/Action/BalanceHeroResourcesAction.ts index 7a19520..7682312 100644 --- a/src/Action/BalanceHeroResourcesAction.ts +++ b/src/Action/BalanceHeroResourcesAction.ts @@ -18,7 +18,7 @@ export class BalanceHeroResourcesAction extends ActionController { return; } - const thisVillageState = this.villageStateRepository.getVillageState(thisVillageId); + const thisVillageState = this.villageFactory.createState(thisVillageId); const requirements = [ thisVillageState.required.balance, diff --git a/src/Action/ClickButtonAction.ts b/src/Action/ClickButtonAction.ts index 494711c..ef616e0 100644 --- a/src/Action/ClickButtonAction.ts +++ b/src/Action/ClickButtonAction.ts @@ -1,5 +1,5 @@ import { ActionController, registerAction } from './ActionController'; -import { AbortTaskError, taskError } from '../Errors'; +import { taskError } from '../Errors'; import { Args } from '../Queue/Args'; import { Task } from '../Queue/TaskProvider'; diff --git a/src/Action/SendResourcesAction.ts b/src/Action/SendResourcesAction.ts index e54dabd..e782de5 100644 --- a/src/Action/SendResourcesAction.ts +++ b/src/Action/SendResourcesAction.ts @@ -18,8 +18,8 @@ export class SendResourcesAction extends ActionController { const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates')); - const senderVillage = this.villageStateRepository.getVillageState(senderVillageId); - const recipientVillage = this.villageStateRepository.getVillageState(targetVillageId); + const senderVillage = this.villageFactory.createState(senderVillageId); + const recipientVillage = this.villageFactory.createState(targetVillageId); const readyToTransfer = this.getResourcesForTransfer(senderVillage, recipientVillage); diff --git a/src/Action/UpgradeResourceToLevel.ts b/src/Action/UpgradeResourceToLevel.ts index 4a1a6d5..09aa7e0 100644 --- a/src/Action/UpgradeResourceToLevel.ts +++ b/src/Action/UpgradeResourceToLevel.ts @@ -1,5 +1,5 @@ import { ActionController, registerAction } from './ActionController'; -import { AbortTaskError, ActionError, taskError, TryLaterError } from '../Errors'; +import { ActionError, taskError, TryLaterError } from '../Errors'; import { grabResourceDeposits } from '../Page/SlotBlock'; import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; import { ResourceDeposit } from '../Game'; diff --git a/src/Container.ts b/src/Container.ts index 06de00e..7f22ee6 100644 --- a/src/Container.ts +++ b/src/Container.ts @@ -8,9 +8,8 @@ import { DataStorageTaskProvider } from './Queue/DataStorageTaskProvider'; import { Statistics } from './Statistics'; import { StatisticsStorage } from './Storage/StatisticsStorage'; import { VillageRepository } from './VillageRepository'; -import { VillageStateRepository } from './VillageState'; import { LogStorage } from './Storage/LogStorage'; -import { VillageControllerFactory } from './VillageControllerFactory'; +import { VillageFactory } from './VillageFactory'; import { GrabberManager } from './Grabber/GrabberManager'; export class Container { @@ -42,15 +41,15 @@ export class Container { return this._statistics; } - private _villageControllerFactory: VillageControllerFactory | undefined; + private _villageFactory: VillageFactory | undefined; - get villageControllerFactory(): VillageControllerFactory { - this._villageControllerFactory = - this._villageControllerFactory || + get villageFactory(): VillageFactory { + this._villageFactory = + this._villageFactory || (() => { - return new VillageControllerFactory(this.villageRepository); + return new VillageFactory(this.villageRepository); })(); - return this._villageControllerFactory; + return this._villageFactory; } private _scheduler: Scheduler | undefined; @@ -68,31 +67,20 @@ export class Container { taskQueue, actionQueue, this.villageRepository, - this.villageControllerFactory, + this.villageFactory, new ConsoleLogger(Scheduler.name) ); })(); 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; get grabberManager(): GrabberManager { this._grabberManager = this._grabberManager || (() => { - return new GrabberManager(this.villageControllerFactory); + return new GrabberManager(this.villageFactory); })(); return this._grabberManager; } @@ -110,8 +98,7 @@ export class Container { return new Executor( this.version, this.scheduler, - this.villageStateRepository, - this.villageControllerFactory, + this.villageFactory, this.grabberManager, this.statistics, logger @@ -126,12 +113,7 @@ export class Container { this._controlPanel = this._controlPanel || (() => { - return new ControlPanel( - this.version, - this.scheduler, - this.villageStateRepository, - this.villageControllerFactory - ); + return new ControlPanel(this.version, this.scheduler, this.villageFactory); })(); return this._controlPanel; } diff --git a/src/ControlPanel.ts b/src/ControlPanel.ts index 93347bb..7efdedd 100644 --- a/src/ControlPanel.ts +++ b/src/ControlPanel.ts @@ -18,11 +18,11 @@ import { ConsoleLogger, Logger } from './Logger'; import { DataStorage } from './DataStorage'; import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors'; import { ExecutionStorage } from './Storage/ExecutionStorage'; -import { VillageState, VillageStateRepository } from './VillageState'; +import { VillageState } from './VillageState'; import { Task } from './Queue/TaskProvider'; import { Action } from './Queue/ActionQueue'; import { createStore } from './DashboardView/Store'; -import { VillageControllerFactory } from './VillageControllerFactory'; +import { VillageFactory } from './VillageFactory'; Vue.use(Vuex); @@ -52,20 +52,13 @@ interface GameState { export class ControlPanel { private readonly version: string; private readonly scheduler: Scheduler; - private readonly villageStateRepository: VillageStateRepository; private readonly logger: Logger; - private villageControllerFactory: VillageControllerFactory; + private readonly villageFactory: VillageFactory; - constructor( - version: string, - scheduler: Scheduler, - villageStateRepository: VillageStateRepository, - villageControllerFactory: VillageControllerFactory - ) { + constructor(version: string, scheduler: Scheduler, villageFactory: VillageFactory) { this.version = version; this.scheduler = scheduler; - this.villageStateRepository = villageStateRepository; - this.villageControllerFactory = villageControllerFactory; + this.villageFactory = villageFactory; this.logger = new ConsoleLogger(this.constructor.name); } @@ -78,7 +71,7 @@ export class ControlPanel { const villageId = grabActiveVillageId(); const scheduler = this.scheduler; - const villageStateRepository = this.villageStateRepository; + const villageFactory = this.villageFactory; const executionState = new ExecutionStorage(); @@ -106,7 +99,7 @@ export class ControlPanel { }, refreshVillages() { - this.villageStates = villageStateRepository.getAllVillageStates(); + this.villageStates = villageFactory.getAllVillageStates(); for (let state of this.villageStates) { if (state.village.active) { this.activeVillageState = state; @@ -136,8 +129,8 @@ export class ControlPanel { DataStorage.onChange(() => state.refresh()); const getBuildingsInQueue = () => - this.villageControllerFactory - .create(villageId) + this.villageFactory + .createTaskCollection(villageId) .getTasks() .filter(t => t.name === UpgradeBuildingTask.name) .map(t => t.args.buildId || 0); @@ -163,21 +156,21 @@ export class ControlPanel { const buildPage = new BuildingPageController( this.scheduler, getBuildingPageAttributes(), - this.villageControllerFactory.create(villageId) + this.villageFactory.createController(villageId) ); 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()}`; jQuery('body').prepend(`
`); new Vue({ el: `#${appId}`, data: gameState, - store: createStore(villageStateRepository), + store: createStore(villageFactory), render: h => h(DashboardApp), }); } diff --git a/src/DashboardView/Store.ts b/src/DashboardView/Store.ts index 932b774..cccf914 100644 --- a/src/DashboardView/Store.ts +++ b/src/DashboardView/Store.ts @@ -1,9 +1,9 @@ import Vuex from 'vuex'; import { LogStorage } from '../Storage/LogStorage'; -import { VillageStateRepository } from '../VillageState'; import { VillageSettings, VillageSettingsDefaults } from '../Core/Village'; import { getNumber, notify } from '../utils'; import { VillageStorage } from '../Storage/VillageStorage'; +import { VillageFactory } from '../VillageFactory'; export enum Mutations { showLogs = 'showLogs', @@ -22,7 +22,7 @@ export enum Actions { SaveVillageSettings = 'save_village_settings', } -export function createStore(villageStateRepository: VillageStateRepository) { +export function createStore(villageFactory: VillageFactory) { const store = new Vuex.Store({ state: { views: { @@ -74,7 +74,7 @@ export function createStore(villageStateRepository: VillageStateRepository) { }, actions: { [Actions.OpenVillageEditor]({ commit }, { villageId }) { - const state = villageStateRepository.getVillageState(villageId); + const state = villageFactory.createState(villageId); const settings = state.settings; commit(Mutations.SetVillageSettings, { villageId: state.id, diff --git a/src/Executor.ts b/src/Executor.ts index 923cd52..a4fbf5b 100644 --- a/src/Executor.ts +++ b/src/Executor.ts @@ -10,8 +10,8 @@ import { ExecutionStorage } from './Storage/ExecutionStorage'; import { Action } from './Queue/ActionQueue'; import { Task } from './Queue/TaskProvider'; import { createTaskHandler } from './Task/TaskMap'; -import { VillageStateRepository } from './VillageState'; -import { VillageControllerFactory } from './VillageControllerFactory'; +import { VillageStateFactory } from './VillageState'; +import { VillageFactory } from './VillageFactory'; export interface ExecutionSettings { pauseTs: number; @@ -20,8 +20,7 @@ export interface ExecutionSettings { export class Executor { private readonly version: string; private readonly scheduler: Scheduler; - private readonly villageStateRepository: VillageStateRepository; - private villageControllerFactory: VillageControllerFactory; + private villageFactory: VillageFactory; private grabberManager: GrabberManager; private statistics: Statistics; private executionState: ExecutionStorage; @@ -30,16 +29,14 @@ export class Executor { constructor( version: string, scheduler: Scheduler, - villageStateRepository: VillageStateRepository, - villageControllerFactory: VillageControllerFactory, + villageFactory: VillageFactory, grabberManager: GrabberManager, statistics: Statistics, logger: Logger ) { this.version = version; this.scheduler = scheduler; - this.villageStateRepository = villageStateRepository; - this.villageControllerFactory = villageControllerFactory; + this.villageFactory = villageFactory; this.grabberManager = grabberManager; this.statistics = statistics; this.executionState = new ExecutionStorage(); @@ -109,7 +106,7 @@ export class Executor { } 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); if (actionHandler) { this.statistics.incrementAction(timestamp()); diff --git a/src/Grabber/BuildingContractGrabber.ts b/src/Grabber/BuildingContractGrabber.ts index 8228c52..a24b91b 100644 --- a/src/Grabber/BuildingContractGrabber.ts +++ b/src/Grabber/BuildingContractGrabber.ts @@ -20,7 +20,7 @@ export class BuildingContractGrabber extends Grabber { const contract = grabContractResources(); - this.controller.updateResources(contract, { + this.taskCollection.updateResources(contract, { type: ContractType.UpgradeBuilding, buildId: building.buildId, }); diff --git a/src/Grabber/ForgePageGrabber.ts b/src/Grabber/ForgePageGrabber.ts index 131db7c..c5836a4 100644 --- a/src/Grabber/ForgePageGrabber.ts +++ b/src/Grabber/ForgePageGrabber.ts @@ -20,7 +20,7 @@ export class ForgePageGrabber extends Grabber { const contracts = grabImprovementContracts(); for (let { resources, unitId } of contracts) { - this.controller.updateResources(resources, { + this.taskCollection.updateResources(resources, { type: ContractType.ImproveTrooper, buildId, unitId, @@ -29,8 +29,7 @@ export class ForgePageGrabber extends Grabber { } private grabTimer(): void { - const storage = this.controller.getStorage(); const seconds = grabRemainingSeconds(); - storage.storeQueueTaskEnding(ProductionQueue.UpgradeUnit, seconds ? seconds + timestamp() : 0); + this.storage.storeQueueTaskEnding(ProductionQueue.UpgradeUnit, seconds ? seconds + timestamp() : 0); } } diff --git a/src/Grabber/Grabber.ts b/src/Grabber/Grabber.ts index db0adc1..8919e93 100644 --- a/src/Grabber/Grabber.ts +++ b/src/Grabber/Grabber.ts @@ -1,10 +1,13 @@ -import { VillageController } from '../VillageController'; +import { VillageTaskCollection } from '../VillageTaskCollection'; +import { VillageStorage } from '../Storage/VillageStorage'; export abstract class Grabber { - protected controller: VillageController; + protected taskCollection: VillageTaskCollection; + protected storage: VillageStorage; - constructor(controller: VillageController) { - this.controller = controller; + constructor(taskCollection: VillageTaskCollection, storage: VillageStorage) { + this.taskCollection = taskCollection; + this.storage = storage; } abstract grab(): void; diff --git a/src/Grabber/GrabberManager.ts b/src/Grabber/GrabberManager.ts index 0b9632d..ac272ff 100644 --- a/src/Grabber/GrabberManager.ts +++ b/src/Grabber/GrabberManager.ts @@ -6,12 +6,12 @@ import { MarketPageGrabber } from './MarketPageGrabber'; import { BuildingContractGrabber } from './BuildingContractGrabber'; import { ForgePageGrabber } from './ForgePageGrabber'; import { GuildHallPageGrabber } from './GuildHallPageGrabber'; -import { VillageControllerFactory } from '../VillageControllerFactory'; +import { VillageFactory } from '../VillageFactory'; export class GrabberManager { - private factory: VillageControllerFactory; + private factory: VillageFactory; - constructor(factory: VillageControllerFactory) { + constructor(factory: VillageFactory) { this.factory = factory; } @@ -23,15 +23,16 @@ export class GrabberManager { } private createGrabbers(): Array { - const controller = this.factory.getActive(); + const storage = this.factory.createStorageForActiveVillage(); + const taskCollection = this.factory.createTaskCollectionForActiveVillage(); const grabbers: Array = []; - grabbers.push(new VillageResourceGrabber(controller)); - grabbers.push(new VillageOverviewPageGrabber(controller)); - grabbers.push(new HeroPageGrabber(controller)); - grabbers.push(new MarketPageGrabber(controller)); - grabbers.push(new BuildingContractGrabber(controller)); - grabbers.push(new ForgePageGrabber(controller)); - grabbers.push(new GuildHallPageGrabber(controller)); + grabbers.push(new VillageResourceGrabber(taskCollection, storage)); + grabbers.push(new VillageOverviewPageGrabber(taskCollection, storage)); + grabbers.push(new HeroPageGrabber(taskCollection, storage)); + grabbers.push(new MarketPageGrabber(taskCollection, storage)); + grabbers.push(new BuildingContractGrabber(taskCollection, storage)); + grabbers.push(new ForgePageGrabber(taskCollection, storage)); + grabbers.push(new GuildHallPageGrabber(taskCollection, storage)); return grabbers; } } diff --git a/src/Grabber/GuildHallPageGrabber.ts b/src/Grabber/GuildHallPageGrabber.ts index e11fa05..60663d8 100644 --- a/src/Grabber/GuildHallPageGrabber.ts +++ b/src/Grabber/GuildHallPageGrabber.ts @@ -11,7 +11,6 @@ export class GuildHallPageGrabber extends Grabber { } const seconds = grabRemainingSeconds(); - const storage = this.controller.getStorage(); - storage.storeQueueTaskEnding(ProductionQueue.Celebration, seconds ? seconds + timestamp() : 0); + this.storage.storeQueueTaskEnding(ProductionQueue.Celebration, seconds ? seconds + timestamp() : 0); } } diff --git a/src/Grabber/MarketPageGrabber.ts b/src/Grabber/MarketPageGrabber.ts index 973cab5..7d000f9 100644 --- a/src/Grabber/MarketPageGrabber.ts +++ b/src/Grabber/MarketPageGrabber.ts @@ -8,7 +8,6 @@ export class MarketPageGrabber extends Grabber { return; } - const storage = this.controller.getStorage(); - storage.storeIncomingMerchants(grabIncomingMerchants()); + this.storage.storeIncomingMerchants(grabIncomingMerchants()); } } diff --git a/src/Grabber/VillageOverviewPageGrabber.ts b/src/Grabber/VillageOverviewPageGrabber.ts index d9d18ed..ee1ebb4 100644 --- a/src/Grabber/VillageOverviewPageGrabber.ts +++ b/src/Grabber/VillageOverviewPageGrabber.ts @@ -12,13 +12,12 @@ export class VillageOverviewPageGrabber extends Grabber { return; } - const storage = this.controller.getStorage(); - storage.storeResourcesPerformance(grabResourcesPerformance()); - storage.storeBuildingQueueInfo(this.grabBuildingQueueInfoOrDefault()); + this.storage.storeResourcesPerformance(grabResourcesPerformance()); + this.storage.storeBuildingQueueInfo(this.grabBuildingQueueInfoOrDefault()); const buildingQueueInfo = this.grabBuildingQueueInfoOrDefault(); const buildingEndTime = buildingQueueInfo.seconds ? buildingQueueInfo.seconds + timestamp() : 0; - storage.storeQueueTaskEnding(ProductionQueue.Building, buildingEndTime); + this.storage.storeQueueTaskEnding(ProductionQueue.Building, buildingEndTime); } private grabBuildingQueueInfoOrDefault() { diff --git a/src/Grabber/VillageResourceGrabber.ts b/src/Grabber/VillageResourceGrabber.ts index 5fa6f81..7f88ead 100644 --- a/src/Grabber/VillageResourceGrabber.ts +++ b/src/Grabber/VillageResourceGrabber.ts @@ -3,8 +3,7 @@ import { grabVillageResources, grabVillageResourceStorage } from '../Page/Resour export class VillageResourceGrabber extends Grabber { grab(): void { - const storage = this.controller.getStorage(); - storage.storeResources(grabVillageResources()); - storage.storeResourceStorage(grabVillageResourceStorage()); + this.storage.storeResources(grabVillageResources()); + this.storage.storeResourceStorage(grabVillageResourceStorage()); } } diff --git a/src/Scheduler.ts b/src/Scheduler.ts index efb977b..0b36091 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -12,8 +12,9 @@ import { ImmutableTaskList, Task, TaskId, uniqTaskId, withTime } from './Queue/T import { MARKET_ID } from './Core/Buildings'; import { VillageRepositoryInterface } from './VillageRepository'; import { isProductionTask } from './Core/ProductionQueue'; -import { VillageControllerFactory } from './VillageControllerFactory'; +import { VillageFactory } from './VillageFactory'; import { RunVillageProductionTask } from './Task/RunVillageProductionTask'; +import { VillageNotFound } from './Errors'; export interface NextExecution { task?: Task; @@ -24,14 +25,14 @@ export class Scheduler { private taskQueue: TaskQueue; private actionQueue: ActionQueue; private villageRepository: VillageRepositoryInterface; - private villageControllerFactory: VillageControllerFactory; + private villageControllerFactory: VillageFactory; private logger: Logger; constructor( taskQueue: TaskQueue, actionQueue: ActionQueue, villageRepository: VillageRepositoryInterface, - villageControllerFactory: VillageControllerFactory, + villageControllerFactory: VillageFactory, logger: Logger ) { this.taskQueue = taskQueue; @@ -96,13 +97,13 @@ export class Scheduler { private replaceTask(task: Task): Task | undefined { if (task.name === RunVillageProductionTask.name && task.args.villageId) { const villageId = task.args.villageId; - const controller = this.villageControllerFactory.create(villageId); + const controller = this.villageControllerFactory.createController(villageId); const villageTask = controller.getReadyProductionTask(); if (villageTask) { this.removeTask(task.id); const newTask = new Task(villageTask.id, 0, villageTask.name, { ...villageTask.args, - villageId: controller.villageId, + villageId: controller.getVillageId(), }); this.taskQueue.add(newTask); return newTask; @@ -113,7 +114,7 @@ export class Scheduler { scheduleTask(name: string, args: Args, ts?: number | undefined): void { if (isProductionTask(name) && args.villageId) { - const controller = this.villageControllerFactory.create(args.villageId); + const controller = this.villageControllerFactory.createController(args.villageId); controller.addTask(name, args); } else { this.logger.info('Schedule task', name, args, ts); @@ -143,7 +144,7 @@ export class Scheduler { const task = this.taskQueue.findById(taskId); const villageId = task ? task.args.villageId : undefined; if (villageId) { - const controller = this.villageControllerFactory.create(villageId); + const controller = this.villageControllerFactory.createController(villageId); controller.removeTask(taskId); } this.removeTask(taskId); @@ -156,7 +157,7 @@ export class Scheduler { } 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); this.removeTask(taskId); } else { @@ -186,7 +187,7 @@ export class Scheduler { this.dropResourceTransferTasks(fromVillageId, toVillageId); const village = this.villageRepository.all().find(v => v.id === toVillageId); if (!village) { - throw new Error('No village'); + throw new VillageNotFound(`Village ${toVillageId} not found`); } this.scheduleTask(SendResourcesTask.name, { villageId: fromVillageId, diff --git a/src/Storage/LogStorage.ts b/src/Storage/LogStorage.ts index 95fc124..881b76d 100644 --- a/src/Storage/LogStorage.ts +++ b/src/Storage/LogStorage.ts @@ -1,5 +1,4 @@ import { DataStorage } from '../DataStorage'; -import { ActionStatistics, StatisticsStorageInterface } from '../Statistics'; import { LogStorageInterface, StorageLogRecord } from '../Logger'; const NAMESPACE = 'logs.v1'; diff --git a/src/Storage/VillageStorage.ts b/src/Storage/VillageStorage.ts index 0a9e4ed..3c7fd89 100644 --- a/src/Storage/VillageStorage.ts +++ b/src/Storage/VillageStorage.ts @@ -112,41 +112,7 @@ export class VillageStorage { }); } - addTask(task: 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): void { + storeTaskList(tasks: Array): void { this.storage.set(TASK_LIST_KEY, tasks); } } diff --git a/src/VillageController.ts b/src/VillageController.ts index feab091..3b45118 100644 --- a/src/VillageController.ts +++ b/src/VillageController.ts @@ -1,93 +1,36 @@ -import { Task, TaskId, uniqTaskId, withResources, withTime } from './Queue/TaskProvider'; -import { VillageStorage } from './Storage/VillageStorage'; +import { VillageTaskCollection } from './VillageTaskCollection'; +import { Task, TaskId } from './Queue/TaskProvider'; import { Args } from './Queue/Args'; -import { isProductionTask, ProductionQueue, ProductionQueueTypes } from './Core/ProductionQueue'; -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'; +import { VillageState } from './VillageState'; export class VillageController { - private readonly _villageId: number; - private readonly _storage: VillageStorage; + private readonly villageId: number; + private taskCollection: VillageTaskCollection; + private readonly state: VillageState; - constructor(villageId: number, storage: VillageStorage) { - this._villageId = villageId; - this._storage = storage; + constructor(villageId: number, taskCollection: VillageTaskCollection, state: VillageState) { + this.villageId = villageId; + this.taskCollection = taskCollection; + this.state = state; } - get 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 { - return this._storage.getTasks(); - } - - removeTask(taskId: TaskId) { - this._storage.removeTasks(t => t.id === taskId); - } - - getTasksInProductionQueue(queue: ProductionQueue): Array { - return this._storage.getTasks().filter(task => getProductionQueue(task.name) === queue); + getVillageId() { + return this.villageId; } getReadyProductionTask(): Task | undefined { - let sortedTasks: Array = []; - for (let queue of ProductionQueueTypes) { - const tasks = this.getTasksInProductionQueue(queue); - sortedTasks = sortedTasks.concat(tasks); - } - return sortedTasks.shift(); + return this.taskCollection.getReadyProductionTask(); + } + + addTask(name: string, args: Args) { + this.taskCollection.addTask(name, args); + } + + removeTask(taskId: TaskId) { + this.taskCollection.removeTask(taskId); } postponeTask(taskId: TaskId, seconds: number) { - const modifyTime = withTime(timestamp() + 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()); + this.taskCollection.postponeTask(taskId, seconds); } } diff --git a/src/VillageControllerFactory.ts b/src/VillageControllerFactory.ts deleted file mode 100644 index da46493..0000000 --- a/src/VillageControllerFactory.ts +++ /dev/null @@ -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); - } -} diff --git a/src/VillageFactory.ts b/src/VillageFactory.ts new file mode 100644 index 0000000..fce1435 --- /dev/null +++ b/src/VillageFactory.ts @@ -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 { + 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)); + } +} diff --git a/src/VillageState.ts b/src/VillageState.ts index a67587d..c3c282e 100644 --- a/src/VillageState.ts +++ b/src/VillageState.ts @@ -7,8 +7,7 @@ import { VillageNotFound } from './Errors'; import { ProductionQueue, ProductionQueueTypes } from './Core/ProductionQueue'; import { Task } from './Queue/TaskProvider'; import { timestamp } from './utils'; -import { VillageControllerFactory } from './VillageControllerFactory'; -import { VillageController } from './VillageController'; +import { VillageTaskCollection } from './VillageTaskCollection'; interface VillageStorageState { resources: Resources; @@ -134,12 +133,12 @@ function taskResourceReducer(resources: Resources, task: Task) { function createProductionQueueState( queue: ProductionQueue, - controller: VillageController + storage: VillageStorage, + taskCollection: VillageTaskCollection ): VillageProductionQueueState { - const storage = controller.getStorage(); const resources = storage.getResources(); 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 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 } = {}; for (let queue of ProductionQueueTypes) { - result[queue] = createProductionQueueState(queue, controller); + result[queue] = createProductionQueueState(queue, storage, taskCollection); } return result; } -function calcFrontierResources(controller: VillageController): Resources { +function calcFrontierResources(taskCollection: VillageTaskCollection): Resources { let result = Resources.zero(); 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()); result = result.add(firstTaskResources); } return result; } -function createVillageOwnState(village: Village, controller: VillageController): VillageOwnState { - const storage = controller.getStorage(); +function createVillageOwnState( + village: Village, + storage: VillageStorage, + taskCollection: VillageTaskCollection +): VillageOwnState { const resources = storage.getResources(); const resourceStorage = storage.getResourceStorage(); const performance = storage.getResourcesPerformance(); const buildQueueInfo = storage.getBuildingQueueInfo(); - const requiredResources = controller.getVillageRequiredResources(); - const frontierResources = calcFrontierResources(controller); - const totalRequiredResources = controller.getTotalVillageRequiredResources(); + const requiredResources = taskCollection.getVillageRequiredResources(); + const frontierResources = calcFrontierResources(taskCollection); + const totalRequiredResources = taskCollection.getTotalVillageRequiredResources(); return { id: village.id, @@ -196,18 +198,22 @@ function createVillageOwnState(village: Village, controller: VillageController): buildRemainingSeconds: buildQueueInfo.seconds, incomingResources: calcIncomingResources(storage), settings: storage.getSettings(), - queues: createAllProductionQueueStates(controller), + queues: createAllProductionQueueStates(storage, taskCollection), }; } function createVillageOwnStates( villages: Array, - villageControllerFactory: VillageControllerFactory + storageFactory: VillageStorageFactory, + taskCollectionFactory: VillageTaskCollectionFactory ): VillageOwnStateDictionary { const result: VillageOwnStateDictionary = {}; for (let village of villages) { - const villageController = villageControllerFactory.create(village.id); - result[village.id] = createVillageOwnState(village, villageController); + result[village.id] = createVillageOwnState( + village, + storageFactory(village.id), + taskCollectionFactory(village.id) + ); } return result; } @@ -226,27 +232,42 @@ function createVillageState(state: VillageOwnState, ownStates: VillageOwnStateDi function getVillageStates( villages: Array, - villageControllerFactory: VillageControllerFactory + storageFactory: VillageStorageFactory, + taskCollectionFactory: VillageTaskCollectionFactory ): Array { - const ownStates = createVillageOwnStates(villages, villageControllerFactory); + const ownStates = createVillageOwnStates(villages, storageFactory, taskCollectionFactory); return villages.map(village => createVillageState(ownStates[village.id], ownStates)); } -export class VillageStateRepository { - private villageRepository: VillageRepositoryInterface; - private villageControllerFactory: VillageControllerFactory; +interface VillageStorageFactory { + (villageId: number): VillageStorage; +} - 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.villageControllerFactory = villageControllerFactory; + this.storageFactory = storageFactory; + this.taskCollectionFactory = taskCollectionFactory; } getAllVillageStates(): Array { - return getVillageStates(this.villageRepository.all(), this.villageControllerFactory); + return getVillageStates(this.villageRepository.all(), this.storageFactory, this.taskCollectionFactory); } 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); if (!needle) { throw new VillageNotFound(`Village ${villageId} not found`); diff --git a/src/VillageTaskCollection.ts b/src/VillageTaskCollection.ts new file mode 100644 index 0000000..68c8c25 --- /dev/null +++ b/src/VillageTaskCollection.ts @@ -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 { + 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 { + return this.storage.getTasks().filter(task => getProductionQueue(task.name) === queue); + } + + getReadyProductionTask(): Task | undefined { + let sortedTasks: Array = []; + 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()); + } +}