import { NetGlobalMiddleware , NetMiddleware } from "../middleware" ; import { DebugLog , DebugWarn } from "../configuration" ; import { findOrCreateRemote , IAsyncListener , IS_CLIENT } from "../internal" ; import MiddlewareEvent , { MiddlewareList } from "./MiddlewareEvent" ; import { MiddlewareOverload } from "../middleware" ; const HttpService = game . GetService ( "HttpService" ) ; const RunService = game . GetService ( "RunService" ) ; type AsyncEventArgs = [ eventId : string , data : unknown ] ; function isEventArgs ( value ) { if ( value . size ( ) < 2 ) return false ; const [ eventId , data ] = value ; return typeIs ( eventId , "string" ) && typeIs ( data , "table" ) ; } export interface ServerAsyncCallback < CallbackArgs extends readonly unknown [ ] , CallbackReturnType > { param callback SetCallback < R extends CallbackReturnType > ( callback ) : void ; SetCallback < R extends Promise < CallbackReturnType > > ( callback ) : void ; } export interface ServerAsyncCaller < CallArgs extends readonly unknown [ ] , CallReturnType > { param player param args CallPlayerAsync ( player , ... args ) : Promise < CallReturnType > ; param timeout SetCallTimeout ( timeout ) : this ; /**
     * Gets the call timeout (in seconds) that this remote will wait before rejecting if no response is recieved
     */ GetCallTimeout ( ) : number ; } /**
 * An asynchronous function for two way communication between the client and server
 */ class ServerAsyncFunction < CallbackArgs extends ReadonlyArray < unknown > = Array < unknown > , CallArgs extends ReadonlyArray < unknown > = Array < unknown > , CallReturnType = unknown , CallbackReturnType = unknown > extends MiddlewareEvent implements ServerAsyncCallback < CallbackArgs , CallbackReturnType > , ServerAsyncCaller < CallArgs , CallReturnType > { private instance : RemoteEvent < Callback > ; private timeout = 10 ; private connector : RBXScriptConnection | undefined ; private listeners = new Map < string , IAsyncListener > ( ) ; private defaultHook ? : RBXScriptConnection ; internal private static readonly DefaultEventHook = ( player , ... args ) => { } ; internal public GetInstance ( ) { return this . instance ; } constructor ( name ) ; constructor ( name , middlewares ) ; constructor ( name , middlewares = [ ] ) { super ( middlewares ) ; this . instance = findOrCreateRemote ( "AsyncRemoteFunction" , name , ( instance ) => { this . defaultHook = instance . OnServerEvent . Connect ( ServerAsyncFunction . DefaultEventHook ) ; } ) ; assert ( ! IS_CLIENT , "Cannot create a NetServerAsyncFunction on the client!" ) ; } public SetCallTimeout ( timeout ) { assert ( timeout > 0 , "timeout must be a positive number" ) ; this . timeout = timeout ; return this ; } public GetCallTimeout ( ) { return this . timeout ; } param callback public SetCallback < R extends CallbackReturnType > ( callback ) { this . defaultHook ?. Disconnect ( ) ; if ( this . connector ) { this . connector . Disconnect ( ) ; this . connector = undefined ; } this . connector = this . instance . OnServerEvent . Connect ( async ( player , ... args ) => { if ( isEventArgs ( args ) ) { const [ eventId , data ] = args ; const result = this . _processMiddleware < CallbackArgs , R > ( callback ) ?. ( player , ... ( data as CallbackArgs ) ) ; if ( Promise . is ( result ) ) { result . then ( ( promiseResult ) => { this . instance . FireClient ( player , eventId , promiseResult ) ; } ) . catch ( ( err ) => { warn ( "[rbx-net] Failed to send response to client: " + err ) ; } ) ; } else { if ( result === undefined ) { warn ( "[rbx-net-async] " + this . instance . Name + " returned undefined" ) ; } this . instance . FireClient ( player , eventId , result ) ; } } else { warn ( "[rbx-net-async] Recieved message without eventId" ) ; } } ) ; } param player param args returns throws public async CallPlayerAsync ( player , ... args ) { const id = HttpService . GenerateGUID ( false ) ; this . instance . FireClient ( player , id , { ... args } ) ; return new Promise ( ( resolve , reject ) => { const startTime = tick ( ) ; DebugLog ( "Connected CallPlayerAsync EventId" , id ) ; const connection = this . instance . OnServerEvent . Connect ( ( fromPlayer , ... recvArgs ) => { const [ eventId , data ] = recvArgs ; if ( typeIs ( eventId , "string" ) && data !== undefined ) { if ( player === player && eventId === id ) { DebugLog ( "Disconnected CallPlayerAsync EventId" , eventId ) ; connection . Disconnect ( ) ; resolve ( data as CallReturnType ) ; } } } ) ; this . listeners . set ( id , { connection , timeout : this . timeout } ) ; do { RunService . Stepped . Wait ( ) ; } while ( connection . Connected && tick ( ) < startTime + this . timeout ) ; this . listeners . delete ( id ) ; if ( tick ( ) >= startTime && connection . Connected ) { DebugWarn ( "(timeout) Disconnected CallPlayerAsync EventId" , id ) ; connection . Disconnect ( ) ; reject ( "Request to client timed out" ) ; } } ) ; } } export default ServerAsyncFunction ;