Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export mist plugin #232

Merged
merged 2 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")]
LtPeriwinkle marked this conversation as resolved.
Show resolved Hide resolved
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
LtPeriwinkle marked this conversation as resolved.
Show resolved Hide resolved
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