use crate::network::*;
use crate::packet::*;
use basin2_lib::result::*;
use bytes::BytesMut;
use either::Either;
use linked_hash_map::LinkedHashMap;
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::sync::Arc;
use std::sync::RwLock;
use uuid::Uuid;
impl ArgumentType {
pub fn serialize(self, buf: &mut BytesMut) {
use ArgumentType::*;
match self {
Double { min, max } => {
let mut flags = 0;
if min.is_some() {
flags |= 1;
}
if max.is_some() {
flags |= 2;
}
buf.set_mc_u8(flags);
match min {
Some(min) => buf.set_mc_f64(min),
_ => (),
}
match max {
Some(max) => buf.set_mc_f64(max),
_ => (),
}
}
Float { min, max } => {
let mut flags = 0;
if min.is_some() {
flags |= 1;
}
if max.is_some() {
flags |= 2;
}
buf.set_mc_u8(flags);
match min {
Some(min) => buf.set_mc_f32(min),
_ => (),
}
match max {
Some(max) => buf.set_mc_f32(max),
_ => (),
}
}
Integer { min, max } => {
let mut flags = 0;
if min.is_some() {
flags |= 1;
}
if max.is_some() {
flags |= 2;
}
buf.set_mc_u8(flags);
match min {
Some(min) => buf.set_mc_i32(min),
_ => (),
}
match max {
Some(max) => buf.set_mc_i32(max),
_ => (),
}
}
Long { min, max } => {
let mut flags = 0;
if min.is_some() {
flags |= 1;
}
if max.is_some() {
flags |= 2;
}
buf.set_mc_u8(flags);
match min {
Some(min) => buf.set_mc_i64(min),
_ => (),
}
match max {
Some(max) => buf.set_mc_i64(max),
_ => (),
}
}
Str(string_type) => {
buf.set_mc_var_int(string_type as i32);
}
Entity {
single,
players_only,
} => {
let mut flags = 0;
if single {
flags |= 1;
}
if players_only {
flags |= 2;
}
buf.set_mc_u8(flags);
}
ScoreHolder { multiple } => {
let mut flags = 0;
if multiple {
flags |= 1;
}
buf.set_mc_u8(flags);
}
_ => (),
}
}
pub fn deserialize(name: String, buf: &mut BytesMut) -> Result<ArgumentType> {
use ArgumentType::*;
Ok(match &*name {
"brigadier:bool" => Bool,
"brigadier:double" => {
let flags = buf.get_mc_u8()?;
let min = if (flags & 1) > 0 {
Some(buf.get_mc_f64()?)
} else {
None
};
let max = if (flags & 2) > 0 {
Some(buf.get_mc_f64()?)
} else {
None
};
Double { min, max }
}
"brigadier:float" => {
let flags = buf.get_mc_u8()?;
let min = if (flags & 1) > 0 {
Some(buf.get_mc_f32()?)
} else {
None
};
let max = if (flags & 2) > 0 {
Some(buf.get_mc_f32()?)
} else {
None
};
Float { min, max }
}
"brigadier:integer" => {
let flags = buf.get_mc_u8()?;
let min = if (flags & 1) > 0 {
Some(buf.get_mc_i32()?)
} else {
None
};
let max = if (flags & 2) > 0 {
Some(buf.get_mc_i32()?)
} else {
None
};
Integer { min, max }
}
"brigadier:long" => {
let flags = buf.get_mc_u8()?;
let min = if (flags & 1) > 0 {
Some(buf.get_mc_i64()?)
} else {
None
};
let max = if (flags & 2) > 0 {
Some(buf.get_mc_i64()?)
} else {
None
};
Long { min, max }
}
"brigadier:string" => Str(buf.get_mc_enum()?),
"minecraft:entity" => {
let flags = buf.get_mc_u8()?;
Entity {
single: (flags & 1) > 0,
players_only: (flags & 2) > 0,
}
}
"minecraft:game_profile" => GameProfile,
"minecraft:block_pos" => BlockPos,
"minecraft:column_pos" => ColumnPos,
"minecraft:vec3" => Vec3,
"minecraft:vec2" => Vec2,
"minecraft:block_state" => BlockState,
"minecraft:block_predicate" => BlockPredicate,
"minecraft:item_stack" => ItemStack,
"minecraft:item_predicate" => ItemPredicate,
"minecraft:color" => Color,
"minecraft:component" => ChatComponent,
"minecraft:message" => Message,
"minecraft:nbt_compound_tag" => NbtCompoundTag,
"minecraft:nbt_tag" => NbtTag,
"minecraft:nbt_path" => NbtPath,
"minecraft:objective" => Objective,
"minecraft:objective_criteria" => ObjectiveCriteria,
"minecraft:operation" => Operation,
"minecraft:particle" => Particle,
"minecraft:rotation" => Rotation,
"minecraft:scoreboard_slot" => ScoreboardSlot,
"minecraft:score_holder" => {
let flags = buf.get_mc_u8()?;
ScoreHolder {
multiple: (flags & 1) > 0,
}
}
"minecraft:swizzle" => Swizzle,
"minecraft:team" => Team,
"minecraft:item_slot" => ItemSlot,
"minecraft:resource_location" => ResourceLocation,
"minecraft:mob_effect" => MobEffect,
"minecraft:function" => Function,
"minecraft:entity_anchor" => EntityAnchor,
"minecraft:int_range" => IntRange,
"minecraft:float_range" => FloatRange,
"minecraft:item_enchantment" => ItemEnchantment,
"minecraft:entity_summon" => EntitySummon,
"minecraft:dimension" => Dimension,
"minecraft:time" => Time,
_ => return Err(Box::new(IoError::from(ErrorKind::InvalidData))),
})
}
}
#[derive(PartialEq, Clone, Debug)]
struct TempCommandNode {
command: bool,
children: Vec<i32>,
redirect: Option<i32>,
data: Option<Either<(String, ArgumentType, Option<String>), String>>,
}
impl TempCommandNode {
fn build(
&self,
index: i32,
temp_nodes: &Vec<TempCommandNode>,
output: &mut HashMap<i32, Arc<CommandNode>>,
) {
if output.contains_key(&index) {
return;
}
let base_node = BaseCommandNode {
uuid: Uuid::new_v4(),
children: LinkedHashMap::new(),
redirect: None,
command: self.command,
};
let node = match &self.data {
None => {
CommandNode::Root {
node: RwLock::new(base_node),
}
}
Some(Either::Left((name, argument_type, custom_suggestions))) => {
CommandNode::Argument {
node: RwLock::new(base_node),
name: name.clone(),
argument_type: *argument_type,
custom_suggestions: custom_suggestions.clone(),
}
}
Some(Either::Right(literal)) => {
CommandNode::Literal {
node: RwLock::new(base_node),
literal: literal.clone(),
}
}
};
let node = Arc::new(node);
output.insert(index, node.clone());
for child_index in &self.children {
let temp_child = temp_nodes.get(*child_index as usize);
if temp_child.is_none() {
continue;
}
let temp_child = temp_child.unwrap();
temp_child.build(*child_index, temp_nodes, output);
let child_node = output[child_index].clone();
let child_name = match &*child_node {
CommandNode::Literal { literal, .. } => literal.clone(),
CommandNode::Argument { name, .. } => name.clone(),
_ => "".to_string(),
};
node.node_mut().children.insert(child_name, child_node);
}
node.node_mut().redirect = match self.redirect {
Some(child_index) => {
let temp_child = temp_nodes.get(child_index as usize);
if temp_child.is_some() {
let temp_child = temp_child.unwrap();
temp_child.build(child_index, temp_nodes, output);
let child_node = output[&child_index].clone();
Some(child_node)
} else {
None
}
}
None => None,
};
}
}
impl CommandNode {
fn mappify(
self: Arc<CommandNode>,
) -> (
HashMap<Uuid, usize>,
Vec<Uuid>,
HashMap<Uuid, Arc<CommandNode>>,
) {
let mut output = HashMap::new();
let mut all_nodes = HashMap::new();
let mut deque: VecDeque<Arc<CommandNode>> = VecDeque::new();
deque.push_back(self);
while !deque.is_empty() {
let node = deque.pop_front().unwrap();
let base_node = node.node();
output.insert(base_node.uuid, output.len());
all_nodes.insert(base_node.uuid, node.clone());
deque.extend(base_node.children.values().map(|value| value.clone()));
if base_node.redirect.is_some() {
deque.push_back(base_node.redirect.clone().unwrap());
}
}
let mut output_vec: Vec<Option<Uuid>> = vec![None; output.len()];
for (node, i) in &output {
output_vec[*i] = Some(node.clone());
}
(output, output_vec.iter().map(|item| item.unwrap()).collect(), all_nodes)
}
fn serialize(self: Arc<CommandNode>, buf: &mut BytesMut, offsets: &HashMap<Uuid, usize>) {
let base_node = self.node();
let mut flags: u8 = 0;
if base_node.redirect.is_some() {
flags |= 8;
}
if base_node.command {
flags |= 4;
}
match &*self {
CommandNode::Root { .. } => {
flags |= 0; }
CommandNode::Argument {
custom_suggestions, ..
} => {
flags |= 2;
if custom_suggestions.is_some() {
flags |= 16;
}
}
CommandNode::Literal { .. } => {
flags |= 1;
}
}
buf.set_mc_u8(flags);
buf.set_mc_var_int(base_node.children.len() as i32);
for (_, child) in &base_node.children {
buf.set_mc_var_int(*offsets.get(&child.node().uuid).expect("malformed offsets") as i32);
}
if base_node.redirect.is_some() {
buf.set_mc_var_int(
*offsets
.get(&base_node.redirect.as_ref().unwrap().node().uuid)
.expect("malformed offsets") as i32,
);
}
match &*self {
CommandNode::Argument {
name,
argument_type,
custom_suggestions,
..
} => {
buf.set_mc_string(name.clone());
buf.set_mc_string(argument_type.name().to_string());
argument_type.serialize(buf);
match custom_suggestions {
Some(custom_suggestions) => {
buf.set_mc_string(custom_suggestions.clone());
}
_ => (),
}
}
CommandNode::Literal { literal, .. } => {
buf.set_mc_string(literal.clone());
}
_ => (),
}
}
fn deserialize(buf: &mut BytesMut) -> Result<TempCommandNode> {
let flags = buf.get_mc_u8()?;
let redirect = (flags & 8) > 0;
let command = (flags & 4) > 0;
let is_argument = (flags & 2) > 0;
let is_literal = (flags & 1) > 0;
let has_suggestions = (flags & 16) > 0;
let child_count = buf.get_mc_var_int()?;
let mut children: Vec<i32> = vec![];
for _ in 0..child_count {
children.push(buf.get_mc_var_int()?);
}
let redirect = if redirect {
Some(buf.get_mc_var_int()?)
} else {
None
};
let data = if is_argument {
let name = buf.get_mc_string(32767)?;
let argument_type = ArgumentType::deserialize(buf.get_mc_string(32767)?, buf)?;
let custom_suggestions = if has_suggestions {
Some(buf.get_mc_string(32767)?)
} else {
None
};
Some(Either::Left((name, argument_type, custom_suggestions)))
} else if is_literal {
Some(Either::Right(buf.get_mc_string(32767)?))
} else {
None
};
Ok(TempCommandNode {
command,
children,
redirect,
data,
})
}
}
pub struct CommandsPacket {
pub root: Arc<CommandNode>,
}
impl PartialEq for CommandsPacket {
fn eq(&self, other: &CommandsPacket) -> bool {
&*self.root as *const CommandNode == &*other.root as *const CommandNode
}
}
impl Clone for CommandsPacket {
fn clone(&self) -> CommandsPacket {
CommandsPacket {
root: self.root.clone(),
}
}
}
impl fmt::Debug for CommandsPacket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CommandsPacket {{ {:x?} }}",
&*self.root as *const CommandNode
)
}
}
impl CodablePacket for CommandsPacket {
fn encode(self, buf: &mut BytesMut) {
let (offsets, uuids, node_lookup) = self.root.clone().mappify();
buf.set_mc_var_int(uuids.len() as i32);
for uuid in uuids {
node_lookup[&uuid].clone().serialize(buf, &offsets);
}
buf.set_mc_var_int(offsets[&self.root.node().uuid] as i32);
}
fn decode(buf: &mut BytesMut) -> Result<Self>
where
Self: Sized,
{
let node_count = buf.get_mc_var_int()?;
let mut temp_nodes: Vec<TempCommandNode> = vec![];
for _ in 0..node_count {
temp_nodes.push(CommandNode::deserialize(buf)?);
}
let root_node_index = buf.get_mc_var_int()?;
let root_temp_node = temp_nodes.get(root_node_index as usize);
if root_temp_node.is_none() {
return Err(Box::new(IoError::from(ErrorKind::InvalidData)));
}
let root_temp_node = root_temp_node.unwrap();
let mut outmap = HashMap::new();
root_temp_node.build(root_node_index, &temp_nodes, &mut outmap);
let root = outmap.get(&root_node_index);
if root.is_none() {
return Err(Box::new(IoError::from(ErrorKind::InvalidData)));
}
return Ok(CommandsPacket {
root: root.unwrap().clone(),
});
}
}