Add village resources view to dashboard

This commit is contained in:
Anton Vakhrushev 2020-04-16 13:07:26 +03:00
parent 29b55158d6
commit fc75b007c8
14 changed files with 180 additions and 56 deletions

View File

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

View File

@ -8,10 +8,10 @@ import { HeroAllResources } from '../Game';
@registerAction
export class BalanceHeroResourcesAction extends ActionController {
async run(args: Args, task: Task): Promise<any> {
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;

View File

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

View File

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

View File

@ -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<any> {
const villageId = grabActiveVillageId();
const resources = grabResources();
const state = new VillageState(villageId);
state.storeResources(resources);
}
}

View File

@ -5,6 +5,8 @@
<task-list></task-list>
<hr class="separator" />
<quick-actions></quick-actions>
<hr class="separator" />
<village-state-list></village-state-list>
</div>
</main>
</template>
@ -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,
};
},
};
</script>

View File

@ -0,0 +1,40 @@
<template>
<section>
<table class="village-table">
<tr v-for="village in shared.villages" :key="village.id">
<td>{{ village.id }} - {{ village.name }}</td>
<td>Д: {{ resources(village.id).lumber }}</td>
<td>Г: {{ resources(village.id).clay }}</td>
<td>Ж: {{ resources(village.id).iron }}</td>
<td>З: {{ resources(village.id).crop }}</td>
</tr>
</table>
</section>
</template>
<script>
export default {
data() {
return {
shared: this.$root.$data,
};
},
methods: {
resources(id) {
return this.shared.getVillageResources(id);
},
},
};
</script>
<style scoped>
.village-table {
width: 100%;
border-collapse: collapse;
}
.village-table td {
border-top: 1px solid #ddd;
padding: 4px;
}
</style>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,43 @@
import { Logger } from '../Logger';
const NAMESPACE = 'travian:v1';
function join(...parts: Array<string>) {
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);
}
}

View File

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

View File

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

View File

@ -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<Command> = [];
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);
}
}