Overview
mist
(https://codeberg.org/periwinkle/mist) is a speedrun timer written in Rust, targeting mainly Linux, but mostly supporting Windows and macOS. It started out as a series of projects that I used to learn Rust, back in 2019 or something. LiveSplit is essentially the
industry standard when it comes to speedrun timers, but it only runs on windows, weighs 80MB, and doesn't really behave under wine, so I struck out to Make A Linux Speedrun Timer. Other options were out there but they either didn't work for me or
were horribly unmaintained. mist is now effectively feature-complete.
Split files
mist stores information about a speedrun and its associated times in a ron file that looks something like this.
version 2
(
game_title: "The Legend of Zelda: Breath of the Wild",
category: "Any%",
offset: Time(2400),
pb: None,
splits: [
"xeryph",
"crying skip",
"magnet man",
"\'learn new plat\' - mako",
"old man",
"wily castle",
"refights",
"wily machine",
"oops wrong game",
],
pb_times: [
None,
None,
None,
None,
None,
None,
None,
None,
None,
],
gold_times: [
Time(275892),
Time(240185),
Time(198240),
Time(177942),
Time(130434),
None,
None,
None,
None,
],
sum_times: [
(6, Time(1388960)),
(5, Time(874973)),
(5, Time(761325)),
(5, Time(743452)),
(6, Time(653603)),
(3, Time(862507)),
(3, Time(361094)),
(1, None),
(1, None),
],
)
I think a lot of the elements are fairly self-explanatory but here we go.
version 2
: tells mist what version of split file this is. Version 1 is used by versions of mist older than 1.16.0.game_title
: the title of the game; used in the window titlecategory
: the speedrun being performed; same as above.offset
: the starting offset of the run, a TimeType. Cannot beSkipped
. A value ofNone
means no offset.pb
: the personal best time, a TimeType.splits
: an array of split names. can be empty for no splits.pb_times
: array of the TimeTypes for each split on the personal best run. (i.e. the one that set the time frompb
.) Must have the same number of elements assplits
.gold_times
: array of TimeTypes for the best ever time for each split. Must have the same number of elements assplits
.sum_times
: array of tuples containing the number of attempts of each split and the total time spent on the split, respectively. Used for calculating averages. Must have the same number of elements assplits
.
TimeType
TimeType
is a representation of a time that allows for no time and a skipped split to be represented. The variants are as follows:
Time(ms)
: A regular time, with a duration ofms
milliseconds.Skipped(ms)
: A split that was skipped on the PB run, but lasted forms
milliseconds before being skipped. This is saved in the split file to help with calculating various comparisons.None
: No time. Can be used to represent that a split, pb, or gold has never been set before, or no offset.
Configuration
mist reads its configuration file from the standard location on each operating system, as follows:
- Linux:
$XDG_CONFIG_HOME/mist/mist.cfg
or$HOME/.config/mist/mist.cfg
, e.g./home/peri/.config/mist/mist.cfg
- macOS:
$HOME/Library/Preferences/mist/mist.cfg
, e.g./Users/Peri/Library/Preferences/mist/mist.cfg
- Windows:
{FOLDERID_RoamingAppData}
, e.g.C:\Users\Peri\AppData\Roaming
Alternatively, if compiled with theportable
feature, it will read configuration from themist.cfg
file in the same directory as the executable.
A configuration file is also in the ron format, and looks like this. In fact, this is the default config file.
#![enable(implicit_some)]
(
def_file: None,
win_size: (300, 500),
img_file: None,
img_scaled: false,
inline_splits: false,
colors: (
ahead: (0, 255, 0, 255),
behind: (255, 0, 0, 255),
gaining: (255, 90, 90, 255),
losing: (135, 255, 125, 255),
gold: (255, 255, 0, 255),
highlight: (0, 0, 255, 255),
line: (128, 128, 128, 255),
background: (0, 0, 0, 0),
text: (255, 255, 255, 255),
),
frame_rounding: None,
panels: [],
t_font: (
ty: System(
name: "SansSerif",
style: Normal,
weight: Bold,
),
size: 60,
),
s_font: (
ty: System(
name: "SansSerif",
style: Italic,
weight: Medium,
),
size: 25,
),
ms_ratio: 1.0,
confirm_exit: true,
binds: (
pause: "Return",
reset: "R",
start_split: "Space",
skip_split: "Right Shift",
un_split: "Backspace",
prev_comp: "Left",
next_comp: "Right",
load_splits: "F1",
load_config: "F2",
),
)
The elements of the file are as follows:
def_file
: the split file that mist should try to open on startup.None
to open no file.win_size
: the size of the window that will openimg_file
(available with bg feature enabled): the path of the image to use as the timer background, orNone
for no background.img_scaled
(available with bg feature enabled): whether the image should be scaled to fit the timer window, or cropped.inline_splits
: whether the split name and time should be in the same row, or on separate rows.colors
: the colors used for the timer's various displays. The fields are as follows:ahead
: the color used for when the timer is aheadbehind
: when the timer is behindgaining
: when the timer is behind but is less behind than beforelosing
: when the timer is ahead but is less ahead than beforegold
: when a record for a split is brokenhighlight
: the color used to highlight the active splitline
: the color of the line between splitsbackground
: the backgroundtext
: the color used for all other text, such as when the timer is not running and for split names
frame_rounding
: what framerate mist should round times to on pause or finish, orNone
for no rounding. See frame rounding for more details.panels
: array of information panels to display on the timer. See panels for more details.t_font
: the font to use for the large timer.ty
: the font type. See fonts for more detailssize
: the font size
s_font
: the font to use for the large timer.ty
: the font type. See fonts for more detailssize
: the font size
ms_ratio
: the size of the milliseconds on the main timer, as a fraction of the rest of the timer's sizeconfirm_exit
: whether mist should ask if you want to exit when you close the window or press escapebinds
: keyboard bindings for functions in mist. Possible keys are the left column of the table on this page.pause
: pause the timer during a run. Also unpauses.reset
: stop a run and clear the timer.start_split
: start a run, or advance through the splits in a run.un_split
: undo a split, returning to the previous split with the current elapsed time.prev_comp
: change the timer to the previous comparisonnext_comp
: change the timer to the next comparison.load_splits
: open a dialog prompting for a new split file to load.load_config
: open a dialog prompting for a new configuration file to load.
Frame Rounding
Time is, of course, continuous, meaning that a stopwatch or speedrun timer could theoretically display any sub-second value when stopped. However, this may be undesirable, for example in
extremely short runs or just ones that require precise timing. Since games have discrete frames, it must take an integer number of frames to complete a run, which means that depending on
the framerate of the game, only certain time values are "actually possible". mist supports displaying times compliant with any framerate, as specified by the frame_rounding
configuration
option. An example of frame rounding at 30 fps is as follows:
Real Time | Rounded Time |
---|---|
1.456 | 1.467 |
0.633 | 0.633 |
3.913 | 3.933 |
4.892 | 4.900 |
Panels
Panels are used to display additional information about the speedrun in mist. There are three types of panels that can be put into the panels
configuration option:
SumOfBest
, Pace
, and CurrentSplitDiff
. SumOfBest shows the sum of the runner's golds, theoretically their best possible time. Pace shows a "prediction" for the pace of the run, depending on
the current status, such as how far behind or ahead the runner is. CurrentSplitDiff shows how far ahead or behind the runner is currently from the time on the current split that they are
comparing against.
The latter two are configurable as follows:
Pace( golds: true ),
CurrentSplitDiff( golds: false )
The golds
option tells mist whether to compare against pb or against golds when calculating the times to display on these panels.
Fonts
mist can search either for system fonts based on name and style, or use a TTF file from a path. These are specified as follows:
// System font, named Victor Mono, normal weight, italic
System(name: "Victor Mono", style: Italic, weight: Normal)
// Filepath
File(path: "/home/peri/fonts/somefont.ttf")
For system fonts, style
can be any of:
Normal
Italic
Oblique
weight
can be:
Thin
ExtraLight
Light
Normal
Medium
SemiBold
Bold
ExtraBold
Black
mist will do its best to find System
fonts, but I am depending on a library for this so if they cannot be found, there isn't much I can do.