Skip to content

Commit

Permalink
add mist export plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
LtPeriwinkle committed Mar 30, 2024
1 parent 2ca0c1e commit 85948bb
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 14 deletions.
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions compiler-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ roman = "0.1.6"
flate2 = "1.0.28"
base64 = "0.21.7"
livesplit-core = "0.13.0"
mist-core = { version = "2.0", default-features = false, features = ["ser"]}

[dev-dependencies]
map-macro = "0.2.6"
Expand Down
15 changes: 2 additions & 13 deletions compiler-core/src/plugin/native/export_livesplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::macros::async_trait;
use crate::plugin::{PluginResult, Runtime};
use crate::res::ResPath;

use super::should_split_on;

pub struct ExportLiveSplit;

#[async_trait(auto)]
Expand Down Expand Up @@ -148,19 +150,6 @@ impl Runtime for ExportLiveSplit {
}
}

fn should_split_on(line: &CompLine, split_types: &BTreeSet<String>) -> bool {
let counter = match &line.counter_text {
Some(counter) => counter,
None => return false,
};
let tag = match &counter.tag {
Some(tag) => tag,
None => return false,
};

split_types.contains(tag)
}

async fn build_icon_cache(
doc: &CompDoc<'_>,
split_sections: &[(&CompSection, Vec<&CompLine>)],
Expand Down
96 changes: 96 additions & 0 deletions compiler-core/src/plugin/native/export_mist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//! Exporter plugin for mist split files

use std::borrow::Cow;
use std::collections::BTreeSet;

use mist_core::timer::Run;

use serde_json::Value;

use super::should_split_on;

use crate::comp::CompDoc;
use crate::expo::{ExpoBlob, ExpoDoc, ExportIcon, ExportMetadata};
use crate::export_error;
use crate::json::Coerce;
use crate::macros::async_trait;
use crate::plugin::{PluginResult, Runtime};

pub struct ExportMist;

#[async_trait(auto)]
impl Runtime for ExportMist {
fn get_id(&self) -> Cow<'static, str> {
Cow::Owned(super::Native::ExportMist.id())
}

async fn on_prepare_export(&mut self) -> PluginResult<Option<Vec<ExportMetadata>>> {
let meta = ExportMetadata {
plugin_id: self.get_id().into_owned(),
name: "mist".into(),
description: "Export to a mist split file".into(),
icon: ExportIcon::Data,
extension: Some("msf".into()),
export_id: None,
example_config: Some(include_str!("./export_mist.yaml").into()),
learn_more: Some("/docs/plugin/export-mist#export-mist".into()),
};
Ok(Some(vec![meta]))
}

async fn on_export_comp_doc<'p>(
&mut self,
_: &str,
payload: &Value,
doc: &CompDoc<'p>,
) -> PluginResult<Option<ExpoDoc>> {
let payload = match payload.as_object() {
Some(payload) => payload,
None => return export_error!("Invalid payload"),
};
let mut split_types = BTreeSet::new();
if let Some(x) = payload.get("split-types") {
let x = match x.as_array() {
Some(x) => x,
_ => return export_error!("Invalid split types"),
};
let names: BTreeSet<String> = x.iter().map(|x| x.coerce_to_string()).collect();
for (tag_name, tag) in doc.config.tags.iter() {
if let Some(split_type) = &tag.split_type {
if names.contains(split_type) {
split_types.insert(tag_name.clone());
}
}
}
}

if split_types.is_empty() {
return export_error!("No splits to export. Make sure you selected at least one split type in the settings.");
}

let mut run = Run::empty();
let mut splits = vec![];
for section in &doc.route {
for line in section.lines.iter() {
if should_split_on(line, &split_types) {
splits.push(line.split_name.as_ref().unwrap_or(&line.text).to_string())
}
}
}

if splits.is_empty() {
return export_error!("No splits to export. Make sure you selected at least one split type in the settings.");
}

run.set_splits(&splits);
let content = run.to_string();
if content.is_err() {
return export_error!("Failed to serialize split file");
}

Ok(Some(ExpoDoc::Success {
file_name: format!("{}.msf", doc.config.meta.title),
file_content: ExpoBlob::from_utf8(content.unwrap()),
}))
}
}
2 changes: 2 additions & 0 deletions compiler-core/src/plugin/native/export_mist.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Keep this as-is to use the splits configured in the settings
split-types: null
20 changes: 20 additions & 0 deletions compiler-core/src/plugin/native/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
//!
//! Built-in plugins are implemented in Rust and directly included in the compiler.

