549 lines
18 KiB
Rust
549 lines
18 KiB
Rust
use crate::data_component_impl::{
|
|
DataComponentImpl, default_impl, get_f32_hash, get_i32_hash, get_str_hash,
|
|
};
|
|
use crc_fast::CrcAlgorithm::Crc32Iscsi;
|
|
use crc_fast::Digest;
|
|
use pumpkin_nbt::compound::NbtCompound;
|
|
use pumpkin_nbt::tag::NbtTag;
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct DyeImpl;
|
|
impl DataComponentImpl for DyeImpl {
|
|
default_impl!(Dye);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct DyedColorImpl {
|
|
pub rgb: i32,
|
|
}
|
|
impl DyedColorImpl {
|
|
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
|
data.extract_int().map(|rgb| Self { rgb })
|
|
}
|
|
}
|
|
impl DataComponentImpl for DyedColorImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
NbtTag::Int(self.rgb)
|
|
}
|
|
fn get_hash(&self) -> i32 {
|
|
get_i32_hash(self.rgb) as i32
|
|
}
|
|
default_impl!(DyedColor);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct MapColorImpl;
|
|
impl DataComponentImpl for MapColorImpl {
|
|
default_impl!(MapColor);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct MapIdImpl {
|
|
pub id: i32,
|
|
}
|
|
impl MapIdImpl {
|
|
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
|
data.extract_int().map(|id| Self { id })
|
|
}
|
|
}
|
|
impl DataComponentImpl for MapIdImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
NbtTag::Int(self.id)
|
|
}
|
|
fn get_hash(&self) -> i32 {
|
|
get_i32_hash(self.id) as i32
|
|
}
|
|
default_impl!(MapId);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct MapDecorationsImpl;
|
|
impl DataComponentImpl for MapDecorationsImpl {
|
|
default_impl!(MapDecorations);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct MapPostProcessingImpl;
|
|
impl DataComponentImpl for MapPostProcessingImpl {
|
|
default_impl!(MapPostProcessing);
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct ChargedProjectilesImpl {
|
|
pub projectiles: Vec<NbtCompound>,
|
|
}
|
|
impl ChargedProjectilesImpl {
|
|
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
|
let list = data.extract_list()?;
|
|
let mut projectiles = Vec::new();
|
|
for item in list {
|
|
projectiles.push(item.extract_compound()?.clone());
|
|
}
|
|
Some(Self { projectiles })
|
|
}
|
|
}
|
|
impl DataComponentImpl for ChargedProjectilesImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
let mut list = Vec::new();
|
|
for item in &self.projectiles {
|
|
list.push(NbtTag::Compound(item.clone()));
|
|
}
|
|
NbtTag::List(list)
|
|
}
|
|
fn get_hash(&self) -> i32 {
|
|
0
|
|
}
|
|
default_impl!(ChargedProjectiles);
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct BundleContentsImpl {
|
|
pub items: Vec<crate::item_stack::ItemStack>,
|
|
}
|
|
impl PartialEq for BundleContentsImpl {
|
|
fn eq(&self, _other: &Self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
impl Eq for BundleContentsImpl {}
|
|
impl std::fmt::Debug for BundleContentsImpl {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "BundleContentsImpl")
|
|
}
|
|
}
|
|
impl BundleContentsImpl {
|
|
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
|
let mut items = Vec::new();
|
|
if let NbtTag::List(l) = tag {
|
|
for item_tag in l {
|
|
if let NbtTag::Compound(c) = item_tag
|
|
&& let Some(stack) = crate::item_stack::ItemStack::read_item_stack(c)
|
|
{
|
|
items.push(stack);
|
|
}
|
|
}
|
|
}
|
|
Some(Self { items })
|
|
}
|
|
pub fn get_weight(&self) -> u32 {
|
|
self.items
|
|
.iter()
|
|
.map(|item| item.item_count as u32 * (64 / item.get_max_stack_size() as u32).max(1))
|
|
.sum()
|
|
}
|
|
pub fn try_insert(&mut self, stack: &mut crate::item_stack::ItemStack) -> bool {
|
|
if stack.is_empty() || stack.get_data_component::<BundleContentsImpl>().is_some() {
|
|
return false;
|
|
}
|
|
let weight_per_item = (64 / stack.get_max_stack_size() as u32).max(1);
|
|
let mut inserted_anything = false;
|
|
while stack.item_count > 0 && self.get_weight() + weight_per_item <= 64 {
|
|
if let Some(top) = self.items.first_mut()
|
|
&& crate::item_stack::ItemStack::are_items_and_components_equal(top, stack)
|
|
&& top.item_count < top.get_max_stack_size()
|
|
{
|
|
top.item_count += 1;
|
|
stack.item_count -= 1;
|
|
inserted_anything = true;
|
|
continue;
|
|
}
|
|
self.items.insert(0, stack.copy_with_count(1));
|
|
stack.item_count -= 1;
|
|
inserted_anything = true;
|
|
}
|
|
inserted_anything
|
|
}
|
|
pub fn try_extract(&mut self) -> Option<crate::item_stack::ItemStack> {
|
|
if self.items.is_empty() {
|
|
None
|
|
} else {
|
|
Some(self.items.remove(0))
|
|
}
|
|
}
|
|
}
|
|
impl DataComponentImpl for BundleContentsImpl {
|
|
default_impl!(BundleContents);
|
|
}
|
|
|
|
/// The dimension and block position a lodestone compass points to.
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct LodestoneTarget {
|
|
pub dimension: String,
|
|
pub x: i32,
|
|
pub y: i32,
|
|
pub z: i32,
|
|
}
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct LodestoneTrackerImpl {
|
|
pub target: Option<LodestoneTarget>,
|
|
pub tracked: bool,
|
|
}
|
|
impl LodestoneTrackerImpl {
|
|
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
|
let compound = data.extract_compound()?;
|
|
let tracked = compound.get_bool("tracked").unwrap_or(true);
|
|
let target = compound.get_compound("target").and_then(|target| {
|
|
let dimension = target.get_string("dimension")?.to_string();
|
|
let pos = target.get_int_array("pos")?;
|
|
if pos.len() != 3 {
|
|
return None;
|
|
}
|
|
Some(LodestoneTarget {
|
|
dimension,
|
|
x: pos[0],
|
|
y: pos[1],
|
|
z: pos[2],
|
|
})
|
|
});
|
|
Some(Self { target, tracked })
|
|
}
|
|
}
|
|
impl DataComponentImpl for LodestoneTrackerImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
let mut compound = NbtCompound::new();
|
|
if let Some(target) = &self.target {
|
|
let mut target_compound = NbtCompound::new();
|
|
target_compound.put_string("dimension", target.dimension.clone());
|
|
target_compound.put("pos", NbtTag::IntArray(vec![target.x, target.y, target.z]));
|
|
compound.put_compound("target", target_compound);
|
|
}
|
|
compound.put_bool("tracked", self.tracked);
|
|
NbtTag::Compound(compound)
|
|
}
|
|
default_impl!(LodestoneTracker);
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum FireworkExplosionShape {
|
|
SmallBall = 0,
|
|
LargeBall = 1,
|
|
Star = 2,
|
|
Creeper = 3,
|
|
Burst = 4,
|
|
}
|
|
impl FireworkExplosionShape {
|
|
pub fn from_id(id: i32) -> Option<Self> {
|
|
match id {
|
|
0 => Some(Self::SmallBall),
|
|
1 => Some(Self::LargeBall),
|
|
2 => Some(Self::Star),
|
|
3 => Some(Self::Creeper),
|
|
4 => Some(Self::Burst),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn to_id(&self) -> i32 {
|
|
*self as i32
|
|
}
|
|
pub fn to_name(&self) -> &str {
|
|
match self {
|
|
Self::SmallBall => "small_ball",
|
|
Self::LargeBall => "large_ball",
|
|
Self::Star => "star",
|
|
Self::Creeper => "creeper",
|
|
Self::Burst => "burst",
|
|
}
|
|
}
|
|
pub fn from_name(name: &str) -> Option<Self> {
|
|
match name {
|
|
"small_ball" => Some(Self::SmallBall),
|
|
"large_ball" => Some(Self::LargeBall),
|
|
"star" => Some(Self::Star),
|
|
"creeper" => Some(Self::Creeper),
|
|
"burst" => Some(Self::Burst),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct FireworkExplosionImpl {
|
|
pub shape: FireworkExplosionShape,
|
|
pub colors: Vec<i32>,
|
|
pub fade_colors: Vec<i32>,
|
|
pub has_trail: bool,
|
|
pub has_twinkle: bool,
|
|
}
|
|
impl FireworkExplosionImpl {
|
|
pub fn new(
|
|
shape: FireworkExplosionShape,
|
|
colors: Vec<i32>,
|
|
fade_colors: Vec<i32>,
|
|
has_trail: bool,
|
|
has_twinkle: bool,
|
|
) -> Self {
|
|
Self {
|
|
shape,
|
|
colors,
|
|
fade_colors,
|
|
has_trail,
|
|
has_twinkle,
|
|
}
|
|
}
|
|
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
|
let compound = tag.extract_compound()?;
|
|
let shape = FireworkExplosionShape::from_name(compound.get_string("shape")?)?;
|
|
let colors = compound
|
|
.get_int_array("colors")
|
|
.map(|v| v.to_vec())
|
|
.unwrap_or_default();
|
|
let fade_colors = compound
|
|
.get_int_array("fade_colors")
|
|
.map(|v| v.to_vec())
|
|
.unwrap_or_default();
|
|
let has_trail = compound.get_bool("has_trail").unwrap_or(false);
|
|
let has_twinkle = compound.get_bool("has_twinkle").unwrap_or(false);
|
|
Some(Self {
|
|
shape,
|
|
colors,
|
|
fade_colors,
|
|
has_trail,
|
|
has_twinkle,
|
|
})
|
|
}
|
|
}
|
|
impl DataComponentImpl for FireworkExplosionImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
let mut compound = NbtCompound::new();
|
|
compound.put_string("shape", self.shape.to_name().to_string());
|
|
compound.put("colors", NbtTag::IntArray(self.colors.clone()));
|
|
compound.put("fade_colors", NbtTag::IntArray(self.fade_colors.clone()));
|
|
compound.put_bool("has_trail", self.has_trail);
|
|
compound.put_bool("has_twinkle", self.has_twinkle);
|
|
NbtTag::Compound(compound)
|
|
}
|
|
fn get_hash(&self) -> i32 {
|
|
let mut digest = Digest::new(Crc32Iscsi);
|
|
digest.update(&[2u8]);
|
|
digest.update(&[self.shape.to_id() as u8]);
|
|
for color in &self.colors {
|
|
digest.update(&get_i32_hash(*color).to_le_bytes());
|
|
}
|
|
digest.update(&[3u8]);
|
|
for color in &self.fade_colors {
|
|
digest.update(&get_i32_hash(*color).to_le_bytes());
|
|
}
|
|
digest.update(&[4u8]);
|
|
digest.update(&[self.has_trail as u8]);
|
|
digest.update(&[self.has_twinkle as u8]);
|
|
digest.finalize() as i32
|
|
}
|
|
default_impl!(FireworkExplosion);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct FireworksImpl {
|
|
pub flight_duration: i32,
|
|
pub explosions: Vec<FireworkExplosionImpl>,
|
|
}
|
|
impl FireworksImpl {
|
|
pub fn new(flight_duration: i32, explosions: Vec<FireworkExplosionImpl>) -> Self {
|
|
Self {
|
|
flight_duration,
|
|
explosions,
|
|
}
|
|
}
|
|
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
|
let compound = tag.extract_compound()?;
|
|
let flight_duration = compound
|
|
.get_byte("flight_duration")
|
|
.map(i32::from)
|
|
.or_else(|| compound.get_int("flight_duration"))
|
|
.unwrap_or(1);
|
|
let mut explosions = Vec::new();
|
|
if let Some(list) = compound.get_list("explosions") {
|
|
for item in list {
|
|
if let Some(explosion) = FireworkExplosionImpl::read_data(item) {
|
|
explosions.push(explosion);
|
|
}
|
|
}
|
|
}
|
|
Some(Self {
|
|
flight_duration,
|
|
explosions,
|
|
})
|
|
}
|
|
}
|
|
impl DataComponentImpl for FireworksImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
let mut compound = NbtCompound::new();
|
|
compound.put_int("flight_duration", self.flight_duration);
|
|
let explosions_list: Vec<NbtTag> = self.explosions.iter().map(|e| e.write_data()).collect();
|
|
compound.put_list("explosions", explosions_list);
|
|
NbtTag::Compound(compound)
|
|
}
|
|
fn get_hash(&self) -> i32 {
|
|
let mut digest = Digest::new(Crc32Iscsi);
|
|
digest.update(&[2u8]);
|
|
digest.update(&get_i32_hash(self.flight_duration).to_le_bytes());
|
|
for explosion in &self.explosions {
|
|
digest.update(&get_i32_hash(explosion.get_hash()).to_le_bytes());
|
|
}
|
|
digest.update(&[3u8]);
|
|
digest.finalize() as i32
|
|
}
|
|
default_impl!(Fireworks);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct ProfileProperty {
|
|
pub name: String,
|
|
pub value: String,
|
|
pub signature: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
|
pub struct ProfileImpl {
|
|
pub name: Option<String>,
|
|
pub id: Option<[i32; 4]>,
|
|
pub properties: Vec<ProfileProperty>,
|
|
pub texture: Option<String>,
|
|
pub cape: Option<String>,
|
|
pub elytra: Option<String>,
|
|
pub model: Option<String>,
|
|
}
|
|
impl ProfileImpl {
|
|
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
|
match data {
|
|
NbtTag::String(name) => Some(Self {
|
|
name: Some(name.to_string()),
|
|
..Default::default()
|
|
}),
|
|
NbtTag::Compound(compound) => {
|
|
let name = compound.get_string("name").map(String::from);
|
|
let id = compound.get_int_array("id").and_then(|arr| {
|
|
if arr.len() == 4 {
|
|
Some([arr[0], arr[1], arr[2], arr[3]])
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
let mut properties = Vec::new();
|
|
if let Some(props_list) = compound.get_list("properties") {
|
|
for prop_tag in props_list {
|
|
if let Some(prop_comp) = prop_tag.extract_compound()
|
|
&& let (Some(prop_name), Some(prop_value)) =
|
|
(prop_comp.get_string("name"), prop_comp.get_string("value"))
|
|
{
|
|
properties.push(ProfileProperty {
|
|
name: prop_name.to_string(),
|
|
value: prop_value.to_string(),
|
|
signature: prop_comp.get_string("signature").map(String::from),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
let texture = compound.get_string("texture").map(String::from);
|
|
let cape = compound.get_string("cape").map(String::from);
|
|
let elytra = compound.get_string("elytra").map(String::from);
|
|
let model = compound.get_string("model").map(String::from);
|
|
Some(Self {
|
|
name,
|
|
id,
|
|
properties,
|
|
texture,
|
|
cape,
|
|
elytra,
|
|
model,
|
|
})
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
impl DataComponentImpl for ProfileImpl {
|
|
fn write_data(&self) -> NbtTag {
|
|
let mut compound = NbtCompound::new();
|
|
if let Some(name) = &self.name {
|
|
compound.put_string("name", name.clone());
|
|
}
|
|
if let Some(id) = &self.id {
|
|
compound.put("id", NbtTag::IntArray(id.to_vec()));
|
|
}
|
|
if !self.properties.is_empty() {
|
|
let mut props_list = Vec::new();
|
|
for prop in &self.properties {
|
|
let mut prop_comp = NbtCompound::new();
|
|
prop_comp.put_string("name", prop.name.clone());
|
|
prop_comp.put_string("value", prop.value.clone());
|
|
if let Some(sig) = &prop.signature {
|
|
prop_comp.put_string("signature", sig.clone());
|
|
}
|
|
props_list.push(NbtTag::Compound(prop_comp));
|
|
}
|
|
compound.put_list("properties", props_list);
|
|
}
|
|
if let Some(texture) = &self.texture {
|
|
compound.put_string("texture", texture.clone());
|
|
}
|
|
if let Some(cape) = &self.cape {
|
|
compound.put_string("cape", cape.clone());
|
|
}
|
|
if let Some(elytra) = &self.elytra {
|
|
compound.put_string("elytra", elytra.clone());
|
|
}
|
|
if let Some(model) = &self.model {
|
|
compound.put_string("model", model.clone());
|
|
}
|
|
NbtTag::Compound(compound)
|
|
}
|
|
fn get_hash(&self) -> i32 {
|
|
let mut digest = Digest::new(Crc32Iscsi);
|
|
if let Some(name) = &self.name {
|
|
digest.update(&[1u8]);
|
|
digest.update(&get_str_hash(name).to_le_bytes());
|
|
}
|
|
if let Some(id) = &self.id {
|
|
digest.update(&[2u8]);
|
|
for val in id {
|
|
digest.update(&get_i32_hash(*val).to_le_bytes());
|
|
}
|
|
}
|
|
if !self.properties.is_empty() {
|
|
digest.update(&[3u8]);
|
|
for prop in &self.properties {
|
|
digest.update(&get_str_hash(&prop.name).to_le_bytes());
|
|
digest.update(&get_str_hash(&prop.value).to_le_bytes());
|
|
if let Some(sig) = &prop.signature {
|
|
digest.update(&[1u8]);
|
|
digest.update(&get_str_hash(sig).to_le_bytes());
|
|
} else {
|
|
digest.update(&[0u8]);
|
|
}
|
|
}
|
|
}
|
|
if let Some(texture) = &self.texture {
|
|
digest.update(&[4u8]);
|
|
digest.update(&get_str_hash(texture).to_le_bytes());
|
|
}
|
|
if let Some(cape) = &self.cape {
|
|
digest.update(&[5u8]);
|
|
digest.update(&get_str_hash(cape).to_le_bytes());
|
|
}
|
|
if let Some(elytra) = &self.elytra {
|
|
digest.update(&[6u8]);
|
|
digest.update(&get_str_hash(elytra).to_le_bytes());
|
|
}
|
|
if let Some(model) = &self.model {
|
|
digest.update(&[7u8]);
|
|
digest.update(&get_str_hash(model).to_le_bytes());
|
|
}
|
|
digest.finalize() as i32
|
|
}
|
|
default_impl!(Profile);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct JukeboxPlayableImpl {
|
|
pub song: &'static str,
|
|
}
|
|
impl DataComponentImpl for JukeboxPlayableImpl {
|
|
default_impl!(JukeboxPlayable);
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
|
pub struct RecipesImpl;
|
|
impl DataComponentImpl for RecipesImpl {
|
|
default_impl!(Recipes);
|
|
}
|