Remove delay between host and viewer
All checks were successful
Deploy Feature / deploy-feature (push) Successful in 26s

This commit is contained in:
Johnny322
2026-02-24 21:52:07 +01:00
parent a16d340cb7
commit 7c12d25367

View File

@@ -316,6 +316,8 @@ type RealtimeState = {
currentSelectorId: string | null
lastAwardedTeamId: string | null
isAnswerClip: boolean
playbackPosition: number
playbackCapturedAt: number
}
type RealtimeMessage = {
@@ -377,7 +379,8 @@ export default {
syncError: '',
isApplyingRemote: false,
queuedRemoteState: null as RealtimeState | null,
syncTimer: 0
syncTimer: 0,
playbackSyncInterval: 0
}
},
async mounted() {
@@ -528,6 +531,7 @@ export default {
this.gameIdInput = ''
this.isHost = false
this.syncError = ''
this.stopHostPlaybackSync()
this.setGameInUrl('')
},
async connectSession() {
@@ -576,6 +580,7 @@ export default {
}).catch(() => {})
},
buildRealtimeState(): RealtimeState {
const player = this.getPlayer()
return {
step: this.step,
teams: this.teams.map((team) => ({ ...team })),
@@ -585,7 +590,9 @@ export default {
currentClipUrl: this.currentClipUrl,
currentSelectorId: this.currentSelectorId,
lastAwardedTeamId: this.lastAwardedTeamId,
isAnswerClip: this.isAnswerClip
isAnswerClip: this.isAnswerClip,
playbackPosition: player?.currentTime || 0,
playbackCapturedAt: Date.now()
}
},
publishState() {
@@ -630,13 +637,27 @@ export default {
this.isApplyingRemote = false
}
await nextTick()
this.syncRemotePlayback(previousClipUrl !== this.currentClipUrl)
this.syncRemotePlayback(previousClipUrl !== this.currentClipUrl, state)
},
getCurrentTileStatus() {
if (!this.currentTileKey) return 'available'
return this.tiles[this.currentTileKey]?.status || 'available'
},
syncRemotePlayback(forceReload: boolean) {
setPlayerCurrentTime(player: HTMLAudioElement, targetTime: number) {
const seek = () => {
try {
player.currentTime = targetTime
} catch {
// ignore seek failures on unsupported ranges
}
}
if (player.readyState > 0) {
seek()
return
}
player.addEventListener('loadedmetadata', seek, { once: true })
},
syncRemotePlayback(forceReload: boolean, state: RealtimeState) {
if (this.canControlGame) return
const player = this.getPlayer()
if (!player) return
@@ -647,11 +668,23 @@ export default {
return
}
const elapsedSeconds = Math.max(0, (Date.now() - (state.playbackCapturedAt || Date.now())) / 1000)
let targetTime = state.playbackPosition || 0
if (tileStatus === 'playing' || tileStatus === 'guessed') {
targetTime += elapsedSeconds
}
if (Number.isFinite(player.duration) && player.duration > 0) {
targetTime = Math.min(targetTime, Math.max(0, player.duration - 0.05))
}
if (forceReload) {
player.currentTime = 0
player.load()
}
if (Math.abs((player.currentTime || 0) - targetTime) > 0.2) {
this.setPlayerCurrentTime(player, targetTime)
}
if (tileStatus === 'paused') {
player.pause()
return
@@ -707,6 +740,7 @@ export default {
this.tentacleLevels = this.tentacleLevels.map(() => 0)
},
teardownAudio() {
this.stopHostPlaybackSync()
if (this.rafId) {
cancelAnimationFrame(this.rafId)
this.rafId = 0
@@ -785,9 +819,25 @@ export default {
if (this.rafId) cancelAnimationFrame(this.rafId)
this.rafId = requestAnimationFrame(tick)
},
startHostPlaybackSync() {
if (!this.isHost || !this.socketConnected || !this.socket) return
this.stopHostPlaybackSync()
this.playbackSyncInterval = window.setInterval(() => {
this.queueStateSync()
}, 400)
},
stopHostPlaybackSync() {
if (!this.playbackSyncInterval) return
window.clearInterval(this.playbackSyncInterval)
this.playbackSyncInterval = 0
},
handlePlayerPlay() {
this.isPlaying = true
this.startPulse()
if (this.isHost) {
this.startHostPlaybackSync()
this.queueStateSync()
}
},
handlePlayerPause() {
this.isPlaying = false
@@ -796,6 +846,10 @@ export default {
this.rafId = 0
}
this.pulseLevel = 0
if (this.isHost) {
this.stopHostPlaybackSync()
this.queueStateSync()
}
},
togglePlayback() {
const player = this.getPlayer()
@@ -1017,6 +1071,7 @@ export default {
this.teardownAudio()
this.closeSocket()
if (this.syncTimer) window.clearTimeout(this.syncTimer)
if (this.playbackSyncInterval) window.clearInterval(this.playbackSyncInterval)
}
}
</script>