UNPKG

@pureweb/platform-streaming-agent

Version:

The PureWeb platform streaming agent enables your game to communicate and stream through the PureWeb Platform

221 lines (220 loc) 9.46 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SteamVRLogMonitorExtension = void 0; const Log_1 = __importDefault(require("../../Log")); const ws_1 = __importDefault(require("ws")); const IExtension_1 = require("../../IExtension"); const ConnectionStates_1 = require("../../ConnectionStates"); const node_fetch_1 = __importDefault(require("node-fetch")); const platform_sdk_1 = require("@pureweb/platform-sdk"); const IPIFY_ENDPOINT_IPV4 = 'https://checkip.amazonaws.com'; const initialReconnectDelay = 1000; //[SteamVR Directory]\bin\win64\vrstartup.exe var HMDState; (function (HMDState) { HMDState[HMDState["NOT_PRESENT"] = 0] = "NOT_PRESENT"; HMDState[HMDState["INACTIVE"] = 1] = "INACTIVE"; HMDState[HMDState["ACTIVE"] = 2] = "ACTIVE"; })(HMDState || (HMDState = {})); var PresenceState; (function (PresenceState) { PresenceState[PresenceState["NOT_PRESENT"] = 0] = "NOT_PRESENT"; PresenceState[PresenceState["PRESENT"] = 1] = "PRESENT"; })(PresenceState || (PresenceState = {})); var GameConnectionState; (function (GameConnectionState) { GameConnectionState[GameConnectionState["CONNECTED"] = 0] = "CONNECTED"; GameConnectionState[GameConnectionState["DISCONNECTED"] = 1] = "DISCONNECTED"; })(GameConnectionState || (GameConnectionState = {})); var EventStreamState; (function (EventStreamState) { EventStreamState[EventStreamState["OPEN"] = 0] = "OPEN"; EventStreamState[EventStreamState["CLOSED"] = 1] = "CLOSED"; EventStreamState[EventStreamState["ERROR"] = 2] = "ERROR"; })(EventStreamState || (EventStreamState = {})); const maxRetries = 5; class SteamVRLogMonitorExtension extends IExtension_1.AbstractExtension { constructor() { super(); this.ws = null; this.currentReconnectDelay = initialReconnectDelay; this.steamVRState = HMDState.NOT_PRESENT; this.presenceState = PresenceState.NOT_PRESENT; this.gameState = GameConnectionState.DISCONNECTED; this.eventStreamState = EventStreamState.CLOSED; this.fetchPublicIP = async () => { Log_1.default.info(`Obtaining Public IP`); return (await (0, node_fetch_1.default)(IPIFY_ENDPOINT_IPV4)).text(); }; this.makeIPContribution = async () => { let IP = await this.IP; IP = IP.replace(/\n/g, ''); Log_1.default.info(`Making IP Contribution ${IP}`); const contribution = await this.agent .makeContribution({ type: 'CXR_SERVICE', data: JSON.stringify({ IP }) }) .catch((err) => { Log_1.default.error('Failed to make contribution: ' + err); this.connectionStateChange?.(ConnectionStates_1.ConnectionStates.INTERNAL_ERROR); }); this.contribution = contribution; }; this.revokeIPContribution = async () => { const IP = await this.IP; if (this.contribution) { Log_1.default.info(`Revoking IP Contribution (${IP})`); await this.agent.removeContribution(this.contribution).catch((err) => { Log_1.default.error('Failed to remove contribution', err); }); this.contribution = undefined; } else { Log_1.default.info(`No IP contribution to revoke`); } }; this.start = async () => { this.IP = this.fetchPublicIP(); this.startRendezvousTimer(); this.monitor(); return true; }; this.stop = () => { return Promise.resolve(true); }; this.id = Date.now(); } async monitor(retryCount = 0) { try { Log_1.default.info(`Attempting to Establish Log Event Connection with SteamVR....`); this.ws = new ws_1.default('ws://localhost:27062', { headers: { origin: 'http://localhost:27062' } }); this.ws.addEventListener('open', () => { this.ws.send('console_open'); this.onWebsocketOpen(); this.ws.close(); }); this.ws.addEventListener('error', async (error) => { if (retryCount < maxRetries) { const delay = Math.pow(2, retryCount) * 500; // Exponential backoff Log_1.default.info(`Retrying connection in ${delay / 1000} seconds...`); await new Promise((resolve) => setTimeout(resolve, delay)); this.monitor(retryCount + 1); } else { // logger.info('Maximum retry attempts reached. Did not connect.'); Log_1.default.error(error); this.connectionStateChange?.(ConnectionStates_1.ConnectionStates.STREAMER_RENDEZVOUS_MISSED); } }); } catch (problem) { // Do nothing. If there is problem with the initial connection our beckoff retry will take over } } onWebsocketOpen() { this.onRuntimeEventStreamConnection(); } onStateChanged(handler) { this.connectionStateChange = handler; } startRendezvousTimer() { // start rendezvous timer try { if (this.config.rendezvousTimeoutSeconds >= 0) { setTimeout(() => { if (this.eventStreamState != EventStreamState.OPEN) { Log_1.default.info(`Failed to connect to SteamVR`); this.connectionStateChange?.(ConnectionStates_1.ConnectionStates.STREAMER_RENDEZVOUS_MISSED); } else if (this.presenceState != PresenceState.PRESENT) { Log_1.default.info(`Agent Rendezvous missed.`); this.connectionStateChange?.(ConnectionStates_1.ConnectionStates.AGENT_RENDEZVOUS_MISSED); } }, this.config.rendezvousTimeoutSeconds * 1000); } } catch (err) { Log_1.default.error(`${err}`); this.stop(); } } onRuntimeEventStreamConnection() { this.eventStreamState = EventStreamState.OPEN; this.startPresenceTimer(); this.monitorPresence(); this.makeIPContribution(); } monitorPresence() { this.agent.subscribeToPresence(platform_sdk_1.PresenceTypes.ALL, { arrivals: (data) => { this.onAgentArrival(data); }, departures: (data) => { this.onAgentDeparture(data); } }); } async onAgentArrival(data) { if (this.agent.id != data.agentId) { Log_1.default.info(`CXR Agent arrived: ${data.agentId}`); this.presenceState = PresenceState.PRESENT; try { if (this.platform?.credentials?.agent?.launchRequestId?.length > 0) { await this.platform.launchRequestAcknowledgements(platform_sdk_1.LaunchRequestAcknowledgementType.StreamingAgentReady, 'OpenXR stream is ready to be consumed'); } else { Log_1.default.info('No associated launch request. Assuming local development workflow'); } } catch (err) { Log_1.default.error('Unable to acknowledge CXR streaming agent ready state: ' + err); } } } async onAgentDeparture(data) { Log_1.default.info(`CXR Agent departed: ${data.agentId}`); this.presenceState = PresenceState.NOT_PRESENT; await this.revokeIPContribution(); try { if (this.platform?.credentials?.agent?.launchRequestId?.length > 0) { await this.platform.launchRequestAcknowledgements(platform_sdk_1.LaunchRequestAcknowledgementType.StreamingAgentFinished, 'CXR Agent has finished consuming stream'); } else { Log_1.default.info('No associated launch request. Assuming local development workflow'); } } catch (err) { Log_1.default.error('Unable to acknowledge CXR streaming agent finish state: ' + err); } this.connectionStateChange?.(ConnectionStates_1.ConnectionStates.PEERS_EXITED); } startPresenceTimer() { // start rendezvous timer try { if (this.config.rendezvousTimeoutSeconds >= 0) { setTimeout(() => { if (this.presenceState === PresenceState.PRESENT) { Log_1.default.info(`CXR Agent Rendezvous successful.`); } else { Log_1.default.info(`CXR Agent Rendezvous missed.`); this.connectionStateChange?.(ConnectionStates_1.ConnectionStates.AGENT_RENDEZVOUS_MISSED); } }, this.config.rendezvousTimeoutSeconds * 1000); } } catch (err) { Log_1.default.error(`${err}`); this.stop(); } } } exports.SteamVRLogMonitorExtension = SteamVRLogMonitorExtension;