Advertisement
Guest User

Untitled

a guest
Feb 16th, 2020
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import pathlib = require('path');
  2. import fs = require('fs');
  3. import _ = require('lodash');
  4. const fetch = require("node-fetch");
  5. var xpath = require('xpath'),
  6.     dom   = require('xmldom').DOMParser;
  7. const TRACKED_GAMES: string[] = [
  8.     '1-S2-1-4632373' // ZC CE
  9. ];
  10. export const http = <T>(req: RequestInfo): Promise<T> =>
  11.   fetch(req).then((res: any) => res.json());
  12.  
  13. const folderUpName = (path: string) => {
  14.     return pathlib.normalize(pathlib.dirname(path).split(pathlib.sep).pop())
  15. }
  16. interface Event {
  17.     type: string;
  18. }
  19. class AsyncQueue<T> {
  20.     private _promises: Promise<T>[] = [];
  21.     private _resolvers: ((t: T) => void)[] = [];
  22.  
  23.     constructor() {}
  24.  
  25.     private _addPromise() {
  26.         this._promises.push(
  27.             new Promise(resolve => {
  28.                 this._resolvers.push(resolve)
  29.             })
  30.         );
  31.     }
  32.     public enqueue(item: T) {
  33.         if (!this._resolvers.length) {
  34.             this._addPromise();
  35.         }
  36.         const resolve = this._resolvers.shift();
  37.         resolve(item);
  38.     }
  39.     public dequeue() {
  40.         if (!this._promises.length) {
  41.             this._addPromise();
  42.         }
  43.         const promise = this._promises.shift();
  44.         return promise;
  45.     }
  46.     public isEmpty() {
  47.         return !this._promises.length;
  48.     }
  49. }
  50. class EventCache<T> extends Array<T> {
  51.     maxSize: number = 10;
  52.     constructor(items?: Array<T>, maxSize: number = 10) {
  53.         super(...items);
  54.         this.maxSize = maxSize;
  55.         Object.setPrototypeOf(this, EventCache.prototype);
  56.     }
  57.     public add(o: T) {
  58.         this.push(o);
  59.         if (this.length > this.maxSize) {
  60.             this.shift();
  61.         }
  62.     }
  63.     public containsEvent(o: T) {
  64.         // TODO: Expensive. How to best
  65.         // check for membership of two JSON objects?
  66.         for (let e of this) {
  67.             if (_.isEqual(o, e)) {
  68.                 return true;
  69.             }
  70.         }
  71.         return false;
  72.  
  73.     }
  74. }
  75. class Game {
  76.     path: string;
  77.     eventFile: string;
  78.     id: string;
  79.     tracked: boolean;
  80.     profileId: string;
  81.     constructor(path: string) {
  82.         this.path = path;
  83.         this.id = path.substring(path.lastIndexOf(pathlib.sep)+1);
  84.         this.eventFile = pathlib.normalize(this.path + pathlib.sep + 'Events.SC2Bank')
  85.         this.tracked = TRACKED_GAMES.includes(this.id);
  86.         this.profileId = folderUpName(pathlib.dirname(this.path));
  87.     }
  88.     public lastEvent(path: string = this.eventFile) {
  89.         const data = fs.readFileSync(path, {encoding: 'utf8'}).toString();
  90.         var re_pattern = '';
  91.         var document = new dom().parseFromString(data, 'text/xml');
  92.         var search = "//Key/@name[not(. < //Key/@name)]";
  93.         var key = xpath.select1(search, document).value;
  94.         search = `//Key[@name='${key}']/Value/@text`
  95.         var result = xpath.select1(search, document).value;
  96.         result = result.replace(/,(?=\s+[]}])/g, "").replace(/`/g, '"');
  97.         result = JSON.parse(result);
  98.         // get game id and annotate
  99.         var gameId = xpath.select1('//Section/@name', document).value;
  100.         result.game_id = Number(gameId);
  101.         return result
  102.        
  103.     }
  104. }
  105. class Client {
  106.     path: string;
  107.     games: Game[] = [];
  108.     queue: AsyncQueue<Event> = new AsyncQueue();
  109.     chokidar: Object;
  110.     eventCache: EventCache<Event> = new EventCache<Event>(); // To keep out duplicates
  111.  
  112.     constructor(path: string) {
  113.         this.path = path;
  114.         var listeners = {
  115.             directories: (root: string, stats: any, next: any) => {
  116.                 for (let stat of stats) {
  117.                     if (stat.type != 'directory')
  118.                         continue;
  119.                     var statspath: string = pathlib.normalize(root + pathlib.sep + stat.name);
  120.                     if (this._isGameDir(statspath)) {
  121.                         this.games.push(new Game(statspath));
  122.                     }
  123.                 }
  124.                 next();
  125.                 }
  126.             }
  127.         require('walk').walkSync(path, {listeners: listeners});
  128.         this.dispatcher();
  129.     }
  130.     private _isGameDir(path: string) {
  131.         return folderUpName(path).toLowerCase() == "banks";
  132.     }
  133.     public gameFromEventPath(path: string) {
  134.         for (let g of this.games) {
  135.             if (g.eventFile == path) { return g }
  136.         }
  137.     }
  138.     public trackedGames() {
  139.         var container = [];
  140.         for (let g of this.games) {
  141.             if (g.tracked) {
  142.                 container.push(g)
  143.             }
  144.         }
  145.         return container;
  146.     }
  147.     public async dispatcher() {
  148.         // Dispatches events in the queue to the API
  149.         const actions: any = {
  150.             match_start: matchStart,
  151.             player_leave: playerLeave,
  152.         }
  153.         while (true) {
  154.             var item = await this.queue.dequeue();
  155.             if (this.eventCache.containsEvent(item)) {
  156.                 // TODO: Fix this madness. This is an expensive
  157.                 // bruteforce way to compare two JSON objects.
  158.                 // This is because most watchers duplicate change events.
  159.                 continue;
  160.             }            
  161.             if (actions.hasOwnProperty(item.type)) {
  162.                 actions[item.type](item);
  163.             }
  164.             this.eventCache.add(item);
  165.  
  166.         }  
  167.     }
  168.     public async watch() {
  169.         const chokidar = require('chokidar');
  170.         var watcher = chokidar.watch(this.path);
  171.         watcher
  172.         .on('change', (path: string) => {
  173.             var game = this.gameFromEventPath(path);
  174.             if (game) {
  175.                 this.queue.enqueue(game.lastEvent());
  176.             }
  177.         });
  178.  
  179.     }
  180. }
  181. const matchStart = async (payload: any) => {
  182.     fetch('http://localhost:8000/api/automatch/', {
  183.         headers: {
  184.             'Accept': 'application/json',
  185.             'Content-Type': 'application/json'
  186.           },    
  187.         method: 'post',
  188.         body: JSON.stringify(payload),
  189.     }).then((res: any) => {
  190.         console.log('api result: ' + res.status)
  191.     })
  192.     }
  193.  
  194. const playerLeave = (payload: Event) => {
  195.     return;
  196. }
  197.  
  198. export default Client;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement