Defer peer negotiation until post-media setup and queue early signals
This commit is contained in:
@@ -257,6 +257,8 @@ let heartbeatAwaitingPong = false;
|
||||
let reconnectInFlight = false;
|
||||
let activeServerInstanceId: string | null = null;
|
||||
let reloadScheduledForVersionMismatch = false;
|
||||
let peerNegotiationReady = false;
|
||||
let pendingSignalMessages: Array<Extract<IncomingMessage, { type: 'signal' }>> = [];
|
||||
let peerListenGainByNickname = settings.loadPeerListenGains();
|
||||
let audioLayers: AudioLayerState = {
|
||||
voice: true,
|
||||
@@ -1571,9 +1573,27 @@ function disconnect(): void {
|
||||
lastSubscriptionRefreshTileY = Math.round(state.player.y);
|
||||
stopTeleportLoopAudio();
|
||||
activeTeleport = null;
|
||||
peerNegotiationReady = false;
|
||||
pendingSignalMessages = [];
|
||||
itemBehaviorRegistry.cleanup();
|
||||
}
|
||||
|
||||
/** Starts peer negotiation only after welcome + media setup sequencing is complete. */
|
||||
async function activatePeerNegotiation(): Promise<void> {
|
||||
if (!state.running) return;
|
||||
if (peerNegotiationReady) return;
|
||||
peerNegotiationReady = true;
|
||||
for (const peer of state.peers.values()) {
|
||||
await peerManager.createOrGetPeer(peer.id, true, peer);
|
||||
}
|
||||
if (pendingSignalMessages.length === 0) return;
|
||||
const queued = pendingSignalMessages;
|
||||
pendingSignalMessages = [];
|
||||
for (const signal of queued) {
|
||||
await onAppMessage(signal);
|
||||
}
|
||||
}
|
||||
|
||||
const onAppMessage = createOnMessageHandler({
|
||||
getWorldGridSize: () => worldGridSize,
|
||||
setWorldGridSize: (size) => {
|
||||
@@ -1640,6 +1660,13 @@ const onAppMessage = createOnMessageHandler({
|
||||
},
|
||||
handleAuthRequired,
|
||||
handleAuthResult,
|
||||
isPeerNegotiationReady: () => peerNegotiationReady,
|
||||
enqueuePendingSignal: (message) => {
|
||||
pendingSignalMessages.push(message);
|
||||
if (pendingSignalMessages.length > 500) {
|
||||
pendingSignalMessages.splice(0, pendingSignalMessages.length - 500);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** Handles signaling packets with heartbeat/restart metadata before app-level dispatch. */
|
||||
@@ -1701,12 +1728,14 @@ async function setupMediaAfterAuth(): Promise<void> {
|
||||
const canProceed = await checkMicPermission();
|
||||
if (!canProceed) {
|
||||
setConnectionStatus('Microphone access is required.');
|
||||
await activatePeerNegotiation();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await populateAudioDevices();
|
||||
if (dom.audioInputSelect.options.length === 0) {
|
||||
setConnectionStatus('No audio input device found. Open Audio setup or connect a microphone.');
|
||||
await activatePeerNegotiation();
|
||||
return;
|
||||
}
|
||||
const inputDeviceId = dom.audioInputSelect.value || mediaSession.getPreferredInputDeviceId();
|
||||
@@ -1714,6 +1743,8 @@ async function setupMediaAfterAuth(): Promise<void> {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setConnectionStatus(describeMediaError(error));
|
||||
} finally {
|
||||
await activatePeerNegotiation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ type MessageHandlerDeps = {
|
||||
playIncomingItemUseSound: (url: string, x: number, y: number) => void;
|
||||
handleAuthRequired: (message: Extract<IncomingMessage, { type: 'auth_required' }>) => void;
|
||||
handleAuthResult: (message: Extract<IncomingMessage, { type: 'auth_result' }>) => Promise<void>;
|
||||
isPeerNegotiationReady: () => boolean;
|
||||
enqueuePendingSignal: (message: Extract<IncomingMessage, { type: 'signal' }>) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -117,7 +119,6 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
|
||||
|
||||
for (const user of message.users) {
|
||||
deps.state.peers.set(user.id, { ...user });
|
||||
await deps.peerManager.createOrGetPeer(user.id, true, user);
|
||||
}
|
||||
deps.state.items.clear();
|
||||
for (const item of message.items || []) {
|
||||
@@ -132,6 +133,18 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
|
||||
break;
|
||||
|
||||
case 'signal': {
|
||||
if (!deps.isPeerNegotiationReady()) {
|
||||
deps.enqueuePendingSignal(message);
|
||||
if (!deps.state.peers.has(message.senderId)) {
|
||||
deps.state.peers.set(message.senderId, {
|
||||
id: message.senderId,
|
||||
nickname: deps.sanitizeName(message.senderNickname || 'user...') || 'user...',
|
||||
x: Number.isFinite(message.x) ? message.x : 20,
|
||||
y: Number.isFinite(message.y) ? message.y : 20,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
const peer = await deps.peerManager.handleSignal(message);
|
||||
if (!deps.state.peers.has(peer.id)) {
|
||||
deps.state.peers.set(peer.id, {
|
||||
|
||||
Reference in New Issue
Block a user