diff --git a/apps/server/src/services/runtime-service/RuntimeService.ts b/apps/server/src/services/runtime-service/RuntimeService.ts index 1d8be1d4f..a4bded621 100644 --- a/apps/server/src/services/runtime-service/RuntimeService.ts +++ b/apps/server/src/services/runtime-service/RuntimeService.ts @@ -48,6 +48,7 @@ class RuntimeService { /** last time we updated the socket */ static previousTimerUpdate: number; + static previousRuntimeUpdate: number; static previousTimerValue: MaybeNumber; // previous timer value, could be null static previousClockUpdate: number; @@ -58,6 +59,7 @@ class RuntimeService { this.eventTimer = eventTimer; RuntimeService.previousTimerUpdate = -1; + RuntimeService.previousRuntimeUpdate = -1; RuntimeService.previousTimerValue = -1; RuntimeService.previousClockUpdate = -1; RuntimeService.previousState = {} as RuntimeState; @@ -678,10 +680,17 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert // we do the comparison by explicitly for each property // to apply custom logic for different datasets - const shouldForceTimerUpdate = getForceUpdate(RuntimeService.previousTimerUpdate, state.clock); + const shouldForceTimerUpdate = getForceUpdate( + RuntimeService.previousTimerUpdate, + state.clock, + state.timer.playback, + ); + const shouldUpdateTimer = shouldForceTimerUpdate || getShouldTimerUpdate(RuntimeService.previousTimerValue, state.timer.current); + const shouldRuntimeUpdate = shouldUpdateTimer || getForceUpdate(RuntimeService.previousRuntimeUpdate, state.clock); + // some changes need an immediate update const hasNewLoaded = state.eventNow?.id !== RuntimeService.previousState?.eventNow?.id; @@ -696,12 +705,20 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert if (hasImmediateChanges || (shouldUpdateTimer && !deepEqual(RuntimeService.previousState?.timer, state.timer))) { RuntimeService.previousTimerUpdate = state.clock; RuntimeService.previousTimerValue = state.timer.current; + RuntimeService.previousClockUpdate = state.clock; + eventStore.set('clock', state.clock); eventStore.set('timer', state.timer); RuntimeService.previousState.timer = { ...state.timer }; } - if (hasChangedPlayback || (shouldUpdateTimer && !deepEqual(RuntimeService.previousState?.runtime, state.runtime))) { + if ( + hasChangedPlayback || + (shouldRuntimeUpdate && !deepEqual(RuntimeService.previousState?.runtime, state.runtime)) + ) { eventStore.set('runtime', state.runtime); + RuntimeService.previousClockUpdate = state.clock; + RuntimeService.previousRuntimeUpdate = state.clock; + eventStore.set('clock', state.clock); RuntimeService.previousState.runtime = { ...state.runtime }; } @@ -711,15 +728,14 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert updateEventIfChanged('eventNext', state); updateEventIfChanged('publicEventNext', state); - let syncBlockStartAt = false; - if (!deepEqual(RuntimeService?.previousState.currentBlock, state.currentBlock)) { eventStore.set('currentBlock', state.currentBlock); RuntimeService.previousState.currentBlock = { ...state.currentBlock }; - syncBlockStartAt = true; + RuntimeService.previousClockUpdate = state.clock; + eventStore.set('clock', state.clock); } - const shouldUpdateClock = syncBlockStartAt || getShouldClockUpdate(RuntimeService.previousClockUpdate, state.clock); + const shouldUpdateClock = getShouldClockUpdate(RuntimeService.previousClockUpdate, state.clock); if (shouldUpdateClock) { RuntimeService.previousClockUpdate = state.clock; diff --git a/apps/server/src/services/runtime-service/rundownService.utils.ts b/apps/server/src/services/runtime-service/rundownService.utils.ts index 9e2e5b1f4..ca0b851ac 100644 --- a/apps/server/src/services/runtime-service/rundownService.utils.ts +++ b/apps/server/src/services/runtime-service/rundownService.utils.ts @@ -1,7 +1,7 @@ import { millisToSeconds } from 'ontime-utils'; import { timerConfig } from '../../config/config.js'; -import { MaybeNumber } from 'ontime-types'; +import { MaybeNumber, Playback } from 'ontime-types'; /** * Checks whether we should update the clock value @@ -34,8 +34,10 @@ export function getShouldTimerUpdate(previousValue: number, currentValue: MaybeN * In some cases we want to force an update to the timer * - if the clock has slid back * - if we have escaped the update rate (clock slid forward) + * - if we are not playing then there is no need to update the timer */ -export function getForceUpdate(previousUpdate: number, now: number): boolean { +export function getForceUpdate(previousUpdate: number, now: number, playbackState: Playback = Playback.Play): boolean { + if (playbackState !== Playback.Play) return false; const isClockBehind = now < previousUpdate; const hasExceededRate = now - previousUpdate >= timerConfig.notificationRate; return isClockBehind || hasExceededRate; diff --git a/apps/server/src/stores/EventStore.ts b/apps/server/src/stores/EventStore.ts index 1405a23ba..7bc2b620e 100644 --- a/apps/server/src/stores/EventStore.ts +++ b/apps/server/src/stores/EventStore.ts @@ -7,6 +7,8 @@ export type StoreGetter = (key: T) => Partial = {}; +const changedKeys = new Set(); +let isUpdatePending: NodeJS.Immediate | null = null; /** * A runtime store that broadcasts its payload * - init: allows for adding an initial payload to the store @@ -23,16 +25,20 @@ export const eventStore = { }, set(key: T, value: RuntimeStore[T]) { store[key] = value; - socket.sendAsJson({ - type: `ontime-${key}`, - payload: value, - }); - }, - batchSet(values: Partial) { - Object.entries(values).forEach(([key, value]) => { - store[key] = value; - }); - this.broadcast(); + + // check if the key is already marked for and update otherwise push it onto the update array + changedKeys.add(key); + + //if there is already and update pending we don't need to schedule another one + if (!isUpdatePending) { + isUpdatePending = setImmediate(() => { + for (const dataKey of changedKeys) { + socket.sendAsJson({ type: `ontime-${dataKey}`, payload: store[dataKey] }); + } + isUpdatePending = null; + changedKeys.clear(); + }); + } }, poll() { return store;