GE Lua Documentation

Press F to search!

updateSlowFromRace

Definition


-- @/lua/ge/extensions/core/lapTimes.lua:240

-- Update slow-changing data from race state
local function updateSlowFromRace(race, playerId)
  if not race or not race.states or not race.states[playerId] then
    return
  end

  local state = race.states[playerId]

  -- Update status from race state
  if state.complete == true then
    status = "complete"
  elseif race.suspended == true then
    status = "paused"
  elseif race.started == true then
    status = "started"
  else
    status = "stopped"
  end

  -- Update current lap/segment
  if state.complete == true then
    -- When race is complete, show max values
    currentLap = totalLaps
    currentSegment = totalSegments
  else
    currentLap = (state.currentLap or 0) + 1
    currentSegment = (#state.currentTimes) + 1
  end

  -- Process historical lap times to find best lap
  local bestLap = nil
  local lapData = {} -- Temporary for this function only

  for i = 1, #state.historicTimes do
    local l = state.historicTimes[i]
    local duration = l.duration or ((l.endTime or 0) - (l.beginTime or 0))

    if not bestLap or duration < bestLap then
      bestLap = duration
    end

    -- Store minimal data needed for stream building
    lapData[i] = {
      lap = (l.lap or (i - 1)) + 1,
      startTime = l.beginTime,
      endTime = l.endTime,
      duration = duration,
      time = l.endTime
    }
  end

  -- Build historical segment times and find bests
  table.clear(segmentTimes)
  table.clear(segmentTimesByLap)
  table.clear(bestSegmentTimes)
  table.clear(bestSegmentLaps)
  local bestPerIndex = {}

  -- Process completed laps from historicTimes
  for i = 1, #state.historicTimes do
    local l = state.historicTimes[i]
    local oneBasedLap = (l.lap or (i - 1)) + 1
    for idx = 1, #l.segmentTimes do
      local s = l.segmentTimes[idx]
      local duration = s.duration or ((s.endTime or 0) - (s.beginTime or 0))
      if not bestPerIndex[idx] or duration < bestPerIndex[idx] then
        bestPerIndex[idx] = duration
        bestSegmentTimes[idx] = duration
        bestSegmentLaps[idx] = oneBasedLap
      end
      local segEntry = {
        lap = oneBasedLap,
        segment = idx,
        startTime = s.beginTime,
        endTime = s.endTime,
        duration = duration,
        skipped = false,
        isBest = false,
        diffToPrevious = nil,
        diffToBest = nil
      }
      table.insert(segmentTimes, segEntry)
      segmentTimesByLap[oneBasedLap] = segmentTimesByLap[oneBasedLap] or {}
      segmentTimesByLap[oneBasedLap][idx] = segEntry
    end
  end

  -- Process current lap segments from currentTimes
  for idx = 1, #state.currentTimes do
    local s = state.currentTimes[idx]
    local duration = s.duration or ((s.endTime or 0) - (s.beginTime or 0))
    if not bestPerIndex[idx] or duration < bestPerIndex[idx] then
      bestPerIndex[idx] = duration
      bestSegmentTimes[idx] = duration
      bestSegmentLaps[idx] = (state.currentLap or 0) + 1
    end
    local segEntry = {
      lap = (state.currentLap or 0) + 1,
      segment = idx,
      startTime = s.beginTime,
      endTime = s.endTime,
      duration = duration,
      skipped = false,
      isBest = false,
      diffToPrevious = nil,
      diffToBest = nil
    }
    table.insert(segmentTimes, segEntry)
    local currentLapIndex1 = (state.currentLap or 0) + 1
    segmentTimesByLap[currentLapIndex1] = segmentTimesByLap[currentLapIndex1] or {}
    segmentTimesByLap[currentLapIndex1][idx] = segEntry
  end

  -- Compute diffs and mark bests
  -- Build quick access map lap->index->duration
  local lapIndexToDuration = {}
  for _, seg in ipairs(segmentTimes) do
    lapIndexToDuration[seg.lap] = lapIndexToDuration[seg.lap] or {}
    lapIndexToDuration[seg.lap][seg.segment] = seg.duration
  end

  for _, seg in ipairs(segmentTimes) do
    if lapIndexToDuration[seg.lap - 1] and lapIndexToDuration[seg.lap - 1][seg.segment] then
      seg.diffToPrevious = seg.duration - lapIndexToDuration[seg.lap - 1][seg.segment]
    end
    local bestAtIdx = bestPerIndex[seg.segment]
    seg.diffToBest = bestAtIdx and (seg.duration - bestAtIdx) or nil
    if bestAtIdx and seg.duration == bestAtIdx then
      seg.isBest = true
    end
  end

  -- Update best lap tracking
  bestLapTime = bestLap
  bestLapIndex = 0
  if bestLapTime then
    for i, lap in ipairs(lapData) do
      if lap.duration == bestLapTime then
        bestLapIndex = i
        break
      end
    end
  end

  -- Populate slow stream data directly
  table.clear(slowStreamData)

  -- Race status
  slowStreamData.status = status

  -- Current lap and segment
  slowStreamData.currentLap = currentLap
  slowStreamData.currentSegment = currentSegment

  -- Best times
  slowStreamData.bestLapTimeFormatted = bestLapTime and M.formatTime(bestLapTime) or nil
  slowStreamData.bestLapIndex = bestLapIndex

  -- Format best segment times with lap information
  slowStreamData.bestSegmentTimesFormatted = {}
  for segmentIndex, time in pairs(bestSegmentTimes) do
    local lap = bestSegmentLaps[segmentIndex] or 1
    slowStreamData.bestSegmentTimesFormatted[segmentIndex] = {
      time = M.formatTime(time),
      lap = lap
    }
  end

  -- Historical lap times - build stream directly from temporary data
  slowStreamData.lapTimes = {}
  local prevLapDuration = nil

  for i, lap in ipairs(lapData) do
    -- Calculate diffs
    local diffToPrevious = prevLapDuration and (lap.duration - prevLapDuration) or nil
    local diffToBest = bestLapTime and (lap.duration - bestLapTime) or nil
    local isBest = bestLapTime and lap.duration == bestLapTime or false
    -- Only mark as best if there are multiple laps to compare
    local hasMultipleLaps = #lapData > 1

    local lapInfo = {
      lap = lap.lap,
      durationFormatted = M.formatTime(lap.duration),
      timeFormatted = lap.time and M.formatTime(lap.time) or nil,
      endTimeFormatted = M.formatTime(lap.endTime),
      lapFlavor = (isBest and hasMultipleLaps) and 'best' or 'default',
      diffToPreviousFormatted = diffToPrevious and M.formatTime(diffToPrevious, true) or nil,
      diffToBestFormatted = diffToBest and M.formatTime(diffToBest, true) or nil,
      diffToPreviousFlavor = diffToPrevious and getDiffFlavor(diffToPrevious) or 'default',
      diffToBestFlavor = diffToBest and getDiffFlavor(diffToBest) or 'default'
    }
    table.insert(slowStreamData.lapTimes, lapInfo)
    prevLapDuration = lap.duration
  end

  -- Historical segment times
  slowStreamData.segmentTimes = {}

  -- Count how many times each segment index appears
  local segmentCounts = {}
  for _, segment in ipairs(segmentTimes) do
    segmentCounts[segment.segment] = (segmentCounts[segment.segment] or 0) + 1
  end

  for i, segment in ipairs(segmentTimes) do
    -- Only mark as best if there are multiple instances of this segment to compare
    local hasMultipleInstances = segmentCounts[segment.segment] > 1

    local segmentInfo = {
      segment = segment.lap and (segment.lap .. '-' .. segment.segment) or segment.segment,
      durationFormatted = M.formatTime(segment.duration),
      timeFormatted = segment.time and M.formatTime(segment.time) or nil,
      endTimeFormatted = M.formatTime(segment.endTime),
      segmentFlavor = (segment.isBest and hasMultipleInstances) and 'best' or 'default',
      diffToPreviousFormatted = segment.diffToPrevious and M.formatTime(segment.diffToPrevious, true) or nil,
      diffToBestFormatted = segment.diffToBest and M.formatTime(segment.diffToBest, true) or nil,
      diffToPreviousFlavor = segment.diffToPrevious and getDiffFlavor(segment.diffToPrevious) or 'default',
      diffToBestFlavor = segment.diffToBest and getDiffFlavor(segment.diffToBest) or 'default'
    }
    table.insert(slowStreamData.segmentTimes, segmentInfo)
  end

  -- Combined lap+segment list (alternating segments then lap for completed laps, plus current lap segments)
  slowStreamData.combinedTimes = {}

  -- Include completed laps (from historicTimes)
  for i, lap in ipairs(lapData) do
    -- Add all segments for this lap
    if segmentTimesByLap[lap.lap] then
      for segmentIndex = 1, totalSegments do
        local segment = segmentTimesByLap[lap.lap][segmentIndex]
        if segment then
          local hasMultipleInstances = segmentCounts[segment.segment] > 1
          local combinedSegmentInfo = {
            type = 'segment',
            lap = segment.lap,
            segment = segment.segment,
            identifier = segment.lap .. '-' .. segment.segment,
            durationFormatted = M.formatTime(segment.duration),
            timeFormatted = segment.time and M.formatTime(segment.time) or nil,
            endTimeFormatted = M.formatTime(segment.endTime),
            flavor = (segment.isBest and hasMultipleInstances) and 'best' or 'default',
            diffToPreviousFormatted = segment.diffToPrevious and M.formatTime(segment.diffToPrevious, true) or nil,
            diffToBestFormatted = segment.diffToBest and M.formatTime(segment.diffToBest, true) or nil,
            diffToPreviousFlavor = segment.diffToPrevious and getDiffFlavor(segment.diffToPrevious) or 'default',
            diffToBestFlavor = segment.diffToBest and getDiffFlavor(segment.diffToBest) or 'default'
          }
          table.insert(slowStreamData.combinedTimes, combinedSegmentInfo)
        end
      end
    end

    -- Add the lap itself after all its segments
    local diffToPrevious = i > 1 and (lap.duration - lapData[i-1].duration) or nil
    local diffToBest = bestLapTime and (lap.duration - bestLapTime) or nil
    local isBest = bestLapTime and lap.duration == bestLapTime or false
    local hasMultipleLaps = #lapData > 1

    local combinedLapInfo = {
      type = 'lap',
      lap = lap.lap,
      identifier = lap.lap,
      durationFormatted = M.formatTime(lap.duration),
      timeFormatted = lap.time and M.formatTime(lap.time) or nil,
      endTimeFormatted = M.formatTime(lap.endTime),
      flavor = (isBest and hasMultipleLaps) and 'best' or 'default',
      diffToPreviousFormatted = diffToPrevious and M.formatTime(diffToPrevious, true) or nil,
      diffToBestFormatted = diffToBest and M.formatTime(diffToBest, true) or nil,
      diffToPreviousFlavor = diffToPrevious and getDiffFlavor(diffToPrevious) or 'default',
      diffToBestFlavor = diffToBest and getDiffFlavor(diffToBest) or 'default'
    }
    table.insert(slowStreamData.combinedTimes, combinedLapInfo)
  end

  -- Add completed segments from current lap (if any)
  local currentLapIndex1 = (state.currentLap or 0) + 1
  if segmentTimesByLap[currentLapIndex1] and not state.complete then
    for segmentIndex = 1, totalSegments do
      local segment = segmentTimesByLap[currentLapIndex1][segmentIndex]
      if segment then
        local hasMultipleInstances = segmentCounts[segment.segment] > 1
        local combinedSegmentInfo = {
          type = 'segment',
          lap = segment.lap,
          segment = segment.segment,
          identifier = segment.lap .. '-' .. segment.segment,
          durationFormatted = M.formatTime(segment.duration),
          timeFormatted = segment.time and M.formatTime(segment.time) or nil,
          endTimeFormatted = M.formatTime(segment.endTime),
          flavor = (segment.isBest and hasMultipleInstances) and 'best' or 'default',
          diffToPreviousFormatted = segment.diffToPrevious and M.formatTime(segment.diffToPrevious, true) or nil,
          diffToBestFormatted = segment.diffToBest and M.formatTime(segment.diffToBest, true) or nil,
          diffToPreviousFlavor = segment.diffToPrevious and getDiffFlavor(segment.diffToPrevious) or 'default',
          diffToBestFlavor = segment.diffToBest and getDiffFlavor(segment.diffToBest) or 'default'
        }
        table.insert(slowStreamData.combinedTimes, combinedSegmentInfo)
      end
    end
  end

  -- Set flag for onUpdate to send the stream
  needSlowStream = true
end

Callers

@/lua/ge/extensions/gameplay/crawl/utils.lua
              updateRaceDataStructure(crawlerId, state, state.raceData)
              core_lapTimes.updateSlowFromRace(state.raceData, crawlerId)
            end
                updateRaceDataStructure(crawlerId, state, state.raceData)
                core_lapTimes.updateSlowFromRace(state.raceData, crawlerId)
              end
        updateRaceDataStructure(crawlerId, state, state.raceData)
        core_lapTimes.updateSlowFromRace(state.raceData, crawlerId)
        core_lapTimes.onRaceStop()
@/lua/ge/extensions/gameplay/race/race.lua
        if core_lapTimes.updateSlowFromRace then
          core_lapTimes.updateSlowFromRace(self, id)
        end
      if core_lapTimes.updateSlowFromRace then
        core_lapTimes.updateSlowFromRace(self, id)
      end
      if core_lapTimes.updateSlowFromRace then
        core_lapTimes.updateSlowFromRace(self, id)
      end
      if core_lapTimes.updateSlowFromRace then
        core_lapTimes.updateSlowFromRace(self, id)
      end