import { OverwolfService } from '../../overwolf.service' ; import { ProcessingQueue } from '../../processing-queue.service' ; export class MindVisionOperationFacade < T > { private processingQueue = new ProcessingQueue < InternalCall < T > > ( ( eventQueue ) => this . processQueue ( eventQueue ) , 1000 , this . serviceName ) ; constructor ( private ow , private serviceName , private mindVisionOperation , public emptyCheck , private transformer , private numberOfRetries = 3 , private delay = 3000 , private resetMindvisionIfEmpty = null ) { } private async processQueue ( eventQueue ) { return new Promise < readonly InternalCall < T > [ ] > ( ( resolve ) => { try { const firstEvent = eventQueue [ 0 ] ; firstEvent . apply ( firstEvent . retriesLeft , ( returnValue , retriesLeft ) => { if ( ! returnValue && retriesLeft > 0 ) { resolve ( [ Object . assign ( firstEvent , { retriesLeft : firstEvent . retriesLeft - 1 } as InternalCall < T > ) , ... eventQueue . slice ( 1 ) , ] ) ; return ; } for ( let i = 0 ; i < eventQueue . length ; i ++ ) { const event = eventQueue [ i ] ; event . resolve ( returnValue ) ; if ( i === eventQueue . length - 1 ) { resolve ( [ ] ) ; } } } ) ; } catch ( e ) { this . error ( 'issue while processing queue' , e ) ; resolve ( [ ] ) ; } } ) ; } public async call ( numberOfRetries ? , forceReset = false , input = null , timeoutMs = 30000 ) { if ( ! ( await this . ow . inGame ( ) ) ) { return null ; } return Promise . race ( [ new Promise < T > ( ( resolve ) => { this . processingQueue . enqueue ( { retriesLeft : numberOfRetries ?? this . numberOfRetries , resolve : resolve , apply : ( left , callback ) => this . callInternal ( ( returnValue , left ) => { callback ( returnValue , left ) ; } , input , left , forceReset ) , } ) ; } ) , new Promise < T > ( ( resolve ) => { setTimeout ( ( ) => { resolve ( null ) ; } , timeoutMs ) ; } ) , ] ) ; } private async callInternal ( callback , input , retriesLeft , forceReset = false ) { if ( ! forceReset && retriesLeft <= 0 ) { this . log ( 'not getting any value' , retriesLeft , forceReset ) ; callback ( null , retriesLeft ) ; return ; } if ( ! ( await this . ow . inGame ( ) ) ) { this . log ( 'not in game' , retriesLeft , forceReset ) ; callback ( null , 0 ) ; return ; } const resultFromMemory = await this . mindVisionOperation ( forceReset , input ) ; if ( ! forceReset && this . resetMindvisionIfEmpty && this . resetMindvisionIfEmpty ( resultFromMemory , retriesLeft ) ) { this . log ( 'result empty, calling with a force reset' ) ; setTimeout ( ( ) => this . callInternal ( callback , input , retriesLeft - 1 , true ) ) ; return ; } if ( ! resultFromMemory || this . emptyCheck ( resultFromMemory ) ) { this . debug ( 'result from memory is empty, retying' ) ; setTimeout ( ( ) => this . callInternal ( callback , input , retriesLeft - 1 ) , this . delay ) ; return ; } const result = this . transformer ( resultFromMemory ) ; callback ( result , retriesLeft - 1 ) ; return ; } private debug ( ... args ) { console . debug ( `[memory-service] ${ this . serviceName }` , ... args ) ; } private log ( ... args ) { console . log ( `[memory-service] ${ this . serviceName }` , ... args ) ; } private error ( ... args ) { console . error ( `[memory-service] ${ this . serviceName }` , args ) ; } } interface InternalCall < T > { resolve ; retriesLeft : number ; apply : ( retriesLeft : number , callback : ( result : T , retriesLeft : number ) => void ) => Promise < void > ; }