Skip to main content

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.

SharedTimer is a type alias for Arc<RwLock<Timer>>, available when the std feature is enabled. It allows multiple owners across threads to safely read and mutate the timer without data races. The HotkeySystem and AutoSplittingRuntime both require a SharedTimer to send commands to the timer from background threads.

Creating a SharedTimer

Convert an owned Timer into a SharedTimer using Timer::into_shared():
use livesplit_core::{Run, Segment, Timer};

let mut run = Run::new();
run.push_segment(Segment::new("Level 1"));
let timer = Timer::new(run).unwrap();

// Convert to a shared, thread-safe timer
let shared = timer.into_shared();
SharedTimer implements Clone — cloning it creates a new Arc reference to the same underlying RwLock<Timer>, not a deep copy of the timer.

Reading and Writing

Use read() and write() to access the underlying Timer:
// Read the current phase without blocking writers for long
let phase = shared.read().unwrap().current_phase();
println!("{:?}", phase);

// Mutate the timer (e.g., record a split)
shared.write().unwrap().split().unwrap();
Hold read and write locks only for the duration of your operation. Holding a write lock while performing slow I/O or waiting on user input will block all other threads that need the timer.

Sharing Across Threads

use livesplit_core::{Run, Segment, Timer};
use std::thread;

let mut run = Run::new();
run.push_segment(Segment::new("Level 1"));
let shared = Timer::new(run).unwrap().into_shared();

// Clone the Arc for the new thread
let shared_clone = shared.clone();
let handle = thread::spawn(move || {
    shared_clone.write().unwrap().start().unwrap();
    println!("Timer started from background thread");
});

handle.join().unwrap();

// Read from the main thread
let phase = shared.read().unwrap().current_phase();
println!("Phase: {:?}", phase);

Using with HotkeySystem and AutoSplittingRuntime

Both HotkeySystem and AutoSplittingRuntime accept a SharedTimer (or any type implementing the CommandSink trait, which SharedTimer implements via the std feature):
use livesplit_core::{HotkeySystem, hotkey::HotkeyConfig};

// SharedTimer implements CommandSink
let shared = timer.into_shared();

// Pass a clone to the hotkey system
let hotkey_system = HotkeySystem::new(shared.clone()).unwrap();

// Pass a clone to the auto splitter runtime (requires auto-splitting feature)
#[cfg(feature = "auto-splitting")]
{
    use livesplit_core::auto_splitting::Runtime;
    use std::path::PathBuf;
    let runtime = Runtime::new(shared.clone()).unwrap();
    runtime.load(PathBuf::from("game.wasm")).unwrap();
}

CommandSink Trait

SharedTimer implements the CommandSink trait, which defines async methods for all timer commands. This is the interface used by the hotkey system and auto splitter to control the timer indirectly:
MethodEquivalent Timer method
start()timer.start()
split()timer.split()
split_or_start()timer.split_or_start()
reset(save_attempt)timer.reset(update_splits)
undo_split()timer.undo_split()
skip_split()timer.skip_split()
pause() / resume()timer.pause() / timer.resume()
toggle_pause_or_start()timer.toggle_pause_or_start()
set_game_time(time)timer.set_game_time(time)
set_loading_times(time)timer.set_loading_times(time)
set_custom_variable(name, value)timer.set_custom_variable(name, value)
If you need to implement your own timer control layer (e.g., to show a confirmation dialog before resetting, or to auto-save on reset), implement CommandSink on a custom type and pass it to HotkeySystem and AutoSplittingRuntime instead of using SharedTimer directly.

Build docs developers (and LLMs) love