Fix staggered heat call-out order and extend its shared clock
getEventDrivers followed the drivers directory's internal creation order instead of the order participants were actually selected for the event, so the fallback qualifying/finals grid, the Start List display, and the staggered-release announcer all read out drivers in a scrambled order relative to how the event was actually entered. Fixed to preserve raceConfig.driverIds' own order. Also, a staggered heat's shared clock/auto-finish target only ever counted the flat session duration, not the staggered release schedule itself - so the last-released driver's own personal window could still be running when the session force-finished. The shared clock now widens by (driverCount - 1) * staggerGapSec on top of the base duration for staggered sessions; each driver's own personal quota is unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -548,7 +548,16 @@ export function getEventDrivers(event, { state }) {
|
||||
if (!event?.raceConfig?.participantsConfigured) {
|
||||
return classDrivers;
|
||||
}
|
||||
return classDrivers.filter((driver) => (event.raceConfig.driverIds || []).includes(driver.id));
|
||||
// Preserves the order participants were selected in (event.raceConfig.driverIds), not
|
||||
// the club's global driver-directory order - the two rarely match, and anything seeded
|
||||
// from this list (a fallback qualifying/finals grid with no practice data yet, a
|
||||
// staggered heat's start-order announcement) should follow the order a club admin
|
||||
// actually built the entry list in, not an unrelated internal array order.
|
||||
const classDriverIds = new Set(classDrivers.map((driver) => driver.id));
|
||||
const byId = new Map(classDrivers.map((driver) => [driver.id, driver]));
|
||||
return (event.raceConfig.driverIds || [])
|
||||
.filter((id) => classDriverIds.has(id))
|
||||
.map((id) => byId.get(id));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -79,7 +79,20 @@ export function getPassingValidationLabel(passing, { t }) {
|
||||
|
||||
|
||||
export function getSessionTiming(session, nowTs = Date.now(), { getSessionTargetMs }) {
|
||||
const targetMs = getSessionTargetMs(session);
|
||||
const baseTargetMs = getSessionTargetMs(session);
|
||||
// A staggered heat doesn't release every car at once (see runStartSequence in
|
||||
// race_audio.js) - the shared clock and auto-finish threshold need to cover the full
|
||||
// release schedule too, or the last-released driver's own personal window could still
|
||||
// be running when the session force-finishes and locks everyone out. Each competitor's
|
||||
// OWN personal duration (used for buildLeaderboard's personalTimeUp) stays exactly
|
||||
// durationMin - this only widens the session-level clock, not any individual's quota.
|
||||
const gridCount = Array.isArray(session?.manualGridIds) && session.manualGridIds.length
|
||||
? session.manualGridIds.length
|
||||
: Array.isArray(session?.driverIds) ? session.driverIds.length : 0;
|
||||
const staggerExtensionMs = session?.startMode === "staggered" && baseTargetMs !== null && gridCount > 1
|
||||
? (gridCount - 1) * Math.max(0, Number(session?.staggerGapSec || 3) || 3) * 1000
|
||||
: 0;
|
||||
const targetMs = baseTargetMs === null ? null : baseTargetMs + staggerExtensionMs;
|
||||
const startedAt = Number(session?.startedAt || 0);
|
||||
const pausedAt = Number(session?.pausedAt || 0) || 0;
|
||||
const endedAt = Number(session?.endedAt || 0) || 0;
|
||||
|
||||
Reference in New Issue
Block a user