Skip to content

Commit

Permalink
seems to work except switching broadcasts mid game doesn't seem to work
Browse files Browse the repository at this point in the history
  • Loading branch information
jmlee337 committed Mar 11, 2024
1 parent 26fd2a3 commit 87e1d81
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 27 deletions.
4 changes: 3 additions & 1 deletion src/broadcast/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default function setupBroadcastIpc({
}) {
let spectateWorker: SpectateWorker | undefined;
let broadcastWorker: BroadcastWorker | undefined;
let prefixOrdinal = 0;

dolphinManager.events
.filter<DolphinPlaybackClosedEvent>((event) => {
Expand All @@ -46,7 +47,8 @@ export default function setupBroadcastIpc({
);

const folderPath = settingsManager.get().settings.spectateSlpPath;
await spectateWorker.startSpectate(broadcasterId, folderPath);
await spectateWorker.startSpectate(broadcasterId, folderPath, { idPostfix: `broadcast${prefixOrdinal}` });
prefixOrdinal += 1;
return { success: true };
});

Expand Down
12 changes: 8 additions & 4 deletions src/broadcast/spectate.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { Observable, Subject } from "threads/observable";
import { expose } from "threads/worker";

import { SpectateManager } from "./spectate_manager";
import type { BroadcasterItem } from "./types";
import type { BroadcasterItem, SpectateDolphinOptions } from "./types";
import { SpectateEvent } from "./types";

interface Methods {
dispose: () => Promise<void>;
startSpectate(broadcastId: string, targetPath: string): Promise<string>;
startSpectate(broadcastId: string, targetPath: string, dolphinOptions: SpectateDolphinOptions): Promise<string>;
stopSpectate(broadcastId: string): Promise<void>;
dolphinClosed(playbackId: string): Promise<void>;
refreshBroadcastList(authToken: string): Promise<void>;
Expand Down Expand Up @@ -71,8 +71,12 @@ const methods: WorkerSpec = {

spectateManager.removeAllListeners();
},
async startSpectate(broadcastId: string, targetPath: string): Promise<string> {
return await spectateManager.watchBroadcast(broadcastId, targetPath);
async startSpectate(
broadcastId: string,
targetPath: string,
dolphinOptions: SpectateDolphinOptions,
): Promise<string> {
return await spectateManager.watchBroadcast(broadcastId, targetPath, dolphinOptions);
},
async stopSpectate(broadcastId: string): Promise<void> {
spectateManager.stopWatchingBroadcast(broadcastId);
Expand Down
37 changes: 18 additions & 19 deletions src/broadcast/spectate_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import * as fs from "fs-extra";
import type { connection, Message } from "websocket";
import { client as WebSocketClient } from "websocket";

import type { BroadcasterItem } from "./types";
import type { BroadcasterItem, SpectateDolphinOptions } from "./types";
import { SpectateEvent } from "./types";

const SLIPPI_WS_SERVER = process.env.SLIPPI_WS_SERVER;

const DOLPHIN_INSTANCE_ID = "spectate";

type BroadcastInfo = {
broadcastId: string;
cursor: string;
Expand All @@ -20,7 +18,11 @@ type BroadcastInfo = {
dolphinId: string;
};

const generatePlaybackId = (broadcastId: string) => `spectate-${broadcastId}`;
// since we can have multiple SpectateManagers, we need to be careful about creating colliding ids
const generatePlaybackId = (postfix?: string) => {
const now = Date.now().toString();
return postfix ? now + postfix : now;
};

/**
* Responsible for retrieving Dolphin game data over enet and sending the data
Expand Down Expand Up @@ -225,27 +227,24 @@ export class SpectateManager extends EventEmitter {
* @param {boolean} [singleton] If true, it will open the broadcasts only
* in a single Dolphin window. Opens each broadcast in their own window otherwise.
*/
public watchBroadcast(broadcastId: string, targetPath: string, singleton?: boolean) {
public watchBroadcast(broadcastId: string, targetPath: string, dolphinOptions: SpectateDolphinOptions) {
Preconditions.checkExists(this.wsConnection, "No websocket connection");

const existingBroadcasts = Object.keys(this.openBroadcasts);
if (existingBroadcasts.includes(broadcastId)) {
// We're already watching this broadcast!
const openBroadcast = this.openBroadcasts[broadcastId];
if (openBroadcast) {
this.emit(SpectateEvent.LOG, `We are already watching the selected broadcast`);
return this.openBroadcasts[broadcastId].dolphinId;
return openBroadcast.dolphinId;
}

let dolphinPlaybackId = generatePlaybackId(broadcastId);

// We're only watching one at at time so stop other broadcasts
if (singleton) {
existingBroadcasts.forEach((broadcastInfo) => {
this.stopWatchingBroadcast(broadcastInfo);
});

// Use the default playback ID
dolphinPlaybackId = DOLPHIN_INSTANCE_ID;
if (dolphinOptions.dolphinId) {
const existingDolphin = Object.values(this.openBroadcasts).find(
(broadcastInfo) => broadcastInfo.dolphinId === dolphinOptions.dolphinId,
);
if (existingDolphin) {
this.stopWatchingBroadcast(existingDolphin.broadcastId);
}
}
const dolphinPlaybackId = dolphinOptions.dolphinId || generatePlaybackId(dolphinOptions.idPostfix);

fs.ensureDirSync(targetPath);
const slpFileWriter = new SlpFileWriter({
Expand Down
5 changes: 5 additions & 0 deletions src/broadcast/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,8 @@ export type BroadcastService = {
startBroadcast(config: StartBroadcastConfig): Promise<void>;
stopBroadcast(): Promise<void>;
};

export type SpectateDolphinOptions = {
dolphinId?: string;
idPostfix?: string;
};
18 changes: 15 additions & 3 deletions src/remote/remote.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BroadcasterItem } from "@broadcast/types";
import type { BroadcasterItem, SpectateDolphinOptions } from "@broadcast/types";
import type { DolphinManager } from "@dolphin/manager";
import type { DolphinPlaybackClosedEvent, ReplayCommunication } from "@dolphin/types";
import { DolphinEventType, DolphinLaunchType } from "@dolphin/types";
Expand Down Expand Up @@ -26,12 +26,15 @@ export default class RemoteServer {
private remoteServer: WebSocketServer | null;
private connection: connection | null;

private prefixOrdinal: number;

constructor(dolphinManager: DolphinManager, settingsManager: SettingsManager) {
this.authToken = "";
this.spectateWorker = null;
this.httpServer = null;
this.remoteServer = null;
this.connection = null;
this.prefixOrdinal = 0;

this.settingsManager = settingsManager;
this.dolphinManager = dolphinManager;
Expand Down Expand Up @@ -133,14 +136,23 @@ export default class RemoteServer {
if (json.op === "list-broadcasts-request") {
await throttledRefresh();
} else if (json.op === "spectate-broadcast-request") {
if (!json.broadcastId) {
const broadcastId = json.broadcastId;
if (!broadcastId) {
newConnection.sendUTF(JSON.stringify({ op: "spectate-broadcast-response", err: "no broadcastId" }));
return;
}

const dolphinOptions: SpectateDolphinOptions = {};
const dolphinId = json.dolphinId;
if (dolphinId && typeof dolphinId === "string") {
dolphinOptions.dolphinId = dolphinId;
} else {
dolphinOptions.idPostfix = `remote${this.prefixOrdinal}`;
this.prefixOrdinal += 1;
}
try {
const path = this.settingsManager.get().settings.spectateSlpPath;
const dolphinId = await this.spectateWorker!.startSpectate(json.broadcastId, path);
const dolphinId = await this.spectateWorker!.startSpectate(broadcastId, path, dolphinOptions);
newConnection.sendUTF(JSON.stringify({ op: "spectate-broadcast-response", dolphinId, path }));
} catch (e) {
const err = typeof e === "string" ? e : e instanceof Error ? e.message : "unknown";
Expand Down

0 comments on commit 87e1d81

Please sign in to comment.