refactor: ensure clocks timer and clock are in sync (#1772)

* refactor: ensure clocks timer and clock are in sync

---------

Co-authored-by: arc-alex <ac@omnivox.dk>
This commit is contained in:
Carlos Valente
2025-09-07 19:22:36 +02:00
committed by Alex Christoffer Rasmussen
parent 02243242f1
commit 4385e0b9c9
2 changed files with 33 additions and 17 deletions
@@ -636,6 +636,8 @@ const eventTimer = new EventTimer({
}); });
export const runtimeService = new RuntimeService(eventTimer); export const runtimeService = new RuntimeService(eventTimer);
type EntryUpdateKeys = keyof Pick<RuntimeState, 'eventNow' | 'eventNext' | 'eventFlag' | 'groupNow'>;
/** /**
* Decorator manages side effects from updating the runtime * Decorator manages side effects from updating the runtime
* This should only be applied to functions that are exposed for consumption * This should only be applied to functions that are exposed for consumption
@@ -672,21 +674,30 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert
// combine all big changes // combine all big changes
const hasImmediateChanges = entryChanged || justStarted || hasChangedPlayback || offsetModeChanged; const hasImmediateChanges = entryChanged || justStarted || hasChangedPlayback || offsetModeChanged;
// clock has changed by a second or more /**
const updateClock = getShouldClockUpdate(RuntimeService.previousState.clock, state.clock); * if any values have changed.
if (updateClock) { * values that have the possibility to tick are updated when the seconds roll over
batch.add('clock', state.clock); */
RuntimeService.previousState.clock = state.clock;
}
// if any values have changed, values that have the possibility to tick are updated when the seconds roll over
const updateTimer = getShouldTimerUpdate(RuntimeService.previousState?.timer, state.timer); const updateTimer = getShouldTimerUpdate(RuntimeService.previousState?.timer, state.timer);
if (updateTimer) { if (updateTimer) {
batch.add('timer', state.timer); batch.add('timer', state.timer);
RuntimeService.previousState.timer = { ...state.timer }; RuntimeService.previousState.timer = { ...state.timer };
} }
// if any values have changed, values that have the possibility to tick are modulated by `hasClockUpdate` /**
* clock has changed by a second or more.
* or the timer updated so we ensure that the timer and clock ticks are in sync
*/
const updateClock = updateTimer || getShouldClockUpdate(RuntimeService.previousState.clock, state.clock);
if (updateClock) {
batch.add('clock', state.clock);
RuntimeService.previousState.clock = state.clock;
}
/**
* if any values have changed.
* values that have the possibility to tick are modulated by `updateClock || hasImmediateChanges`
*/
const updateRuntime = getShouldOffsetUpdate( const updateRuntime = getShouldOffsetUpdate(
RuntimeService.previousState?.offset, RuntimeService.previousState?.offset,
state.offset, state.offset,
@@ -697,16 +708,16 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert
RuntimeService.previousState.offset = structuredClone(state.offset); RuntimeService.previousState.offset = structuredClone(state.offset);
} }
// if any values have changed /**
* if any values have changed.
*/
const updateRundownData = !deepEqual(RuntimeService.previousState.rundown, state.rundown); const updateRundownData = !deepEqual(RuntimeService.previousState.rundown, state.rundown);
if (updateRundownData) { if (updateRundownData) {
batch.add('rundown', state.rundown); batch.add('rundown', state.rundown);
RuntimeService.previousState.rundown = structuredClone(state.rundown); RuntimeService.previousState.rundown = structuredClone(state.rundown);
} }
function updateMaybeEntryIfChanged< function updateMaybeEntryIfChanged<K extends EntryUpdateKeys>(key: K) {
K extends keyof Pick<RuntimeState, 'eventNow' | 'eventNext' | 'eventFlag' | 'groupNow'>,
>(key: K) {
const previousEntry = RuntimeService.previousState[key]; const previousEntry = RuntimeService.previousState[key];
const currentEntry = state[key]; const currentEntry = state[key];
@@ -24,7 +24,6 @@ export function isNewSecond(
/** /**
* Checks whether we should update the clock value * Checks whether we should update the clock value
* - we have rolled into a new seconds unit * - we have rolled into a new seconds unit
* this is different from the timer update as it looks at the clock as counting up
*/ */
export function getShouldClockUpdate(previousUpdate: number, now: number): boolean { export function getShouldClockUpdate(previousUpdate: number, now: number): boolean {
const newSeconds = millisToSeconds(now, TimerType.CountUp) !== millisToSeconds(previousUpdate, TimerType.CountUp); const newSeconds = millisToSeconds(now, TimerType.CountUp) !== millisToSeconds(previousUpdate, TimerType.CountUp);
@@ -32,8 +31,10 @@ export function getShouldClockUpdate(previousUpdate: number, now: number): boole
} }
/** /**
* Checks whether we should update the timer value * Checks whether we should update the timer values
* - we have rolled into a new seconds unit * - `current` and `secondaryTimer` trigger on seconds roll over
* - the rest trigger on any change
* - `elapsed` and `expectedFinish` is not checked
*/ */
export function getShouldTimerUpdate(previousValue: TimerState | undefined, currentValue: TimerState): boolean { export function getShouldTimerUpdate(previousValue: TimerState | undefined, currentValue: TimerState): boolean {
if (previousValue === undefined) return true; if (previousValue === undefined) return true;
@@ -53,6 +54,11 @@ export function getShouldTimerUpdate(previousValue: TimerState | undefined, curr
); );
} }
/**
* Checks whether we should update the offset values
* - `mode` triggers update
* - `absolute`, `relative`, `expected**End` are ticked with `didDependencyUpdate`
*/
export function getShouldOffsetUpdate( export function getShouldOffsetUpdate(
previousValue: Offset | undefined, previousValue: Offset | undefined,
currentValue: Offset, currentValue: Offset,
@@ -60,7 +66,6 @@ export function getShouldOffsetUpdate(
): boolean { ): boolean {
if (previousValue === undefined) return true; if (previousValue === undefined) return true;
if (previousValue.mode !== currentValue.mode) return true; if (previousValue.mode !== currentValue.mode) return true;
// absolute, relative, expected*End are ticked with `didDependencyUpdate`
return didDependencyUpdate && !deepEqual(previousValue, currentValue); return didDependencyUpdate && !deepEqual(previousValue, currentValue);
} }