fix bug with voice being global
This commit is contained in:
@@ -58,6 +58,10 @@ export class WebRTCService implements OnDestroy {
|
||||
private lastJoinedServer: JoinedServerInfo | null = null;
|
||||
private readonly memberServerIds = new Set<string>();
|
||||
private activeServerId: string | null = null;
|
||||
/** The server ID where voice is currently active, or `null` when not in voice. */
|
||||
private voiceServerId: string | null = null;
|
||||
/** Maps each remote peer ID to the server they were discovered from. */
|
||||
private readonly peerServerMap = new Map<string, string>();
|
||||
private readonly serviceDestroyed$ = new Subject<void>();
|
||||
|
||||
private readonly _localPeerId = signal<string>(uuidv4());
|
||||
@@ -194,18 +198,40 @@ export class WebRTCService implements OnDestroy {
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGNALING_TYPE_SERVER_USERS:
|
||||
this.logger.info('Server users', { count: Array.isArray(message.users) ? message.users.length : 0 });
|
||||
case SIGNALING_TYPE_SERVER_USERS: {
|
||||
this.logger.info('Server users', { count: Array.isArray(message.users) ? message.users.length : 0, serverId: message.serverId });
|
||||
|
||||
// Only create peer connections for the voice server (if in voice)
|
||||
// or the currently active/viewed server (if not in voice).
|
||||
const effectiveServerId = this.voiceServerId || this.activeServerId;
|
||||
if (message.serverId && effectiveServerId && message.serverId !== effectiveServerId) {
|
||||
this.logger.info('Skipping peer connections for non-target server', {
|
||||
messageServerId: message.serverId,
|
||||
effectiveServerId,
|
||||
voiceActive: !!this.voiceServerId,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.users && Array.isArray(message.users)) {
|
||||
// Close stale peer connections from other servers
|
||||
if (message.serverId) {
|
||||
this.closePeersNotInServer(message.serverId);
|
||||
}
|
||||
|
||||
message.users.forEach((user: { oderId: string; displayName: string }) => {
|
||||
if (user.oderId && !this.peerManager.activePeerConnections.has(user.oderId)) {
|
||||
this.logger.info('Create peer connection to existing user', { oderId: user.oderId });
|
||||
this.logger.info('Create peer connection to existing user', { oderId: user.oderId, serverId: message.serverId });
|
||||
this.peerManager.createPeerConnection(user.oderId, true);
|
||||
this.peerManager.createAndSendOffer(user.oderId);
|
||||
if (message.serverId) {
|
||||
this.peerServerMap.set(user.oderId, message.serverId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SIGNALING_TYPE_USER_JOINED:
|
||||
this.logger.info('User joined', { displayName: message.displayName, oderId: message.oderId });
|
||||
@@ -217,6 +243,11 @@ export class WebRTCService implements OnDestroy {
|
||||
|
||||
case SIGNALING_TYPE_OFFER:
|
||||
if (message.fromUserId && message.payload?.sdp) {
|
||||
// Track inbound peer as belonging to our effective server
|
||||
const offerEffectiveServer = this.voiceServerId || this.activeServerId;
|
||||
if (offerEffectiveServer && !this.peerServerMap.has(message.fromUserId)) {
|
||||
this.peerServerMap.set(message.fromUserId, offerEffectiveServer);
|
||||
}
|
||||
this.peerManager.handleOffer(message.fromUserId, message.payload.sdp);
|
||||
}
|
||||
break;
|
||||
@@ -235,6 +266,28 @@ export class WebRTCService implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all peer connections that were discovered from a server
|
||||
* other than `serverId`. Also removes their entries from
|
||||
* {@link peerServerMap} so the bookkeeping stays clean.
|
||||
*
|
||||
* This ensures audio (and data channels) are scoped to only
|
||||
* the voice-active (or currently viewed) server.
|
||||
*/
|
||||
private closePeersNotInServer(serverId: string): void {
|
||||
const peersToClose: string[] = [];
|
||||
this.peerServerMap.forEach((peerServerId, peerId) => {
|
||||
if (peerServerId !== serverId) {
|
||||
peersToClose.push(peerId);
|
||||
}
|
||||
});
|
||||
for (const peerId of peersToClose) {
|
||||
this.logger.info('Closing peer from different server', { peerId, currentServer: serverId });
|
||||
this.peerManager.removePeer(peerId);
|
||||
this.peerServerMap.delete(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
private getCurrentVoiceState(): VoiceStateSnapshot {
|
||||
return {
|
||||
isConnected: this._isVoiceConnected(),
|
||||
@@ -424,6 +477,15 @@ export class WebRTCService implements OnDestroy {
|
||||
return this.peerManager.remotePeerStreams.get(peerId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current local media stream (microphone audio).
|
||||
*
|
||||
* @returns The local {@link MediaStream}, or `null` if voice is not active.
|
||||
*/
|
||||
getLocalStream(): MediaStream | null {
|
||||
return this.mediaManager.getLocalStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request microphone access and start sending audio to all peers.
|
||||
*
|
||||
@@ -437,6 +499,7 @@ export class WebRTCService implements OnDestroy {
|
||||
|
||||
/** Stop local voice capture and remove audio senders from peers. */
|
||||
disableVoice(): void {
|
||||
this.voiceServerId = null;
|
||||
this.mediaManager.disableVoice();
|
||||
this._isVoiceConnected.set(false);
|
||||
}
|
||||
@@ -501,10 +564,20 @@ export class WebRTCService implements OnDestroy {
|
||||
/**
|
||||
* Start broadcasting voice-presence heartbeats to all peers.
|
||||
*
|
||||
* Also marks the given server as the active voice server and closes
|
||||
* any peer connections that belong to other servers so that audio
|
||||
* is isolated to the correct voice channel.
|
||||
*
|
||||
* @param roomId - The voice channel room ID.
|
||||
* @param serverId - The voice channel server ID.
|
||||
*/
|
||||
startVoiceHeartbeat(roomId?: string, serverId?: string): void {
|
||||
if (serverId) {
|
||||
this.voiceServerId = serverId;
|
||||
// Remove peer connections that belong to a different server
|
||||
// so audio does not leak across voice channels.
|
||||
this.closePeersNotInServer(serverId);
|
||||
}
|
||||
this.mediaManager.startVoiceHeartbeat(roomId, serverId);
|
||||
}
|
||||
|
||||
@@ -535,6 +608,8 @@ export class WebRTCService implements OnDestroy {
|
||||
|
||||
/** Disconnect from the signaling server and clean up all state. */
|
||||
disconnect(): void {
|
||||
this.voiceServerId = null;
|
||||
this.peerServerMap.clear();
|
||||
this.leaveRoom();
|
||||
this.mediaManager.stopVoiceHeartbeat();
|
||||
this.signalingManager.close();
|
||||
@@ -551,6 +626,8 @@ export class WebRTCService implements OnDestroy {
|
||||
}
|
||||
|
||||
private fullCleanup(): void {
|
||||
this.voiceServerId = null;
|
||||
this.peerServerMap.clear();
|
||||
this.peerManager.closeAllPeers();
|
||||
this._connectedPeers.set([]);
|
||||
this.mediaManager.disableVoice();
|
||||
|
||||
Reference in New Issue
Block a user