Basic implementation of execute
This commit is contained in:
parent
6572243ce4
commit
c494031163
|
@ -10,6 +10,7 @@ default = ["zip"]
|
|||
zip = ["dep:zip"]
|
||||
|
||||
[dependencies]
|
||||
getset = "0.1.2"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
zip = { version = "0.6.6", default-features = false, features = ["deflate", "time"], optional = true }
|
||||
|
|
|
@ -15,13 +15,10 @@ let mut dp = Datapack::new("shulkerpack", 20) // Create a new datapack with the
|
|||
.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
|
||||
.unwrap();
|
||||
let mut namespace = Namespace::new("shulker"); // Create a new namespace with the name "shulker"
|
||||
let namespace = datapack.namespace_mut("shulker"); // Create a new namespace with the name "shulker"
|
||||
|
||||
let mut hello_function = Function::new(); // Create a new function
|
||||
hello_function.add_command("say Hello, world!".into()); // Add a command to the function
|
||||
namespace.add_function("hello", hello_function); // Add the function to the namespace
|
||||
|
||||
dp.add_namespace(namespace); // Add the namespace to the datapack
|
||||
let hello_function = namespace.function_mut("hello"); // Create a new function
|
||||
hello_function.add_command("say Hello, world!"); // Add a command to the function
|
||||
|
||||
let v_folder = dp.compile(&CompileOptions::default()); // Compile the datapack with default options
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use std::ops::{BitAnd, BitOr, Not};
|
||||
use std::ops::{BitAnd, BitOr, Deref, Not};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::util::compile::{CompileOptions, MutCompilerState, MutFunctionCompilerState};
|
||||
|
||||
use super::Command;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Execute {
|
||||
Align(String, Box<Execute>),
|
||||
|
@ -18,10 +21,228 @@ pub enum Execute {
|
|||
Rotated(String, Box<Execute>),
|
||||
Store(String, Box<Execute>),
|
||||
Summon(String, Box<Execute>),
|
||||
If(Condition, Box<Execute>),
|
||||
Run(String, Box<Command>),
|
||||
If(Condition, Box<Execute>, Option<Box<Execute>>),
|
||||
Run(Box<Command>),
|
||||
Runs(Vec<Command>),
|
||||
}
|
||||
|
||||
impl Execute {
|
||||
/// Compile the execute command into a list of strings.
|
||||
pub fn compile(
|
||||
&self,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &MutFunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
self.compile_internal(
|
||||
String::from("execute "),
|
||||
false,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
)
|
||||
}
|
||||
fn compile_internal(
|
||||
&self,
|
||||
prefix: String,
|
||||
require_grouping: bool,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &MutFunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
match self {
|
||||
Self::Align(align, next) => format_execute(
|
||||
prefix,
|
||||
&format!("align {align} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Anchored(anchor, next) => format_execute(
|
||||
prefix,
|
||||
&format!("anchored {anchor} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::As(selector, next) => format_execute(
|
||||
prefix,
|
||||
&format!("as {selector} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::At(selector, next) => format_execute(
|
||||
prefix,
|
||||
&format!("at {selector} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::AsAt(selector, next) => format_execute(
|
||||
prefix,
|
||||
&format!("as {selector} at @s "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Facing(facing, next) => format_execute(
|
||||
prefix,
|
||||
&format!("facing {facing} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::In(dim, next) => format_execute(
|
||||
prefix,
|
||||
&format!("in {dim} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::On(dim, next) => format_execute(
|
||||
prefix,
|
||||
&format!("on {dim} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Positioned(pos, next) => format_execute(
|
||||
prefix,
|
||||
&format!("positioned {pos} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Rotated(rot, next) => format_execute(
|
||||
prefix,
|
||||
&format!("rotated {rot} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Store(store, next) => format_execute(
|
||||
prefix,
|
||||
&format!("store {store} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Summon(entity, next) => format_execute(
|
||||
prefix,
|
||||
&format!("summon {entity} "),
|
||||
next,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::If(cond, then, el) => {
|
||||
// TODO: group commands if needed and only run else commands if the first commands have not executed
|
||||
let str_cond = cond.clone().compile(options, global_state, function_state);
|
||||
let require_grouping = str_cond.len() > 1;
|
||||
let then = then.compile_internal(
|
||||
String::new(),
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
);
|
||||
let then_commands = combine_conditions_commands(str_cond, then);
|
||||
let el = el
|
||||
.as_deref()
|
||||
.map(|el| {
|
||||
let else_cond =
|
||||
(!cond.clone()).compile(options, global_state, function_state);
|
||||
let el = el.compile_internal(
|
||||
String::new(),
|
||||
else_cond.len() > 1,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
);
|
||||
combine_conditions_commands(else_cond, el)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
then_commands
|
||||
.into_iter()
|
||||
.chain(el)
|
||||
.map(|cmd| prefix.clone() + &cmd + " ")
|
||||
.collect()
|
||||
}
|
||||
Self::Run(command) if !require_grouping => command
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| prefix.clone() + "run " + &c)
|
||||
.collect(),
|
||||
Self::Run(command) => Command::Group(vec![command.deref().clone()])
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| prefix.clone() + "run " + &c)
|
||||
.collect(),
|
||||
Self::Runs(commands) if !require_grouping => commands
|
||||
.iter()
|
||||
.flat_map(|c| c.compile(options, global_state, function_state))
|
||||
.map(|c| prefix.clone() + "run " + &c)
|
||||
.collect(),
|
||||
Self::Runs(commands) => Command::Group(commands.clone())
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| prefix.clone() + "run " + &c)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_execute(
|
||||
prefix: String,
|
||||
new: &str,
|
||||
next: &Execute,
|
||||
require_grouping: bool,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &MutFunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
next.compile_internal(
|
||||
prefix + new,
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
)
|
||||
}
|
||||
|
||||
fn combine_conditions_commands(conditions: Vec<String>, commands: Vec<String>) -> Vec<String> {
|
||||
conditions
|
||||
.into_iter()
|
||||
.flat_map(|cond| commands.iter().map(move |cmd| cond.clone() + " " + cmd))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Condition {
|
||||
Atom(String),
|
||||
|
@ -30,11 +251,12 @@ pub enum Condition {
|
|||
Or(Box<Condition>, Box<Condition>),
|
||||
}
|
||||
impl Condition {
|
||||
pub fn normalize(self) -> Self {
|
||||
/// Normalize the condition.
|
||||
pub fn normalize(&self) -> Self {
|
||||
match self {
|
||||
Self::Atom(_) => self,
|
||||
Self::Not(c) => match *c {
|
||||
Self::Atom(c) => Self::Not(Box::new(Self::Atom(c))),
|
||||
Self::Atom(_) => self.clone(),
|
||||
Self::Not(c) => match *c.clone() {
|
||||
Self::Atom(c) => Self::Not(Box::new(Self::Atom(c.clone()))),
|
||||
Self::Not(c) => c.normalize(),
|
||||
Self::And(c1, c2) => ((!*c1).normalize()) | ((!*c2).normalize()),
|
||||
Self::Or(c1, c2) => ((!*c1).normalize()) & ((!*c2).normalize()),
|
||||
|
@ -43,6 +265,42 @@ impl Condition {
|
|||
Self::Or(c1, c2) => c1.normalize() | c2.normalize(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile the condition into a list of strings.
|
||||
pub fn compile(
|
||||
&self,
|
||||
_options: &CompileOptions,
|
||||
_global_state: &MutCompilerState,
|
||||
_function_state: &MutFunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
match self.normalize() {
|
||||
Self::Atom(a) => vec!["if ".to_string() + &a],
|
||||
Self::Not(n) => match n.as_ref() {
|
||||
Self::Atom(a) => vec!["unless ".to_string() + a],
|
||||
_ => unreachable!("Cannot happen because of normalization"),
|
||||
},
|
||||
Self::And(c1, c2) => {
|
||||
let c1 = c1.compile(_options, _global_state, _function_state);
|
||||
let c2 = c2.compile(_options, _global_state, _function_state);
|
||||
|
||||
c1.into_iter()
|
||||
.flat_map(|c1| c2.iter().map(move |c2| c1.clone() + " " + c2))
|
||||
.collect()
|
||||
}
|
||||
Self::Or(c1, c2) => {
|
||||
let mut c1 = c1.compile(_options, _global_state, _function_state);
|
||||
let c2 = c2.compile(_options, _global_state, _function_state);
|
||||
c1.extend(c2);
|
||||
c1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Condition {
|
||||
fn from(s: &str) -> Self {
|
||||
Condition::Atom(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Condition {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
//! Represents a command that can be included in a function.
|
||||
|
||||
mod execute;
|
||||
pub use execute::Execute;
|
||||
|
||||
pub use execute::{Condition, Execute};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::util::compile::{CompileOptions, MutCompilerState, MutFunctionCompilerState};
|
||||
|
||||
use super::Function;
|
||||
|
||||
/// Represents a command that can be included in a function.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Command {
|
||||
|
@ -16,6 +19,8 @@ pub enum Command {
|
|||
Debug(String),
|
||||
/// Execute command
|
||||
Execute(Execute),
|
||||
/// Group of commands to be called instantly after each other
|
||||
Group(Vec<Command>),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
|
@ -30,14 +35,18 @@ impl Command {
|
|||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &MutFunctionCompilerState,
|
||||
) -> String {
|
||||
let _ = options;
|
||||
let _ = global_state;
|
||||
let _ = function_state;
|
||||
) -> Vec<String> {
|
||||
match self {
|
||||
Self::Raw(command) => command.clone(),
|
||||
Self::Raw(command) => vec![command.clone()],
|
||||
Self::Debug(message) => compile_debug(message, options),
|
||||
Self::Execute(_) => todo!(),
|
||||
Self::Execute(ex) => ex.compile(options, global_state, function_state),
|
||||
Self::Group(commands) => {
|
||||
// TODO: implement correctly
|
||||
commands
|
||||
.iter()
|
||||
.flat_map(|c| c.compile(options, global_state, function_state))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,14 +56,24 @@ impl From<&str> for Command {
|
|||
Self::raw(command)
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_debug(message: &str, option: &CompileOptions) -> String {
|
||||
if option.debug {
|
||||
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 message to 'false' to disable"}}]}}}},{{"text":"]","color":"dark_blue"}},{{"text":" {}","color":"black"}}]"#,
|
||||
message
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
impl From<&Function> for Command {
|
||||
fn from(value: &Function) -> Self {
|
||||
Self::Raw(format!("function {}:{}", value.namespace(), value.name()))
|
||||
}
|
||||
}
|
||||
impl From<&mut Function> for Command {
|
||||
fn from(value: &mut Function) -> Self {
|
||||
Self::Raw(format!("function {}:{}", value.namespace(), value.name()))
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_debug(message: &str, option: &CompileOptions) -> Vec<String> {
|
||||
if option.debug {
|
||||
vec![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 message to 'false' to disable"}}]}}}},{{"text":"]","color":"dark_blue"}},{{"text":" {}","color":"black"}}]"#,
|
||||
message
|
||||
)]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use getset::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
|
@ -12,20 +13,28 @@ use crate::{
|
|||
use super::command::Command;
|
||||
|
||||
/// Function that can be called by a command
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, Getters)]
|
||||
pub struct Function {
|
||||
commands: Vec<Command>,
|
||||
/// Name of the function
|
||||
#[get = "pub"]
|
||||
name: String,
|
||||
/// Namespace of the function
|
||||
#[get = "pub"]
|
||||
namespace: String,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Create a new function.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub(in crate::datapack) fn new(namespace: &str, name: &str) -> Self {
|
||||
Self {
|
||||
commands: Vec::new(),
|
||||
name: name.to_string(),
|
||||
namespace: namespace.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a command to the function.
|
||||
pub fn add_command(&mut self, command: Command) {
|
||||
self.commands.push(command);
|
||||
pub fn add_command(&mut self, command: impl Into<Command>) {
|
||||
self.commands.push(command.into());
|
||||
}
|
||||
|
||||
/// Get the commands of the function.
|
||||
|
@ -40,7 +49,7 @@ impl Function {
|
|||
let content = self
|
||||
.commands
|
||||
.iter()
|
||||
.map(|c| c.compile(options, state, &function_state))
|
||||
.flat_map(|c| c.compile(options, state, &function_state))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
VFile::Text(content)
|
||||
|
|
|
@ -4,7 +4,7 @@ mod command;
|
|||
mod function;
|
||||
mod namespace;
|
||||
pub mod tag;
|
||||
pub use command::Command;
|
||||
pub use command::{Command, Condition, Execute};
|
||||
pub use function::Function;
|
||||
pub use namespace::Namespace;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -72,13 +72,16 @@ impl Datapack {
|
|||
})
|
||||
}
|
||||
|
||||
/// Add a namespace to the datapack.
|
||||
pub fn add_namespace(&mut self, namespace: Namespace) {
|
||||
if !namespace.get_main_function().get_commands().is_empty() {
|
||||
self.add_tick(&format!("{}:main", namespace.get_name()));
|
||||
/// Get a namespace by name.
|
||||
pub fn namespace(&self, name: &str) -> Option<&Namespace> {
|
||||
self.namespaces.get(name)
|
||||
}
|
||||
|
||||
/// Butably 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 {
|
||||
self.namespaces
|
||||
.insert(namespace.get_name().to_string(), namespace);
|
||||
.entry(name.to_string())
|
||||
.or_insert_with(|| Namespace::new(name))
|
||||
}
|
||||
|
||||
/// Add a function to the tick function list.
|
||||
|
|
|
@ -21,7 +21,7 @@ pub struct Namespace {
|
|||
|
||||
impl Namespace {
|
||||
/// Create a new namespace.
|
||||
pub fn new(name: &str) -> Self {
|
||||
pub(in crate::datapack) fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
functions: HashMap::new(),
|
||||
|
@ -54,9 +54,16 @@ impl Namespace {
|
|||
&self.tags
|
||||
}
|
||||
|
||||
/// Add a function to the namespace.
|
||||
pub fn add_function(&mut self, name: &str, function: Function) {
|
||||
self.functions.insert(name.to_string(), function);
|
||||
/// Get a function by name.
|
||||
pub fn function(&self, name: &str) -> Option<&Function> {
|
||||
self.functions.get(name)
|
||||
}
|
||||
|
||||
/// Mutably get a function by name or create a new one if it doesn't exist.
|
||||
pub fn function_mut(&mut self, name: &str) -> &mut Function {
|
||||
self.functions
|
||||
.entry(name.to_string())
|
||||
.or_insert_with(|| Function::new(&self.name, name))
|
||||
}
|
||||
|
||||
/// Add a tag to the namespace.
|
||||
|
|
Loading…
Reference in New Issue