Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import pathlib = require('path');
- import fs = require('fs');
- import _ = require('lodash');
- const fetch = require("node-fetch");
- var xpath = require('xpath'),
- dom = require('xmldom').DOMParser;
- const TRACKED_GAMES: string[] = [
- '1-S2-1-4632373' // ZC CE
- ];
- export const http = <T>(req: RequestInfo): Promise<T> =>
- fetch(req).then((res: any) => res.json());
- const folderUpName = (path: string) => {
- return pathlib.normalize(pathlib.dirname(path).split(pathlib.sep).pop())
- }
- interface Event {
- type: string;
- }
- class AsyncQueue<T> {
- private _promises: Promise<T>[] = [];
- private _resolvers: ((t: T) => void)[] = [];
- constructor() {}
- private _addPromise() {
- this._promises.push(
- new Promise(resolve => {
- this._resolvers.push(resolve)
- })
- );
- }
- public enqueue(item: T) {
- if (!this._resolvers.length) {
- this._addPromise();
- }
- const resolve = this._resolvers.shift();
- resolve(item);
- }
- public dequeue() {
- if (!this._promises.length) {
- this._addPromise();
- }
- const promise = this._promises.shift();
- return promise;
- }
- public isEmpty() {
- return !this._promises.length;
- }
- }
- class EventCache<T> extends Array<T> {
- maxSize: number = 10;
- constructor(items?: Array<T>, maxSize: number = 10) {
- super(...items);
- this.maxSize = maxSize;
- Object.setPrototypeOf(this, EventCache.prototype);
- }
- public add(o: T) {
- this.push(o);
- if (this.length > this.maxSize) {
- this.shift();
- }
- }
- public containsEvent(o: T) {
- // TODO: Expensive. How to best
- // check for membership of two JSON objects?
- for (let e of this) {
- if (_.isEqual(o, e)) {
- return true;
- }
- }
- return false;
- }
- }
- class Game {
- path: string;
- eventFile: string;
- id: string;
- tracked: boolean;
- profileId: string;
- constructor(path: string) {
- this.path = path;
- this.id = path.substring(path.lastIndexOf(pathlib.sep)+1);
- this.eventFile = pathlib.normalize(this.path + pathlib.sep + 'Events.SC2Bank')
- this.tracked = TRACKED_GAMES.includes(this.id);
- this.profileId = folderUpName(pathlib.dirname(this.path));
- }
- public lastEvent(path: string = this.eventFile) {
- const data = fs.readFileSync(path, {encoding: 'utf8'}).toString();
- var re_pattern = '';
- var document = new dom().parseFromString(data, 'text/xml');
- var search = "//Key/@name[not(. < //Key/@name)]";
- var key = xpath.select1(search, document).value;
- search = `//Key[@name='${key}']/Value/@text`
- var result = xpath.select1(search, document).value;
- result = result.replace(/,(?=\s+[]}])/g, "").replace(/`/g, '"');
- result = JSON.parse(result);
- // get game id and annotate
- var gameId = xpath.select1('//Section/@name', document).value;
- result.game_id = Number(gameId);
- return result
- }
- }
- class Client {
- path: string;
- games: Game[] = [];
- queue: AsyncQueue<Event> = new AsyncQueue();
- chokidar: Object;
- eventCache: EventCache<Event> = new EventCache<Event>(); // To keep out duplicates
- constructor(path: string) {
- this.path = path;
- var listeners = {
- directories: (root: string, stats: any, next: any) => {
- for (let stat of stats) {
- if (stat.type != 'directory')
- continue;
- var statspath: string = pathlib.normalize(root + pathlib.sep + stat.name);
- if (this._isGameDir(statspath)) {
- this.games.push(new Game(statspath));
- }
- }
- next();
- }
- }
- require('walk').walkSync(path, {listeners: listeners});
- this.dispatcher();
- }
- private _isGameDir(path: string) {
- return folderUpName(path).toLowerCase() == "banks";
- }
- public gameFromEventPath(path: string) {
- for (let g of this.games) {
- if (g.eventFile == path) { return g }
- }
- }
- public trackedGames() {
- var container = [];
- for (let g of this.games) {
- if (g.tracked) {
- container.push(g)
- }
- }
- return container;
- }
- public async dispatcher() {
- // Dispatches events in the queue to the API
- const actions: any = {
- match_start: matchStart,
- player_leave: playerLeave,
- }
- while (true) {
- var item = await this.queue.dequeue();
- if (this.eventCache.containsEvent(item)) {
- // TODO: Fix this madness. This is an expensive
- // bruteforce way to compare two JSON objects.
- // This is because most watchers duplicate change events.
- continue;
- }
- if (actions.hasOwnProperty(item.type)) {
- actions[item.type](item);
- }
- this.eventCache.add(item);
- }
- }
- public async watch() {
- const chokidar = require('chokidar');
- var watcher = chokidar.watch(this.path);
- watcher
- .on('change', (path: string) => {
- var game = this.gameFromEventPath(path);
- if (game) {
- this.queue.enqueue(game.lastEvent());
- }
- });
- }
- }
- const matchStart = async (payload: any) => {
- fetch('http://localhost:8000/api/automatch/', {
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- method: 'post',
- body: JSON.stringify(payload),
- }).then((res: any) => {
- console.log('api result: ' + res.status)
- })
- }
- const playerLeave = (payload: Event) => {
- return;
- }
- export default Client;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement