Compare commits
	
		
			No commits in common. "d4689c696a35328c041bcbbfd203abd5818c46d3" and "b6ecdf6385b93556a559479e0b93cc7da3d5fabe" have entirely different histories.
		
	
	
		
			d4689c696a
			...
			b6ecdf6385
		
	
		| 
						 | 
				
			
			@ -5,7 +5,6 @@ use std::{
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    datapack::command::Group,
 | 
			
		||||
    prelude::Command,
 | 
			
		||||
    util::{
 | 
			
		||||
        compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState},
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +78,7 @@ fn compile_using_data_storage(
 | 
			
		|||
    #[allow(clippy::option_if_let_else)]
 | 
			
		||||
    let then = if let Some(success_uid) = require_grouping_uid.as_deref() {
 | 
			
		||||
        // prepare commands for grouping
 | 
			
		||||
        let mut group_cmds = match then.clone() {
 | 
			
		||||
        let mut group_cmd = match then.clone() {
 | 
			
		||||
            Execute::Run(cmd) => vec![*cmd],
 | 
			
		||||
            Execute::Runs(cmds) => cmds,
 | 
			
		||||
            ex => vec![Command::Execute(ex)],
 | 
			
		||||
| 
						 | 
				
			
			@ -87,13 +86,13 @@ fn compile_using_data_storage(
 | 
			
		|||
        // add success condition to the group
 | 
			
		||||
        // this condition will be checked after the group ran to determine if the else part should be executed
 | 
			
		||||
        if el.is_some() && str_cond.len() <= 1 {
 | 
			
		||||
            group_cmds.push(
 | 
			
		||||
            group_cmd.push(
 | 
			
		||||
                format!("data modify storage shulkerbox:cond {success_uid} set value true")
 | 
			
		||||
                    .as_str()
 | 
			
		||||
                    .into(),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        let group = Command::Group(Group::new(group_cmds));
 | 
			
		||||
        let group = Command::Group(group_cmd);
 | 
			
		||||
        let allows_prefix = !group.forbid_prefix();
 | 
			
		||||
        group
 | 
			
		||||
            .compile(options, global_state, function_state)
 | 
			
		||||
| 
						 | 
				
			
			@ -228,7 +227,7 @@ fn compile_since_20_format(
 | 
			
		|||
                global_state,
 | 
			
		||||
                function_state,
 | 
			
		||||
            );
 | 
			
		||||
            let group = Command::Group(Group::new(group_cmds));
 | 
			
		||||
            let group = Command::Group(group_cmds);
 | 
			
		||||
            let cmds = group.compile(options, global_state, function_state);
 | 
			
		||||
            if contains_macros {
 | 
			
		||||
                cmds.into_iter()
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +254,7 @@ fn compile_since_20_format(
 | 
			
		|||
            Execute::Runs(cmds) => cmds,
 | 
			
		||||
            ex => vec![Command::Execute(ex)],
 | 
			
		||||
        };
 | 
			
		||||
        let group_cmd = Command::Group(Group::new(then_cmds));
 | 
			
		||||
        let group_cmd = Command::Group(then_cmds);
 | 
			
		||||
        let then_cmd = if group_cmd.forbid_prefix() {
 | 
			
		||||
            group_cmd
 | 
			
		||||
        } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -347,7 +346,7 @@ fn handle_return_group_case_since_20(
 | 
			
		|||
        Execute::Runs(cmds) => cmds,
 | 
			
		||||
        ex => vec![Command::Execute(ex)],
 | 
			
		||||
    };
 | 
			
		||||
    let group = Command::Group(Group::new(then_cmd));
 | 
			
		||||
    let group = Command::Group(then_cmd);
 | 
			
		||||
    let then_cmd_concat = if group.forbid_prefix() {
 | 
			
		||||
        group
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,9 @@
 | 
			
		|||
use std::{collections::HashSet, ops::RangeInclusive, string::ToString};
 | 
			
		||||
 | 
			
		||||
use super::Command;
 | 
			
		||||
use crate::{
 | 
			
		||||
    datapack::command::Group,
 | 
			
		||||
    util::{
 | 
			
		||||
use crate::util::{
 | 
			
		||||
    compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState},
 | 
			
		||||
    ExtendableQueue, MacroString,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod conditional;
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +162,7 @@ impl Execute {
 | 
			
		|||
                })
 | 
			
		||||
                .collect(),
 | 
			
		||||
            Self::Runs(commands) => {
 | 
			
		||||
                let group = Command::Group(Group::new(commands.clone()));
 | 
			
		||||
                let group = Command::Group(commands.clone());
 | 
			
		||||
                group
 | 
			
		||||
                    .compile(options, global_state, function_state)
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,6 @@
 | 
			
		|||
mod execute;
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::{HashMap, HashSet},
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    ops::RangeInclusive,
 | 
			
		||||
    sync::LazyLock,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +33,7 @@ pub enum Command {
 | 
			
		|||
    /// Execute command
 | 
			
		||||
    Execute(Execute),
 | 
			
		||||
    /// Group of commands to be called instantly after each other
 | 
			
		||||
    Group(Group),
 | 
			
		||||
    Group(Vec<Command>),
 | 
			
		||||
    /// Comment to be added to the function
 | 
			
		||||
    Comment(String),
 | 
			
		||||
    /// Return value
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +64,7 @@ impl Command {
 | 
			
		|||
            }
 | 
			
		||||
            Self::Debug(message) => compile_debug(message, options),
 | 
			
		||||
            Self::Execute(ex) => ex.compile(options, global_state, function_state),
 | 
			
		||||
            Self::Group(group) => group.compile(options, global_state, function_state),
 | 
			
		||||
            Self::Group(commands) => compile_group(commands, options, global_state, function_state),
 | 
			
		||||
            Self::Comment(comment) => {
 | 
			
		||||
                vec![CompiledCommand::new("#".to_string() + comment).with_forbid_prefix(true)]
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -102,8 +101,7 @@ impl Command {
 | 
			
		|||
            Self::Raw(cmd) => cmd.split('\n').count(),
 | 
			
		||||
            Self::UsesMacro(cmd) => cmd.line_count(),
 | 
			
		||||
            Self::Execute(ex) => ex.get_count(options),
 | 
			
		||||
            Self::Group(group) => group.get_count(options),
 | 
			
		||||
            Self::Return(_) => 1,
 | 
			
		||||
            Self::Group(_) | Self::Return(_) => 1,
 | 
			
		||||
            Self::Concat(a, b) => a.get_count(options) + b.get_count(options) - 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +135,7 @@ impl Command {
 | 
			
		|||
        match self {
 | 
			
		||||
            Self::Raw(_) | Self::Comment(_) => false,
 | 
			
		||||
            Self::UsesMacro(s) | Self::Debug(s) => s.contains_macro(),
 | 
			
		||||
            Self::Group(group) => group.contains_macro(),
 | 
			
		||||
            Self::Group(commands) => group_contains_macro(commands),
 | 
			
		||||
            Self::Execute(ex) => ex.contains_macro(),
 | 
			
		||||
            Self::Return(ret) => match ret {
 | 
			
		||||
                ReturnCommand::Value(value) => value.contains_macro(),
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +151,7 @@ impl Command {
 | 
			
		|||
        match self {
 | 
			
		||||
            Self::Raw(_) | Self::Comment(_) => HashSet::new(),
 | 
			
		||||
            Self::UsesMacro(s) | Self::Debug(s) => s.get_macros(),
 | 
			
		||||
            Self::Group(group) => group.get_macros(),
 | 
			
		||||
            Self::Group(commands) => group_get_macros(commands),
 | 
			
		||||
            Self::Execute(ex) => ex.get_macros(),
 | 
			
		||||
            Self::Return(ret) => match ret {
 | 
			
		||||
                ReturnCommand::Value(value) => value.get_macros(),
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +171,7 @@ impl Command {
 | 
			
		|||
        match self {
 | 
			
		||||
            Self::Comment(_) => true,
 | 
			
		||||
            Self::Raw(_) | Self::Debug(_) | Self::Execute(_) | Self::UsesMacro(_) => false,
 | 
			
		||||
            Self::Group(group) => group.forbid_prefix(),
 | 
			
		||||
            Self::Group(commands) => commands.len() == 1 && commands[0].forbid_prefix(),
 | 
			
		||||
            Self::Return(ret) => match ret {
 | 
			
		||||
                ReturnCommand::Value(_) => false,
 | 
			
		||||
                ReturnCommand::Command(cmd) => cmd.forbid_prefix(),
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +190,7 @@ impl Command {
 | 
			
		|||
            Self::Execute(exec) => exec.contains_return(),
 | 
			
		||||
            Self::Raw(cmd) => cmd.starts_with("return "),
 | 
			
		||||
            Self::UsesMacro(m) => m.compile().starts_with("return "),
 | 
			
		||||
            Self::Group(g) => g.contains_return(),
 | 
			
		||||
            Self::Group(g) => g.iter().any(Self::contains_return),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -213,85 +211,54 @@ impl From<&mut Function> for Command {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a group of commands to be executed in sequence.
 | 
			
		||||
/// Represents a command that returns a value.
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct Group {
 | 
			
		||||
    /// The commands in the group.
 | 
			
		||||
    commands: Vec<Command>,
 | 
			
		||||
    /// Whether to always create a function for this group, even if it contains only one command.
 | 
			
		||||
    always_create_function: bool,
 | 
			
		||||
    /// Optional name for the data storage used for arguments.
 | 
			
		||||
    data_storage_name: Option<String>,
 | 
			
		||||
    /// Optional set of macros that should not be passed to the function, even though they are contained.
 | 
			
		||||
    /// This can be used together with `data_storage_name` to dynamically pass arguments to the function.
 | 
			
		||||
    block_pass_macros: Option<HashSet<String>>,
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
			
		||||
pub enum ReturnCommand {
 | 
			
		||||
    /// Returns the value
 | 
			
		||||
    Value(MacroString),
 | 
			
		||||
    /// Returns the result of the command
 | 
			
		||||
    Command(Box<Command>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Group {
 | 
			
		||||
    /// Create a new group of commands.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn new(commands: Vec<Command>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            commands,
 | 
			
		||||
            always_create_function: false,
 | 
			
		||||
            data_storage_name: None,
 | 
			
		||||
            block_pass_macros: None,
 | 
			
		||||
        }
 | 
			
		||||
fn compile_debug(message: &MacroString, option: &CompileOptions) -> Vec<CompiledCommand> {
 | 
			
		||||
    if option.debug {
 | 
			
		||||
        vec![CompiledCommand::new(format!(
 | 
			
		||||
            r#"tellraw @a [{{"text":"[","color":"dark_blue"}},{{"text":"DEBUG","color":"dark_green","hoverEvent":{{"action":"show_text","value":[{{"text":"Debug message generated by Shulkerbox"}},{{"text":"\nSet debug to 'false' to disable"}}]}}}},{{"text":"] ","color":"dark_blue"}},{{"text":"{}","color":"black"}}]"#,
 | 
			
		||||
            message.compile()
 | 
			
		||||
        ))]
 | 
			
		||||
    } else {
 | 
			
		||||
        Vec::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn with_always_create_function(mut self, always_create_function: bool) -> Self {
 | 
			
		||||
        self.always_create_function = always_create_function;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn with_block_pass_macros(mut self, block: HashSet<String>) -> Self {
 | 
			
		||||
        self.block_pass_macros = Some(block);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn with_data_storage_name(mut self, name: String) -> Self {
 | 
			
		||||
        self.data_storage_name = Some(name);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Compile the execute command into a list of compiled commands.
 | 
			
		||||
    #[expect(clippy::too_many_lines)]
 | 
			
		||||
    #[tracing::instrument(skip_all, fields(commands = ?self.commands))]
 | 
			
		||||
    pub fn compile(
 | 
			
		||||
        &self,
 | 
			
		||||
#[tracing::instrument(skip_all, fields(commands = ?commands))]
 | 
			
		||||
fn compile_group(
 | 
			
		||||
    commands: &[Command],
 | 
			
		||||
    options: &CompileOptions,
 | 
			
		||||
    global_state: &MutCompilerState,
 | 
			
		||||
    function_state: &FunctionCompilerState,
 | 
			
		||||
    ) -> Vec<CompiledCommand> {
 | 
			
		||||
        let command_count = self
 | 
			
		||||
            .commands
 | 
			
		||||
) -> Vec<CompiledCommand> {
 | 
			
		||||
    let command_count = commands
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|cmd| cmd.get_count(options))
 | 
			
		||||
        .sum::<usize>();
 | 
			
		||||
    // only create a function if there are more than one command
 | 
			
		||||
    match command_count {
 | 
			
		||||
            0 if !self.always_create_function => Vec::new(),
 | 
			
		||||
            1 if !self.always_create_function => {
 | 
			
		||||
                self.commands[0].compile(options, global_state, function_state)
 | 
			
		||||
            }
 | 
			
		||||
        0 => Vec::new(),
 | 
			
		||||
        1 => commands[0].compile(options, global_state, function_state),
 | 
			
		||||
        _ => {
 | 
			
		||||
                let pass_macros = self.contains_macro();
 | 
			
		||||
                let contains_return = self.contains_return();
 | 
			
		||||
            let pass_macros = group_contains_macro(commands);
 | 
			
		||||
            let contains_return = commands.iter().any(Command::contains_return);
 | 
			
		||||
 | 
			
		||||
            // calculate a hashed path for the function in the `sb` subfolder
 | 
			
		||||
                let function_path = Self::generate_function_path(function_state);
 | 
			
		||||
            let function_path = generate_group_function_path(function_state);
 | 
			
		||||
 | 
			
		||||
            let namespace = function_state.namespace();
 | 
			
		||||
 | 
			
		||||
            // create a new function with the commands
 | 
			
		||||
            let mut function = Function::new(namespace, &function_path);
 | 
			
		||||
                function
 | 
			
		||||
                    .get_commands_mut()
 | 
			
		||||
                    .extend(self.commands.iter().cloned());
 | 
			
		||||
            function.get_commands_mut().extend(commands.iter().cloned());
 | 
			
		||||
            function_state.add_function(&function_path, function);
 | 
			
		||||
 | 
			
		||||
            let mut function_invocation = format!("function {namespace}:{function_path}");
 | 
			
		||||
| 
						 | 
				
			
			@ -356,123 +323,31 @@ impl Group {
 | 
			
		|||
                None
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
                let prepare_data_storage = if pass_macros {
 | 
			
		||||
                    let contained_macros = self.get_macros();
 | 
			
		||||
                    let not_all_macros_blocked = self
 | 
			
		||||
                        .block_pass_macros
 | 
			
		||||
                        .as_ref()
 | 
			
		||||
                        .is_none_or(|b| contained_macros.iter().any(|&m| !b.contains(m)));
 | 
			
		||||
 | 
			
		||||
                    if !contained_macros.is_empty()
 | 
			
		||||
                        && (self.data_storage_name.is_some() || not_all_macros_blocked)
 | 
			
		||||
                    {
 | 
			
		||||
                        use std::fmt::Write as _;
 | 
			
		||||
 | 
			
		||||
            if pass_macros {
 | 
			
		||||
                // WARNING: this seems to be the only way to pass macros to the function called.
 | 
			
		||||
                // Because everything is passed as a string, it looses one "level" of escaping per pass.
 | 
			
		||||
                        let macros_block = self
 | 
			
		||||
                            .get_macros()
 | 
			
		||||
                let macros_block = group_get_macros(commands)
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
                            .filter(|&m| {
 | 
			
		||||
                                self.block_pass_macros
 | 
			
		||||
                                    .as_ref()
 | 
			
		||||
                                    .is_none_or(|b| !b.contains(m))
 | 
			
		||||
                            })
 | 
			
		||||
                    .map(|m| format!(r#"{m}:"$({m})""#))
 | 
			
		||||
                    .collect::<Vec<_>>()
 | 
			
		||||
                    .join(",");
 | 
			
		||||
 | 
			
		||||
                        if let Some(data_storage_name) = self.data_storage_name.as_deref() {
 | 
			
		||||
                            let _ =
 | 
			
		||||
                                write!(function_invocation, " with storage {data_storage_name}");
 | 
			
		||||
 | 
			
		||||
                            not_all_macros_blocked.then(|| {
 | 
			
		||||
                                CompiledCommand::new(format!(
 | 
			
		||||
                                    "data merge storage {data_storage_name} {{{macros_block}}}"
 | 
			
		||||
                                ))
 | 
			
		||||
                                .with_contains_macros(true)
 | 
			
		||||
                            })
 | 
			
		||||
                        } else {
 | 
			
		||||
                            let _ = write!(function_invocation, " {{{macros_block}}}");
 | 
			
		||||
 | 
			
		||||
                            None
 | 
			
		||||
                function_invocation.push_str(&format!(" {{{macros_block}}}"));
 | 
			
		||||
            }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        None
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            if let Some((mut pre_cmds, post_cmds)) = additional_return_cmds {
 | 
			
		||||
                    if let Some(prepare_datastorage_cmd) = prepare_data_storage {
 | 
			
		||||
                        pre_cmds.push(prepare_datastorage_cmd);
 | 
			
		||||
                    }
 | 
			
		||||
                pre_cmds.push(
 | 
			
		||||
                        CompiledCommand::new(function_invocation)
 | 
			
		||||
                            .with_contains_macros(pass_macros && self.data_storage_name.is_none()),
 | 
			
		||||
                    CompiledCommand::new(function_invocation).with_contains_macros(pass_macros),
 | 
			
		||||
                );
 | 
			
		||||
                pre_cmds.extend(post_cmds);
 | 
			
		||||
                pre_cmds
 | 
			
		||||
            } else {
 | 
			
		||||
                    prepare_data_storage
 | 
			
		||||
                        .into_iter()
 | 
			
		||||
                        .chain(std::iter::once(
 | 
			
		||||
                            CompiledCommand::new(function_invocation).with_contains_macros(
 | 
			
		||||
                                pass_macros && self.data_storage_name.is_none(),
 | 
			
		||||
                            ),
 | 
			
		||||
                        ))
 | 
			
		||||
                        .collect()
 | 
			
		||||
                }
 | 
			
		||||
                vec![CompiledCommand::new(function_invocation).with_contains_macros(pass_macros)]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /// Get a reference to the commands in the group.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn commands(&self) -> &[Command] {
 | 
			
		||||
        &self.commands
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get a mutable reference to the commands in the group.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn commands_mut(&mut self) -> &mut Vec<Command> {
 | 
			
		||||
        &mut self.commands
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get whether to always create a function for this group.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn always_create_function(&self) -> bool {
 | 
			
		||||
        self.always_create_function
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the optional data storage name.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn data_storage_name(&self) -> Option<&String> {
 | 
			
		||||
        self.data_storage_name.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the optional set of blocked macros.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn block_pass_macros(&self) -> Option<&HashSet<String>> {
 | 
			
		||||
        self.block_pass_macros.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check whether the group contains a macro.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn contains_macro(&self) -> bool {
 | 
			
		||||
        self.commands.iter().any(Command::contains_macro)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check whether the group contains a return command.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn contains_return(&self) -> bool {
 | 
			
		||||
        self.commands.iter().any(Command::contains_return)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Generate a unique function path based on the function state.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    fn generate_function_path(function_state: &FunctionCompilerState) -> String {
 | 
			
		||||
fn generate_group_function_path(function_state: &FunctionCompilerState) -> String {
 | 
			
		||||
    let uid = function_state.request_uid();
 | 
			
		||||
    let function_path = function_state.path();
 | 
			
		||||
    let function_path = function_path.strip_prefix("sb/").unwrap_or(function_path);
 | 
			
		||||
| 
						 | 
				
			
			@ -481,119 +356,18 @@ impl Group {
 | 
			
		|||
    let hash = md5::hash(pre_hash_path).to_hex_lowercase();
 | 
			
		||||
 | 
			
		||||
    "sb/".to_string() + function_path + "/" + &hash[..16]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /// Returns the names of the macros used
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn get_macros(&self) -> HashSet<&str> {
 | 
			
		||||
fn group_contains_macro(commands: &[Command]) -> bool {
 | 
			
		||||
    commands.iter().any(Command::contains_macro)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn group_get_macros(commands: &[Command]) -> HashSet<&str> {
 | 
			
		||||
    let mut macros = HashSet::new();
 | 
			
		||||
        for cmd in &self.commands {
 | 
			
		||||
    for cmd in commands {
 | 
			
		||||
        macros.extend(cmd.get_macros());
 | 
			
		||||
    }
 | 
			
		||||
    macros
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check whether the group should not have a prefix.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn forbid_prefix(&self) -> bool {
 | 
			
		||||
        self.commands.len() == 1 && self.commands[0].forbid_prefix()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the count of the commands this command will compile into.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    fn get_count(&self, options: &CompileOptions) -> usize {
 | 
			
		||||
        let command_count = self
 | 
			
		||||
            .commands
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cmd| cmd.get_count(options))
 | 
			
		||||
            .sum::<usize>();
 | 
			
		||||
        // only create a function if there are more than one command
 | 
			
		||||
        match command_count {
 | 
			
		||||
            0 if !self.always_create_function => 0,
 | 
			
		||||
            1 if !self.always_create_function => 1,
 | 
			
		||||
            _ => {
 | 
			
		||||
                let pass_macros = self.contains_macro();
 | 
			
		||||
                let contains_return = self.contains_return();
 | 
			
		||||
 | 
			
		||||
                let additional_return_cmds = if contains_return {
 | 
			
		||||
                    let post_cmd_store = Command::Execute(Execute::If(
 | 
			
		||||
                        Condition::Atom("".into()),
 | 
			
		||||
                        Box::new(Execute::Run(Box::new(Command::Raw(String::new())))),
 | 
			
		||||
                        None,
 | 
			
		||||
                    ))
 | 
			
		||||
                    .get_count(options);
 | 
			
		||||
 | 
			
		||||
                    let post_cmd_return = Command::Execute(Execute::If(
 | 
			
		||||
                        Condition::Atom("".into()),
 | 
			
		||||
                        Box::new(Execute::Run(Box::new(Command::Raw(String::new())))),
 | 
			
		||||
                        None,
 | 
			
		||||
                    ))
 | 
			
		||||
                    .get_count(options);
 | 
			
		||||
 | 
			
		||||
                    let post_cmds = post_cmd_store + post_cmd_return;
 | 
			
		||||
 | 
			
		||||
                    Some(post_cmds + 1)
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let prepare_data_storage = if pass_macros {
 | 
			
		||||
                    let contained_macros = self.get_macros();
 | 
			
		||||
                    let not_all_macros_blocked = self
 | 
			
		||||
                        .block_pass_macros
 | 
			
		||||
                        .as_ref()
 | 
			
		||||
                        .is_none_or(|b| contained_macros.iter().any(|&m| !b.contains(m)));
 | 
			
		||||
 | 
			
		||||
                    !contained_macros.is_empty()
 | 
			
		||||
                        && (self.data_storage_name.is_some() || not_all_macros_blocked)
 | 
			
		||||
                            & self.data_storage_name.is_some()
 | 
			
		||||
                } else {
 | 
			
		||||
                    false
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                additional_return_cmds.map_or_else(
 | 
			
		||||
                    || 1 + usize::from(prepare_data_storage),
 | 
			
		||||
                    |additional_return_cmds| {
 | 
			
		||||
                        additional_return_cmds + 1 + usize::from(prepare_data_storage)
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Hash for Group {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.commands.hash(state);
 | 
			
		||||
        self.always_create_function.hash(state);
 | 
			
		||||
        if let Some(block) = &self.block_pass_macros {
 | 
			
		||||
            #[expect(clippy::collection_is_never_read)]
 | 
			
		||||
            let mut block_vec = block.iter().collect::<Vec<_>>();
 | 
			
		||||
            block_vec.sort();
 | 
			
		||||
            block_vec.hash(state);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a command that returns a value.
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
			
		||||
pub enum ReturnCommand {
 | 
			
		||||
    /// Returns the value
 | 
			
		||||
    Value(MacroString),
 | 
			
		||||
    /// Returns the result of the command
 | 
			
		||||
    Command(Box<Command>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn compile_debug(message: &MacroString, option: &CompileOptions) -> Vec<CompiledCommand> {
 | 
			
		||||
    if option.debug {
 | 
			
		||||
        vec![CompiledCommand::new(format!(
 | 
			
		||||
            r#"tellraw @a [{{"text":"[","color":"dark_blue"}},{{"text":"DEBUG","color":"dark_green","hoverEvent":{{"action":"show_text","value":[{{"text":"Debug message generated by Shulkerbox"}},{{"text":"\nSet debug to 'false' to disable"}}]}}}},{{"text":"] ","color":"dark_blue"}},{{"text":"{}","color":"black"}}]"#,
 | 
			
		||||
            message.compile()
 | 
			
		||||
        ))]
 | 
			
		||||
    } else {
 | 
			
		||||
        Vec::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ReturnCommand {
 | 
			
		||||
| 
						 | 
				
			
			@ -626,7 +400,7 @@ impl ReturnCommand {
 | 
			
		|||
                vec![store_cmd, return_cmd]
 | 
			
		||||
            }
 | 
			
		||||
            (Self::Command(cmd), None) => {
 | 
			
		||||
                let compiled_cmd = Command::Group(Group::new(vec![*cmd.clone()])).compile(
 | 
			
		||||
                let compiled_cmd = Command::Group(vec![*cmd.clone()]).compile(
 | 
			
		||||
                    options,
 | 
			
		||||
                    global_state,
 | 
			
		||||
                    function_state,
 | 
			
		||||
| 
						 | 
				
			
			@ -640,9 +414,7 @@ impl ReturnCommand {
 | 
			
		|||
            (Self::Command(cmd), Some(data_path)) => {
 | 
			
		||||
                let compiled_cmd = Command::Execute(Execute::Store(
 | 
			
		||||
                    format!("result storage shulkerbox:return {data_path} int 1.0").into(),
 | 
			
		||||
                    Box::new(Execute::Run(Box::new(Command::Group(Group::new(vec![
 | 
			
		||||
                        *cmd.clone(),
 | 
			
		||||
                    ]))))),
 | 
			
		||||
                    Box::new(Execute::Run(Box::new(Command::Group(vec![*cmd.clone()])))),
 | 
			
		||||
                ))
 | 
			
		||||
                .compile(options, global_state, function_state);
 | 
			
		||||
                let compiled_cmd = compiled_cmd
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ mod command;
 | 
			
		|||
mod function;
 | 
			
		||||
mod namespace;
 | 
			
		||||
pub mod tag;
 | 
			
		||||
pub use command::{Command, Condition, Execute, Group, ReturnCommand};
 | 
			
		||||
pub use command::{Command, Condition, Execute, ReturnCommand};
 | 
			
		||||
pub use function::Function;
 | 
			
		||||
pub use namespace::Namespace;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ pub struct Datapack {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Datapack {
 | 
			
		||||
    pub const LATEST_FORMAT: u8 = 81;
 | 
			
		||||
    pub const LATEST_FORMAT: u8 = 61;
 | 
			
		||||
 | 
			
		||||
    /// Create a new Minecraft datapack.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue