diff --git a/src/Action/ActionController.ts b/src/Action/ActionController.ts index 94a6c5a..758e1cc 100644 --- a/src/Action/ActionController.ts +++ b/src/Action/ActionController.ts @@ -1,6 +1,6 @@ import { Args } from '../Common'; import { Task } from '../Storage/TaskQueue'; -import { GameState } from '../Storage/GameState'; +import { DataStorage } from '../Storage/DataStorage'; import { Scheduler } from '../Scheduler'; const actionMap: { [name: string]: Function | undefined } = {}; @@ -9,20 +9,18 @@ export function registerAction(constructor: Function) { actionMap[constructor.name] = constructor; } -export function createAction(name: string, state: GameState, scheduler: Scheduler): ActionController | undefined { +export function createAction(name: string, scheduler: Scheduler): ActionController | undefined { const storedFunction = actionMap[name]; if (storedFunction === undefined) { return undefined; } const constructor = (storedFunction as unknown) as typeof ActionController; - return new constructor(state, scheduler); + return new constructor(scheduler); } export class ActionController { - protected state: GameState; protected scheduler: Scheduler; - constructor(state: GameState, scheduler: Scheduler) { - this.state = state; + constructor(scheduler: Scheduler) { this.scheduler = scheduler; } diff --git a/src/Action/BalanceHeroResourcesAction.ts b/src/Action/BalanceHeroResourcesAction.ts index 5942a5a..98c8813 100644 --- a/src/Action/BalanceHeroResourcesAction.ts +++ b/src/Action/BalanceHeroResourcesAction.ts @@ -8,10 +8,10 @@ import { HeroAllResources } from '../Game'; @registerAction export class BalanceHeroResourcesAction extends ActionController { async run(args: Args, task: Task): Promise { - const resources = grabResources().asList(); + const resourcesAsList = grabResources().asList(); const currentType = grabCurrentHeroResource(); - const sorted = resources.sort((x, y) => x.value - y.value); + const sorted = resourcesAsList.sort((x, y) => x.value - y.value); const min = sorted[0]; const max = sorted[sorted.length - 1]; const delta = max.value - min.value; diff --git a/src/Action/GrabHeroAttributesAction.ts b/src/Action/GrabHeroAttributesAction.ts index 0dc651e..59736b1 100644 --- a/src/Action/GrabHeroAttributesAction.ts +++ b/src/Action/GrabHeroAttributesAction.ts @@ -16,6 +16,6 @@ export class GrabHeroAttributesAction extends ActionController { let normalized = text.replace(/[^0-9]/g, ''); const value = getNumber(normalized); - this.state.set('hero', { health: value }); + // this.state.set('hero', { health: value }); } } diff --git a/src/Action/SendOnAdventureAction.ts b/src/Action/SendOnAdventureAction.ts index 8c5e6e9..da3edd3 100644 --- a/src/Action/SendOnAdventureAction.ts +++ b/src/Action/SendOnAdventureAction.ts @@ -28,7 +28,7 @@ export class SendOnAdventureAction extends ActionController { adventures.sort((x, y) => x.level - y.level); const easiest = adventures.shift(); - const hero = this.state.get('hero') || {}; + const hero = { health: 0 }; console.log('EASIEST', easiest); console.log('HERO', hero); diff --git a/src/Action/StoreVillageState.ts b/src/Action/StoreVillageState.ts new file mode 100644 index 0000000..e96bcac --- /dev/null +++ b/src/Action/StoreVillageState.ts @@ -0,0 +1,16 @@ +import { ActionController, registerAction } from './ActionController'; +import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import { grabResources } from '../Page/ResourcesBlock'; +import { grabActiveVillageId } from '../Page/VillageBlock'; +import { VillageState } from '../Storage/VillageState'; + +@registerAction +export class StoreVillageState extends ActionController { + async run(args: Args, task: Task): Promise { + const villageId = grabActiveVillageId(); + const resources = grabResources(); + const state = new VillageState(villageId); + state.storeResources(resources); + } +} diff --git a/src/Dashboard/Components/DashboardApp.vue b/src/Dashboard/Components/DashboardApp.vue index 0662fd7..89a0cad 100644 --- a/src/Dashboard/Components/DashboardApp.vue +++ b/src/Dashboard/Components/DashboardApp.vue @@ -5,6 +5,8 @@
+
+ @@ -13,14 +15,18 @@ import Header from './Header'; import TaskList from './TaskList'; import QuickActions from './QuickActions'; +import VillageStateList from './VillageStateList'; export default { components: { hdr: Header, 'task-list': TaskList, 'quick-actions': QuickActions, + 'village-state-list': VillageStateList, }, data() { - return {}; + return { + shared: this.$root.$data, + }; }, }; diff --git a/src/Dashboard/Components/VillageStateList.vue b/src/Dashboard/Components/VillageStateList.vue new file mode 100644 index 0000000..7cc6136 --- /dev/null +++ b/src/Dashboard/Components/VillageStateList.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/src/Dashboard/Dashboard.ts b/src/Dashboard/Dashboard.ts index 0a63fdd..269a3fe 100644 --- a/src/Dashboard/Dashboard.ts +++ b/src/Dashboard/Dashboard.ts @@ -3,7 +3,7 @@ import { getNumber, toNumber, uniqId, waitForLoad } from '../utils'; import { Scheduler } from '../Scheduler'; import { BuildPage } from '../Page/BuildPage'; import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; -import { grabActiveVillage, grabActiveVillageId } from '../Page/VillageBlock'; +import { grabActiveVillage, grabActiveVillageId, grabVillageList } from '../Page/VillageBlock'; import { grabResourceDeposits, onResourceSlotCtrlClick, @@ -14,6 +14,8 @@ import Vue from 'vue'; import DashboardApp from './Components/DashboardApp.vue'; import { ResourcesToLevel } from '../Task/ResourcesToLevel'; import { Logger } from '../Logger'; +import { Resources } from '../Game'; +import { VillageState } from '../Storage/VillageState'; interface QuickAction { label: string; @@ -45,6 +47,7 @@ export class Dashboard { const state = { name: 'Dashboard', village: grabActiveVillage(), + villages: grabVillageList(), version: this.version, taskList: this.scheduler.getTaskItems(), quickActions: quickActions, @@ -57,6 +60,11 @@ export class Dashboard { scheduler.removeTask(taskId); this.taskList = scheduler.getTaskItems(); }, + + getVillageResources(villageId): Resources { + const state = new VillageState(villageId); + return state.getResources(); + }, }; setInterval(() => state.refreshTasks(), 1000); diff --git a/src/ModeDetector.ts b/src/ModeDetector.ts index a50ed9f..3dc0cfa 100644 --- a/src/ModeDetector.ts +++ b/src/ModeDetector.ts @@ -2,6 +2,7 @@ import * as URLParse from 'url-parse'; const SESSION_KEY = 'travian_automation_mode'; const SESSION_VALUE = 'command_execution'; +const MODE_PARAMETER_NAME = 'auto-management'; export class ModeDetector { isAuto(): boolean { @@ -14,7 +15,7 @@ export class ModeDetector { private isAutoByLocation(): boolean { const p = new URLParse(window.location.href, true); - return p.query['auto-management'] !== undefined; + return p.query[MODE_PARAMETER_NAME] !== undefined; } private isAutoBySession(): boolean { diff --git a/src/Scheduler.ts b/src/Scheduler.ts index f31ccc8..8049892 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -8,23 +8,21 @@ import { TaskQueueRenderer } from './TaskQueueRenderer'; import { createAction } from './Action/ActionController'; import { createTask } from './Task/TaskController'; import { SendOnAdventureTask } from './Task/SendOnAdventureTask'; -import { GameState } from './Storage/GameState'; import { BalanceHeroResourcesTask } from './Task/BalanceHeroResourcesTask'; import { Logger } from './Logger'; import { BuildBuildingTask } from './Task/BuildBuildingTask'; +import { GrabVillageState } from './Task/GrabVillageState'; export class Scheduler { private readonly version: string; private taskQueue: TaskQueue; private actionQueue: ActionQueue; - private gameState: GameState; private logger: Logger; constructor(version: string) { this.version = version; this.taskQueue = new TaskQueue(); this.actionQueue = new ActionQueue(); - this.gameState = new GameState(); this.logger = new Logger(this.constructor.name); } @@ -38,6 +36,7 @@ export class Scheduler { this.scheduleUniqTask(3600, SendOnAdventureTask.name); this.scheduleUniqTask(1200, BalanceHeroResourcesTask.name); + this.scheduleUniqTask(300, GrabVillageState.name); while (true) { await this.doTaskProcessingStep(); @@ -52,7 +51,7 @@ export class Scheduler { private scheduleUniqTask(seconds: number, name: string, args: Args = {}) { const taskScheduler = () => { if (!this.taskQueue.hasNamed(name)) { - this.taskQueue.push(name, args, timestamp() + 10 * 60); + this.taskQueue.push(name, args, timestamp() + 5 * 60); } }; taskScheduler(); @@ -90,7 +89,7 @@ export class Scheduler { } private async processActionCommand(cmd: Command, task: Task) { - const actionController = createAction(cmd.name, this.gameState, this); + const actionController = createAction(cmd.name, this); this.logger.log('PROCESS ACTION', cmd.name, actionController); if (actionController) { await actionController.run(cmd.args, task); diff --git a/src/Storage/DataStorage.ts b/src/Storage/DataStorage.ts new file mode 100644 index 0000000..6a60e21 --- /dev/null +++ b/src/Storage/DataStorage.ts @@ -0,0 +1,43 @@ +import { Logger } from '../Logger'; + +const NAMESPACE = 'travian:v1'; + +function join(...parts: Array) { + return parts.map(p => p.replace(/[:]+$/g, '').replace(/^[:]+/g, '')).join(':'); +} + +export class DataStorage { + private readonly logger; + private readonly name: string; + + constructor(name: string) { + this.name = name; + this.logger = new Logger(this.constructor.name); + } + + get(key: string): any { + const fullKey = join(NAMESPACE, this.name, key); + this.logger.log('GET', key); + try { + const serialized = localStorage.getItem(fullKey); + return JSON.parse(serialized || '"null"'); + } catch (e) { + if (e instanceof SyntaxError) { + return null; + } + throw e; + } + } + + has(key: string): boolean { + const fullKey = join(NAMESPACE, this.name, key); + return localStorage.getItem(fullKey) !== null; + } + + set(key: string, value: any) { + const fullKey = join(NAMESPACE, this.name, key); + let serialized = JSON.stringify(value); + this.logger.log('SET', fullKey, serialized); + localStorage.setItem(fullKey, serialized); + } +} diff --git a/src/Storage/GameState.ts b/src/Storage/GameState.ts deleted file mode 100644 index 7c43c51..0000000 --- a/src/Storage/GameState.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Logger } from '../Logger'; - -const NAMESPACE = 'game_state:v1'; - -function join(x: string, y: string) { - return x.replace(/[:]+$/g, '') + ':' + y.replace(/^[:]+/g, ''); -} - -export class GameState { - private readonly logger; - - constructor() { - this.logger = new Logger(this.constructor.name); - } - - get(key: string): any { - this.logger.log('GET', key); - try { - const serialized = localStorage.getItem(join(NAMESPACE, key)); - return JSON.parse(serialized || 'null'); - } catch (e) { - if (e instanceof SyntaxError) { - return null; - } - throw e; - } - } - - has(key: string): boolean { - return localStorage.getItem(join(NAMESPACE, key)) === null; - } - - set(key: string, value: any) { - let serialized = JSON.stringify(value); - this.logger.log('SET', key, serialized); - localStorage.setItem(join(NAMESPACE, key), serialized); - } -} diff --git a/src/Storage/VillageState.ts b/src/Storage/VillageState.ts new file mode 100644 index 0000000..fd98e55 --- /dev/null +++ b/src/Storage/VillageState.ts @@ -0,0 +1,19 @@ +import { DataStorage } from './DataStorage'; +import { Resources } from '../Game'; + +export class VillageState { + private storage: DataStorage; + constructor(villageId: number) { + this.storage = new DataStorage(`village.${villageId}`); + } + + storeResources(resources: Resources) { + this.storage.set('res', resources); + } + + getResources(): Resources { + let plain = this.storage.get('res'); + let res = new Resources(0, 0, 0, 0, 0, 0); + return Object.assign(res, plain) as Resources; + } +} diff --git a/src/Task/GrabVillageState.ts b/src/Task/GrabVillageState.ts new file mode 100644 index 0000000..f86709b --- /dev/null +++ b/src/Task/GrabVillageState.ts @@ -0,0 +1,32 @@ +import { Args, Command } from '../Common'; +import { CompleteTaskAction } from '../Action/CompleteTaskAction'; +import { GoToPageAction } from '../Action/GoToPageAction'; +import { path } from '../utils'; +import { Task } from '../Storage/TaskQueue'; +import { TaskController, registerTask } from './TaskController'; +import { grabVillageList } from '../Page/VillageBlock'; +import { StoreVillageState } from '../Action/StoreVillageState'; + +@registerTask +export class GrabVillageState extends TaskController { + async run(task: Task) { + const args: Args = { ...task.args, taskId: task.id }; + + const actions: Array = []; + + const villages = grabVillageList(); + for (let village of villages) { + actions.push( + new Command(GoToPageAction.name, { + ...args, + path: path('/dorf1.php', { newdid: village.id }), + }) + ); + actions.push(new Command(StoreVillageState.name, args)); + } + + actions.push(new Command(CompleteTaskAction.name, args)); + + this.scheduler.scheduleActions(actions); + } +}