Documentation Index
Fetch the complete documentation index at: https://mintlify.com/LiveSplit/livesplit-core/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The livesplit_core::analysis module provides standalone functions for calculating run statistics from timer snapshots, runs, and segments. These functions are the computational backbone of the layout components: for example, the PbChance component calls analysis::pb_chance::for_timer internally, and the CurrentPace component calls analysis::current_pace::calculate.
You can call these functions directly when building a custom UI, implementing overlays, or computing statistics outside of a rendered layout.
All functions are pure calculations — they take immutable references and produce results without side effects.
current_pace
pub mod current_pace {
pub fn calculate(timer: &Snapshot, comparison: &str) -> (Option<TimeSpan>, bool);
}
Calculates the projected final time of the current attempt at the current pace, using the chosen comparison as the reference.
- Returns
(Option<TimeSpan>, bool) where the bool indicates whether the value is a live delta (i.e. actively changing because the runner is losing time on the current segment).
- If the timer is running or paused, the pace is extrapolated from the last known delta and the current live segment time.
- If the timer has ended, the actual final split time is returned.
- If the timer is not running, the comparison’s final time is returned.
use livesplit_core::analysis::current_pace;
let (pace, is_live) = current_pace::calculate(&timer.snapshot(), "Personal Best");
if let Some(time) = pace {
println!("Projected finish: {:?} (live: {})", time, is_live);
}
delta
pub mod delta {
pub fn calculate(timer: &Snapshot, comparison: &str) -> (Option<TimeSpan>, bool);
}
Calculates the time delta of the current attempt relative to the chosen comparison at the current split.
- A positive delta means the runner is behind (slower than) the comparison.
- A negative delta means the runner is ahead (faster than) the comparison.
- Returns
(Option<TimeSpan>, bool) where the bool indicates a live delta.
- Returns
(None, false) when the timer is not running.
- When the timer has ended, the delta is computed from the final split time.
use livesplit_core::analysis::delta;
let (d, is_live) = delta::calculate(&timer.snapshot(), "Personal Best");
match d {
Some(delta) if delta < TimeSpan::zero() => println!("Ahead by {:?}", -delta),
Some(delta) => println!("Behind by {:?}", delta),
None => println!("Timer not running"),
}
pb_chance
pub mod pb_chance {
/// PB chance for a run (no active attempt context).
pub fn for_run(run: &Run, method: TimingMethod) -> f64;
/// PB chance for a timer (considers the current attempt).
pub fn for_timer(timer: &Snapshot) -> (f64, bool);
}
Calculates the probability (in the range 0.0–1.0, representing 0%–100%) of achieving a Personal Best.
The calculation is based on the skill curve model: it uses the segment history to build a distribution of historical finishing performances and returns the percentile at which the current (projected) finish time lies. This is also where the BalancedPB comparison sources its split times.
for_run — calculates the general PB probability for a run with no active attempt in context. Useful for displaying a baseline probability between runs.
for_timer — calculates PB probability in the context of a live attempt:
- If the runner is losing time on a live segment, that is factored in immediately.
- Returns
(probability, is_live) where is_live is true when the value is actively changing.
- If there is no PB time recorded, returns
1.0 (100%).
- If the run has ended and it was a PB (or there was no previous PB), returns
1.0; otherwise 0.0.
use livesplit_core::analysis::pb_chance;
use livesplit_core::TimingMethod;
// General probability for the run
let chance = pb_chance::for_run(timer.run(), TimingMethod::RealTime);
println!("General PB chance: {:.1}%", chance * 100.0);
// Live probability during an attempt
let (chance, is_live) = pb_chance::for_timer(&timer.snapshot());
println!("Current PB chance: {:.1}% (live: {})", chance * 100.0, is_live);
possible_time_save
pub mod possible_time_save {
/// Time save for a specific segment.
pub fn calculate(
timer: &Snapshot,
segment_index: usize,
comparison: &str,
live: bool,
) -> (Option<TimeSpan>, bool);
/// Total time save for all remaining segments.
pub fn calculate_total(
timer: &Snapshot,
segment_index: usize,
comparison: &str,
) -> (TimeSpan, bool);
}
Calculates how much time could be saved on a specific segment (or all remaining segments) based on the best segments recorded in the history. This is an approximation; best segments don’t represent theoretically perfect times.
calculate returns the possible time save for a single segment. Setting live to true causes the value to shrink in real-time as the current segment progresses (if the runner is on that segment).
- The returned
TimeSpan is never negative — if the comparison time is already at or below the best segment time, Some(zero) is returned.
calculate_total sums the possible time saves for all segments from segment_index onward, always using the live mode.
use livesplit_core::analysis::possible_time_save;
let current_index = timer.current_split_index().unwrap_or(0);
let (save, is_live) = possible_time_save::calculate(
&timer.snapshot(),
current_index,
"Personal Best",
true,
);
println!("Possible time save: {:?}", save);
sum_of_segments
pub mod sum_of_segments {
/// Sum of Best Segments (fastest possible run).
pub fn calculate_best(
segments: &[Segment],
simple_calculation: bool,
use_current_run: bool,
method: TimingMethod,
) -> Option<TimeSpan>;
/// Sum of Worst Segments (slowest possible run).
pub fn calculate_worst(
segments: &[Segment],
use_current_run: bool,
method: TimingMethod,
) -> Option<TimeSpan>;
}
Calculates the Sum of Best Segments (theoretical fastest finish) and Sum of Worst Segments (theoretical slowest finish) based on all recorded attempt history.
The Sum of Best is not simply the sum of each segment’s best time: skipped segments can create combined-segment records that are faster than the sum of their parts. The calculation therefore uses dynamic programming over the full segment history to find the optimal path.
simple_calculation — if true, skips the dynamic-programming step and uses only the best individual segment times (ignoring combined segments from skips). Faster but less accurate.
use_current_run — if true, includes the current in-progress attempt in the calculation.
use livesplit_core::analysis::sum_of_segments;
use livesplit_core::TimingMethod;
let sob = sum_of_segments::calculate_best(
timer.run().segments(),
false,
false,
TimingMethod::RealTime,
);
println!("Sum of Best: {:?}", sob);
total_playtime
pub mod total_playtime {
pub trait TotalPlaytime {
fn total_playtime(&self) -> TimeSpan;
}
pub fn calculate<T: TotalPlaytime>(source: T) -> TimeSpan;
}
Calculates the total time invested in a game across all recorded attempts. The TotalPlaytime trait is implemented for both Run and Timer:
Run — sums the duration of every entry in the attempt history (with pause time subtracted). For attempts from LiveSplit versions before 1.6.0 that didn’t record duration, it falls back to summing segment history times.
Timer — adds the current attempt’s elapsed time on top of the run’s historical total.
use livesplit_core::analysis::total_playtime;
// Total time across all completed attempts in the run file
let total = total_playtime::calculate(timer.run());
println!("Total playtime: {:?}", total);
// Including the current attempt
let total_with_current = total_playtime::calculate(&timer);
println!("Total playtime (incl. current): {:?}", total_with_current);
Direct Use in Custom UIs
The analysis functions are surfaced automatically when you use the corresponding layout components. For custom UIs or data export tools that don’t use the layout system, you can call them directly:
use livesplit_core::analysis;
use livesplit_core::TimingMethod;
let snap = timer.snapshot();
// Calculate total time invested in this game
let total = analysis::total_playtime::calculate(snap.run());
println!("Total playtime: {:?}", total);
// Check live PB probability
let (chance, _) = analysis::pb_chance::for_timer(&snap);
println!("PB chance: {:.0}%", chance * 100.0);
// Project finish time
let (pace, _) = analysis::current_pace::calculate(&snap, "Personal Best");
println!("Projected time: {:?}", pace);