use std::collections::BTreeSet;

use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::comp::CompLine;
use crate::pack::CompileContext;

use super::{BoxedEarlyRuntime, BoxedRuntime, PluginResult};

mod botw_unstable;
mod export_livesplit;
mod export_mist;
mod link;
mod metrics;
mod split_format;
Expand All @@ -22,6 +26,8 @@ pub enum Native {
BotwAbilityUnstable, // TODO #24: remove this
#[serde(rename = "export-livesplit")]
ExportLiveSplit,
#[serde(rename = "export-mist")]
ExportMist,
Link,
Metrics,
SplitFormat,
Expand All @@ -43,6 +49,7 @@ impl Native {
botw_unstable::BotwAbilityUnstable::from_props(props),
)),
Self::ExportLiveSplit => Ok(Box::new(export_livesplit::ExportLiveSplit)),
Self::ExportMist => Ok(Box::new(export_mist::ExportMist)),
Self::Link => Ok(Box::new(link::Link)),
Self::Metrics => Ok(Box::new(metrics::Metrics::from_props(
props,
Expand All @@ -59,3 +66,16 @@ impl Native {
.unwrap_or_default()
}
}

fn should_split_on(line: &CompLine, split_types: &BTreeSet<String>) -> bool {
let counter = match &line.counter_text {
Some(counter) => counter,
None => return false,
};
let tag = match &counter.tag {
Some(tag) => tag,
None => return false,
};

split_types.contains(tag)
}
1 change: 1 addition & 0 deletions docs/src/.vitepress/nav/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const pluginsSideBar = {
text: "Built-in Exporter Plugins",
items: [
{ text: "Export LiveSplit", link: "/plugin/export-livesplit" },
{ text: "Export mist", link: "/plugin/export-mist" },
],
},
{
Expand Down
34 changes: 34 additions & 0 deletions docs/src/plugin/export-mist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

# Export mist
The `export-mist` plugin lets you export the route to a split file (.msf) for mist.

This plugin comes pre-configured in the web app:

1. Click on <FluentIcon name="Settings20Regular" /> `Settings`.
2. Select the <FluentIcon name="Wrench20Regular" /> `Plugins` category.
3. Under `App Plugins`, make sure `Export split files` is checked.

Alternatively, you can add it to the route configuration:
```yaml
config:
- plugins:
- use: export-mist
```
## Extra Options
The plugin provides extra configuration when exporting.
### Split Types
The recommended way to configure which split types are exported is through [Split Settings](../doc#splits)
By having `split-types: null`, Celer will automatically add the split settings for you.
You can also override it by putting an array of split types. For example:
```yaml
split-types:
- Lightroots
- Shrines
- Tears
```
:::tip
The split type names should match exactly with the checkbox labels in Split Settings. Case matters.
:::
2 changes: 1 addition & 1 deletion web-client/src/core/doc/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function createExportRequest(

/// Get the plugin configs when the "Export Split" option is enabled
export function getSplitExportPluginConfigs() {
return [{ use: "export-livesplit" }];
return [{ use: "export-livesplit" }, { use: "export-mist" }];
}

export function injectSplitTypesIntoRequest(
Expand Down

0 comments on commit 85948bb

Please sign in to comment.