add group options for always extracting and manually loading macros
This commit is contained in:
parent
b6ecdf6385
commit
89709834da
|
@ -5,6 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
datapack::command::Group,
|
||||
prelude::Command,
|
||||
util::{
|
||||
compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState},
|
||||
|
@ -78,7 +79,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_cmd = match then.clone() {
|
||||
let mut group_cmds = match then.clone() {
|
||||
Execute::Run(cmd) => vec![*cmd],
|
||||
Execute::Runs(cmds) => cmds,
|
||||
ex => vec![Command::Execute(ex)],
|
||||
|
@ -86,13 +87,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_cmd.push(
|
||||
group_cmds.push(
|
||||
format!("data modify storage shulkerbox:cond {success_uid} set value true")
|
||||
.as_str()
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let group = Command::Group(group_cmd);
|
||||
let group = Command::Group(Group::new(group_cmds));
|
||||
let allows_prefix = !group.forbid_prefix();
|
||||
group
|
||||
.compile(options, global_state, function_state)
|
||||
|
@ -227,7 +228,7 @@ fn compile_since_20_format(
|
|||
global_state,
|
||||
function_state,
|
||||
);
|
||||
let group = Command::Group(group_cmds);
|
||||
let group = Command::Group(Group::new(group_cmds));
|
||||
let cmds = group.compile(options, global_state, function_state);
|
||||
if contains_macros {
|
||||
cmds.into_iter()
|
||||
|
@ -254,7 +255,7 @@ fn compile_since_20_format(
|
|||
Execute::Runs(cmds) => cmds,
|
||||
ex => vec![Command::Execute(ex)],
|
||||
};
|
||||
let group_cmd = Command::Group(then_cmds);
|
||||
let group_cmd = Command::Group(Group::new(then_cmds));
|
||||
let then_cmd = if group_cmd.forbid_prefix() {
|
||||
group_cmd
|
||||
} else {
|
||||
|
@ -346,7 +347,7 @@ fn handle_return_group_case_since_20(
|
|||
Execute::Runs(cmds) => cmds,
|
||||
ex => vec![Command::Execute(ex)],
|
||||
};
|
||||
let group = Command::Group(then_cmd);
|
||||
let group = Command::Group(Group::new(then_cmd));
|
||||
let then_cmd_concat = if group.forbid_prefix() {
|
||||
group
|
||||
} else {
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use std::{collections::HashSet, ops::RangeInclusive, string::ToString};
|
||||
|
||||
use super::Command;
|
||||
use crate::util::{
|
||||
compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState},
|
||||
ExtendableQueue, MacroString,
|
||||
use crate::{
|
||||
datapack::command::Group,
|
||||
util::{
|
||||
compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState},
|
||||
ExtendableQueue, MacroString,
|
||||
},
|
||||
};
|
||||
|
||||
mod conditional;
|
||||
|
@ -162,7 +165,7 @@ impl Execute {
|
|||
})
|
||||
.collect(),
|
||||
Self::Runs(commands) => {
|
||||
let group = Command::Group(commands.clone());
|
||||
let group = Command::Group(Group::new(commands.clone()));
|
||||
group
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
mod execute;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
hash::Hash,
|
||||
ops::RangeInclusive,
|
||||
sync::LazyLock,
|
||||
};
|
||||
|
@ -33,7 +34,7 @@ pub enum Command {
|
|||
/// Execute command
|
||||
Execute(Execute),
|
||||
/// Group of commands to be called instantly after each other
|
||||
Group(Vec<Command>),
|
||||
Group(Group),
|
||||
/// Comment to be added to the function
|
||||
Comment(String),
|
||||
/// Return value
|
||||
|
@ -64,7 +65,7 @@ impl Command {
|
|||
}
|
||||
Self::Debug(message) => compile_debug(message, options),
|
||||
Self::Execute(ex) => ex.compile(options, global_state, function_state),
|
||||
Self::Group(commands) => compile_group(commands, options, global_state, function_state),
|
||||
Self::Group(group) => group.compile(options, global_state, function_state),
|
||||
Self::Comment(comment) => {
|
||||
vec![CompiledCommand::new("#".to_string() + comment).with_forbid_prefix(true)]
|
||||
}
|
||||
|
@ -101,7 +102,8 @@ impl Command {
|
|||
Self::Raw(cmd) => cmd.split('\n').count(),
|
||||
Self::UsesMacro(cmd) => cmd.line_count(),
|
||||
Self::Execute(ex) => ex.get_count(options),
|
||||
Self::Group(_) | Self::Return(_) => 1,
|
||||
Self::Group(group) => group.get_count(options),
|
||||
Self::Return(_) => 1,
|
||||
Self::Concat(a, b) => a.get_count(options) + b.get_count(options) - 1,
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +137,7 @@ impl Command {
|
|||
match self {
|
||||
Self::Raw(_) | Self::Comment(_) => false,
|
||||
Self::UsesMacro(s) | Self::Debug(s) => s.contains_macro(),
|
||||
Self::Group(commands) => group_contains_macro(commands),
|
||||
Self::Group(group) => group.contains_macro(),
|
||||
Self::Execute(ex) => ex.contains_macro(),
|
||||
Self::Return(ret) => match ret {
|
||||
ReturnCommand::Value(value) => value.contains_macro(),
|
||||
|
@ -151,7 +153,7 @@ impl Command {
|
|||
match self {
|
||||
Self::Raw(_) | Self::Comment(_) => HashSet::new(),
|
||||
Self::UsesMacro(s) | Self::Debug(s) => s.get_macros(),
|
||||
Self::Group(commands) => group_get_macros(commands),
|
||||
Self::Group(group) => group.get_macros(),
|
||||
Self::Execute(ex) => ex.get_macros(),
|
||||
Self::Return(ret) => match ret {
|
||||
ReturnCommand::Value(value) => value.get_macros(),
|
||||
|
@ -171,7 +173,7 @@ impl Command {
|
|||
match self {
|
||||
Self::Comment(_) => true,
|
||||
Self::Raw(_) | Self::Debug(_) | Self::Execute(_) | Self::UsesMacro(_) => false,
|
||||
Self::Group(commands) => commands.len() == 1 && commands[0].forbid_prefix(),
|
||||
Self::Group(group) => group.forbid_prefix(),
|
||||
Self::Return(ret) => match ret {
|
||||
ReturnCommand::Value(_) => false,
|
||||
ReturnCommand::Command(cmd) => cmd.forbid_prefix(),
|
||||
|
@ -190,7 +192,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.iter().any(Self::contains_return),
|
||||
Self::Group(g) => g.contains_return(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,6 +213,335 @@ impl From<&mut Function> for Command {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a group of commands to be executed in sequence.
|
||||
#[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>>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn always_create_function(mut self, always_create_function: bool) -> Self {
|
||||
self.always_create_function = always_create_function;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn block_pass_macros(mut self, block: HashSet<String>) -> Self {
|
||||
self.block_pass_macros = Some(block);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn 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,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &FunctionCompilerState,
|
||||
) -> Vec<CompiledCommand> {
|
||||
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 => Vec::new(),
|
||||
1 if !self.always_create_function => {
|
||||
self.commands[0].compile(options, global_state, function_state)
|
||||
}
|
||||
_ => {
|
||||
let pass_macros = self.contains_macro();
|
||||
let contains_return = self.contains_return();
|
||||
|
||||
// calculate a hashed path for the function in the `sb` subfolder
|
||||
let function_path = Self::generate_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_state.add_function(&function_path, function);
|
||||
|
||||
let mut function_invocation = format!("function {namespace}:{function_path}");
|
||||
|
||||
let additional_return_cmds = if contains_return {
|
||||
let full_path = format!("{namespace}:{function_path}");
|
||||
let return_data_path = md5::hash(&full_path).to_hex_lowercase();
|
||||
|
||||
let pre_cmds = Command::Raw(format!(
|
||||
"data remove storage shulkerbox:return {return_data_path}"
|
||||
))
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| c.with_forbid_prefix(true))
|
||||
.collect::<Vec<_>>();
|
||||
let post_condition = Condition::Atom(
|
||||
format!("data storage shulkerbox:return {return_data_path}").into(),
|
||||
);
|
||||
|
||||
let post_cmd_store = global_state
|
||||
.read()
|
||||
.unwrap()
|
||||
.functions_with_special_return
|
||||
.get(&format!(
|
||||
"{}:{}",
|
||||
function_state.namespace(),
|
||||
function_state.path()
|
||||
))
|
||||
.cloned().map(|parent_return_data_path| {
|
||||
Command::Execute(Execute::If(
|
||||
post_condition.clone(),
|
||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||
"data modify storage shulkerbox:return {parent_return_data_path} set from storage shulkerbox:return {return_data_path}"
|
||||
))))),
|
||||
None,
|
||||
))
|
||||
});
|
||||
|
||||
let post_cmd_return = Command::Execute(Execute::If(
|
||||
post_condition,
|
||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||
"return run data get storage shulkerbox:return {return_data_path}"
|
||||
))))),
|
||||
None,
|
||||
));
|
||||
|
||||
let post_cmds = post_cmd_store
|
||||
.into_iter()
|
||||
.chain(std::iter::once(post_cmd_return))
|
||||
.flat_map(|cmd| cmd.compile(options, global_state, function_state))
|
||||
.map(|c| c.with_forbid_prefix(true))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
global_state
|
||||
.write()
|
||||
.unwrap()
|
||||
.functions_with_special_return
|
||||
.insert(full_path, return_data_path);
|
||||
|
||||
Some((pre_cmds, post_cmds))
|
||||
} 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)));
|
||||
|
||||
if !contained_macros.is_empty()
|
||||
&& (self.data_storage_name.is_some() || not_all_macros_blocked)
|
||||
{
|
||||
use std::fmt::Write as _;
|
||||
|
||||
// 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()
|
||||
.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
|
||||
}
|
||||
} 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()),
|
||||
);
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the group contains a macro.
|
||||
pub fn contains_macro(&self) -> bool {
|
||||
self.commands.iter().any(Command::contains_macro)
|
||||
}
|
||||
|
||||
/// Check whether the group contains a return command.
|
||||
pub fn contains_return(&self) -> bool {
|
||||
self.commands.iter().any(Command::contains_return)
|
||||
}
|
||||
|
||||
/// Generate a unique function path based on the function state.
|
||||
fn generate_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);
|
||||
|
||||
let pre_hash_path = function_path.to_owned() + ":" + &uid.to_string();
|
||||
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> {
|
||||
let mut macros = HashSet::new();
|
||||
for cmd in &self.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)]
|
||||
|
@ -232,144 +563,6 @@ fn compile_debug(message: &MacroString, option: &CompileOptions) -> Vec<Compiled
|
|||
}
|
||||
}
|
||||
|
||||
#[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 = 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 => Vec::new(),
|
||||
1 => commands[0].compile(options, global_state, function_state),
|
||||
_ => {
|
||||
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 = 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(commands.iter().cloned());
|
||||
function_state.add_function(&function_path, function);
|
||||
|
||||
let mut function_invocation = format!("function {namespace}:{function_path}");
|
||||
|
||||
let additional_return_cmds = if contains_return {
|
||||
let full_path = format!("{namespace}:{function_path}");
|
||||
let return_data_path = md5::hash(&full_path).to_hex_lowercase();
|
||||
|
||||
let pre_cmds = Command::Raw(format!(
|
||||
"data remove storage shulkerbox:return {return_data_path}"
|
||||
))
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| c.with_forbid_prefix(true))
|
||||
.collect::<Vec<_>>();
|
||||
let post_condition = Condition::Atom(
|
||||
format!("data storage shulkerbox:return {return_data_path}").into(),
|
||||
);
|
||||
|
||||
let post_cmd_store = global_state
|
||||
.read()
|
||||
.unwrap()
|
||||
.functions_with_special_return
|
||||
.get(&format!(
|
||||
"{}:{}",
|
||||
function_state.namespace(),
|
||||
function_state.path()
|
||||
))
|
||||
.cloned().map(|parent_return_data_path| {
|
||||
Command::Execute(Execute::If(
|
||||
post_condition.clone(),
|
||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||
"data modify storage shulkerbox:return {parent_return_data_path} set from storage shulkerbox:return {return_data_path}"
|
||||
))))),
|
||||
None,
|
||||
))
|
||||
});
|
||||
|
||||
let post_cmd_return = Command::Execute(Execute::If(
|
||||
post_condition,
|
||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||
"return run data get storage shulkerbox:return {return_data_path}"
|
||||
))))),
|
||||
None,
|
||||
));
|
||||
|
||||
let post_cmds = post_cmd_store
|
||||
.into_iter()
|
||||
.chain(std::iter::once(post_cmd_return))
|
||||
.flat_map(|cmd| cmd.compile(options, global_state, function_state))
|
||||
.map(|c| c.with_forbid_prefix(true))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
global_state
|
||||
.write()
|
||||
.unwrap()
|
||||
.functions_with_special_return
|
||||
.insert(full_path, return_data_path);
|
||||
|
||||
Some((pre_cmds, post_cmds))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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 = group_get_macros(commands)
|
||||
.into_iter()
|
||||
.map(|m| format!(r#"{m}:"$({m})""#))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
function_invocation.push_str(&format!(" {{{macros_block}}}"));
|
||||
}
|
||||
|
||||
if let Some((mut pre_cmds, post_cmds)) = additional_return_cmds {
|
||||
pre_cmds.push(
|
||||
CompiledCommand::new(function_invocation).with_contains_macros(pass_macros),
|
||||
);
|
||||
pre_cmds.extend(post_cmds);
|
||||
pre_cmds
|
||||
} else {
|
||||
vec![CompiledCommand::new(function_invocation).with_contains_macros(pass_macros)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let pre_hash_path = function_path.to_owned() + ":" + &uid.to_string();
|
||||
let hash = md5::hash(pre_hash_path).to_hex_lowercase();
|
||||
|
||||
"sb/".to_string() + function_path + "/" + &hash[..16]
|
||||
}
|
||||
|
||||
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 commands {
|
||||
macros.extend(cmd.get_macros());
|
||||
}
|
||||
macros
|
||||
}
|
||||
|
||||
impl ReturnCommand {
|
||||
pub fn compile(
|
||||
&self,
|
||||
|
@ -400,12 +593,12 @@ impl ReturnCommand {
|
|||
vec![store_cmd, return_cmd]
|
||||
}
|
||||
(Self::Command(cmd), None) => {
|
||||
let compiled_cmd = Command::Group(vec![*cmd.clone()]).compile(
|
||||
let compiled_cmd = Command::Group(Group::new(vec![*cmd.clone()])).compile(
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
);
|
||||
let compiled_cmd = compiled_cmd
|
||||
let compiled_cmd = dbg!(compiled_cmd)
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("group will always return exactly one command");
|
||||
|
@ -414,7 +607,9 @@ 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(vec![*cmd.clone()])))),
|
||||
Box::new(Execute::Run(Box::new(Command::Group(Group::new(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, ReturnCommand};
|
||||
pub use command::{Command, Condition, Execute, Group, ReturnCommand};
|
||||
pub use function::Function;
|
||||
pub use namespace::Namespace;
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub struct Datapack {
|
|||
}
|
||||
|
||||
impl Datapack {
|
||||
pub const LATEST_FORMAT: u8 = 61;
|
||||
pub const LATEST_FORMAT: u8 = 81;
|
||||
|
||||
/// Create a new Minecraft datapack.
|
||||
#[must_use]
|
||||
|
|
Loading…
Reference in New Issue