use std::collections::{BTreeMap, HashMap};
use graphite_mc_protocol::{play::server, types::CommandNode};
use crate::dispatcher::{ArgumentNode, DispatchNode, RootDispatchNode};
use super::{
parsers::MinecraftParser,
proto_nodes::{MinecraftArgumentNode, MinecraftDispatchNode, MinecraftRootDispatchNode},
};
pub fn create_dispatcher_and_brigadier_packet(
root: MinecraftRootDispatchNode,
) -> (RootDispatchNode, server::Commands) {
let mut command_nodes = Vec::new();
let literal_count = root.literals.len();
let aliases_count = root.aliases.len();
let mut children: Vec<i32> = Vec::with_capacity(literal_count + aliases_count + 2);
let mut graphite_alias_map = HashMap::new();
let mut literals = HashMap::new();
for (literal_name, literal_node) in &root.literals {
let child = process_named_dispatch_node(literal_name, literal_node, &mut command_nodes);
let (child_dispatch_node, child_command_node) = child;
literals.insert(*literal_name, child_dispatch_node);
graphite_alias_map.insert(literal_name, child_command_node.clone());
let brigadier_index = command_nodes.len() as i32;
children.push(brigadier_index);
command_nodes.push(child_command_node);
}
for (alias_from, alias_to) in &root.aliases {
let alias_for = graphite_alias_map.remove(alias_to).unwrap();
match alias_for {
CommandNode::Literal {
children: node_children,
is_executable,
redirect,
name: _,
} => {
let command_node = CommandNode::Literal {
children: node_children,
is_executable,
redirect,
name: alias_from,
};
let brigadier_index = command_nodes.len() as i32;
children.push(brigadier_index);
command_nodes.push(command_node);
}
_ => unreachable!(),
}
}
let root_dispatch_node = RootDispatchNode {
literals,
aliases: root.aliases,
};
let root_command_node = CommandNode::Root { children };
let root_index = command_nodes.len() as i32;
command_nodes.push(root_command_node);
let brigadier_packet = server::Commands {
nodes: command_nodes,
root_index,
};
(root_dispatch_node, brigadier_packet)
}
fn process_named_dispatch_node(
name: &'static str,
dispatch: &MinecraftDispatchNode,
command_nodes: &mut Vec<CommandNode>,
) -> (DispatchNode, CommandNode) {
let is_executable = dispatch.executor.is_some();
let (dispatch_node, brig_children) = process_dispatch_node(dispatch, command_nodes);
let command_node = CommandNode::Literal {
children: brig_children,
is_executable,
redirect: None,
name,
};
(dispatch_node, command_node)
}
fn process_dispatch_node(
dispatch: &MinecraftDispatchNode,
command_nodes: &mut Vec<CommandNode>,
) -> (DispatchNode, Vec<i32>) {
let literal_count = dispatch.literals.len();
let aliases_count = dispatch.aliases.len();
let mut children: Vec<i32> = Vec::with_capacity(literal_count + aliases_count + 2);
let mut graphite_alias_map = HashMap::new();
let mut literals = BTreeMap::new();
for (literal_name, literal_node) in &dispatch.literals {
let child = process_named_dispatch_node(literal_name, literal_node, command_nodes);
let (child_dispatch_node, child_command_node) = child;
literals.insert(*literal_name, child_dispatch_node);
graphite_alias_map.insert(literal_name, child_command_node.clone());
let brigadier_index = command_nodes.len() as i32;
children.push(brigadier_index);
command_nodes.push(child_command_node);
}
for (alias_from, alias_to) in &dispatch.aliases {
let alias_for = graphite_alias_map.remove(alias_to).unwrap();
match alias_for {
CommandNode::Literal {
children: node_children,
is_executable,
redirect,
name: _,
} => {
let command_node = CommandNode::Literal {
children: node_children,
is_executable,
redirect,
name: alias_from,
};
let brigadier_index = command_nodes.len() as i32;
children.push(brigadier_index);
command_nodes.push(command_node);
}
_ => unreachable!(),
}
}
let mut parsers = Vec::new();
if let Some(numeric_parser) = dispatch.numeric_parser.as_ref() {
let argument = process_argument_node(numeric_parser, command_nodes);
let (argument_node, command_node) = argument;
parsers.push(argument_node);
let brigadier_index = command_nodes.len() as i32;
children.push(brigadier_index);
command_nodes.push(command_node);
}
if let Some(string_parser) = dispatch.string_parser.as_ref() {
let argument = process_argument_node(string_parser, command_nodes);
let (argument_node, command_node) = argument;
parsers.push(argument_node);
let brigadier_index = command_nodes.len() as i32;
children.push(brigadier_index);
command_nodes.push(command_node);
}
let dispatch_node = DispatchNode {
literals,
aliases: dispatch.aliases.clone(),
parsers,
executor: dispatch.executor,
};
(dispatch_node, children)
}
fn process_argument_node<T: MinecraftParser>(
argument: &MinecraftArgumentNode<T>,
command_nodes: &mut Vec<CommandNode>,
) -> (ArgumentNode, CommandNode) {
let (dispatch_node, brig_children) =
process_dispatch_node(&argument.dispatch_node, command_nodes);
let argument_node = ArgumentNode {
parse: argument.parse.get_parse_func(),
dispatch_node,
};
let command_node = CommandNode::Argument {
children: brig_children,
is_executable: argument.dispatch_node.executor.is_some(),
redirect: None,
suggestion: None,
name: argument.name,
parser: argument.parse.get_brigadier_parser(),
};
(argument_node, command_node)
}