Merge branch 'feature/scoreboard_management' into develop
This commit is contained in:
		
						commit
						ed62e75e89
					
				| 
						 | 
					@ -3,7 +3,7 @@ on:
 | 
				
			||||||
    push:
 | 
					    push:
 | 
				
			||||||
        branches: 
 | 
					        branches: 
 | 
				
			||||||
            - main
 | 
					            - main
 | 
				
			||||||
            - development
 | 
					            - develop
 | 
				
			||||||
            - 'releases/**'
 | 
					            - 'releases/**'
 | 
				
			||||||
    pull_request:
 | 
					    pull_request:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Added
 | 
					### Added
 | 
				
			||||||
- support for commands using macros
 | 
					- support for commands using macros
 | 
				
			||||||
 | 
					- support for registering scoreboards (automatic creation and deletion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Changed
 | 
					### Changed
 | 
				
			||||||
- use "return" command for conditionals instead of data storage when using supported pack format
 | 
					- use "return" command for conditionals instead of data storage when using supported pack format
 | 
				
			||||||
 | 
					- update latest datapack format to 61
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Removed
 | 
					### Removed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								Cargo.toml
								
								
								
								
							
							
						
						
									
										18
									
								
								Cargo.toml
								
								
								
								
							| 
						 | 
					@ -20,12 +20,16 @@ serde = ["dep:serde"]
 | 
				
			||||||
zip = ["dep:zip"]
 | 
					zip = ["dep:zip"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
chksum-md5 = "0.0.0"
 | 
					chksum-md5 = "0.1.0"
 | 
				
			||||||
getset = "0.1.2"
 | 
					getset = "0.1.5"
 | 
				
			||||||
serde = { version = "1.0.197", optional = true, features = ["derive"] }
 | 
					serde = { version = "1.0.219", optional = true, features = ["derive"] }
 | 
				
			||||||
serde_json = "1.0.114"
 | 
					serde_json = "1.0.140"
 | 
				
			||||||
tracing = "0.1.40"
 | 
					tracing = "0.1.41"
 | 
				
			||||||
zip = { version = "2.1.3", default-features = false, features = ["deflate", "time"], optional = true }
 | 
					zip = { version = "2.6.1", default-features = false, features = ["deflate", "time"], optional = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
tempfile = "3.13.0"
 | 
					tempfile = "3.19.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.metadata.docs.rs]
 | 
				
			||||||
 | 
					all-features = true
 | 
				
			||||||
 | 
					rustdoc-args = ["--cfg", "docsrs"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ use shulkerbox::{
 | 
				
			||||||
    util::compile::CompileOptions,
 | 
					    util::compile::CompileOptions,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let mut dp = Datapack::new("shulkerpack", 20) // Create a new datapack with the name "shulkerpack" and the pack format 20
 | 
					let mut dp = Datapack::new("shulker", 20) // Create a new datapack with the name "shulkerpack" and the pack format 20
 | 
				
			||||||
    .with_description("I created this datapack with rust") // Add a description to the datapack
 | 
					    .with_description("I created this datapack with rust") // Add a description to the datapack
 | 
				
			||||||
    .with_supported_formats(16..=20) // Add the supported formats of the datapack
 | 
					    .with_supported_formats(16..=20) // Add the supported formats of the datapack
 | 
				
			||||||
    .with_template_folder(Path::new("./template")) // Add a template folder to the datapack. This will include all files in the template folder in the root of the datapack and can be used for including the "pack.png" file
 | 
					    .with_template_folder(Path::new("./template")) // Add a template folder to the datapack. This will include all files in the template folder in the root of the datapack and can be used for including the "pack.png" file
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,10 +5,12 @@ use shulkerbox::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    // create a new datapack
 | 
					    // create a new datapack
 | 
				
			||||||
    let mut dp = Datapack::new(16).with_supported_formats(16..=20);
 | 
					    let mut dp = Datapack::new("example", 16).with_supported_formats(16..=20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // get the namespace "test"
 | 
					    dp.register_scoreboard("example_scoreboard", None::<&str>, None::<&str>);
 | 
				
			||||||
    let namespace = dp.namespace_mut("test");
 | 
					
 | 
				
			||||||
 | 
					    // get the namespace "example"
 | 
				
			||||||
 | 
					    let namespace = dp.namespace_mut("example");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // get the function "foo" of the namespace "test" and add some commands
 | 
					    // get the function "foo" of the namespace "test" and add some commands
 | 
				
			||||||
    let foo_function = namespace.function_mut("foo");
 | 
					    let foo_function = namespace.function_mut("foo");
 | 
				
			||||||
| 
						 | 
					@ -32,7 +34,7 @@ fn main() {
 | 
				
			||||||
        )),
 | 
					        )),
 | 
				
			||||||
    )));
 | 
					    )));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dp.add_load("test:foo");
 | 
					    dp.add_load("example:foo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // compile the datapack
 | 
					    // compile the datapack
 | 
				
			||||||
    let v_folder = dp.compile(&CompileOptions::default());
 | 
					    let v_folder = dp.compile(&CompileOptions::default());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// import the prelude to get all the necessary structs
 | 
				
			||||||
 | 
					use shulkerbox::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let mut dp = Datapack::new("main", 20).with_supported_formats(16..=20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get the namespace "test"
 | 
				
			||||||
 | 
					    let namespace = dp.namespace_mut("test");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get the function "foo" of the namespace "test" and add some commands
 | 
				
			||||||
 | 
					    let foo_function = namespace.function_mut("foo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let ex = Execute::If(
 | 
				
			||||||
 | 
					        Condition::from("entity A"),
 | 
				
			||||||
 | 
					        Box::new(Execute::Run(Box::new("say A".into()))),
 | 
				
			||||||
 | 
					        Some(Box::new(Execute::If(
 | 
				
			||||||
 | 
					            Condition::from("entity B"),
 | 
				
			||||||
 | 
					            Box::new(Execute::Run(Box::new("say B".into()))),
 | 
				
			||||||
 | 
					            Some(Box::new(Execute::Run(Box::new("say C".into())))),
 | 
				
			||||||
 | 
					        ))),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    foo_function.add_command(ex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // compile the datapack
 | 
				
			||||||
 | 
					    let v_folder = dp.compile(&CompileOptions::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // place the compiled datapack in the "./dist" folder
 | 
				
			||||||
 | 
					    v_folder.place(Path::new("./dist")).unwrap();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -439,7 +439,7 @@ impl Condition {
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn to_truth_table(&self) -> Vec<Self> {
 | 
					    pub fn to_truth_table(&self) -> Vec<Self> {
 | 
				
			||||||
        match self.normalize() {
 | 
					        match self.normalize() {
 | 
				
			||||||
            Self::Atom(_) | Self::Not(_) => vec![self.clone()],
 | 
					            Self::Atom(_) | Self::Not(_) => vec![self.normalize()],
 | 
				
			||||||
            Self::Or(a, b) => a
 | 
					            Self::Or(a, b) => a
 | 
				
			||||||
                .to_truth_table()
 | 
					                .to_truth_table()
 | 
				
			||||||
                .into_iter()
 | 
					                .into_iter()
 | 
				
			||||||
| 
						 | 
					@ -461,7 +461,7 @@ impl Condition {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Convert the condition into a [`MacroString`].
 | 
					    /// Convert the condition into a [`MacroString`].
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Will fail if the condition contains an `Or` variant. Use `compile` instead.
 | 
					    /// Will fail if the condition contains an `Or` or double nested `Not` variant. Use `compile` instead.
 | 
				
			||||||
    fn str_cond(&self) -> Option<MacroString> {
 | 
					    fn str_cond(&self) -> Option<MacroString> {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::Atom(s) => Some(MacroString::from("if ") + s.clone()),
 | 
					            Self::Atom(s) => Some(MacroString::from("if ") + s.clone()),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,7 +234,7 @@ impl Execute {
 | 
				
			||||||
            Self::If(_, next, el) => {
 | 
					            Self::If(_, next, el) => {
 | 
				
			||||||
                pack_formats.start() >= &4
 | 
					                pack_formats.start() >= &4
 | 
				
			||||||
                    && next.validate(pack_formats)
 | 
					                    && next.validate(pack_formats)
 | 
				
			||||||
                    && el.as_deref().map_or(true, |el| el.validate(pack_formats))
 | 
					                    && el.as_deref().is_none_or(|el| el.validate(pack_formats))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::Summon(_, next) | Self::On(_, next) => {
 | 
					            Self::Summon(_, next) | Self::On(_, next) => {
 | 
				
			||||||
                pack_formats.start() >= &12 && next.validate(pack_formats)
 | 
					                pack_formats.start() >= &12 && next.validate(pack_formats)
 | 
				
			||||||
| 
						 | 
					@ -261,7 +261,7 @@ impl Execute {
 | 
				
			||||||
            Self::If(cond, then, el) => {
 | 
					            Self::If(cond, then, el) => {
 | 
				
			||||||
                cond.contains_macro()
 | 
					                cond.contains_macro()
 | 
				
			||||||
                    || then.contains_macro()
 | 
					                    || then.contains_macro()
 | 
				
			||||||
                    || el.as_deref().map_or(false, Self::contains_macro)
 | 
					                    || el.as_deref().is_some_and(Self::contains_macro)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::Run(cmd) => cmd.contains_macro(),
 | 
					            Self::Run(cmd) => cmd.contains_macro(),
 | 
				
			||||||
            Self::Runs(cmds) => cmds.iter().any(super::Command::contains_macro),
 | 
					            Self::Runs(cmds) => cmds.iter().any(super::Command::contains_macro),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -363,8 +363,8 @@ fn validate_raw_cmd(cmd: &str, pack_formats: &RangeInclusive<u8>) -> bool {
 | 
				
			||||||
        map
 | 
					        map
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cmd.split_ascii_whitespace().next().map_or(true, |cmd| {
 | 
					    cmd.split_ascii_whitespace().next().is_none_or(|cmd| {
 | 
				
			||||||
        cmd_formats.get(cmd).map_or(true, |range| {
 | 
					        cmd_formats.get(cmd).is_none_or(|range| {
 | 
				
			||||||
            let start_cmd = range.start();
 | 
					            let start_cmd = range.start();
 | 
				
			||||||
            let end_cmd = range.end();
 | 
					            let end_cmd = range.end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,11 +25,11 @@ pub struct Function {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Function {
 | 
					impl Function {
 | 
				
			||||||
    pub(in crate::datapack) fn new(namespace: &str, name: &str) -> Self {
 | 
					    pub(in crate::datapack) fn new(namespace: impl Into<String>, name: impl Into<String>) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            commands: Vec::new(),
 | 
					            commands: Vec::new(),
 | 
				
			||||||
            name: name.to_string(),
 | 
					            name: name.into(),
 | 
				
			||||||
            namespace: namespace.to_string(),
 | 
					            namespace: namespace.into(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    /// Add a command to the function.
 | 
					    /// Add a command to the function.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ pub use command::{Command, Condition, Execute};
 | 
				
			||||||
pub use function::Function;
 | 
					pub use function::Function;
 | 
				
			||||||
pub use namespace::Namespace;
 | 
					pub use namespace::Namespace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{collections::HashMap, ops::RangeInclusive, sync::Mutex};
 | 
					use std::{borrow::Cow, collections::BTreeMap, ops::RangeInclusive, sync::Mutex};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    util::compile::{CompileOptions, CompilerState, MutCompilerState},
 | 
					    util::compile::{CompileOptions, CompilerState, MutCompilerState},
 | 
				
			||||||
| 
						 | 
					@ -23,21 +23,28 @@ pub struct Datapack {
 | 
				
			||||||
    description: String,
 | 
					    description: String,
 | 
				
			||||||
    pack_format: u8,
 | 
					    pack_format: u8,
 | 
				
			||||||
    supported_formats: Option<RangeInclusive<u8>>,
 | 
					    supported_formats: Option<RangeInclusive<u8>>,
 | 
				
			||||||
    namespaces: HashMap<String, Namespace>,
 | 
					    main_namespace_name: String,
 | 
				
			||||||
 | 
					    namespaces: BTreeMap<String, Namespace>,
 | 
				
			||||||
 | 
					    /// Scoreboard name -> (criteria, display name)
 | 
				
			||||||
 | 
					    scoreboards: BTreeMap<String, (Option<String>, Option<String>)>,
 | 
				
			||||||
 | 
					    uninstall_commands: Vec<Command>,
 | 
				
			||||||
    custom_files: VFolder,
 | 
					    custom_files: VFolder,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Datapack {
 | 
					impl Datapack {
 | 
				
			||||||
    pub const LATEST_FORMAT: u8 = 48;
 | 
					    pub const LATEST_FORMAT: u8 = 61;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Create a new Minecraft datapack.
 | 
					    /// Create a new Minecraft datapack.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn new(pack_format: u8) -> Self {
 | 
					    pub fn new(main_namespace_name: impl Into<String>, pack_format: u8) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            description: String::from("A Minecraft datapack created with shulkerbox"),
 | 
					            description: String::from("A Minecraft datapack created with shulkerbox"),
 | 
				
			||||||
            pack_format,
 | 
					            pack_format,
 | 
				
			||||||
            supported_formats: None,
 | 
					            supported_formats: None,
 | 
				
			||||||
            namespaces: HashMap::new(),
 | 
					            main_namespace_name: main_namespace_name.into(),
 | 
				
			||||||
 | 
					            namespaces: BTreeMap::new(),
 | 
				
			||||||
 | 
					            scoreboards: BTreeMap::new(),
 | 
				
			||||||
 | 
					            uninstall_commands: Vec::new(),
 | 
				
			||||||
            custom_files: VFolder::new(),
 | 
					            custom_files: VFolder::new(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -65,6 +72,7 @@ impl Datapack {
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - If loading the directory fails
 | 
					    /// - If loading the directory fails
 | 
				
			||||||
    #[cfg(feature = "fs_access")]
 | 
					    #[cfg(feature = "fs_access")]
 | 
				
			||||||
 | 
					    #[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
 | 
				
			||||||
    pub fn with_template_folder<P>(self, path: P) -> std::io::Result<Self>
 | 
					    pub fn with_template_folder<P>(self, path: P) -> std::io::Result<Self>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        P: AsRef<std::path::Path>,
 | 
					        P: AsRef<std::path::Path>,
 | 
				
			||||||
| 
						 | 
					@ -90,24 +98,49 @@ impl Datapack {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mutably get a namespace by name or create a new one if it doesn't exist.
 | 
					    /// Mutably get a namespace by name or create a new one if it doesn't exist.
 | 
				
			||||||
    pub fn namespace_mut(&mut self, name: &str) -> &mut Namespace {
 | 
					    pub fn namespace_mut(&mut self, name: impl Into<String>) -> &mut Namespace {
 | 
				
			||||||
 | 
					        let name = name.into();
 | 
				
			||||||
        self.namespaces
 | 
					        self.namespaces
 | 
				
			||||||
            .entry(name.to_string())
 | 
					            .entry(name.clone())
 | 
				
			||||||
            .or_insert_with(|| Namespace::new(name))
 | 
					            .or_insert_with(|| Namespace::new(name))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Add a function to the tick function list.
 | 
					    /// Add a function to the tick function list.
 | 
				
			||||||
    pub fn add_tick(&mut self, function: &str) {
 | 
					    pub fn add_tick(&mut self, function: impl Into<String>) {
 | 
				
			||||||
        self.namespace_mut("minecraft")
 | 
					        self.namespace_mut("minecraft")
 | 
				
			||||||
            .tag_mut("tick", tag::TagType::Function)
 | 
					            .tag_mut("tick", tag::TagType::Function)
 | 
				
			||||||
            .add_value(tag::TagValue::Simple(function.to_string()));
 | 
					            .add_value(tag::TagValue::Simple(function.into()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Add a function to the load function list.
 | 
					    /// Add a function to the load function list.
 | 
				
			||||||
    pub fn add_load(&mut self, function: &str) {
 | 
					    pub fn add_load(&mut self, function: impl Into<String>) {
 | 
				
			||||||
        self.namespace_mut("minecraft")
 | 
					        self.namespace_mut("minecraft")
 | 
				
			||||||
            .tag_mut("load", tag::TagType::Function)
 | 
					            .tag_mut("load", tag::TagType::Function)
 | 
				
			||||||
            .add_value(tag::TagValue::Simple(function.to_string()));
 | 
					            .add_value(tag::TagValue::Simple(function.into()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Register a scoreboard.
 | 
				
			||||||
 | 
					    pub fn register_scoreboard(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        name: impl Into<String>,
 | 
				
			||||||
 | 
					        criteria: Option<impl Into<String>>,
 | 
				
			||||||
 | 
					        display_name: Option<impl Into<String>>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        self.scoreboards.insert(
 | 
				
			||||||
 | 
					            name.into(),
 | 
				
			||||||
 | 
					            (criteria.map(Into::into), display_name.map(Into::into)),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Scoreboards registered in the datapack.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn scoreboards(&self) -> &BTreeMap<String, (Option<String>, Option<String>)> {
 | 
				
			||||||
 | 
					        &self.scoreboards
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Add commands to the uninstall function.
 | 
				
			||||||
 | 
					    pub fn add_uninstall_commands(&mut self, commands: Vec<Command>) {
 | 
				
			||||||
 | 
					        self.uninstall_commands.extend(commands);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Add a custom file to the datapack.
 | 
					    /// Add a custom file to the datapack.
 | 
				
			||||||
| 
						 | 
					@ -132,8 +165,71 @@ impl Datapack {
 | 
				
			||||||
        root_folder.add_file("pack.mcmeta", mcmeta);
 | 
					        root_folder.add_file("pack.mcmeta", mcmeta);
 | 
				
			||||||
        let mut data_folder = VFolder::new();
 | 
					        let mut data_folder = VFolder::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut modified_namespaces = self
 | 
				
			||||||
 | 
					            .namespaces
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|(name, namespace)| (name.as_str(), Cow::Borrowed(namespace)))
 | 
				
			||||||
 | 
					            .collect::<BTreeMap<_, _>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut uninstall_commands = options
 | 
				
			||||||
 | 
					            .uninstall_function
 | 
				
			||||||
 | 
					            .then_some(Cow::Borrowed(&self.uninstall_commands));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !self.scoreboards.is_empty() {
 | 
				
			||||||
 | 
					            let main_namespace = modified_namespaces
 | 
				
			||||||
 | 
					                .entry(&self.main_namespace_name)
 | 
				
			||||||
 | 
					                .or_insert_with(|| Cow::Owned(Namespace::new(&self.main_namespace_name)));
 | 
				
			||||||
 | 
					            let register_scoreboard_function = main_namespace
 | 
				
			||||||
 | 
					                .to_mut()
 | 
				
			||||||
 | 
					                .function_mut("sb/register_scoreboards");
 | 
				
			||||||
 | 
					            for (name, (criteria, display_name)) in &self.scoreboards {
 | 
				
			||||||
 | 
					                let mut creation_command = format!(
 | 
				
			||||||
 | 
					                    "scoreboard objectives add {name} {criteria}",
 | 
				
			||||||
 | 
					                    criteria = criteria.as_deref().unwrap_or("dummy")
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                if let Some(display_name) = display_name {
 | 
				
			||||||
 | 
					                    creation_command.push(' ');
 | 
				
			||||||
 | 
					                    creation_command.push_str(display_name);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                register_scoreboard_function.add_command(Command::Raw(creation_command));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(uninstall_commands) = uninstall_commands.as_mut() {
 | 
				
			||||||
 | 
					                    uninstall_commands
 | 
				
			||||||
 | 
					                        .to_mut()
 | 
				
			||||||
 | 
					                        .push(Command::Raw(format!("scoreboard objectives remove {name}")));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let minecraft_namespace = modified_namespaces
 | 
				
			||||||
 | 
					                .entry("minecraft")
 | 
				
			||||||
 | 
					                .or_insert_with(|| Cow::Owned(Namespace::new("minecraft")));
 | 
				
			||||||
 | 
					            minecraft_namespace
 | 
				
			||||||
 | 
					                .to_mut()
 | 
				
			||||||
 | 
					                .tag_mut("load", tag::TagType::Function)
 | 
				
			||||||
 | 
					                .values_mut()
 | 
				
			||||||
 | 
					                .insert(
 | 
				
			||||||
 | 
					                    0,
 | 
				
			||||||
 | 
					                    tag::TagValue::Simple(format!(
 | 
				
			||||||
 | 
					                        "{}:sb/register_scoreboards",
 | 
				
			||||||
 | 
					                        self.main_namespace_name
 | 
				
			||||||
 | 
					                    )),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(uninstall_commands) = uninstall_commands {
 | 
				
			||||||
 | 
					            if !uninstall_commands.is_empty() {
 | 
				
			||||||
 | 
					                let main_namespace = modified_namespaces
 | 
				
			||||||
 | 
					                    .entry(&self.main_namespace_name)
 | 
				
			||||||
 | 
					                    .or_insert_with(|| Cow::Owned(Namespace::new(&self.main_namespace_name)));
 | 
				
			||||||
 | 
					                let uninstall_function = main_namespace.to_mut().function_mut("uninstall");
 | 
				
			||||||
 | 
					                uninstall_function
 | 
				
			||||||
 | 
					                    .get_commands_mut()
 | 
				
			||||||
 | 
					                    .extend(uninstall_commands.into_owned());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Compile namespaces
 | 
					        // Compile namespaces
 | 
				
			||||||
        for (name, namespace) in &self.namespaces {
 | 
					        for (name, namespace) in modified_namespaces {
 | 
				
			||||||
            let namespace_folder = namespace.compile(&options, &compiler_state);
 | 
					            let namespace_folder = namespace.compile(&options, &compiler_state);
 | 
				
			||||||
            data_folder.add_existing_folder(name, namespace_folder);
 | 
					            data_folder.add_existing_folder(name, namespace_folder);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -180,7 +276,7 @@ mod tests {
 | 
				
			||||||
    fn test_datapack() {
 | 
					    fn test_datapack() {
 | 
				
			||||||
        let template_dir = tempfile::tempdir().expect("error creating tempdir");
 | 
					        let template_dir = tempfile::tempdir().expect("error creating tempdir");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut dp = Datapack::new(Datapack::LATEST_FORMAT)
 | 
					        let mut dp = Datapack::new("main", Datapack::LATEST_FORMAT)
 | 
				
			||||||
            .with_description("My datapack")
 | 
					            .with_description("My datapack")
 | 
				
			||||||
            .with_template_folder(template_dir.path())
 | 
					            .with_template_folder(template_dir.path())
 | 
				
			||||||
            .expect("error reading template folder");
 | 
					            .expect("error reading template folder");
 | 
				
			||||||
| 
						 | 
					@ -193,7 +289,7 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_generate_mcmeta() {
 | 
					    fn test_generate_mcmeta() {
 | 
				
			||||||
        let dp = &Datapack::new(Datapack::LATEST_FORMAT).with_description("foo");
 | 
					        let dp = &Datapack::new("main", Datapack::LATEST_FORMAT).with_description("foo");
 | 
				
			||||||
        let state = Mutex::new(CompilerState::default());
 | 
					        let state = Mutex::new(CompilerState::default());
 | 
				
			||||||
        let mcmeta = generate_mcmeta(dp, &CompileOptions::default(), &state);
 | 
					        let mcmeta = generate_mcmeta(dp, &CompileOptions::default(), &state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,9 +28,9 @@ pub struct Namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Namespace {
 | 
					impl Namespace {
 | 
				
			||||||
    /// Create a new namespace.
 | 
					    /// Create a new namespace.
 | 
				
			||||||
    pub(in crate::datapack) fn new(name: &str) -> Self {
 | 
					    pub(in crate::datapack) fn new(name: impl Into<String>) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            name: name.to_string(),
 | 
					            name: name.into(),
 | 
				
			||||||
            functions: HashMap::new(),
 | 
					            functions: HashMap::new(),
 | 
				
			||||||
            tags: HashMap::new(),
 | 
					            tags: HashMap::new(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -62,23 +62,24 @@ impl Namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mutably get a function by name or create a new one if it doesn't exist.
 | 
					    /// Mutably get a function by name or create a new one if it doesn't exist.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn function_mut(&mut self, name: &str) -> &mut Function {
 | 
					    pub fn function_mut(&mut self, name: impl Into<String>) -> &mut Function {
 | 
				
			||||||
 | 
					        let name = name.into();
 | 
				
			||||||
        self.functions
 | 
					        self.functions
 | 
				
			||||||
            .entry(name.to_string())
 | 
					            .entry(name.clone())
 | 
				
			||||||
            .or_insert_with(|| Function::new(&self.name, name))
 | 
					            .or_insert_with(|| Function::new(&self.name, name))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get a tag by name and type.
 | 
					    /// Get a tag by name and type.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn tag(&self, name: &str, tag_type: TagType) -> Option<&Tag> {
 | 
					    pub fn tag(&self, name: impl Into<String>, tag_type: TagType) -> Option<&Tag> {
 | 
				
			||||||
        self.tags.get(&(name.to_string(), tag_type))
 | 
					        self.tags.get(&(name.into(), tag_type))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Mutably get a tag by name and type or create a new one if it doesn't exist.
 | 
					    /// Mutably get a tag by name and type or create a new one if it doesn't exist.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn tag_mut(&mut self, name: &str, tag_type: TagType) -> &mut Tag {
 | 
					    pub fn tag_mut(&mut self, name: impl Into<String>, tag_type: TagType) -> &mut Tag {
 | 
				
			||||||
        self.tags
 | 
					        self.tags
 | 
				
			||||||
            .entry((name.to_string(), tag_type))
 | 
					            .entry((name.into(), tag_type))
 | 
				
			||||||
            .or_insert_with(|| Tag::new(false))
 | 
					            .or_insert_with(|| Tag::new(false))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,10 +37,16 @@ impl Tag {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get the values of the tag.
 | 
					    /// Get the values of the tag.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn get_values(&self) -> &Vec<TagValue> {
 | 
					    pub fn values(&self) -> &[TagValue] {
 | 
				
			||||||
        &self.values
 | 
					        &self.values
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a mutable reference to the values of the tag.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn values_mut(&mut self) -> &mut Vec<TagValue> {
 | 
				
			||||||
 | 
					        &mut self.values
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Add a value to the tag.
 | 
					    /// Add a value to the tag.
 | 
				
			||||||
    pub fn add_value(&mut self, value: TagValue) {
 | 
					    pub fn add_value(&mut self, value: TagValue) {
 | 
				
			||||||
        self.values.push(value);
 | 
					        self.values.push(value);
 | 
				
			||||||
| 
						 | 
					@ -186,7 +192,7 @@ mod tests {
 | 
				
			||||||
            required: true,
 | 
					            required: true,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_eq!(tag.get_values().len(), 2);
 | 
					        assert_eq!(tag.values().len(), 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let compiled = tag.compile(&CompileOptions::default(), &MutCompilerState::default());
 | 
					        let compiled = tag.compile(&CompileOptions::default(), &MutCompilerState::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
)]
 | 
					)]
 | 
				
			||||||
#![warn(clippy::all, clippy::pedantic, clippy::perf)]
 | 
					#![warn(clippy::all, clippy::pedantic, clippy::perf)]
 | 
				
			||||||
#![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)]
 | 
					#![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)]
 | 
				
			||||||
 | 
					#![cfg_attr(docsrs, feature(doc_cfg))]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod datapack;
 | 
					pub mod datapack;
 | 
				
			||||||
pub mod util;
 | 
					pub mod util;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,8 @@ pub struct CompileOptions {
 | 
				
			||||||
    pub(crate) pack_format: u8,
 | 
					    pub(crate) pack_format: u8,
 | 
				
			||||||
    /// Whether to compile in debug mode.
 | 
					    /// Whether to compile in debug mode.
 | 
				
			||||||
    pub(crate) debug: bool,
 | 
					    pub(crate) debug: bool,
 | 
				
			||||||
 | 
					    /// Whether to generate an uninstall function.
 | 
				
			||||||
 | 
					    pub(crate) uninstall_function: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CompileOptions {
 | 
					impl CompileOptions {
 | 
				
			||||||
| 
						 | 
					@ -25,6 +27,15 @@ impl CompileOptions {
 | 
				
			||||||
    pub fn with_debug(self, debug: bool) -> Self {
 | 
					    pub fn with_debug(self, debug: bool) -> Self {
 | 
				
			||||||
        Self { debug, ..self }
 | 
					        Self { debug, ..self }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set whether to generate an uninstall function.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn with_uninstall_function(self, uninstall_function: bool) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            uninstall_function,
 | 
				
			||||||
 | 
					            ..self
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for CompileOptions {
 | 
					impl Default for CompileOptions {
 | 
				
			||||||
| 
						 | 
					@ -32,6 +43,7 @@ impl Default for CompileOptions {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            pack_format: Datapack::LATEST_FORMAT,
 | 
					            pack_format: Datapack::LATEST_FORMAT,
 | 
				
			||||||
            debug: true,
 | 
					            debug: true,
 | 
				
			||||||
 | 
					            uninstall_function: true,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,6 +131,7 @@ impl VFolder {
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - If the folder cannot be written
 | 
					    /// - If the folder cannot be written
 | 
				
			||||||
    #[cfg(feature = "fs_access")]
 | 
					    #[cfg(feature = "fs_access")]
 | 
				
			||||||
 | 
					    #[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
 | 
				
			||||||
    pub fn place<P>(&self, path: P) -> std::io::Result<()>
 | 
					    pub fn place<P>(&self, path: P) -> std::io::Result<()>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        P: AsRef<std::path::Path>,
 | 
					        P: AsRef<std::path::Path>,
 | 
				
			||||||
| 
						 | 
					@ -162,6 +163,7 @@ impl VFolder {
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - If the zip archive cannot be written
 | 
					    /// - If the zip archive cannot be written
 | 
				
			||||||
    #[cfg(all(feature = "fs_access", feature = "zip"))]
 | 
					    #[cfg(all(feature = "fs_access", feature = "zip"))]
 | 
				
			||||||
 | 
					    #[cfg_attr(docsrs, doc(cfg(all(feature = "fs_access", feature = "zip"))))]
 | 
				
			||||||
    pub fn zip<P>(&self, path: P) -> std::io::Result<()>
 | 
					    pub fn zip<P>(&self, path: P) -> std::io::Result<()>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        P: AsRef<std::path::Path>,
 | 
					        P: AsRef<std::path::Path>,
 | 
				
			||||||
| 
						 | 
					@ -174,6 +176,7 @@ impl VFolder {
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - If the zip archive cannot be written
 | 
					    /// - If the zip archive cannot be written
 | 
				
			||||||
    #[cfg(all(feature = "fs_access", feature = "zip"))]
 | 
					    #[cfg(all(feature = "fs_access", feature = "zip"))]
 | 
				
			||||||
 | 
					    #[cfg_attr(docsrs, doc(cfg(all(feature = "fs_access", feature = "zip"))))]
 | 
				
			||||||
    pub fn zip_with_comment<P, S>(&self, path: P, comment: S) -> std::io::Result<()>
 | 
					    pub fn zip_with_comment<P, S>(&self, path: P, comment: S) -> std::io::Result<()>
 | 
				
			||||||
    where
 | 
					    where
 | 
				
			||||||
        P: AsRef<std::path::Path>,
 | 
					        P: AsRef<std::path::Path>,
 | 
				
			||||||
| 
						 | 
					@ -246,14 +249,14 @@ impl VFolder {
 | 
				
			||||||
    /// Recursively merge another folder into this folder.
 | 
					    /// Recursively merge another folder into this folder.
 | 
				
			||||||
    /// Returns a list of paths that were replaced by other.
 | 
					    /// Returns a list of paths that were replaced by other.
 | 
				
			||||||
    pub fn merge(&mut self, other: Self) -> Vec<String> {
 | 
					    pub fn merge(&mut self, other: Self) -> Vec<String> {
 | 
				
			||||||
        self._merge(other, "")
 | 
					        self.merge_(other, "")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn _merge(&mut self, other: Self, prefix: &str) -> Vec<String> {
 | 
					    fn merge_(&mut self, other: Self, prefix: &str) -> Vec<String> {
 | 
				
			||||||
        let mut replaced = Vec::new();
 | 
					        let mut replaced = Vec::new();
 | 
				
			||||||
        for (name, folder) in other.folders {
 | 
					        for (name, folder) in other.folders {
 | 
				
			||||||
            if let Some(existing_folder) = self.folders.get_mut(&name) {
 | 
					            if let Some(existing_folder) = self.folders.get_mut(&name) {
 | 
				
			||||||
                let replaced_folder = existing_folder._merge(folder, &format!("{prefix}{name}/"));
 | 
					                let replaced_folder = existing_folder.merge_(folder, &format!("{prefix}{name}/"));
 | 
				
			||||||
                replaced.extend(replaced_folder);
 | 
					                replaced.extend(replaced_folder);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                self.folders.insert(name, folder);
 | 
					                self.folders.insert(name, folder);
 | 
				
			||||||
| 
						 | 
					@ -271,6 +274,7 @@ impl VFolder {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "fs_access")]
 | 
					#[cfg(feature = "fs_access")]
 | 
				
			||||||
 | 
					#[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
 | 
				
			||||||
impl TryFrom<&std::path::Path> for VFolder {
 | 
					impl TryFrom<&std::path::Path> for VFolder {
 | 
				
			||||||
    type Error = std::io::Error;
 | 
					    type Error = std::io::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -374,6 +378,7 @@ impl From<&[u8]> for VFile {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "fs_access")]
 | 
					#[cfg(feature = "fs_access")]
 | 
				
			||||||
 | 
					#[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
 | 
				
			||||||
impl TryFrom<&std::path::Path> for VFile {
 | 
					impl TryFrom<&std::path::Path> for VFile {
 | 
				
			||||||
    type Error = std::io::Error;
 | 
					    type Error = std::io::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue