From 12832d598b7426496d5b3f1747ee6a3eb7cecd11 Mon Sep 17 00:00:00 2001 From: Nikhil Narayana Date: Mon, 6 Jun 2022 18:23:59 -0700 Subject: [PATCH] feat: pass consoleNick or broadcaster connect code to comm file when spectating (#320) * feat: pass consoleNick to comm file when mirroring * feat: pass name to comm file when spectating * address comments --- src/broadcast/spectate.worker.interface.ts | 3 +- src/broadcast/spectate.worker.ts | 10 +++---- src/broadcast/spectateManager.ts | 30 +++++++++++-------- src/console/mirror.worker.interface.ts | 3 +- src/console/mirror.worker.ts | 30 +++++++++++++++---- src/console/mirrorManager.ts | 8 ++++- src/console/types.ts | 1 + src/dolphin/types.ts | 1 + .../Console/SavedConnectionItem.tsx | 3 +- 9 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/broadcast/spectate.worker.interface.ts b/src/broadcast/spectate.worker.interface.ts index c38de105e..1f64518b7 100644 --- a/src/broadcast/spectate.worker.interface.ts +++ b/src/broadcast/spectate.worker.interface.ts @@ -30,10 +30,11 @@ export async function createSpectateWorker(dolphinManager: DolphinManager): Prom const errorMessage = err instanceof Error ? err.message : err; void ipc_spectateErrorOccurredEvent.main!.trigger({ errorMessage }); }); - worker.getSpectateDetailsObservable().subscribe(({ playbackId, filePath }) => { + worker.getSpectateDetailsObservable().subscribe(({ playbackId, filePath, broadcasterName }) => { const replayComm: ReplayCommunication = { mode: "mirror", replay: filePath, + gameStation: broadcasterName, }; dolphinManager.launchPlaybackDolphin(playbackId, replayComm).catch(log.error); }); diff --git a/src/broadcast/spectate.worker.ts b/src/broadcast/spectate.worker.ts index 0a4127f6a..438daaced 100644 --- a/src/broadcast/spectate.worker.ts +++ b/src/broadcast/spectate.worker.ts @@ -19,7 +19,7 @@ interface Methods { getLogObservable(): Observable; getErrorObservable(): Observable; getBroadcastListObservable(): Observable; - getSpectateDetailsObservable(): Observable<{ playbackId: string; filePath: string }>; + getSpectateDetailsObservable(): Observable<{ playbackId: string; filePath: string; broadcasterName: string }>; getReconnectObservable(): Observable>; } @@ -30,7 +30,7 @@ const spectateManager = new SpectateManager(); const logSubject = new Subject(); const errorSubject = new Subject(); const broadcastListSubject = new Subject(); -const spectateDetailsSubject = new Subject<{ playbackId: string; filePath: string }>(); +const spectateDetailsSubject = new Subject<{ playbackId: string; filePath: string; broadcasterName: string }>(); const reconnectSubject = new Subject>(); // Forward the events to the renderer @@ -46,8 +46,8 @@ spectateManager.on(SpectateEvent.ERROR, async (err: Error | string) => { errorSubject.next(err); }); -spectateManager.on(SpectateEvent.NEW_FILE, async (playbackId: string, filePath: string) => { - spectateDetailsSubject.next({ playbackId, filePath }); +spectateManager.on(SpectateEvent.NEW_FILE, async (playbackId: string, filePath: string, broadcasterName: string) => { + spectateDetailsSubject.next({ playbackId, filePath, broadcasterName }); }); spectateManager.on(SpectateEvent.RECONNECT, async () => { @@ -87,7 +87,7 @@ const methods: WorkerSpec = { getBroadcastListObservable(): Observable { return Observable.from(broadcastListSubject); }, - getSpectateDetailsObservable(): Observable<{ playbackId: string; filePath: string }> { + getSpectateDetailsObservable(): Observable<{ playbackId: string; filePath: string; broadcasterName: string }> { return Observable.from(spectateDetailsSubject); }, getReconnectObservable(): Observable> { diff --git a/src/broadcast/spectateManager.ts b/src/broadcast/spectateManager.ts index 4523eb76b..ae0df21b1 100644 --- a/src/broadcast/spectateManager.ts +++ b/src/broadcast/spectateManager.ts @@ -29,22 +29,23 @@ const generatePlaybackId = (broadcastId: string) => `spectate-${broadcastId}`; * Dealing with dolphin related details should be handled elsewhere. */ export class SpectateManager extends EventEmitter { - private broadcastInfo: Record = {}; + private openBroadcasts: Record = {}; + private availableBroadcasts: Record = {}; private wsConnection: connection | null = null; constructor() { super(); } - private async _playFile(filePath: string, playbackId: string) { - this.emit(SpectateEvent.NEW_FILE, playbackId, filePath); + private async _playFile(filePath: string, playbackId: string, broadcasterName: string) { + this.emit(SpectateEvent.NEW_FILE, playbackId, filePath, broadcasterName); } private _handleEvents(obj: { type: string; broadcastId: string; cursor: string; events: any[] }) { const events = obj.events ?? []; const broadcastId: string = obj.broadcastId; - const broadcastInfo = this.broadcastInfo[broadcastId]; + const broadcastInfo = this.openBroadcasts[broadcastId]; if (!broadcastInfo) { // We've stopped watching this broadcast already return; @@ -110,7 +111,7 @@ export class SpectateManager extends EventEmitter { this.wsConnection = connection; // Reconnect to all the broadcasts that we were already watching - Object.entries(this.broadcastInfo).forEach(([broadcastId, info]) => { + Object.entries(this.openBroadcasts).forEach(([broadcastId, info]) => { const watchMsg: { type: string; broadcastId: string; startCursor?: string } = { type: "watch-broadcast", broadcastId, @@ -158,6 +159,10 @@ export class SpectateManager extends EventEmitter { case "list-broadcasts-resp": { const broadcasts: BroadcasterItem[] = obj.broadcasts ?? []; this.emit(SpectateEvent.BROADCAST_LIST_UPDATE, broadcasts); + this.availableBroadcasts = {}; + broadcasts.forEach((broadcast) => { + this.availableBroadcasts[broadcast.id] = broadcast; + }); break; } case "events": { @@ -204,10 +209,10 @@ export class SpectateManager extends EventEmitter { } // End the file writing and remove from the map - const info = this.broadcastInfo[broadcastId]; + const info = this.openBroadcasts[broadcastId]; if (info) { info.fileWriter.endCurrentFile(); - delete this.broadcastInfo[broadcastId]; + delete this.openBroadcasts[broadcastId]; } } @@ -224,7 +229,7 @@ export class SpectateManager extends EventEmitter { throw new Error("No websocket connection"); } - const existingBroadcasts = Object.keys(this.broadcastInfo); + const existingBroadcasts = Object.keys(this.openBroadcasts); if (existingBroadcasts.includes(broadcastId)) { // We're already watching this broadcast! this.emit(SpectateEvent.LOG, `We are already watching the selected broadcast`); @@ -248,11 +253,12 @@ export class SpectateManager extends EventEmitter { folderPath: targetPath, }); + const broadcasterName = this.availableBroadcasts[broadcastId].name; slpFileWriter.on(SlpFileWriterEvent.NEW_FILE, (currFilePath) => { - this._playFile(currFilePath, dolphinPlaybackId).catch(console.warn); + this._playFile(currFilePath, dolphinPlaybackId, broadcasterName).catch(console.warn); }); - this.broadcastInfo[broadcastId] = { + this.openBroadcasts[broadcastId] = { broadcastId, cursor: "", fileWriter: slpFileWriter, @@ -270,11 +276,11 @@ export class SpectateManager extends EventEmitter { // Play an empty file such that we just launch into the waiting for game screen, this is // used to clear out any previous file that we were reading for. The file will get updated // by the fileWriter - this._playFile("", dolphinPlaybackId).catch(console.warn); + this._playFile("", dolphinPlaybackId, broadcasterName).catch(console.warn); } public handleClosedDolphin(playbackId: string) { - const broadcastInfo = Object.values(this.broadcastInfo).find((info) => info.dolphinId === playbackId); + const broadcastInfo = Object.values(this.openBroadcasts).find((info) => info.dolphinId === playbackId); if (!broadcastInfo) { // This is not one of the spectator dolphin instances return; diff --git a/src/console/mirror.worker.interface.ts b/src/console/mirror.worker.interface.ts index 62529581d..d2306c4e5 100644 --- a/src/console/mirror.worker.interface.ts +++ b/src/console/mirror.worker.interface.ts @@ -33,11 +33,12 @@ export async function createMirrorWorker(dolphinManager: DolphinManager): Promis ipc_consoleMirrorErrorMessageEvent.main!.trigger({ message }).catch(log.error); }); - worker.getMirrorDetailsObservable().subscribe(({ playbackId, filePath, isRealtime }) => { + worker.getMirrorDetailsObservable().subscribe(({ playbackId, filePath, isRealtime, nickname }) => { const replayComm: ReplayCommunication = { mode: "mirror", isRealTimeMode: isRealtime, replay: filePath, + gameStation: nickname, }; dolphinManager.launchPlaybackDolphin(playbackId, replayComm).catch(log.error); }); diff --git a/src/console/mirror.worker.ts b/src/console/mirror.worker.ts index c09ed035b..190ca2f4b 100644 --- a/src/console/mirror.worker.ts +++ b/src/console/mirror.worker.ts @@ -18,7 +18,12 @@ interface Methods { dolphinClosed(playbackId: string): Promise; getLogObservable(): Observable; getErrorObservable(): Observable; - getMirrorDetailsObservable(): Observable<{ playbackId: string; filePath: string; isRealtime: boolean }>; + getMirrorDetailsObservable(): Observable<{ + playbackId: string; + filePath: string; + isRealtime: boolean; + nickname?: string; + }>; getMirrorStatusObservable(): Observable<{ ip: string; info: Partial }>; } @@ -28,7 +33,12 @@ const mirrorManager = new MirrorManager(); const logSubject = new Subject(); const errorSubject = new Subject(); -const mirrorDetailsSubject = new Subject<{ playbackId: string; filePath: string; isRealtime: boolean }>(); +const mirrorDetailsSubject = new Subject<{ + playbackId: string; + filePath: string; + isRealtime: boolean; + nickname?: string; +}>(); const mirrorStatusSubject = new Subject<{ ip: string; info: Partial }>(); // Forward the events to the renderer @@ -40,9 +50,12 @@ mirrorManager.on(MirrorEvent.ERROR, async (error: Error | string) => { errorSubject.next(error); }); -mirrorManager.on(MirrorEvent.NEW_FILE, async (playbackId: string, filePath: string, isRealtime: boolean) => { - mirrorDetailsSubject.next({ playbackId, filePath, isRealtime }); -}); +mirrorManager.on( + MirrorEvent.NEW_FILE, + async (playbackId: string, filePath: string, isRealtime: boolean, nickname?: string) => { + mirrorDetailsSubject.next({ playbackId, filePath, isRealtime, nickname: nickname }); + }, +); mirrorManager.on( MirrorEvent.MIRROR_STATUS_CHANGE, @@ -79,7 +92,12 @@ const methods: WorkerSpec = { getErrorObservable(): Observable { return Observable.from(errorSubject); }, - getMirrorDetailsObservable(): Observable<{ playbackId: string; filePath: string; isRealtime: boolean }> { + getMirrorDetailsObservable(): Observable<{ + playbackId: string; + filePath: string; + isRealtime: boolean; + nickname?: string; + }> { return Observable.from(mirrorDetailsSubject); }, getMirrorStatusObservable(): Observable<{ ip: string; info: Partial }> { diff --git a/src/console/mirrorManager.ts b/src/console/mirrorManager.ts index 8e41c9aca..038be0d89 100644 --- a/src/console/mirrorManager.ts +++ b/src/console/mirrorManager.ts @@ -239,7 +239,13 @@ export class MirrorManager extends EventEmitter { } private async _playFile(filePath: string, playbackId: string) { - return this.emit(MirrorEvent.NEW_FILE, playbackId, filePath, this.mirrors[playbackId].isRealtime); + return this.emit( + MirrorEvent.NEW_FILE, + playbackId, + filePath, + this.mirrors[playbackId].isRealtime, + this.mirrors[playbackId].nickname, + ); } public async handleClosedDolphin(playbackId: string) { diff --git a/src/console/types.ts b/src/console/types.ts index edd0b651d..7593c3454 100644 --- a/src/console/types.ts +++ b/src/console/types.ts @@ -20,6 +20,7 @@ export interface MirrorConfig { enableRelay: boolean; autoSwitcherSettings?: AutoSwitcherSettings; useNicknameFolders: boolean; + nickname?: string; } export interface MirrorDetails extends MirrorConfig { diff --git a/src/dolphin/types.ts b/src/dolphin/types.ts index 851d2b77c..63f2046bb 100644 --- a/src/dolphin/types.ts +++ b/src/dolphin/types.ts @@ -8,6 +8,7 @@ export interface ReplayCommunication { isRealTimeMode?: boolean; // default false; keeps dolphin fairly close to real time (about 2-3 frames); only relevant in mirror mode shouldResync?: boolean; // default true; disables the resync functionality rollbackDisplayMethod?: "off" | "normal" | "visible"; // default off; normal shows like a player experienced it, visible shows ALL frames (normal and rollback) + gameStation?: string; queue?: ReplayQueueItem[]; } diff --git a/src/renderer/containers/Console/SavedConnectionItem.tsx b/src/renderer/containers/Console/SavedConnectionItem.tsx index c0c2a9885..e73449f91 100644 --- a/src/renderer/containers/Console/SavedConnectionItem.tsx +++ b/src/renderer/containers/Console/SavedConnectionItem.tsx @@ -59,6 +59,7 @@ export const SavedConnectionItem: React.FC = ({ isRealtime: conn.isRealtime, enableRelay: conn.enableRelay, useNicknameFolders: conn.useNicknameFolders, + nickname, }; // Add OBS config if necessary @@ -71,7 +72,7 @@ export const SavedConnectionItem: React.FC = ({ } await consoleService.connectToConsoleMirror(config); - }, [consoleService, connection]); + }, [consoleService, connection, nickname]); const onMirror = React.useCallback(() => { consoleService.startMirroring(connection.ipAddress).catch(showError); }, [consoleService, connection, showError]);