Skip to content

Commit

Permalink
Run parser: deserialize the AutoSplitterSettings
Browse files Browse the repository at this point in the history
This commit implements the deserialization of the AutoSplitterSettings XML section of a LiveSplit splits file.
This can surely use some improvements regarding the compatibility without the auto-splitting feature and some other things.
I think it is good as a first step though and we can keep iterating on it.
  • Loading branch information
Refragg authored and AlexKnauth committed Nov 17, 2024
1 parent 47a2b51 commit ffca5d4
Show file tree
Hide file tree
Showing 10 changed files with 666 additions and 24 deletions.
8 changes: 4 additions & 4 deletions crates/livesplit-auto-splitting/src/settings/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

/// A setting widget that is meant to be shown to and modified by the user.
#[non_exhaustive]
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct Widget {
/// A unique identifier for this setting. This is not meant to be shown to
/// the user and is only used to keep track of the setting. This key is used
Expand All @@ -19,7 +19,7 @@ pub struct Widget {
}

/// The type of a [`Widget`] and additional information about it.
#[derive(Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum WidgetKind {
/// A title that is shown to the user. It doesn't by itself store a value
/// and is instead used to group settings together.
Expand Down Expand Up @@ -51,7 +51,7 @@ pub enum WidgetKind {
}

/// A filter for a file selection setting.
#[derive(Clone)]
#[derive(Clone, Debug, PartialEq)]
pub enum FileFilter {
/// A filter that matches on the name of the file.
Name {
Expand Down Expand Up @@ -82,7 +82,7 @@ pub enum FileFilter {
}

/// An option for a choice setting.
#[derive(Clone, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct ChoiceOption {
/// The unique identifier of the option. This is not meant to be shown to
/// the user and is only used to keep track of the option. This key is used
Expand Down
35 changes: 30 additions & 5 deletions src/auto_splitting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,15 +559,16 @@ use crate::{
use arc_swap::ArcSwapOption;
pub use livesplit_auto_splitting::{settings, wasi_path};
use livesplit_auto_splitting::{
AutoSplitter, Config, CreationError, LogLevel, Timer as AutoSplitTimer, TimerState,
AutoSplitter, CompiledAutoSplitter, Config, CreationError, LogLevel,
Timer as AutoSplitTimer, TimerState,
};
use snafu::Snafu;
use std::{
fmt, fs, io,
path::PathBuf,
sync::{
mpsc::{self, Receiver, RecvTimeoutError, Sender},
Condvar, Mutex,
Condvar, Mutex, RwLock,
},
thread,
time::{Duration, Instant},
Expand Down Expand Up @@ -602,6 +603,7 @@ pub struct Runtime<T: event::CommandSink + TimerQuery + Send + 'static> {
shared_state: Arc<SharedState<T>>,
changed_sender: Sender<()>,
runtime: livesplit_auto_splitting::Runtime,
compiled_auto_splitter: RwLock<Option<CompiledAutoSplitter>>,
}

struct SharedState<T> {
Expand Down Expand Up @@ -676,18 +678,32 @@ impl<T: event::CommandSink + TimerQuery + Send + 'static> Runtime<T> {
changed_sender,
// TODO: unwrap?
runtime: livesplit_auto_splitting::Runtime::new(Config::default()).unwrap(),
compiled_auto_splitter: RwLock::new(None),
}
}

/// Attempts to load a wasm file containing an auto splitter module.
pub fn load(&self, path: PathBuf, timer: T) -> Result<(), Error> {
let data = fs::read(path).map_err(|e| Error::ReadFileFailed { source: e })?;

let auto_splitter = self
let compiled_auto_splitter = self
.runtime
.compile(&data)
.map_err(|e| Error::LoadFailed { source: e })?
.instantiate(Timer(timer), None, None)
.map_err(|e| Error::LoadFailed { source: e })?;
self.instantiate(&compiled_auto_splitter, timer)?;
*self.compiled_auto_splitter.write().unwrap() = Some(compiled_auto_splitter);
Ok(())
}

/// Instantiates the compiled auto splitter.
fn instantiate(
&self,
compiled_auto_splitter: &CompiledAutoSplitter,
timer: T,
) -> Result<(), Error> {
let settings_map = timer.get_timer().run().auto_splitter_settings_map_load();
let auto_splitter = compiled_auto_splitter
.instantiate(Timer(timer), settings_map, None)
.map_err(|e| Error::LoadFailed { source: e })?;

self.shared_state
Expand All @@ -710,6 +726,15 @@ impl<T: event::CommandSink + TimerQuery + Send + 'static> Runtime<T> {
.map_err(|_| Error::ThreadStopped)
}

/// Reloads the auto splitter without re-compiling.
pub fn reload(&self, timer: T) -> Result<(), Error> {
self.unload()?;
if let Some(compiled_auto_splitter) = self.compiled_auto_splitter.read().unwrap().as_ref() {
self.instantiate(compiled_auto_splitter, timer)?;
}
Ok(())
}

/// Accesses a copy of the currently stored settings. The auto splitter can
/// change these at any time. If you intend to make modifications to the
/// settings, you need to set them again via
Expand Down
2 changes: 2 additions & 0 deletions src/platform/no_std/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod time;
pub use self::time::*;

#[allow(unused)]
pub struct RwLock<T>(core::cell::RefCell<T>);

#[allow(unused)]
impl<T> RwLock<T> {
pub fn new(value: T) -> Self {
Self(core::cell::RefCell::new(value))
Expand Down
24 changes: 24 additions & 0 deletions src/run/auto_splitter_settings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::run::parser::livesplit::Version;
use core::fmt::Debug;
use livesplit_auto_splitting::settings;

#[derive(Debug, Default, Clone, PartialEq)]
pub struct AutoSplitterSettings {
pub version: Version,
pub script_path: String,
pub custom_settings: settings::Map,
}

impl AutoSplitterSettings {
pub fn set_version(&mut self, version: Version) {
self.version = version;
}

pub fn set_script_path(&mut self, script_path: String) {
self.script_path = script_path;
}

pub fn set_custom_settings(&mut self, custom_settings: settings::Map) {
self.custom_settings = custom_settings;
}
}
43 changes: 43 additions & 0 deletions src/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
//! ```
mod attempt;

#[cfg(feature = "auto-splitting")]
mod auto_splitter_settings;
mod comparisons;
pub mod editor;
mod linked_layout;
Expand All @@ -35,6 +38,8 @@ pub use run_metadata::{CustomVariable, RunMetadata};
pub use segment::Segment;
pub use segment_history::SegmentHistory;

#[cfg(feature = "auto-splitting")]
use crate::run::auto_splitter_settings::AutoSplitterSettings;
use crate::{
comparison::{default_generators, personal_best, ComparisonGenerator, RACE_COMPARISON_PREFIX},
platform::prelude::*,
Expand Down Expand Up @@ -75,6 +80,8 @@ pub struct Run {
custom_comparisons: Vec<String>,
comparison_generators: ComparisonGenerators,
auto_splitter_settings: String,
#[cfg(feature = "auto-splitting")]
parsed_auto_splitter_settings: Option<AutoSplitterSettings>,
linked_layout: Option<LinkedLayout>,
}

Expand Down Expand Up @@ -128,6 +135,8 @@ impl Run {
custom_comparisons: vec![personal_best::NAME.to_string()],
comparison_generators: ComparisonGenerators(default_generators()),
auto_splitter_settings: String::new(),
#[cfg(feature = "auto-splitting")]
parsed_auto_splitter_settings: None,
linked_layout: None,
}
}
Expand Down Expand Up @@ -326,6 +335,40 @@ impl Run {
&mut self.auto_splitter_settings
}

/// Loads a copy of the Auto Splitter Settings as a settings map.
#[inline]
#[cfg(feature = "auto-splitting")]
pub fn auto_splitter_settings_map_load(
&self,
) -> Option<livesplit_auto_splitting::settings::Map> {
if let Some(p) = &self.parsed_auto_splitter_settings {
return Some(p.custom_settings.clone());
}
None
}

/// Stores a settings map into the parsed auto splitter settings.
#[cfg(feature = "auto-splitting")]
pub fn auto_splitter_settings_map_store(
&mut self,
settings_map: livesplit_auto_splitting::settings::Map,
) {
let p = &mut self.parsed_auto_splitter_settings;
match p {
None => {
if settings_map.is_empty() {
return;
}
let mut a = AutoSplitterSettings::default();
a.set_custom_settings(settings_map);
*p = Some(a);
}
Some(a) => {
a.set_custom_settings(settings_map);
}
}
}

/// Accesses the [`LinkedLayout`] of this `Run`. If a
/// [`Layout`](crate::Layout) is linked, it is supposed to be loaded to
/// visualize the `Run`.
Expand Down
Loading

0 comments on commit ffca5d4

Please sign in to comment.