Revert a55c42c83
This commit is contained in:
parent
3a811e05cb
commit
60bfdf62db
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2953,14 +2953,12 @@ version = "0.1.0-dev+26.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cesu8",
|
"cesu8",
|
||||||
"criterion",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"pumpkin-codecs",
|
"pumpkin-codecs",
|
||||||
"serde",
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -32,13 +32,10 @@ use std::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
pub trait DataComponentImpl: Send + Sync {
|
pub trait DataComponentImpl: Send + Sync {
|
||||||
fn write_data(&self) -> NbtTag {
|
fn write_data(&self) -> NbtTag {
|
||||||
NbtTag::End
|
NbtTag::End
|
||||||
}
|
}
|
||||||
fn write_data_pnbt(&self, _nbt: &mut PNbtCompound) {}
|
|
||||||
fn get_hash(&self) -> i32 {
|
fn get_hash(&self) -> i32 {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@ -73,21 +70,6 @@ pub fn read_data(id: DataComponent, data: &NbtTag) -> Option<Box<dyn DataCompone
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn read_data_pnbt(
|
|
||||||
id: DataComponent,
|
|
||||||
nbt: &mut PNbtCompound,
|
|
||||||
) -> Option<Box<dyn DataComponentImpl>> {
|
|
||||||
match id {
|
|
||||||
MaxStackSize => Some(MaxStackSizeImpl::read_data_pnbt(nbt)?.to_dyn()),
|
|
||||||
Enchantments => Some(EnchantmentsImpl::read_data_pnbt(nbt)?.to_dyn()),
|
|
||||||
Damage => Some(DamageImpl::read_data_pnbt(nbt)?.to_dyn()),
|
|
||||||
Unbreakable => Some(UnbreakableImpl::read_data_pnbt(nbt)?.to_dyn()),
|
|
||||||
MapId => Some(MapIdImpl::read_data_pnbt(nbt)?.to_dyn()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Also Pumpkin\pumpkin-protocol\src\codec\data_component.rs
|
// Also Pumpkin\pumpkin-protocol\src\codec\data_component.rs
|
||||||
|
|
||||||
macro_rules! default_impl {
|
macro_rules! default_impl {
|
||||||
@ -154,18 +136,11 @@ impl MaxStackSizeImpl {
|
|||||||
fn read_data(data: &NbtTag) -> Option<Self> {
|
fn read_data(data: &NbtTag) -> Option<Self> {
|
||||||
data.extract_int().map(|size| Self { size: size as u8 })
|
data.extract_int().map(|size| Self { size: size as u8 })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data_pnbt(nbt: &mut PNbtCompound) -> Option<Self> {
|
|
||||||
nbt.get_u8().ok().map(|size| Self { size })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl DataComponentImpl for MaxStackSizeImpl {
|
impl DataComponentImpl for MaxStackSizeImpl {
|
||||||
fn write_data(&self) -> NbtTag {
|
fn write_data(&self) -> NbtTag {
|
||||||
NbtTag::Int(i32::from(self.size))
|
NbtTag::Int(i32::from(self.size))
|
||||||
}
|
}
|
||||||
fn write_data_pnbt(&self, nbt: &mut PNbtCompound) {
|
|
||||||
nbt.put_u8(self.size);
|
|
||||||
}
|
|
||||||
fn get_hash(&self) -> i32 {
|
fn get_hash(&self) -> i32 {
|
||||||
get_i32_hash(i32::from(self.size)) as i32
|
get_i32_hash(i32::from(self.size)) as i32
|
||||||
}
|
}
|
||||||
@ -187,19 +162,11 @@ impl DamageImpl {
|
|||||||
fn read_data(data: &NbtTag) -> Option<Self> {
|
fn read_data(data: &NbtTag) -> Option<Self> {
|
||||||
data.extract_int().map(|damage| Self { damage })
|
data.extract_int().map(|damage| Self { damage })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data_pnbt(nbt: &mut PNbtCompound) -> Option<Self> {
|
|
||||||
nbt.get_int().ok().map(|damage| Self { damage })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl DataComponentImpl for DamageImpl {
|
impl DataComponentImpl for DamageImpl {
|
||||||
fn write_data(&self) -> NbtTag {
|
fn write_data(&self) -> NbtTag {
|
||||||
NbtTag::Int(self.damage)
|
NbtTag::Int(self.damage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data_pnbt(&self, nbt: &mut PNbtCompound) {
|
|
||||||
nbt.put_int(self.damage);
|
|
||||||
}
|
|
||||||
fn get_hash(&self) -> i32 {
|
fn get_hash(&self) -> i32 {
|
||||||
get_i32_hash(self.damage) as i32
|
get_i32_hash(self.damage) as i32
|
||||||
}
|
}
|
||||||
@ -211,16 +178,11 @@ impl UnbreakableImpl {
|
|||||||
const fn read_data(_data: &NbtTag) -> Option<Self> {
|
const fn read_data(_data: &NbtTag) -> Option<Self> {
|
||||||
Some(Self)
|
Some(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data_pnbt(_nbt: &mut PNbtCompound) -> Option<Self> {
|
|
||||||
Some(Self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl DataComponentImpl for UnbreakableImpl {
|
impl DataComponentImpl for UnbreakableImpl {
|
||||||
fn write_data(&self) -> NbtTag {
|
fn write_data(&self) -> NbtTag {
|
||||||
NbtTag::Compound(NbtCompound::new())
|
NbtTag::Compound(NbtCompound::new())
|
||||||
}
|
}
|
||||||
fn write_data_pnbt(&self, _nbt: &mut PNbtCompound) {}
|
|
||||||
fn get_hash(&self) -> i32 {
|
fn get_hash(&self) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -291,19 +253,6 @@ impl EnchantmentsImpl {
|
|||||||
enchantment: Cow::from(enc),
|
enchantment: Cow::from(enc),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data_pnbt(nbt: &mut PNbtCompound) -> Option<Self> {
|
|
||||||
let len = nbt.get_u32().ok()? as usize;
|
|
||||||
let mut enc = Vec::with_capacity(len);
|
|
||||||
for _ in 0..len {
|
|
||||||
let name = nbt.get_string().ok()?;
|
|
||||||
let level = nbt.get_int().ok()?;
|
|
||||||
enc.push((Enchantment::from_name(name.as_str())?, level));
|
|
||||||
}
|
|
||||||
Some(Self {
|
|
||||||
enchantment: Cow::from(enc),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_str_hash(val: &str) -> u32 {
|
fn get_str_hash(val: &str) -> u32 {
|
||||||
@ -421,21 +370,12 @@ fn hash() {
|
|||||||
|
|
||||||
impl DataComponentImpl for EnchantmentsImpl {
|
impl DataComponentImpl for EnchantmentsImpl {
|
||||||
fn write_data(&self) -> NbtTag {
|
fn write_data(&self) -> NbtTag {
|
||||||
let mut compound = NbtCompound::new();
|
let mut data = NbtCompound::new();
|
||||||
for (enc, level) in self.enchantment.iter() {
|
for (enc, level) in self.enchantment.iter() {
|
||||||
compound.put_int(enc.name, *level);
|
data.put_int(enc.name, *level);
|
||||||
}
|
}
|
||||||
NbtTag::Compound(compound)
|
NbtTag::Compound(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data_pnbt(&self, nbt: &mut PNbtCompound) {
|
|
||||||
nbt.put_u32(self.enchantment.len() as u32);
|
|
||||||
for (enc, level) in self.enchantment.iter() {
|
|
||||||
nbt.put_string(enc.name);
|
|
||||||
nbt.put_int(*level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hash(&self) -> i32 {
|
fn get_hash(&self) -> i32 {
|
||||||
let mut digest = Digest::new(Crc32Iscsi);
|
let mut digest = Digest::new(Crc32Iscsi);
|
||||||
digest.update(&[2u8]);
|
digest.update(&[2u8]);
|
||||||
@ -1153,18 +1093,15 @@ impl DataComponentImpl for WeaponImpl {
|
|||||||
default_impl!(Weapon);
|
default_impl!(Weapon);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
pub enum EquipmentType {
|
pub enum EquipmentType {
|
||||||
#[default]
|
|
||||||
Any,
|
|
||||||
Hand,
|
Hand,
|
||||||
HumanoidArmor,
|
HumanoidArmor,
|
||||||
AnimalArmor,
|
AnimalArmor,
|
||||||
Body,
|
|
||||||
Saddle,
|
Saddle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Hash, Eq, PartialEq, Default)]
|
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||||
pub struct EquipmentSlotData {
|
pub struct EquipmentSlotData {
|
||||||
pub slot_type: EquipmentType,
|
pub slot_type: EquipmentType,
|
||||||
pub entity_id: i32,
|
pub entity_id: i32,
|
||||||
@ -1230,7 +1167,7 @@ impl EquipmentSlot {
|
|||||||
name: Cow::Borrowed("head"),
|
name: Cow::Borrowed("head"),
|
||||||
});
|
});
|
||||||
pub const BODY: Self = Self::Body(EquipmentSlotData {
|
pub const BODY: Self = Self::Body(EquipmentSlotData {
|
||||||
slot_type: EquipmentType::Body,
|
slot_type: EquipmentType::AnimalArmor,
|
||||||
entity_id: 0,
|
entity_id: 0,
|
||||||
index: 6,
|
index: 6,
|
||||||
max_count: 1,
|
max_count: 1,
|
||||||
@ -1568,10 +1505,6 @@ impl MapIdImpl {
|
|||||||
fn read_data(data: &NbtTag) -> Option<Self> {
|
fn read_data(data: &NbtTag) -> Option<Self> {
|
||||||
data.extract_int().map(|id| Self { id })
|
data.extract_int().map(|id| Self { id })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data_pnbt(nbt: &mut PNbtCompound) -> Option<Self> {
|
|
||||||
nbt.get_i32().ok().map(|id| Self { id })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataComponentImpl for MapIdImpl {
|
impl DataComponentImpl for MapIdImpl {
|
||||||
@ -1579,10 +1512,6 @@ impl DataComponentImpl for MapIdImpl {
|
|||||||
NbtTag::Int(self.id)
|
NbtTag::Int(self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data_pnbt(&self, nbt: &mut PNbtCompound) {
|
|
||||||
nbt.put_i32(self.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hash(&self) -> i32 {
|
fn get_hash(&self) -> i32 {
|
||||||
get_i32_hash(self.id) as i32
|
get_i32_hash(self.id) as i32
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,14 +3,13 @@ use crate::data_component::DataComponent::Enchantments;
|
|||||||
use crate::data_component_impl::{
|
use crate::data_component_impl::{
|
||||||
BlocksAttacksImpl, ConsumableImpl, DamageImpl, DataComponentImpl, EnchantmentsImpl, IDSet,
|
BlocksAttacksImpl, ConsumableImpl, DamageImpl, DataComponentImpl, EnchantmentsImpl, IDSet,
|
||||||
MaxDamageImpl, MaxStackSizeImpl, ToolImpl, UnbreakableImpl, UseCooldownImpl, get, get_mut,
|
MaxDamageImpl, MaxStackSizeImpl, ToolImpl, UnbreakableImpl, UseCooldownImpl, get, get_mut,
|
||||||
read_data, read_data_pnbt,
|
read_data,
|
||||||
};
|
};
|
||||||
use crate::item::Item;
|
use crate::item::Item;
|
||||||
use crate::recipes::RecipeResultStruct;
|
use crate::recipes::RecipeResultStruct;
|
||||||
use crate::tag::Taggable;
|
use crate::tag::Taggable;
|
||||||
use crate::{Block, Enchantment};
|
use crate::{Block, Enchantment};
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
use pumpkin_util::GameMode;
|
use pumpkin_util::GameMode;
|
||||||
use rand;
|
use rand;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@ -537,25 +536,6 @@ impl ItemStack {
|
|||||||
compound.put_compound("components", tag);
|
compound.put_compound("components", tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_item_stack_pnbt(&self, nbt: &mut PNbtCompound) {
|
|
||||||
// Positional: write ID as string, then count as u8
|
|
||||||
nbt.put_string(&format!("minecraft:{}", self.item.registry_key));
|
|
||||||
nbt.put_u8(self.item_count);
|
|
||||||
|
|
||||||
// Positional: write number of components
|
|
||||||
nbt.put_u32(self.patch.len() as u32);
|
|
||||||
for (id, data) in &self.patch {
|
|
||||||
// Write component ID as u8 (matching DataComponent enum)
|
|
||||||
nbt.put_u8(*id as u8);
|
|
||||||
if let Some(data) = data {
|
|
||||||
nbt.put_bool(true); // Is presence
|
|
||||||
data.write_data_pnbt(nbt);
|
|
||||||
} else {
|
|
||||||
nbt.put_bool(false); // Is deletion (!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn read_item_stack(compound: &NbtCompound) -> Option<Self> {
|
pub fn read_item_stack(compound: &NbtCompound) -> Option<Self> {
|
||||||
// Get ID, which is a string like "minecraft:diamond_sword"
|
// Get ID, which is a string like "minecraft:diamond_sword"
|
||||||
@ -588,35 +568,6 @@ impl ItemStack {
|
|||||||
|
|
||||||
Some(item_stack)
|
Some(item_stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn read_item_stack_pnbt(nbt: &mut PNbtCompound) -> Option<Self> {
|
|
||||||
// Read ID as string
|
|
||||||
let full_id = nbt.get_string().ok()?;
|
|
||||||
let registry_key = full_id.strip_prefix("minecraft:").unwrap_or(&full_id);
|
|
||||||
let item = Item::from_registry_key(registry_key)?;
|
|
||||||
|
|
||||||
// Read count as u8
|
|
||||||
let count = nbt.get_u8().ok()?;
|
|
||||||
|
|
||||||
let mut item_stack = Self::new(count, item);
|
|
||||||
|
|
||||||
// Read components
|
|
||||||
let patch_len = nbt.get_u32().ok()? as usize;
|
|
||||||
for _ in 0..patch_len {
|
|
||||||
let component_id_raw = nbt.get_u8().ok()?;
|
|
||||||
let id = DataComponent::try_from_id(component_id_raw)?;
|
|
||||||
let is_present = nbt.get_bool().ok()?;
|
|
||||||
|
|
||||||
if is_present {
|
|
||||||
item_stack.patch.push((id, Some(read_data_pnbt(id, nbt)?)));
|
|
||||||
} else {
|
|
||||||
item_stack.patch.push((id, None));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(item_stack)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&RecipeResultStruct> for ItemStack {
|
impl From<&RecipeResultStruct> for ItemStack {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ pumpkin-codecs.workspace = true
|
|||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
uuid.workspace = true
|
|
||||||
|
|
||||||
cesu8.workspace = true
|
cesu8.workspace = true
|
||||||
flate2.workspace = true
|
flate2.workspace = true
|
||||||
@ -18,11 +17,6 @@ tracing.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
criterion.workspace = true
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "nbt_bench"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
# PNBT Specification
|
|
||||||
|
|
||||||
PNBT is a high-speed, positional binary format designed for maximum storage efficiency and extreme serialization/deserialization throughput. Unlike standard NBT, PNBT is **positional** and does not store field names or tag IDs in the stream, making it ideal for internal storage (player data, level metadata) where the schema is stable.
|
|
||||||
|
|
||||||
### Key Features
|
|
||||||
- **Zero Overhead:** No string keys or tag IDs stored in the binary stream.
|
|
||||||
- **ZigZag Varints:** Uses LEB128 encoding for all integers and lengths, with ZigZag for signed types.
|
|
||||||
- **Zero-Copy Deserialization:** Directly borrows strings and bytes from the input buffer.
|
|
||||||
- **Extreme Performance:** Specifically optimized for high-frequency internal data storage.
|
|
||||||
|
|
||||||
### Binary Layout
|
|
||||||
PNBT follows a strict positional layout defined by the Rust struct being serialized:
|
|
||||||
- **Primitives:** LEB128/ZigZag varints for integers. Fixed size for floats.
|
|
||||||
- **Strings/Bytes:** Varint length followed by raw payload.
|
|
||||||
- **Sequences/Maps:** Varint length followed by positional elements.
|
|
||||||
|
|
||||||
## Performance (vs Vanilla NBT)
|
|
||||||
- **Size Efficiency:** **~43% to 47% smaller** footprint.
|
|
||||||
- **Serialization Speed:** **~8x faster** than vanilla NBT (554 ns vs 4.51 µs).
|
|
||||||
- **Deserialization Speed:** **~3x faster** than vanilla NBT (3.41 µs vs 9.92 µs).
|
|
||||||
|
|
||||||
Note: Because PNBT is positional, any changes to the struct layout (adding/removing/reordering fields) will make existing serialized data incompatible unless handled manually (e.g. via `Option` or versioned structs).
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
#![allow(clippy::print_stdout)]
|
|
||||||
|
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
|
||||||
use pumpkin_nbt::{from_bytes_unnamed, from_pnbt, to_bytes_unnamed, to_pnbt};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::hint::black_box;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
struct LargeData {
|
|
||||||
id: i32,
|
|
||||||
name: String,
|
|
||||||
metadata: Vec<Metadata>,
|
|
||||||
inventory: Vec<Item>,
|
|
||||||
scores: Vec<i32>,
|
|
||||||
active: bool,
|
|
||||||
position: (f64, f64, f64),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
struct Metadata {
|
|
||||||
key: String,
|
|
||||||
value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
struct Item {
|
|
||||||
id: String,
|
|
||||||
count: i8,
|
|
||||||
slot: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_large_data() -> LargeData {
|
|
||||||
LargeData {
|
|
||||||
id: 1234567,
|
|
||||||
name: "Pumpkin King".to_string(),
|
|
||||||
metadata: (0..50)
|
|
||||||
.map(|i| Metadata {
|
|
||||||
key: format!("meta_key_{i}"),
|
|
||||||
value: format!("meta_value_{i}"),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
inventory: (0..27)
|
|
||||||
.map(|i| Item {
|
|
||||||
id: "minecraft:diamond_sword".to_string(),
|
|
||||||
count: 64,
|
|
||||||
slot: i,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
scores: (0..100).map(|i| i * 1000).collect(),
|
|
||||||
active: true,
|
|
||||||
position: (1234.56, 64.0, -789.12),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_nbt(c: &mut Criterion) {
|
|
||||||
let data = create_large_data();
|
|
||||||
|
|
||||||
// Size comparison
|
|
||||||
let mut vanilla_bytes = Vec::new();
|
|
||||||
to_bytes_unnamed(&data, &mut vanilla_bytes).unwrap();
|
|
||||||
let pnbt_bytes = to_pnbt(&data).unwrap();
|
|
||||||
|
|
||||||
println!("\nSize Comparison (LargeData):");
|
|
||||||
println!("Vanilla NBT size: {} bytes", vanilla_bytes.len());
|
|
||||||
println!("PNBT size: {} bytes", pnbt_bytes.len());
|
|
||||||
println!(
|
|
||||||
"Reduction: {:.2}%\n",
|
|
||||||
(1.0 - (pnbt_bytes.len() as f64 / vanilla_bytes.len() as f64)) * 100.0
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut group = c.benchmark_group("NBT Comparison");
|
|
||||||
|
|
||||||
group.bench_function("Vanilla Serialize", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut out = Vec::with_capacity(vanilla_bytes.len());
|
|
||||||
to_bytes_unnamed(black_box(&data), &mut out).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.bench_function("PNBT Serialize", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
to_pnbt(black_box(&data)).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.bench_function("Vanilla Deserialize", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let cursor = Cursor::new(&vanilla_bytes);
|
|
||||||
let _: LargeData = from_bytes_unnamed(cursor).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.bench_function("PNBT Deserialize", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let _: LargeData = from_pnbt(black_box(&pnbt_bytes)).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, bench_nbt);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@ -16,12 +16,10 @@ pub mod compound;
|
|||||||
pub mod deserializer;
|
pub mod deserializer;
|
||||||
pub mod nbt_compress;
|
pub mod nbt_compress;
|
||||||
pub mod nbt_ops;
|
pub mod nbt_ops;
|
||||||
pub mod pnbt;
|
|
||||||
pub mod serializer;
|
pub mod serializer;
|
||||||
pub mod tag;
|
pub mod tag;
|
||||||
|
|
||||||
pub use deserializer::{from_bytes, from_bytes_unnamed};
|
pub use deserializer::{from_bytes, from_bytes_unnamed};
|
||||||
pub use pnbt::{from_pnbt, to_pnbt};
|
|
||||||
pub use serializer::{to_bytes, to_bytes_named, to_bytes_unnamed};
|
pub use serializer::{to_bytes, to_bytes_named, to_bytes_unnamed};
|
||||||
|
|
||||||
// This NBT crate is inspired from CrabNBT
|
// This NBT crate is inspired from CrabNBT
|
||||||
|
|||||||
@ -1,907 +0,0 @@
|
|||||||
use crate::{Error, NBT_ARRAY_TAG, NBT_BYTE_ARRAY_TAG, NBT_INT_ARRAY_TAG, NBT_LONG_ARRAY_TAG};
|
|
||||||
use serde::{
|
|
||||||
Deserialize, Serialize,
|
|
||||||
de::{self, MapAccess, SeqAccess, Visitor},
|
|
||||||
ser,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Serializes struct to PNBT (Pumpkin NBT) format.
|
|
||||||
/// PNBT is a high-performance, positional binary format.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_pnbt<T: Serialize>(value: &T) -> Result<Vec<u8>, Error> {
|
|
||||||
let mut serializer = Serializer::new();
|
|
||||||
value.serialize(&mut serializer)?;
|
|
||||||
Ok(serializer.output)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserializes struct from PNBT format.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_pnbt<'a, T: Deserialize<'a>>(input: &'a [u8]) -> Result<T, Error> {
|
|
||||||
let mut deserializer = Deserializer::new(input);
|
|
||||||
T::deserialize(&mut deserializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `PNbtCompound` is a direct byte-wrapper for building or reading PNBT data.
|
|
||||||
/// It provides a manual Protobuf-like API without string keys.
|
|
||||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct PNbtCompound {
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
pub read_pos: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PNbtCompound {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
data: Vec::with_capacity(1024),
|
|
||||||
read_pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn from_bytes(data: Vec<u8>) -> Self {
|
|
||||||
Self { data, read_pos: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn into_bytes(self) -> Vec<u8> {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Writing API ---
|
|
||||||
|
|
||||||
pub fn put_bool(&mut self, v: bool) {
|
|
||||||
self.data.push(u8::from(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_i8(&mut self, v: i8) {
|
|
||||||
self.data.push(v as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `put_i8`
|
|
||||||
pub fn put_byte(&mut self, v: i8) {
|
|
||||||
self.put_i8(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_u8(&mut self, v: u8) {
|
|
||||||
self.data.push(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_varint(&mut self, mut value: u64) {
|
|
||||||
loop {
|
|
||||||
let byte = (value & 0x7f) as u8;
|
|
||||||
value >>= 7;
|
|
||||||
if value == 0 {
|
|
||||||
self.data.push(byte);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
self.data.push(byte | 0x80);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_zigzag(&mut self, value: i64) {
|
|
||||||
let encoded = ((value << 1) ^ (value >> 63)) as u64;
|
|
||||||
self.write_varint(encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_i16(&mut self, v: i16) {
|
|
||||||
self.write_zigzag(i64::from(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `put_i16`
|
|
||||||
pub fn put_short(&mut self, v: i16) {
|
|
||||||
self.put_i16(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_u16(&mut self, v: u16) {
|
|
||||||
self.write_varint(u64::from(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_i32(&mut self, v: i32) {
|
|
||||||
self.write_zigzag(i64::from(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `put_i32`
|
|
||||||
pub fn put_int(&mut self, v: i32) {
|
|
||||||
self.put_i32(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_u32(&mut self, v: u32) {
|
|
||||||
self.write_varint(u64::from(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_i64(&mut self, v: i64) {
|
|
||||||
self.write_zigzag(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `put_i64`
|
|
||||||
pub fn put_long(&mut self, v: i64) {
|
|
||||||
self.put_i64(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_u64(&mut self, v: u64) {
|
|
||||||
self.write_varint(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_f32(&mut self, v: f32) {
|
|
||||||
self.data.extend_from_slice(&v.to_le_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `put_f32`
|
|
||||||
pub fn put_float(&mut self, v: f32) {
|
|
||||||
self.put_f32(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_f64(&mut self, v: f64) {
|
|
||||||
self.data.extend_from_slice(&v.to_le_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `put_f64`
|
|
||||||
pub fn put_double(&mut self, v: f64) {
|
|
||||||
self.put_f64(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_string(&mut self, v: &str) {
|
|
||||||
self.write_varint(v.len() as u64);
|
|
||||||
self.data.extend_from_slice(v.as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_bytes(&mut self, v: &[u8]) {
|
|
||||||
self.write_varint(v.len() as u64);
|
|
||||||
self.data.extend_from_slice(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_uuid(&mut self, v: &uuid::Uuid) {
|
|
||||||
self.data.extend_from_slice(v.as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Reading API ---
|
|
||||||
|
|
||||||
fn read_byte(&mut self) -> Result<u8, Error> {
|
|
||||||
if self.read_pos >= self.data.len() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let b = self.data[self.read_pos];
|
|
||||||
self.read_pos += 1;
|
|
||||||
Ok(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_varint(&mut self) -> Result<u64, Error> {
|
|
||||||
let mut value = 0;
|
|
||||||
let mut shift = 0;
|
|
||||||
loop {
|
|
||||||
let byte = self.read_byte()?;
|
|
||||||
value |= ((byte & 0x7f) as u64) << shift;
|
|
||||||
if byte & 0x80 == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
shift += 7;
|
|
||||||
}
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_zigzag(&mut self) -> Result<i64, Error> {
|
|
||||||
let value = self.read_varint()?;
|
|
||||||
Ok(((value >> 1) as i64) ^ (-((value & 1) as i64)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bool(&mut self) -> Result<bool, Error> {
|
|
||||||
Ok(self.read_byte()? != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_i8(&mut self) -> Result<i8, Error> {
|
|
||||||
Ok(self.read_byte()? as i8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `get_i8`
|
|
||||||
pub fn get_byte(&mut self) -> Result<i8, Error> {
|
|
||||||
self.get_i8()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_u8(&mut self) -> Result<u8, Error> {
|
|
||||||
self.read_byte()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_i16(&mut self) -> Result<i16, Error> {
|
|
||||||
Ok(self.read_zigzag()? as i16)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `get_i16`
|
|
||||||
pub fn get_short(&mut self) -> Result<i16, Error> {
|
|
||||||
self.get_i16()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_u16(&mut self) -> Result<u16, Error> {
|
|
||||||
Ok(self.read_varint()? as u16)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_i32(&mut self) -> Result<i32, Error> {
|
|
||||||
Ok(self.read_zigzag()? as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `get_i32`
|
|
||||||
pub fn get_int(&mut self) -> Result<i32, Error> {
|
|
||||||
self.get_i32()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_u32(&mut self) -> Result<u32, Error> {
|
|
||||||
Ok(self.read_varint()? as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_i64(&mut self) -> Result<i64, Error> {
|
|
||||||
self.read_zigzag()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `get_i64`
|
|
||||||
pub fn get_long(&mut self) -> Result<i64, Error> {
|
|
||||||
self.get_i64()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_u64(&mut self) -> Result<u64, Error> {
|
|
||||||
self.read_varint()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_f32(&mut self) -> Result<f32, Error> {
|
|
||||||
if self.read_pos + 4 > self.data.len() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let mut b = [0u8; 4];
|
|
||||||
b.copy_from_slice(&self.data[self.read_pos..self.read_pos + 4]);
|
|
||||||
self.read_pos += 4;
|
|
||||||
Ok(f32::from_le_bytes(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `get_f32`
|
|
||||||
pub fn get_float(&mut self) -> Result<f32, Error> {
|
|
||||||
self.get_f32()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_f64(&mut self) -> Result<f64, Error> {
|
|
||||||
if self.read_pos + 8 > self.data.len() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let mut b = [0u8; 8];
|
|
||||||
b.copy_from_slice(&self.data[self.read_pos..self.read_pos + 8]);
|
|
||||||
self.read_pos += 8;
|
|
||||||
Ok(f64::from_le_bytes(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alias for `get_f64`
|
|
||||||
pub fn get_double(&mut self) -> Result<f64, Error> {
|
|
||||||
self.get_f64()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_string(&mut self) -> Result<String, Error> {
|
|
||||||
let len = self.read_varint()? as usize;
|
|
||||||
if self.read_pos + len > self.data.len() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let s = std::str::from_utf8(&self.data[self.read_pos..self.read_pos + len])
|
|
||||||
.map_err(|e| Error::SerdeError(e.to_string()))?;
|
|
||||||
self.read_pos += len;
|
|
||||||
Ok(s.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bytes(&mut self) -> Result<Vec<u8>, Error> {
|
|
||||||
let len = self.read_varint()? as usize;
|
|
||||||
if self.read_pos + len > self.data.len() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let b = self.data[self.read_pos..self.read_pos + len].to_vec();
|
|
||||||
self.read_pos += len;
|
|
||||||
Ok(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_uuid(&mut self) -> Result<uuid::Uuid, Error> {
|
|
||||||
if self.read_pos + 16 > self.data.len() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let mut b = [0u8; 16];
|
|
||||||
b.copy_from_slice(&self.data[self.read_pos..self.read_pos + 16]);
|
|
||||||
self.read_pos += 16;
|
|
||||||
Ok(uuid::Uuid::from_bytes(b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Serializer {
|
|
||||||
output: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serializer {
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
output: Vec::with_capacity(1024),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write_varint(&mut self, mut value: u64) {
|
|
||||||
loop {
|
|
||||||
let byte = (value & 0x7f) as u8;
|
|
||||||
value >>= 7;
|
|
||||||
if value == 0 {
|
|
||||||
self.output.push(byte);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
self.output.push(byte | 0x80);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write_zigzag(&mut self, value: i64) {
|
|
||||||
let encoded = ((value << 1) ^ (value >> 63)) as u64;
|
|
||||||
self.write_varint(encoded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Serializer {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ser::Serializer for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
type SerializeSeq = Self;
|
|
||||||
type SerializeTuple = Self;
|
|
||||||
type SerializeTupleStruct = Self;
|
|
||||||
type SerializeTupleVariant = Self;
|
|
||||||
type SerializeMap = Self;
|
|
||||||
type SerializeStruct = Self;
|
|
||||||
type SerializeStructVariant = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn serialize_bool(self, v: bool) -> Result<(), Error> {
|
|
||||||
self.output.push(u8::from(v));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_i8(self, v: i8) -> Result<(), Error> {
|
|
||||||
self.output.push(v as u8);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_i16(self, v: i16) -> Result<(), Error> {
|
|
||||||
self.write_zigzag(i64::from(v));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_i32(self, v: i32) -> Result<(), Error> {
|
|
||||||
self.write_zigzag(i64::from(v));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_i64(self, v: i64) -> Result<(), Error> {
|
|
||||||
self.write_zigzag(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_u8(self, v: u8) -> Result<(), Error> {
|
|
||||||
self.output.push(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_u16(self, v: u16) -> Result<(), Error> {
|
|
||||||
self.write_varint(u64::from(v));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_u32(self, v: u32) -> Result<(), Error> {
|
|
||||||
self.write_varint(u64::from(v));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_u64(self, v: u64) -> Result<(), Error> {
|
|
||||||
self.write_varint(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_f32(self, v: f32) -> Result<(), Error> {
|
|
||||||
self.output.extend_from_slice(&v.to_le_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_f64(self, v: f64) -> Result<(), Error> {
|
|
||||||
self.output.extend_from_slice(&v.to_le_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_char(self, v: char) -> Result<(), Error> {
|
|
||||||
self.serialize_str(&v.to_string())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_str(self, v: &str) -> Result<(), Error> {
|
|
||||||
self.write_varint(v.len() as u64);
|
|
||||||
self.output.extend_from_slice(v.as_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_bytes(self, v: &[u8]) -> Result<(), Error> {
|
|
||||||
self.write_varint(v.len() as u64);
|
|
||||||
self.output.extend_from_slice(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_none(self) -> Result<(), Error> {
|
|
||||||
self.output.push(0);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<(), Error> {
|
|
||||||
self.output.push(1);
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
fn serialize_unit(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_unit_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
idx: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.write_varint(u64::from(idx));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
|
||||||
self,
|
|
||||||
name: &'static str,
|
|
||||||
idx: u32,
|
|
||||||
variant: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if name == NBT_ARRAY_TAG {
|
|
||||||
match variant {
|
|
||||||
NBT_BYTE_ARRAY_TAG | NBT_INT_ARRAY_TAG | NBT_LONG_ARRAY_TAG => {
|
|
||||||
// Positional: skip indices/tags, just write data
|
|
||||||
return value.serialize(self);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.write_varint(u64::from(idx));
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
|
|
||||||
let len = len.ok_or_else(|| Error::SerdeError("Length required".to_string()))?;
|
|
||||||
self.write_varint(len as u64);
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Error> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
fn serialize_tuple_struct(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleStruct, Error> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
fn serialize_tuple_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleVariant, Error> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Error> {
|
|
||||||
let len = len.ok_or_else(|| Error::SerdeError("Length required".to_string()))?;
|
|
||||||
self.write_varint(len as u64);
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
fn serialize_struct(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeStruct, Error> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
fn serialize_struct_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeStructVariant, Error> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ser::SerializeSeq for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ser::SerializeTuple for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ser::SerializeTupleStruct for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ser::SerializeTupleVariant for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ser::SerializeStruct for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
_key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ser::SerializeStructVariant for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
_key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ser::SerializeMap for &mut Serializer {
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<(), Error> {
|
|
||||||
key.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Error> {
|
|
||||||
value.serialize(&mut **self)
|
|
||||||
}
|
|
||||||
fn end(self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Deserializer<'de> {
|
|
||||||
input: &'de [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserializer<'de> {
|
|
||||||
#[must_use]
|
|
||||||
pub const fn new(input: &'de [u8]) -> Self {
|
|
||||||
Self { input }
|
|
||||||
}
|
|
||||||
fn read_byte(&mut self) -> Result<u8, Error> {
|
|
||||||
if self.input.is_empty() {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let b = self.input[0];
|
|
||||||
self.input = &self.input[1..];
|
|
||||||
Ok(b)
|
|
||||||
}
|
|
||||||
fn read_varint(&mut self) -> Result<u64, Error> {
|
|
||||||
let mut value = 0;
|
|
||||||
let mut shift = 0;
|
|
||||||
loop {
|
|
||||||
let byte = self.read_byte()?;
|
|
||||||
value |= ((byte & 0x7f) as u64) << shift;
|
|
||||||
if byte & 0x80 == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
shift += 7;
|
|
||||||
}
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
fn read_zigzag(&mut self) -> Result<i64, Error> {
|
|
||||||
let value = self.read_varint()?;
|
|
||||||
Ok(((value >> 1) as i64) ^ (-((value & 1) as i64)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
|
|
||||||
type Error = Error;
|
|
||||||
fn deserialize_any<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Error> {
|
|
||||||
Err(Error::SerdeError("Positional PNBT needs types".to_string()))
|
|
||||||
}
|
|
||||||
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_bool(self.read_byte()? != 0)
|
|
||||||
}
|
|
||||||
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_i8(self.read_byte()? as i8)
|
|
||||||
}
|
|
||||||
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_i16(self.read_zigzag()? as i16)
|
|
||||||
}
|
|
||||||
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_i32(self.read_zigzag()? as i32)
|
|
||||||
}
|
|
||||||
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_i64(self.read_zigzag()?)
|
|
||||||
}
|
|
||||||
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_u8(self.read_byte()?)
|
|
||||||
}
|
|
||||||
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_u16(self.read_varint()? as u16)
|
|
||||||
}
|
|
||||||
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_u32(self.read_varint()? as u32)
|
|
||||||
}
|
|
||||||
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_u64(self.read_varint()?)
|
|
||||||
}
|
|
||||||
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
if self.input.len() < 4 {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let mut b = [0u8; 4];
|
|
||||||
b.copy_from_slice(&self.input[..4]);
|
|
||||||
self.input = &self.input[4..];
|
|
||||||
visitor.visit_f32(f32::from_le_bytes(b))
|
|
||||||
}
|
|
||||||
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
if self.input.len() < 8 {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let mut b = [0u8; 8];
|
|
||||||
b.copy_from_slice(&self.input[..8]);
|
|
||||||
self.input = &self.input[8..];
|
|
||||||
visitor.visit_f64(f64::from_le_bytes(b))
|
|
||||||
}
|
|
||||||
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
self.deserialize_str(visitor)
|
|
||||||
}
|
|
||||||
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
let len = self.read_varint()? as usize;
|
|
||||||
if self.input.len() < len {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let s = std::str::from_utf8(&self.input[..len])
|
|
||||||
.map_err(|e| Error::SerdeError(e.to_string()))?;
|
|
||||||
self.input = &self.input[len..];
|
|
||||||
visitor.visit_borrowed_str(s)
|
|
||||||
}
|
|
||||||
fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
self.deserialize_str(visitor)
|
|
||||||
}
|
|
||||||
fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
let len = self.read_varint()? as usize;
|
|
||||||
if self.input.len() < len {
|
|
||||||
return Err(Error::SerdeError("EOF".to_string()));
|
|
||||||
}
|
|
||||||
let b = &self.input[..len];
|
|
||||||
self.input = &self.input[len..];
|
|
||||||
visitor.visit_borrowed_bytes(b)
|
|
||||||
}
|
|
||||||
fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
self.deserialize_bytes(visitor)
|
|
||||||
}
|
|
||||||
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
if self.read_byte()? == 0 {
|
|
||||||
visitor.visit_none()
|
|
||||||
} else {
|
|
||||||
visitor.visit_some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_unit()
|
|
||||||
}
|
|
||||||
fn deserialize_unit_struct<V: Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_unit()
|
|
||||||
}
|
|
||||||
fn deserialize_newtype_struct<V: Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_newtype_struct(self)
|
|
||||||
}
|
|
||||||
fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
let len = self.read_varint()? as usize;
|
|
||||||
visitor.visit_seq(RawSeq { de: self, len })
|
|
||||||
}
|
|
||||||
fn deserialize_tuple<V: Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_seq(RawSeq { de: self, len })
|
|
||||||
}
|
|
||||||
fn deserialize_tuple_struct<V: Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
len: usize,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_seq(RawSeq { de: self, len })
|
|
||||||
}
|
|
||||||
fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
let len = self.read_varint()? as usize;
|
|
||||||
visitor.visit_map(RawMap { de: self, len })
|
|
||||||
}
|
|
||||||
fn deserialize_struct<V: Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
fields: &'static [&'static str],
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_seq(RawSeq {
|
|
||||||
de: self,
|
|
||||||
len: fields.len(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn deserialize_enum<V: Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variants: &'static [&'static str],
|
|
||||||
_visitor: V,
|
|
||||||
) -> Result<V::Value, Error> {
|
|
||||||
Err(Error::SerdeError("Unimplemented".to_string()))
|
|
||||||
}
|
|
||||||
fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
self.deserialize_str(visitor)
|
|
||||||
}
|
|
||||||
fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
|
|
||||||
visitor.visit_unit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RawSeq<'a, 'de> {
|
|
||||||
de: &'a mut Deserializer<'de>,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
impl<'de> SeqAccess<'de> for RawSeq<'_, 'de> {
|
|
||||||
type Error = Error;
|
|
||||||
fn next_element_seed<E: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: E,
|
|
||||||
) -> Result<Option<E::Value>, Error> {
|
|
||||||
if self.len == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
self.len -= 1;
|
|
||||||
seed.deserialize(&mut *self.de).map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RawMap<'a, 'de> {
|
|
||||||
de: &'a mut Deserializer<'de>,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
impl<'de> MapAccess<'de> for RawMap<'_, 'de> {
|
|
||||||
type Error = Error;
|
|
||||||
fn next_key_seed<K: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: K,
|
|
||||||
) -> Result<Option<K::Value>, Error> {
|
|
||||||
if self.len == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
self.len -= 1;
|
|
||||||
seed.deserialize(&mut *self.de).map(Some)
|
|
||||||
}
|
|
||||||
fn next_value_seed<V: de::DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, Error> {
|
|
||||||
seed.deserialize(&mut *self.de)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
||||||
struct TestStruct {
|
|
||||||
a: i32,
|
|
||||||
b: String,
|
|
||||||
c: Vec<i8>,
|
|
||||||
d: Inner,
|
|
||||||
active: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
||||||
struct Inner {
|
|
||||||
e: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pnbt_serde() {
|
|
||||||
let t = TestStruct {
|
|
||||||
a: 123456,
|
|
||||||
b: "hello world".to_string(),
|
|
||||||
c: vec![1, 2, 3, 4, 5],
|
|
||||||
d: Inner {
|
|
||||||
e: std::f32::consts::PI,
|
|
||||||
},
|
|
||||||
active: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
let bytes = to_pnbt(&t).unwrap();
|
|
||||||
let decoded: TestStruct = from_pnbt(&bytes).unwrap();
|
|
||||||
assert_eq!(t, decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nbt_arrays_pnbt() {
|
|
||||||
use crate::{nbt_byte_array, nbt_int_array, nbt_long_array};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
||||||
struct ArrayStruct {
|
|
||||||
#[serde(serialize_with = "nbt_byte_array")]
|
|
||||||
b: Vec<u8>,
|
|
||||||
#[serde(serialize_with = "nbt_int_array")]
|
|
||||||
i: Vec<i32>,
|
|
||||||
#[serde(serialize_with = "nbt_long_array")]
|
|
||||||
l: Vec<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = ArrayStruct {
|
|
||||||
b: vec![1, 2, 3],
|
|
||||||
i: vec![100, 200, 300],
|
|
||||||
l: vec![1000, 2000, 3000],
|
|
||||||
};
|
|
||||||
|
|
||||||
let bytes = to_pnbt(&t).unwrap();
|
|
||||||
let decoded: ArrayStruct = from_pnbt(&bytes).unwrap();
|
|
||||||
assert_eq!(t, decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pnbt_compound_manual() {
|
|
||||||
let mut compound = PNbtCompound::new();
|
|
||||||
compound.put_i32(123456);
|
|
||||||
compound.put_string("manual pnbt");
|
|
||||||
compound.put_bool(true);
|
|
||||||
compound.put_f32(std::f32::consts::PI);
|
|
||||||
|
|
||||||
let bytes = compound.into_bytes();
|
|
||||||
let mut reader = PNbtCompound::from_bytes(bytes);
|
|
||||||
|
|
||||||
assert_eq!(reader.get_i32().unwrap(), 123456);
|
|
||||||
assert_eq!(reader.get_string().unwrap(), "manual pnbt");
|
|
||||||
assert!(reader.get_bool().unwrap());
|
|
||||||
assert!((reader.get_f32().unwrap() - std::f32::consts::PI).abs() < 0.001);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,6 +13,7 @@ use pumpkin_nbt::{compound::NbtCompound, nbt_long_array};
|
|||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chunk::{
|
chunk::{
|
||||||
@ -79,8 +80,9 @@ impl ChunkData {
|
|||||||
chunk_data: &[u8],
|
chunk_data: &[u8],
|
||||||
position: Vector2<i32>,
|
position: Vector2<i32>,
|
||||||
) -> Result<Self, ChunkParsingError> {
|
) -> Result<Self, ChunkParsingError> {
|
||||||
let chunk_data = pumpkin_nbt::from_pnbt::<ChunkNbt>(chunk_data)
|
let chunk_data =
|
||||||
.map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?;
|
pumpkin_nbt::from_bytes_unnamed::<ChunkNbt>(std::io::Cursor::new(chunk_data))
|
||||||
|
.map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?;
|
||||||
|
|
||||||
if chunk_data.x_pos != position.x || chunk_data.z_pos != position.y {
|
if chunk_data.x_pos != position.x || chunk_data.z_pos != position.y {
|
||||||
return Err(ChunkParsingError::ErrorDeserializingChunk(format!(
|
return Err(ChunkParsingError::ErrorDeserializingChunk(format!(
|
||||||
@ -221,8 +223,9 @@ impl ChunkData {
|
|||||||
light_correct: is_light_correct,
|
light_correct: is_light_correct,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result =
|
let mut result = Vec::new();
|
||||||
pumpkin_nbt::to_pnbt(&nbt_ref).map_err(ChunkSerializingError::ErrorSerializingChunk)?;
|
pumpkin_nbt::to_bytes_unnamed(&nbt_ref, &mut result)
|
||||||
|
.map_err(ChunkSerializingError::ErrorSerializingChunk)?;
|
||||||
|
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
@ -271,8 +274,9 @@ impl ChunkEntityData {
|
|||||||
chunk_data: &[u8],
|
chunk_data: &[u8],
|
||||||
position: Vector2<i32>,
|
position: Vector2<i32>,
|
||||||
) -> Result<Self, ChunkParsingError> {
|
) -> Result<Self, ChunkParsingError> {
|
||||||
let chunk_entity_data = pumpkin_nbt::from_pnbt::<EntityNbt>(chunk_data)
|
let chunk_entity_data =
|
||||||
.map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?;
|
pumpkin_nbt::from_bytes_unnamed::<EntityNbt>(std::io::Cursor::new(chunk_data))
|
||||||
|
.map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?;
|
||||||
|
|
||||||
if chunk_entity_data.position[0] != position.x
|
if chunk_entity_data.position[0] != position.x
|
||||||
|| chunk_entity_data.position[1] != position.y
|
|| chunk_entity_data.position[1] != position.y
|
||||||
@ -286,16 +290,27 @@ impl ChunkEntityData {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let mut map = FxHashMap::default();
|
let mut map = FxHashMap::default();
|
||||||
for mut entity_nbt in chunk_entity_data.entities {
|
for entity_nbt in chunk_entity_data.entities {
|
||||||
let uuid = {
|
let uuid = if let Some(uuid) = entity_nbt.get_int_array("UUID") {
|
||||||
let _id = entity_nbt.get_string().ok();
|
if uuid.len() != 4 {
|
||||||
entity_nbt.get_uuid().ok()
|
debug!(
|
||||||
};
|
"Entity in chunk {},{} has invalid UUID array length {}: {:?}",
|
||||||
entity_nbt.read_pos = 0;
|
position.x,
|
||||||
|
position.y,
|
||||||
let Some(uuid) = uuid else {
|
uuid.len(),
|
||||||
|
entity_nbt
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Uuid::from_u128(
|
||||||
|
(uuid[0] as u128) << 96
|
||||||
|
| (uuid[1] as u128) << 64
|
||||||
|
| (uuid[2] as u128) << 32
|
||||||
|
| (uuid[3] as u128),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"Entity in chunk {},{} is missing UUID or ID: {:?}",
|
"Entity in chunk {},{} is missing UUID: {:?}",
|
||||||
position.x, position.y, entity_nbt
|
position.x, position.y, entity_nbt
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -319,8 +334,9 @@ impl ChunkEntityData {
|
|||||||
entities: self.data.lock().await.values().cloned().collect(),
|
entities: self.data.lock().await.values().cloned().collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result =
|
let mut result = Vec::new();
|
||||||
pumpkin_nbt::to_pnbt(&nbt).map_err(ChunkSerializingError::ErrorSerializingChunk)?;
|
pumpkin_nbt::to_bytes_unnamed(&nbt, &mut result)
|
||||||
|
.map_err(ChunkSerializingError::ErrorSerializingChunk)?;
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,7 +494,6 @@ struct ChunkNbt {
|
|||||||
light_correct: bool,
|
light_correct: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used ONLY for saving (to_bytes). Borrows data, zero clones.
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
struct ChunkNbtRef<'a> {
|
struct ChunkNbtRef<'a> {
|
||||||
@ -503,12 +518,10 @@ struct ChunkNbtRef<'a> {
|
|||||||
light_correct: bool,
|
light_correct: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
struct EntityNbt {
|
struct EntityNbt {
|
||||||
data_version: i32,
|
data_version: i32,
|
||||||
position: [i32; 2],
|
position: [i32; 2],
|
||||||
entities: Vec<PNbtCompound>,
|
entities: Vec<NbtCompound>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,8 +55,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn write(&self, backend: &Self::WriteBackend) -> Result<(), std::io::Error> {
|
async fn write(&self, backend: &Self::WriteBackend) -> Result<(), std::io::Error> {
|
||||||
let bytes =
|
let mut bytes = Vec::new();
|
||||||
pumpkin_nbt::to_pnbt(&self.data).map_err(|e| std::io::Error::other(e.to_string()))?;
|
pumpkin_nbt::to_bytes_unnamed(&self.data, &mut bytes)
|
||||||
|
.map_err(|e| std::io::Error::other(e.to_string()))?;
|
||||||
|
|
||||||
let compressed = compress_to_vec(&bytes[..], CompressionLevel::Fastest);
|
let compressed = compress_to_vec(&bytes[..], CompressionLevel::Fastest);
|
||||||
|
|
||||||
@ -70,11 +71,12 @@ where
|
|||||||
std::io::Read::read_to_end(&mut decoder, &mut decompressed)
|
std::io::Read::read_to_end(&mut decoder, &mut decompressed)
|
||||||
.map_err(|e| ChunkReadingError::IoError(e.kind()))?;
|
.map_err(|e| ChunkReadingError::IoError(e.kind()))?;
|
||||||
|
|
||||||
let data: PumpData = pumpkin_nbt::from_pnbt(&decompressed).map_err(|e| {
|
let data: PumpData = pumpkin_nbt::from_bytes_unnamed(std::io::Cursor::new(decompressed))
|
||||||
ChunkReadingError::ParsingError(
|
.map_err(|e| {
|
||||||
crate::chunk::ChunkParsingError::ErrorDeserializingChunk(e.to_string()),
|
ChunkReadingError::ParsingError(
|
||||||
)
|
crate::chunk::ChunkParsingError::ErrorDeserializingChunk(e.to_string()),
|
||||||
})?;
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
data,
|
data,
|
||||||
|
|||||||
@ -84,14 +84,12 @@ pub struct ChunkData {
|
|||||||
pub dirty: AtomicBool,
|
pub dirty: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
pub struct ChunkEntityData {
|
pub struct ChunkEntityData {
|
||||||
/// Chunk X
|
/// Chunk X
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
/// Chunk Z
|
/// Chunk Z
|
||||||
pub z: i32,
|
pub z: i32,
|
||||||
pub data: Mutex<FxHashMap<uuid::Uuid, PNbtCompound>>,
|
pub data: Mutex<FxHashMap<uuid::Uuid, NbtCompound>>,
|
||||||
|
|
||||||
pub dirty: AtomicBool,
|
pub dirty: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use std::fs::{File, create_dir_all};
|
use std::fs::{File, create_dir_all};
|
||||||
use std::io::{self, Read, Write};
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Manages the storage and retrieval of player data from disk and memory cache.
|
/// Manages the storage and retrieval of player data from disk and memory cache.
|
||||||
///
|
///
|
||||||
/// This struct provides functions to load and save player data to/from PNBT files.
|
/// This struct provides functions to load and save player data to/from NBT files,
|
||||||
|
/// with a memory cache to handle player disconnections temporarily.
|
||||||
pub struct PlayerDataStorage {
|
pub struct PlayerDataStorage {
|
||||||
/// Path to the directory where player data is stored
|
/// Path to the directory where player data is stored
|
||||||
data_path: PathBuf,
|
data_path: PathBuf,
|
||||||
@ -62,7 +63,10 @@ impl PlayerDataStorage {
|
|||||||
self.get_data_path().join(format!("{uuid}.dat"))
|
self.get_data_path().join(format!("{uuid}.dat"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads player data from PNBT file.
|
/// Loads player data from NBT file or cache.
|
||||||
|
///
|
||||||
|
/// This function first checks if player data exists in the cache.
|
||||||
|
/// If not, it attempts to load the data from a .dat file on disk.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@ -71,20 +75,20 @@ impl PlayerDataStorage {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A Result containing either the player's NBT data or an error.
|
/// A Result containing either the player's NBT data or an error.
|
||||||
pub fn load_player_data(&self, uuid: &Uuid) -> Result<(bool, PNbtCompound), PlayerDataError> {
|
pub fn load_player_data(&self, uuid: &Uuid) -> Result<(bool, NbtCompound), PlayerDataError> {
|
||||||
// If player data saving is disabled, return empty data
|
// If player data saving is disabled, return empty data
|
||||||
if !self.is_save_enabled() {
|
if !self.is_save_enabled() {
|
||||||
return Ok((false, PNbtCompound::new()));
|
return Ok((false, NbtCompound::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load from disk
|
// If not in cache, load from disk
|
||||||
let path = self.get_player_data_path(uuid);
|
let path = self.get_player_data_path(uuid);
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
debug!("No player data file found for {uuid}");
|
debug!("No player data file found for {uuid}");
|
||||||
return Ok((false, PNbtCompound::new()));
|
return Ok((false, NbtCompound::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file = match File::open(&path) {
|
let file = match File::open(&path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to open player data file for {uuid}: {e}");
|
error!("Failed to open player data file for {uuid}: {e}");
|
||||||
@ -92,14 +96,22 @@ impl PlayerDataStorage {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bytes = Vec::new();
|
match pumpkin_nbt::nbt_compress::read_gzip_compound_tag(file) {
|
||||||
file.read_to_end(&mut bytes)?;
|
Ok(nbt) => {
|
||||||
|
debug!("Loaded player data for {uuid} from disk");
|
||||||
debug!("Loaded player data for {uuid} from disk (PNBT Raw)");
|
Ok((true, nbt))
|
||||||
Ok((true, PNbtCompound::from_bytes(bytes)))
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to read player data for {uuid}: {e}");
|
||||||
|
Err(PlayerDataError::Nbt(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves player data to PNBT file.
|
/// Saves player data to NBT file and updates cache.
|
||||||
|
///
|
||||||
|
/// This function saves the player's data to a .dat file on disk and also
|
||||||
|
/// updates the in-memory cache with the latest data.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@ -109,7 +121,7 @@ impl PlayerDataStorage {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A Result indicating success or the error that occurred.
|
/// A Result indicating success or the error that occurred.
|
||||||
pub fn save_player_data(&self, uuid: &Uuid, data: PNbtCompound) -> Result<(), PlayerDataError> {
|
pub fn save_player_data(&self, uuid: &Uuid, data: NbtCompound) -> Result<(), PlayerDataError> {
|
||||||
// Skip saving if disabled in config
|
// Skip saving if disabled in config
|
||||||
if !self.is_save_enabled() {
|
if !self.is_save_enabled() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -125,16 +137,14 @@ impl PlayerDataStorage {
|
|||||||
return Err(PlayerDataError::Io(e));
|
return Err(PlayerDataError::Io(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = data.into_bytes();
|
// Create the file and write directly with GZip compression
|
||||||
|
|
||||||
// Create the file and write PNBT bytes
|
|
||||||
match File::create(&path) {
|
match File::create(&path) {
|
||||||
Ok(mut file) => {
|
Ok(file) => {
|
||||||
if let Err(e) = file.write_all(&bytes) {
|
if let Err(e) = pumpkin_nbt::nbt_compress::write_gzip_compound_tag(data, file) {
|
||||||
error!("Failed to write player data for {uuid}: {e}");
|
error!("Failed to write compressed player data for {uuid}: {e}");
|
||||||
Err(PlayerDataError::Io(e))
|
Err(PlayerDataError::Nbt(e.to_string()))
|
||||||
} else {
|
} else {
|
||||||
debug!("Saved player data for {uuid} to disk (PNBT Raw)");
|
debug!("Saved player data for {uuid} to disk");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{Read, Write},
|
io::{Cursor, Read},
|
||||||
path::Path,
|
path::Path,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
@ -21,7 +21,7 @@ pub const LEVEL_DAT_BACKUP_FILE_NAME: &str = "level.dat_old";
|
|||||||
|
|
||||||
pub struct AnvilLevelInfo;
|
pub struct AnvilLevelInfo;
|
||||||
|
|
||||||
fn check_file_data_version(raw_pnbt: &[u8]) -> Result<(), WorldInfoError> {
|
fn check_file_data_version(raw_nbt: &[u8]) -> Result<(), WorldInfoError> {
|
||||||
// Define a struct that only has the data version. This is necessary because if a user tries to
|
// Define a struct that only has the data version. This is necessary because if a user tries to
|
||||||
// load a world with different data, they will get a generic "Failed to deserialize level.dat error".
|
// load a world with different data, they will get a generic "Failed to deserialize level.dat error".
|
||||||
// When only checking for the data version, we can determine if we can support the full
|
// When only checking for the data version, we can determine if we can support the full
|
||||||
@ -37,10 +37,10 @@ fn check_file_data_version(raw_pnbt: &[u8]) -> Result<(), WorldInfoError> {
|
|||||||
data: LevelData,
|
data: LevelData,
|
||||||
}
|
}
|
||||||
|
|
||||||
let info: LevelDat = pumpkin_nbt::from_bytes(std::io::Cursor::new(raw_pnbt)).map_err(|e| {
|
let info: LevelDat = pumpkin_nbt::from_bytes(Cursor::new(raw_nbt))
|
||||||
error!("The level.dat file does not have a data version or is not a valid NBT file!");
|
.map_err(|e|{
|
||||||
WorldInfoError::DeserializationError(e.to_string())
|
error!("The level.dat file does not have a data version! This means it is either corrupt or very old (read unsupported)");
|
||||||
})?;
|
WorldInfoError::DeserializationError(e.to_string())})?;
|
||||||
|
|
||||||
let data_version = info.data.data_version;
|
let data_version = info.data.data_version;
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ fn check_file_data_version(raw_pnbt: &[u8]) -> Result<(), WorldInfoError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_file_level_version(raw_pnbt: &[u8]) -> Result<(), WorldInfoError> {
|
fn check_file_level_version(raw_nbt: &[u8]) -> Result<(), WorldInfoError> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct LevelData {
|
struct LevelData {
|
||||||
version: i32,
|
version: i32,
|
||||||
@ -64,10 +64,10 @@ fn check_file_level_version(raw_pnbt: &[u8]) -> Result<(), WorldInfoError> {
|
|||||||
data: LevelData,
|
data: LevelData,
|
||||||
}
|
}
|
||||||
|
|
||||||
let info: LevelDat = pumpkin_nbt::from_bytes(std::io::Cursor::new(raw_pnbt)).map_err(|e| {
|
let info: LevelDat = pumpkin_nbt::from_bytes(Cursor::new(raw_nbt))
|
||||||
error!("The level.dat file does not have a level version or is not a valid NBT file!");
|
.map_err(|e|{
|
||||||
WorldInfoError::DeserializationError(e.to_string())
|
error!("The level.dat file does not have a level version! This means it is either corrupt or very old (read unsupported)");
|
||||||
})?;
|
WorldInfoError::DeserializationError(e.to_string())})?;
|
||||||
|
|
||||||
let level_version = info.data.version;
|
let level_version = info.data.version;
|
||||||
|
|
||||||
@ -89,9 +89,11 @@ impl WorldInfoReader for AnvilLevelInfo {
|
|||||||
|
|
||||||
check_file_data_version(&buf)?;
|
check_file_data_version(&buf)?;
|
||||||
check_file_level_version(&buf)?;
|
check_file_level_version(&buf)?;
|
||||||
let info = pumpkin_nbt::from_bytes::<LevelDat>(std::io::Cursor::new(&buf))
|
let info = pumpkin_nbt::from_bytes::<LevelDat>(Cursor::new(buf))
|
||||||
.map_err(|e| WorldInfoError::DeserializationError(e.to_string()))?;
|
.map_err(|e| WorldInfoError::DeserializationError(e.to_string()))?;
|
||||||
|
|
||||||
|
// TODO: check version
|
||||||
|
|
||||||
Ok(info.data)
|
Ok(info.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,14 +116,11 @@ impl WorldInfoWriter for AnvilLevelInfo {
|
|||||||
let path = level_folder.join(LEVEL_DAT_FILE_NAME);
|
let path = level_folder.join(LEVEL_DAT_FILE_NAME);
|
||||||
let world_info_file = File::create(path)?;
|
let world_info_file = File::create(path)?;
|
||||||
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
pumpkin_nbt::to_bytes(&level, &mut bytes)
|
|
||||||
.map_err(|e| WorldInfoError::DeserializationError(e.to_string()))?;
|
|
||||||
|
|
||||||
// write compressed data into file
|
// write compressed data into file
|
||||||
let mut encoder = GzEncoder::new(world_info_file, Compression::best());
|
let compression_writer = GzEncoder::new(world_info_file, Compression::best());
|
||||||
encoder.write_all(&bytes)?;
|
// TODO: Proper error handling
|
||||||
encoder.finish()?;
|
pumpkin_nbt::to_bytes(&level, compression_writer)
|
||||||
|
.expect("Failed to write level.dat to disk");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,16 +135,21 @@ pub struct LevelDat {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use std::{fs, sync::LazyLock};
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::{Cursor, Read},
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use flate2::read::GzDecoder;
|
||||||
use pumpkin_data::game_rules::GameRuleRegistry;
|
use pumpkin_data::game_rules::GameRuleRegistry;
|
||||||
use pumpkin_nbt::{from_bytes_unnamed, to_bytes_unnamed};
|
use pumpkin_nbt::{deserializer::from_bytes, serializer::to_bytes};
|
||||||
use pumpkin_util::{Difficulty, world_seed::Seed};
|
use pumpkin_util::{Difficulty, world_seed::Seed};
|
||||||
use temp_dir::TempDir;
|
use temp_dir::TempDir;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
global_path,
|
global_path,
|
||||||
world_info::{DataPacks, LevelData, WorldGenSettings, WorldVersion},
|
world_info::{DataPacks, LevelData, WorldGenSettings, WorldInfoError, WorldVersion},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AnvilLevelInfo, LEVEL_DAT_FILE_NAME, LevelDat, WorldInfoReader, WorldInfoWriter};
|
use super::{AnvilLevelInfo, LEVEL_DAT_FILE_NAME, LevelDat, WorldInfoReader, WorldInfoWriter};
|
||||||
@ -243,10 +247,13 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_level_dat() {
|
fn deserialize_level_dat() {
|
||||||
let mut bytes = Vec::new();
|
let raw_compressed_nbt = fs::read("assets/level_1_21_4.dat").unwrap();
|
||||||
to_bytes_unnamed(&*LEVEL_DAT, &mut bytes).unwrap();
|
assert!(!raw_compressed_nbt.is_empty());
|
||||||
let level_dat: LevelDat =
|
|
||||||
from_bytes_unnamed(std::io::Cursor::new(bytes)).expect("Failed to decode NBT");
|
let mut decoder = GzDecoder::new(&raw_compressed_nbt[..]);
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
decoder.read_to_end(&mut buf).unwrap();
|
||||||
|
let level_dat: LevelDat = from_bytes(Cursor::new(buf)).expect("Failed to decode from file");
|
||||||
|
|
||||||
assert_eq!(level_dat, *LEVEL_DAT);
|
assert_eq!(level_dat, *LEVEL_DAT);
|
||||||
}
|
}
|
||||||
@ -254,12 +261,12 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn serialize_level_dat() {
|
fn serialize_level_dat() {
|
||||||
let mut serialized = Vec::new();
|
let mut serialized = Vec::new();
|
||||||
to_bytes_unnamed(&*LEVEL_DAT, &mut serialized).expect("Failed to encode to NBT");
|
to_bytes(&*LEVEL_DAT, &mut serialized).expect("Failed to encode to bytes");
|
||||||
|
|
||||||
assert!(!serialized.is_empty());
|
assert!(!serialized.is_empty());
|
||||||
|
|
||||||
let level_dat_again: LevelDat = from_bytes_unnamed(std::io::Cursor::new(&serialized))
|
let level_dat_again: LevelDat =
|
||||||
.expect("Failed to decode from NBT");
|
from_bytes(Cursor::new(serialized)).expect("Failed to decode from bytes");
|
||||||
|
|
||||||
assert_eq!(level_dat_again, *LEVEL_DAT);
|
assert_eq!(level_dat_again, *LEVEL_DAT);
|
||||||
}
|
}
|
||||||
@ -276,6 +283,10 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let result = AnvilLevelInfo.read_world_info(temp_dir.path());
|
let result = AnvilLevelInfo.read_world_info(temp_dir.path());
|
||||||
assert!(result.is_err());
|
match result {
|
||||||
|
Ok(_) => panic!("This should fail!"),
|
||||||
|
Err(WorldInfoError::UnsupportedDataVersion(_)) => {}
|
||||||
|
Err(_) => panic!("Wrong error!"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::command::{
|
|||||||
use crate::entity::NBTStorage;
|
use crate::entity::NBTStorage;
|
||||||
use CommandError::InvalidConsumption;
|
use CommandError::InvalidConsumption;
|
||||||
use pumpkin_data::translation;
|
use pumpkin_data::translation;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_nbt::tag::NbtTag;
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
use pumpkin_util::text::color::NamedColor;
|
use pumpkin_util::text::color::NamedColor;
|
||||||
@ -43,7 +43,6 @@ impl CommandExecutor for GetEntityDataExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn snbt_colorful_display(tag: &NbtTag, depth: usize) -> Result<TextComponent, String> {
|
pub fn snbt_colorful_display(tag: &NbtTag, depth: usize) -> Result<TextComponent, String> {
|
||||||
let folded = TextComponent::text("<...>").color_named(NamedColor::Gray);
|
let folded = TextComponent::text("<...>").color_named(NamedColor::Gray);
|
||||||
match tag {
|
match tag {
|
||||||
@ -219,18 +218,19 @@ pub fn snbt_colorful_display(tag: &NbtTag, depth: usize) -> Result<TextComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn display_data(
|
async fn display_data(
|
||||||
storage: &dyn NBTStorage,
|
storage: &dyn NBTStorage,
|
||||||
target_name: TextComponent,
|
target_name: TextComponent,
|
||||||
sender: &CommandSender,
|
sender: &CommandSender,
|
||||||
) -> Result<i32, CommandError> {
|
) -> Result<i32, CommandError> {
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
storage.write_nbt(&mut nbt).await;
|
storage.write_nbt(&mut nbt).await;
|
||||||
|
let tag = NbtTag::Compound(nbt);
|
||||||
|
|
||||||
// PNBT is positional and doesn't directly map to SNBT.
|
let result = get_i32_result(&tag)?;
|
||||||
// For now, we'll just show the length of the data.
|
let display = snbt_colorful_display(&tag, 0)
|
||||||
let display = TextComponent::text(format!("PNBT Data ({} bytes)", nbt.data.len()));
|
.map_err(|string| CommandError::CommandFailed(TextComponent::text(string)))?;
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::translate_cross(
|
.send_message(TextComponent::translate_cross(
|
||||||
translation::java::COMMANDS_DATA_ENTITY_QUERY,
|
translation::java::COMMANDS_DATA_ENTITY_QUERY,
|
||||||
@ -239,10 +239,9 @@ async fn display_data(
|
|||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(1)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_i32_result(tag: &NbtTag) -> Result<i32, CommandError> {
|
fn get_i32_result(tag: &NbtTag) -> Result<i32, CommandError> {
|
||||||
match tag {
|
match tag {
|
||||||
NbtTag::End => Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
NbtTag::End => Err(CommandError::CommandFailed(TextComponent::translate_cross(
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use pumpkin_inventory::screen_handler::ScreenHandler;
|
use pumpkin_inventory::screen_handler::ScreenHandler;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_world::data::player_data::{PlayerDataError, PlayerDataStorage};
|
use pumpkin_world::data::player_data::{PlayerDataError, PlayerDataStorage};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
@ -53,7 +53,7 @@ impl ServerPlayerData {
|
|||||||
.await;
|
.await;
|
||||||
player.on_handled_screen_closed().await;
|
player.on_handled_screen_closed().await;
|
||||||
|
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
player.write_nbt(&mut nbt).await;
|
player.write_nbt(&mut nbt).await;
|
||||||
|
|
||||||
let storage = self.storage.clone();
|
let storage = self.storage.clone();
|
||||||
@ -82,7 +82,7 @@ impl ServerPlayerData {
|
|||||||
// Save all online players periodically across all worlds
|
// Save all online players periodically across all worlds
|
||||||
for world in server.worlds.load().iter() {
|
for world in server.worlds.load().iter() {
|
||||||
for player in world.players.load().iter() {
|
for player in world.players.load().iter() {
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
player.write_nbt(&mut nbt).await;
|
player.write_nbt(&mut nbt).await;
|
||||||
|
|
||||||
let storage = self.storage.clone();
|
let storage = self.storage.clone();
|
||||||
@ -141,7 +141,7 @@ impl ServerPlayerData {
|
|||||||
pub async fn load_data(
|
pub async fn load_data(
|
||||||
&self,
|
&self,
|
||||||
uuid: &uuid::Uuid,
|
uuid: &uuid::Uuid,
|
||||||
) -> Result<Option<PNbtCompound>, PlayerDataError> {
|
) -> Result<Option<NbtCompound>, PlayerDataError> {
|
||||||
let storage = self.storage.clone();
|
let storage = self.storage.clone();
|
||||||
let uuid = *uuid;
|
let uuid = *uuid;
|
||||||
let result = tokio::task::spawn_blocking(move || storage.load_player_data(&uuid))
|
let result = tokio::task::spawn_blocking(move || storage.load_player_data(&uuid))
|
||||||
@ -190,7 +190,7 @@ impl ServerPlayerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uuid = player.gameprofile.id;
|
let uuid = player.gameprofile.id;
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
player.write_nbt(&mut nbt).await;
|
player.write_nbt(&mut nbt).await;
|
||||||
|
|
||||||
let storage = self.storage.clone();
|
let storage = self.storage.clone();
|
||||||
@ -205,7 +205,7 @@ impl ServerPlayerData {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::data::player_server::ServerPlayerData;
|
use crate::data::player_server::ServerPlayerData;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_world::data::player_data::PlayerDataStorage;
|
use pumpkin_world::data::player_data::PlayerDataStorage;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
@ -247,19 +247,19 @@ mod test {
|
|||||||
let uuid = Uuid::new_v4();
|
let uuid = Uuid::new_v4();
|
||||||
|
|
||||||
// Create test data
|
// Create test data
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
nbt.put_string("TestValue");
|
nbt.put_string("TestKey", "TestValue".to_string());
|
||||||
nbt.put_i32(42);
|
nbt.put_int("TestInt", 42);
|
||||||
|
|
||||||
// Save the data
|
// Save the data
|
||||||
storage.save_player_data(&uuid, nbt).unwrap();
|
storage.save_player_data(&uuid, nbt).unwrap();
|
||||||
|
|
||||||
// Load the data
|
// Load the data
|
||||||
let (load_success, mut loaded_nbt) = storage.load_player_data(&uuid).unwrap();
|
let (load_success, loaded_nbt) = storage.load_player_data(&uuid).unwrap();
|
||||||
|
|
||||||
assert!(load_success);
|
assert!(load_success);
|
||||||
assert_eq!(loaded_nbt.get_string().unwrap(), "TestValue");
|
assert_eq!(loaded_nbt.get_string("TestKey").unwrap(), "TestValue");
|
||||||
assert_eq!(loaded_nbt.get_i32().unwrap(), 42);
|
assert_eq!(loaded_nbt.get_int("TestInt").unwrap(), 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -275,7 +275,7 @@ mod test {
|
|||||||
let (load_success, empty_nbt) = storage.load_player_data(&uuid).unwrap();
|
let (load_success, empty_nbt) = storage.load_player_data(&uuid).unwrap();
|
||||||
|
|
||||||
assert!(!load_success);
|
assert!(!load_success);
|
||||||
assert_eq!(empty_nbt.as_bytes().len(), 0);
|
assert_eq!(empty_nbt.child_tags.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -286,8 +286,8 @@ mod test {
|
|||||||
let storage = PlayerDataStorage::new(path, false);
|
let storage = PlayerDataStorage::new(path, false);
|
||||||
|
|
||||||
let uuid = Uuid::new_v4();
|
let uuid = Uuid::new_v4();
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
nbt.put_string("TestValue");
|
nbt.put_string("TestKey", "TestValue".to_string());
|
||||||
|
|
||||||
// Save should succeed but do nothing
|
// Save should succeed but do nothing
|
||||||
let save_result = storage.save_player_data(&uuid, nbt);
|
let save_result = storage.save_player_data(&uuid, nbt);
|
||||||
@ -296,7 +296,7 @@ mod test {
|
|||||||
// Load should return empty data
|
// Load should return empty data
|
||||||
let (load_success, empty_nbt) = storage.load_player_data(&uuid).unwrap();
|
let (load_success, empty_nbt) = storage.load_player_data(&uuid).unwrap();
|
||||||
assert!(!load_success);
|
assert!(!load_success);
|
||||||
assert_eq!(empty_nbt.as_bytes().len(), 0);
|
assert_eq!(empty_nbt.child_tags.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -322,9 +322,9 @@ mod test {
|
|||||||
let storage = PlayerDataStorage::new(path, true);
|
let storage = PlayerDataStorage::new(path, true);
|
||||||
|
|
||||||
// Create and save player data
|
// Create and save player data
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
nbt.put_string("TestPlayer");
|
nbt.put_string("name", "TestPlayer".to_string());
|
||||||
nbt.put_i32(42);
|
nbt.put_int("level", 42);
|
||||||
storage.save_player_data(&uuid, nbt).unwrap();
|
storage.save_player_data(&uuid, nbt).unwrap();
|
||||||
|
|
||||||
// Verify the file exists
|
// Verify the file exists
|
||||||
@ -332,9 +332,9 @@ mod test {
|
|||||||
assert!(player_data_path.exists());
|
assert!(player_data_path.exists());
|
||||||
|
|
||||||
// Load it again and verify content
|
// Load it again and verify content
|
||||||
let (success, mut loaded_data) = storage.load_player_data(&uuid).unwrap();
|
let (success, loaded_data) = storage.load_player_data(&uuid).unwrap();
|
||||||
assert!(success);
|
assert!(success);
|
||||||
assert_eq!(loaded_data.get_string().unwrap(), "TestPlayer");
|
assert_eq!(loaded_data.get_string("name").unwrap(), "TestPlayer");
|
||||||
assert_eq!(loaded_data.get_i32().unwrap(), 42);
|
assert_eq!(loaded_data.get_int("level").unwrap(), 42);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -236,116 +236,74 @@ impl ArmorStandEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for ArmorStandEntity {
|
impl NBTStorage for ArmorStandEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.living_entity.write_nbt(nbt).await;
|
self.living_entity.write_nbt(nbt).await;
|
||||||
let disabled_slots = self.disabled_slots.load(Ordering::Relaxed);
|
let disabled_slots = self.disabled_slots.load(Ordering::Relaxed);
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
nbt.put_bool(self.is_invisible());
|
nbt.put_bool("Invisible", self.is_invisible());
|
||||||
nbt.put_bool(self.is_small());
|
nbt.put_bool("Small", self.is_small());
|
||||||
nbt.put_bool(self.should_show_arms());
|
nbt.put_bool("ShowArms", self.should_show_arms());
|
||||||
nbt.put_int(disabled_slots);
|
nbt.put_int("DisabledSlots", disabled_slots);
|
||||||
nbt.put_bool(!self.should_show_base_plate());
|
nbt.put_bool("NoBasePlate", !self.should_show_base_plate());
|
||||||
nbt.put_bool(self.is_marker());
|
if self.is_marker() {
|
||||||
|
nbt.put_bool("Marker", true);
|
||||||
|
}
|
||||||
|
|
||||||
let pose = self.pack_rotation();
|
nbt.put("Pose", self.pack_rotation());
|
||||||
nbt.put_f32(pose.head.pitch);
|
|
||||||
nbt.put_f32(pose.head.yaw);
|
|
||||||
nbt.put_f32(pose.head.roll);
|
|
||||||
nbt.put_f32(pose.body.pitch);
|
|
||||||
nbt.put_f32(pose.body.yaw);
|
|
||||||
nbt.put_f32(pose.body.roll);
|
|
||||||
nbt.put_f32(pose.left_arm.pitch);
|
|
||||||
nbt.put_f32(pose.left_arm.yaw);
|
|
||||||
nbt.put_f32(pose.left_arm.roll);
|
|
||||||
nbt.put_f32(pose.right_arm.pitch);
|
|
||||||
nbt.put_f32(pose.right_arm.yaw);
|
|
||||||
nbt.put_f32(pose.right_arm.roll);
|
|
||||||
nbt.put_f32(pose.left_leg.pitch);
|
|
||||||
nbt.put_f32(pose.left_leg.yaw);
|
|
||||||
nbt.put_f32(pose.left_leg.roll);
|
|
||||||
nbt.put_f32(pose.right_leg.pitch);
|
|
||||||
nbt.put_f32(pose.right_leg.yaw);
|
|
||||||
nbt.put_f32(pose.right_leg.roll);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.living_entity.read_nbt_non_mut(nbt).await;
|
self.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
let mut flags = 0u8;
|
let mut flags = 0u8;
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
let invisible = nbt.get_bool().unwrap_or(false);
|
if let Some(invisible) = nbt.get_bool("Invisible")
|
||||||
if invisible {
|
&& invisible
|
||||||
|
{
|
||||||
self.get_entity().set_invisible(invisible).await;
|
self.get_entity().set_invisible(invisible).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if nbt.get_bool().unwrap_or(false) {
|
if let Some(small) = nbt.get_bool("Small")
|
||||||
|
&& small
|
||||||
|
{
|
||||||
flags |= ArmorStandFlags::Small as u8;
|
flags |= ArmorStandFlags::Small as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if nbt.get_bool().unwrap_or(false) {
|
if let Some(show_arms) = nbt.get_bool("ShowArms")
|
||||||
|
&& show_arms
|
||||||
|
{
|
||||||
flags |= ArmorStandFlags::ShowArms as u8;
|
flags |= ArmorStandFlags::ShowArms as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
let disabled_slots = nbt.get_int().unwrap_or(0);
|
if let Some(disabled_slots) = nbt.get_int("DisabledSlots") {
|
||||||
self.disabled_slots.store(disabled_slots, Ordering::Relaxed);
|
self.disabled_slots.store(disabled_slots, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
let no_base_plate = nbt.get_bool().unwrap_or(false);
|
if let Some(no_base_plate) = nbt.get_bool("NoBasePlate") {
|
||||||
if !no_base_plate {
|
if !no_base_plate {
|
||||||
|
flags |= ArmorStandFlags::HideBasePlate as u8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
flags |= ArmorStandFlags::HideBasePlate as u8;
|
flags |= ArmorStandFlags::HideBasePlate as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if nbt.get_bool().unwrap_or(false) {
|
if let Some(marker) = nbt.get_bool("Marker")
|
||||||
|
&& marker
|
||||||
|
{
|
||||||
flags |= ArmorStandFlags::Marker as u8;
|
flags |= ArmorStandFlags::Marker as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.armor_stand_flags.store(flags, Ordering::Relaxed);
|
self.armor_stand_flags.store(flags, Ordering::Relaxed);
|
||||||
|
|
||||||
let head = EulerAngle::new(
|
if let Some(pose_tag) = nbt.get("Pose") {
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
let packed: PackedRotation = pose_tag.clone().into();
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
self.unpack_rotation(&packed);
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
}
|
||||||
);
|
|
||||||
let body = EulerAngle::new(
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
);
|
|
||||||
let left_arm = EulerAngle::new(
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
);
|
|
||||||
let right_arm = EulerAngle::new(
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
);
|
|
||||||
let left_leg = EulerAngle::new(
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
);
|
|
||||||
let right_leg = EulerAngle::new(
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
nbt.get_f32().unwrap_or(0.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.unpack_rotation(&PackedRotation {
|
|
||||||
head,
|
|
||||||
body,
|
|
||||||
left_arm,
|
|
||||||
right_arm,
|
|
||||||
left_leg,
|
|
||||||
right_leg,
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use crate::entity::{
|
|||||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, living::LivingEntity,
|
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, living::LivingEntity,
|
||||||
};
|
};
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
pub struct PaintingEntity {
|
pub struct PaintingEntity {
|
||||||
@ -17,20 +18,18 @@ impl PaintingEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for PaintingEntity {
|
impl NBTStorage for PaintingEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.entity.write_nbt(nbt).await;
|
self.entity.write_nbt(nbt).await;
|
||||||
nbt.put_byte(self.entity.data.load(Ordering::Relaxed) as i8);
|
nbt.put_byte("facing", self.entity.data.load(Ordering::Relaxed) as i8);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.entity.read_nbt_non_mut(nbt).await;
|
self.entity.read_nbt_non_mut(nbt).await;
|
||||||
let facing = nbt.get_byte().unwrap_or(3);
|
let facing = nbt.get_byte("facing").unwrap_or(3);
|
||||||
self.entity.data.store(facing as i32, Ordering::Relaxed);
|
self.entity.data.store(facing as i32, Ordering::Relaxed);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,52 @@
|
|||||||
use crate::entity::{NBTInitFuture, NBTStorage, NBTStorageInit, NbtFuture};
|
use crate::entity::{NBTInitFuture, NBTStorage, NBTStorageInit, NbtFuture};
|
||||||
use pumpkin_data::effect::StatusEffect;
|
use pumpkin_data::effect::StatusEffect;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
impl NBTStorage for pumpkin_data::potion::Effect {
|
impl NBTStorage for pumpkin_data::potion::Effect {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
nbt.put_string(self.effect_type.minecraft_name);
|
nbt.put("id", self.effect_type.minecraft_name);
|
||||||
nbt.put_u8(self.amplifier);
|
if self.amplifier > 0 {
|
||||||
nbt.put_int(self.duration);
|
nbt.put("amplifier", NbtTag::Int(i32::from(self.amplifier)));
|
||||||
nbt.put_bool(self.ambient);
|
}
|
||||||
nbt.put_bool(self.show_particles);
|
nbt.put("duration", NbtTag::Int(self.duration));
|
||||||
nbt.put_bool(self.show_icon);
|
if self.ambient {
|
||||||
|
nbt.put("ambient", NbtTag::Byte(1));
|
||||||
|
}
|
||||||
|
if !self.show_particles {
|
||||||
|
nbt.put("show_particles", NbtTag::Byte(0));
|
||||||
|
}
|
||||||
|
let show_icon: i8 = i8::from(self.show_icon);
|
||||||
|
nbt.put("show_icon", NbtTag::Byte(show_icon));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorageInit for pumpkin_data::potion::Effect {
|
impl NBTStorageInit for pumpkin_data::potion::Effect {
|
||||||
fn create_from_nbt<'a>(nbt: &'a mut PNbtCompound) -> NBTInitFuture<'a, Self>
|
fn create_from_nbt<'a>(nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self>
|
||||||
where
|
where
|
||||||
Self: 'a,
|
Self: 'a,
|
||||||
{
|
{
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let effect_id = nbt.get_string().ok()?;
|
let Some(effect_id) = nbt.get_string("id") else {
|
||||||
let effect_type = StatusEffect::from_minecraft_name(&effect_id).or_else(|| {
|
warn!("Unable to read effect. Effect id is not present");
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let Some(effect_type) = StatusEffect::from_minecraft_name(effect_id) else {
|
||||||
warn!("Unable to read effect. Unknown effect type: {effect_id}");
|
warn!("Unable to read effect. Unknown effect type: {effect_id}");
|
||||||
None
|
return None;
|
||||||
})?;
|
};
|
||||||
let amplifier = nbt.get_u8().unwrap_or(0);
|
let Some(show_icon) = nbt.get_byte("show_icon") else {
|
||||||
let duration = nbt.get_int().unwrap_or(0);
|
warn!("Unable to read effect. Show icon is not present");
|
||||||
let ambient = nbt.get_bool().unwrap_or(false);
|
return None;
|
||||||
let show_particles = nbt.get_bool().unwrap_or(true);
|
};
|
||||||
let show_icon = nbt.get_bool().unwrap_or(true);
|
let amplifier = nbt.get_int("amplifier").unwrap_or(0) as u8;
|
||||||
|
let duration = nbt.get_int("duration").unwrap_or(0);
|
||||||
|
let ambient = nbt.get_byte("ambient").unwrap_or(0) == 1;
|
||||||
|
let show_particles = nbt.get_byte("show_particles").unwrap_or(1) == 1;
|
||||||
|
let show_icon = show_icon == 1;
|
||||||
Some(Self {
|
Some(Self {
|
||||||
effect_type,
|
effect_type,
|
||||||
duration,
|
duration,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use super::{EntityBase, NBTStorage, NBTStorageInit, player::Player};
|
|||||||
use crate::entity::NbtFuture;
|
use crate::entity::NbtFuture;
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::Difficulty;
|
use pumpkin_util::Difficulty;
|
||||||
|
|
||||||
const MAX_FOOD: u8 = 20;
|
const MAX_FOOD: u8 = 20;
|
||||||
@ -174,24 +175,26 @@ impl HungerManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for HungerManager {
|
impl NBTStorage for HungerManager {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
nbt.put_int(self.level.load().into());
|
nbt.put_int("foodLevel", self.level.load().into());
|
||||||
nbt.put_float(self.saturation.load());
|
nbt.put_float("foodSaturationLevel", self.saturation.load());
|
||||||
nbt.put_float(self.exhaustion.load());
|
nbt.put_float("foodExhaustionLevel", self.exhaustion.load());
|
||||||
nbt.put_int(self.tick_timer.load() as i32);
|
nbt.put_int("foodTickTimer", self.tick_timer.load() as i32);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt<'a>(&'a mut self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.level.store(nbt.get_int().unwrap_or(20) as u8);
|
self.level
|
||||||
self.saturation.store(nbt.get_float().unwrap_or(5.0));
|
.store(nbt.get_int("foodLevel").unwrap_or(20) as u8);
|
||||||
self.exhaustion.store(nbt.get_float().unwrap_or(0.0));
|
self.saturation
|
||||||
self.tick_timer.store(nbt.get_int().unwrap_or(0) as u32);
|
.store(nbt.get_float("foodSaturationLevel").unwrap_or(5.0));
|
||||||
|
self.exhaustion
|
||||||
|
.store(nbt.get_float("foodExhaustionLevel").unwrap_or(0.0));
|
||||||
|
self.tick_timer
|
||||||
|
.store(nbt.get_int("foodTickTimer").unwrap_or(0) as u32);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,8 @@ use pumpkin_data::sound::SoundCategory;
|
|||||||
use pumpkin_data::{Block, translation};
|
use pumpkin_data::{Block, translation};
|
||||||
use pumpkin_data::{damage::DamageType, sound::Sound};
|
use pumpkin_data::{damage::DamageType, sound::Sound};
|
||||||
use pumpkin_inventory::entity_equipment::EntityEquipment;
|
use pumpkin_inventory::entity_equipment::EntityEquipment;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use pumpkin_protocol::codec::var_int::VarInt;
|
use pumpkin_protocol::codec::var_int::VarInt;
|
||||||
use pumpkin_protocol::java::client::play::{
|
use pumpkin_protocol::java::client::play::{
|
||||||
Animation, CEntityAnimation, CHurtAnimation, CSetPlayerInventory, CTakeItemEntity,
|
Animation, CEntityAnimation, CHurtAnimation, CSetPlayerInventory, CTakeItemEntity,
|
||||||
@ -1780,10 +1781,10 @@ impl LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for LivingEntity {
|
impl NBTStorage for LivingEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.entity.write_nbt(nbt).await;
|
self.entity.write_nbt(nbt).await;
|
||||||
nbt.put_float(self.health.load());
|
nbt.put("Health", NbtTag::Float(self.health.load()));
|
||||||
// Avoid persisting a lethal fall distance when the entity is dead to prevent death loops
|
// Avoid persisting a lethal fall distance when the entity is dead to prevent death loops
|
||||||
let fall_distance = if self.dead.load(Relaxed) {
|
let fall_distance = if self.dead.load(Relaxed) {
|
||||||
0.0
|
0.0
|
||||||
@ -1791,13 +1792,19 @@ impl NBTStorage for LivingEntity {
|
|||||||
self.fall_distance.load()
|
self.fall_distance.load()
|
||||||
};
|
};
|
||||||
// Persist current absorption amount
|
// Persist current absorption amount
|
||||||
nbt.put_float(self.absorption.load());
|
nbt.put("AbsorptionAmount", NbtTag::Float(self.absorption.load()));
|
||||||
nbt.put_float(fall_distance);
|
nbt.put("fall_distance", NbtTag::Float(fall_distance));
|
||||||
{
|
{
|
||||||
let effects = self.active_effects.lock().await;
|
let effects = self.active_effects.lock().await;
|
||||||
nbt.put_u32(effects.len() as u32);
|
if !effects.is_empty() {
|
||||||
for effect in effects.values() {
|
// Iterate effects and create Box<[NbtTag]>
|
||||||
effect.write_nbt(nbt).await;
|
let mut effects_list = Vec::with_capacity(effects.len());
|
||||||
|
for effect in effects.values() {
|
||||||
|
let mut effect_nbt = pumpkin_nbt::compound::NbtCompound::new();
|
||||||
|
effect.write_nbt(&mut effect_nbt).await;
|
||||||
|
effects_list.push(NbtTag::Compound(effect_nbt));
|
||||||
|
}
|
||||||
|
nbt.put("active_effects", NbtTag::List(effects_list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO: write equipment
|
//TODO: write equipment
|
||||||
@ -1805,20 +1812,20 @@ impl NBTStorage for LivingEntity {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.entity.read_nbt_non_mut(nbt).await;
|
self.entity.read_nbt_non_mut(nbt).await;
|
||||||
self.health.store(nbt.get_float().unwrap_or(0.0));
|
self.health.store(nbt.get_float("Health").unwrap_or(0.0));
|
||||||
|
|
||||||
// Clamp any persisted absorption to the entity's configured max
|
// Clamp any persisted absorption to the entity's configured max
|
||||||
let raw_abs = nbt.get_float().unwrap_or(0.0);
|
let raw_abs = nbt.get_float("AbsorptionAmount").unwrap_or(0.0);
|
||||||
let max_abs = self.get_attribute_value(&Attributes::MAX_ABSORPTION) as f32;
|
let max_abs = self.get_attribute_value(&Attributes::MAX_ABSORPTION) as f32;
|
||||||
let clamped_abs = raw_abs.max(0.0).min(max_abs);
|
let clamped_abs = raw_abs.max(0.0).min(max_abs);
|
||||||
self.absorption.store(clamped_abs);
|
self.absorption.store(clamped_abs);
|
||||||
|
|
||||||
// Load fall distance, but if this entity is currently marked dead ensure we don't restore
|
// Load fall distance, but if this entity is currently marked dead ensure we don't restore
|
||||||
// a lethal fall distance that would immediately re-kill on spawn.
|
// a lethal fall distance that would immediately re-kill on spawn.
|
||||||
let fd = nbt.get_float().unwrap_or(0.0);
|
let fd = nbt.get_float("fall_distance").unwrap_or(0.0);
|
||||||
if self.dead.load(Relaxed) {
|
if self.dead.load(Relaxed) {
|
||||||
self.fall_distance.store(0.0);
|
self.fall_distance.store(0.0);
|
||||||
} else {
|
} else {
|
||||||
@ -1826,16 +1833,20 @@ impl NBTStorage for LivingEntity {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut active_effects = self.active_effects.lock().await;
|
let mut active_effects = self.active_effects.lock().await;
|
||||||
let effects_len = nbt.get_u32().unwrap_or(0);
|
let nbt_effects = nbt.get_list("active_effects");
|
||||||
for _ in 0..effects_len {
|
if let Some(nbt_effects) = nbt_effects {
|
||||||
let effect = Effect::create_from_nbt(nbt).await;
|
for effect in nbt_effects {
|
||||||
if effect.is_none() {
|
if let NbtTag::Compound(effect_nbt) = effect {
|
||||||
warn!("Unable to read effect from nbt");
|
let effect = Effect::create_from_nbt(&mut effect_nbt.clone()).await;
|
||||||
continue;
|
if effect.is_none() {
|
||||||
|
warn!("Unable to read effect from nbt");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut effect = effect.unwrap();
|
||||||
|
effect.blend = true; // TODO: change, is taken from effect give command
|
||||||
|
active_effects.insert(effect.effect_type, effect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut effect = effect.unwrap();
|
|
||||||
effect.blend = true; // TODO: change, is taken from effect give command
|
|
||||||
active_effects.insert(effect.effect_type, effect);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::sync::atomic::{AtomicBool, AtomicI32, Ordering::Relaxed};
|
|||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
use pumpkin_data::sound::Sound;
|
use pumpkin_data::sound::Sound;
|
||||||
use pumpkin_data::tag::{self, Taggable};
|
use pumpkin_data::tag::{self, Taggable};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use pumpkin_world::chunk::ChunkHeightmapType;
|
use pumpkin_world::chunk::ChunkHeightmapType;
|
||||||
@ -92,21 +93,19 @@ impl BatEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for BatEntity {
|
impl NBTStorage for BatEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 };
|
let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 };
|
||||||
nbt.put_byte(flags as i8);
|
nbt.put_byte("BatFlags", flags as i8);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
let flags = nbt.get_byte().unwrap_or(0) as u8;
|
let flags = nbt.get_byte("BatFlags").unwrap_or(0) as u8;
|
||||||
let roosting = (flags & ROOSTING_FLAG) != 0;
|
let roosting = (flags & ROOSTING_FLAG) != 0;
|
||||||
self.set_roosting(roosting).await;
|
self.set_roosting(roosting).await;
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use pumpkin_data::{
|
|||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
tracked_data::TrackedData,
|
tracked_data::TrackedData,
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata};
|
use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata};
|
||||||
|
|
||||||
use crate::entity::{
|
use crate::entity::{
|
||||||
@ -116,30 +117,36 @@ impl CreeperEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for CreeperEntity {
|
impl NBTStorage for CreeperEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_bool(self.charged.load(Ordering::Relaxed));
|
nbt.put_bool("powered", self.charged.load(Ordering::Relaxed));
|
||||||
nbt.put_short(self.fuse_time.load(Ordering::Relaxed) as i16);
|
nbt.put_short("Fuse", self.fuse_time.load(Ordering::Relaxed) as i16);
|
||||||
nbt.put_byte(self.explosion_radius.load(Ordering::Relaxed) as i8);
|
nbt.put_byte(
|
||||||
nbt.put_bool(self.ignited.load(Ordering::Relaxed));
|
"ExplosionRadius",
|
||||||
|
self.explosion_radius.load(Ordering::Relaxed) as i8,
|
||||||
|
);
|
||||||
|
nbt.put_bool("ignited", self.ignited.load(Ordering::Relaxed));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
self.charged
|
if let Some(powered) = nbt.get_bool("powered") {
|
||||||
.store(nbt.get_bool().unwrap_or(false), Ordering::Relaxed);
|
self.charged.store(powered, Ordering::Relaxed);
|
||||||
self.fuse_time
|
}
|
||||||
.store(i32::from(nbt.get_short().unwrap_or(30)), Ordering::Relaxed);
|
if let Some(fuse) = nbt.get_short("Fuse") {
|
||||||
self.explosion_radius
|
self.fuse_time.store(i32::from(fuse), Ordering::Relaxed);
|
||||||
.store(i32::from(nbt.get_byte().unwrap_or(3)), Ordering::Relaxed);
|
}
|
||||||
self.ignited
|
if let Some(radius) = nbt.get_byte("ExplosionRadius") {
|
||||||
.store(nbt.get_bool().unwrap_or(false), Ordering::Relaxed);
|
self.explosion_radius
|
||||||
|
.store(i32::from(radius), Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
if let Some(ignited) = nbt.get_bool("ignited") {
|
||||||
|
self.ignited.store(ignited, Ordering::Relaxed);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ use pumpkin_data::{
|
|||||||
tag::Taggable,
|
tag::Taggable,
|
||||||
tracked_data::TrackedData,
|
tracked_data::TrackedData,
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
codec::var_int::VarInt,
|
codec::var_int::VarInt,
|
||||||
java::client::play::{CEntityPositionSync, Metadata},
|
java::client::play::{CEntityPositionSync, Metadata},
|
||||||
@ -396,26 +397,20 @@ impl EndermanEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for EndermanEntity {
|
impl NBTStorage for EndermanEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
if let Some(block_state) = self.carried_block.load() {
|
if let Some(block_state) = self.carried_block.load() {
|
||||||
nbt.put_bool(true);
|
nbt.put_int("carriedBlockState", block_state as i32);
|
||||||
nbt.put_int(block_state as i32);
|
|
||||||
} else {
|
|
||||||
nbt.put_bool(false);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
if nbt.get_bool().unwrap_or(false) {
|
if let Some(block_state) = nbt.get_int("carriedBlockState") {
|
||||||
let block_state = nbt.get_int().unwrap_or(0);
|
|
||||||
self.set_carried_block(Some(block_state as u16)).await;
|
self.set_carried_block(Some(block_state as u16)).await;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use pumpkin_data::entity::EntityType;
|
|||||||
use pumpkin_data::meta_data_type::MetaDataType;
|
use pumpkin_data::meta_data_type::MetaDataType;
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
use pumpkin_data::tracked_data::TrackedData;
|
use pumpkin_data::tracked_data::TrackedData;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_protocol::codec::var_int::VarInt;
|
use pumpkin_protocol::codec::var_int::VarInt;
|
||||||
use pumpkin_protocol::java::client::play::{CEntityPositionSync, Metadata};
|
use pumpkin_protocol::java::client::play::{CEntityPositionSync, Metadata};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -320,25 +320,25 @@ impl ShulkerEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for ShulkerEntity {
|
impl NBTStorage for ShulkerEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_byte(self.attach_face.load(Ordering::Relaxed) as i8);
|
nbt.put_byte("AttachFace", self.attach_face.load(Ordering::Relaxed) as i8);
|
||||||
nbt.put_byte(self.peek_amount.load(Ordering::Relaxed) as i8);
|
nbt.put_byte("PeekAmount", self.peek_amount.load(Ordering::Relaxed) as i8);
|
||||||
nbt.put_byte(self.color.load(Ordering::Relaxed) as i8);
|
nbt.put_byte("Color", self.color.load(Ordering::Relaxed) as i8);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
if let Ok(face) = nbt.get_byte() {
|
if let Some(face) = nbt.get_byte("AttachFace") {
|
||||||
self.attach_face.store(face as u8, Ordering::Relaxed);
|
self.attach_face.store(face as u8, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if let Ok(peek) = nbt.get_byte() {
|
if let Some(peek) = nbt.get_byte("PeekAmount") {
|
||||||
self.peek_amount.store(peek as u8, Ordering::Relaxed);
|
self.peek_amount.store(peek as u8, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if let Ok(color) = nbt.get_byte() {
|
if let Some(color) = nbt.get_byte("Color") {
|
||||||
self.color.store(color as u8, Ordering::Relaxed);
|
self.color.store(color as u8, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::entity::{
|
|||||||
},
|
},
|
||||||
mob::{Mob, MobEntity},
|
mob::{Mob, MobEntity},
|
||||||
};
|
};
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
pub mod bogged;
|
pub mod bogged;
|
||||||
pub mod parched;
|
pub mod parched;
|
||||||
@ -58,11 +58,11 @@ impl SkeletonEntityBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for SkeletonEntityBase {
|
impl NBTStorage for SkeletonEntityBase {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt)
|
self.mob_entity.living_entity.write_nbt(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
use pumpkin_data::sound::Sound;
|
use pumpkin_data::sound::Sound;
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
use crate::entity::{
|
use crate::entity::{
|
||||||
Entity, NBTStorage, NbtFuture,
|
Entity, NBTStorage, NbtFuture,
|
||||||
@ -58,18 +60,29 @@ impl SlimeEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for SlimeEntity {
|
impl NBTStorage for SlimeEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.entity.living_entity.write_nbt(nbt).await;
|
self.entity.living_entity.write_nbt(nbt).await;
|
||||||
|
nbt.put_int(
|
||||||
|
"Size",
|
||||||
|
self.entity
|
||||||
|
.living_entity
|
||||||
|
.entity
|
||||||
|
.data
|
||||||
|
.load(Ordering::Relaxed),
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
|
self.entity
|
||||||
|
.living_entity
|
||||||
|
.entity
|
||||||
|
.data
|
||||||
|
.store(nbt.get_int("Size").unwrap_or(0), Ordering::Relaxed);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::entity::{
|
|||||||
ai::goal::{active_target::ActiveTargetGoal, look_at_entity::LookAtEntityGoal},
|
ai::goal::{active_target::ActiveTargetGoal, look_at_entity::LookAtEntityGoal},
|
||||||
};
|
};
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
pub mod drowned;
|
pub mod drowned;
|
||||||
@ -71,11 +71,11 @@ impl ZombieEntityBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for ZombieEntityBase {
|
impl NBTStorage for ZombieEntityBase {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt)
|
self.mob_entity.living_entity.write_nbt(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,7 @@ use pumpkin_data::{
|
|||||||
entity::{EntityPose, EntityType},
|
entity::{EntityPose, EntityType},
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||||
use pumpkin_protocol::java::client::play::{CUpdateEntityPos, CUpdateEntityPosRot};
|
use pumpkin_protocol::java::client::play::{CUpdateEntityPos, CUpdateEntityPosRot};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
PositionFlag,
|
PositionFlag,
|
||||||
@ -2764,55 +2765,74 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for Entity {
|
impl NBTStorage for Entity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let position = self.pos.load();
|
let position = self.pos.load();
|
||||||
nbt.put_string(self.entity_type.resource_name);
|
nbt.put_string(
|
||||||
nbt.put_uuid(&self.entity_uuid);
|
"id",
|
||||||
|
format!("minecraft:{}", self.entity_type.resource_name),
|
||||||
// Pos
|
);
|
||||||
nbt.put_f64(position.x);
|
let uuid = self.entity_uuid.as_u128();
|
||||||
nbt.put_f64(position.y);
|
nbt.put(
|
||||||
nbt.put_f64(position.z);
|
"UUID",
|
||||||
|
NbtTag::IntArray(vec![
|
||||||
// Motion
|
(uuid >> 96) as i32,
|
||||||
|
((uuid >> 64) & 0xFFFF_FFFF) as i32,
|
||||||
|
((uuid >> 32) & 0xFFFF_FFFF) as i32,
|
||||||
|
(uuid & 0xFFFF_FFFF) as i32,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
nbt.put(
|
||||||
|
"Pos",
|
||||||
|
NbtTag::List(vec![
|
||||||
|
position.x.into(),
|
||||||
|
position.y.into(),
|
||||||
|
position.z.into(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
let velocity = self.velocity.load();
|
let velocity = self.velocity.load();
|
||||||
nbt.put_f64(velocity.x);
|
nbt.put(
|
||||||
nbt.put_f64(velocity.y);
|
"Motion",
|
||||||
nbt.put_f64(velocity.z);
|
NbtTag::List(vec![
|
||||||
|
velocity.x.into(),
|
||||||
// Rotation
|
velocity.y.into(),
|
||||||
nbt.put_f32(self.yaw.load());
|
velocity.z.into(),
|
||||||
nbt.put_f32(self.pitch.load());
|
]),
|
||||||
|
);
|
||||||
nbt.put_short(self.fire_ticks.load(Relaxed) as i16);
|
nbt.put(
|
||||||
nbt.put_bool(self.on_ground.load(Relaxed));
|
"Rotation",
|
||||||
nbt.put_bool(self.invulnerable.load(Relaxed));
|
NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()]),
|
||||||
nbt.put_int(self.portal_cooldown.load(Relaxed) as i32);
|
);
|
||||||
nbt.put_bool(self.has_visual_fire.load(Relaxed));
|
nbt.put_short("Fire", self.fire_ticks.load(Relaxed) as i16);
|
||||||
|
nbt.put_bool("OnGround", self.on_ground.load(Relaxed));
|
||||||
|
nbt.put_bool("Invulnerable", self.invulnerable.load(Relaxed));
|
||||||
|
nbt.put_int("PortalCooldown", self.portal_cooldown.load(Relaxed) as i32);
|
||||||
|
if self.has_visual_fire.load(Relaxed) {
|
||||||
|
nbt.put_bool("HasVisualFire", true);
|
||||||
|
}
|
||||||
|
|
||||||
// todo more...
|
// todo more...
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
let _id = nbt.get_string().unwrap();
|
let position = nbt.get_list("Pos").unwrap();
|
||||||
let _uuid = nbt.get_uuid().unwrap();
|
let x = position[0].extract_double().unwrap_or(0.0);
|
||||||
|
let y = position[1].extract_double().unwrap_or(0.0);
|
||||||
let x = nbt.get_f64().unwrap_or(0.0);
|
let z = position[2].extract_double().unwrap_or(0.0);
|
||||||
let y = nbt.get_f64().unwrap_or(0.0);
|
|
||||||
let z = nbt.get_f64().unwrap_or(0.0);
|
|
||||||
let pos = Vector3::new(x, y, z);
|
let pos = Vector3::new(x, y, z);
|
||||||
self.set_pos(pos);
|
self.set_pos(pos);
|
||||||
self.last_sent_pos.store(pos);
|
self.last_sent_pos.store(pos);
|
||||||
self.first_loaded_chunk_position.store(Some(pos.to_i32()));
|
self.first_loaded_chunk_position.store(Some(pos.to_i32()));
|
||||||
let vx = nbt.get_f64().unwrap_or(0.0);
|
let velocity = nbt.get_list("Motion").unwrap();
|
||||||
let vy = nbt.get_f64().unwrap_or(0.0);
|
let x = velocity[0].extract_double().unwrap_or(0.0);
|
||||||
let vz = nbt.get_f64().unwrap_or(0.0);
|
let y = velocity[1].extract_double().unwrap_or(0.0);
|
||||||
self.velocity.store(Vector3::new(vx, vy, vz));
|
let z = velocity[2].extract_double().unwrap_or(0.0);
|
||||||
let yaw = nbt.get_f32().unwrap_or(0.0);
|
self.velocity.store(Vector3::new(x, y, z));
|
||||||
let pitch = nbt.get_f32().unwrap_or(0.0);
|
let rotation = nbt.get_list("Rotation").unwrap();
|
||||||
|
let yaw = rotation[0].extract_float().unwrap_or(0.0);
|
||||||
|
let pitch = rotation[1].extract_float().unwrap_or(0.0);
|
||||||
self.set_rotation(yaw, pitch);
|
self.set_rotation(yaw, pitch);
|
||||||
let yaw_byte = (yaw * 256.0 / 360.0).rem_euclid(256.0) as u8;
|
let yaw_byte = (yaw * 256.0 / 360.0).rem_euclid(256.0) as u8;
|
||||||
let pitch_byte = (pitch * 256.0 / 360.0).rem_euclid(256.0) as u8;
|
let pitch_byte = (pitch * 256.0 / 360.0).rem_euclid(256.0) as u8;
|
||||||
@ -2821,15 +2841,15 @@ impl NBTStorage for Entity {
|
|||||||
self.head_yaw.store(yaw);
|
self.head_yaw.store(yaw);
|
||||||
self.last_sent_head_yaw.store(yaw_byte, Relaxed);
|
self.last_sent_head_yaw.store(yaw_byte, Relaxed);
|
||||||
self.fire_ticks
|
self.fire_ticks
|
||||||
.store(i32::from(nbt.get_short().unwrap_or(0)), Relaxed);
|
.store(i32::from(nbt.get_short("Fire").unwrap_or(0)), Relaxed);
|
||||||
self.on_ground
|
self.on_ground
|
||||||
.store(nbt.get_bool().unwrap_or(false), Relaxed);
|
.store(nbt.get_bool("OnGround").unwrap_or(false), Relaxed);
|
||||||
self.invulnerable
|
self.invulnerable
|
||||||
.store(nbt.get_bool().unwrap_or(false), Relaxed);
|
.store(nbt.get_bool("Invulnerable").unwrap_or(false), Relaxed);
|
||||||
self.portal_cooldown
|
self.portal_cooldown
|
||||||
.store(nbt.get_int().unwrap_or(0) as u32, Relaxed);
|
.store(nbt.get_int("PortalCooldown").unwrap_or(0) as u32, Relaxed);
|
||||||
self.has_visual_fire
|
self.has_visual_fire
|
||||||
.store(nbt.get_bool().unwrap_or(false), Relaxed);
|
.store(nbt.get_bool("HasVisualFire").unwrap_or(false), Relaxed);
|
||||||
// todo more...
|
// todo more...
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2924,22 +2944,20 @@ impl EntityBase for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
pub type NbtFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
pub type NbtFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||||
|
|
||||||
pub trait NBTStorage: Send + Sync {
|
pub trait NBTStorage: Send + Sync {
|
||||||
fn write_nbt<'a>(&'a self, _nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, _nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {})
|
Box::pin(async {})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt<'a>(&'a mut self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.read_nbt_non_mut(nbt).await;
|
self.read_nbt_non_mut(nbt).await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, _nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, _nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {})
|
Box::pin(async {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2947,7 +2965,7 @@ pub trait NBTStorage: Send + Sync {
|
|||||||
pub type NBTInitFuture<'a, T> = Pin<Box<dyn Future<Output = Option<T>> + Send + 'a>>;
|
pub type NBTInitFuture<'a, T> = Pin<Box<dyn Future<Output = Option<T>> + Send + 'a>>;
|
||||||
|
|
||||||
pub trait NBTStorageInit: Send + Sync + Sized {
|
pub trait NBTStorageInit: Send + Sync + Sized {
|
||||||
fn create_from_nbt<'a>(_nbt: &'a mut PNbtCompound) -> NBTInitFuture<'a, Self>
|
fn create_from_nbt<'a>(_nbt: &'a mut NbtCompound) -> NBTInitFuture<'a, Self>
|
||||||
where
|
where
|
||||||
Self: 'a,
|
Self: 'a,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,7 +20,7 @@ use crate::entity::{
|
|||||||
mob::{Mob, MobEntity},
|
mob::{Mob, MobEntity},
|
||||||
player::Player,
|
player::Player,
|
||||||
};
|
};
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
const TEMPT_ITEMS: &[&Item] = &[
|
const TEMPT_ITEMS: &[&Item] = &[
|
||||||
&Item::WHEAT_SEEDS,
|
&Item::WHEAT_SEEDS,
|
||||||
@ -74,18 +74,18 @@ impl ChickenEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for ChickenEntity {
|
impl NBTStorage for ChickenEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_int(self.egg_lay_time.load(Ordering::Relaxed));
|
nbt.put_int("EggLayTime", self.egg_lay_time.load(Ordering::Relaxed));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
self.egg_lay_time
|
self.egg_lay_time
|
||||||
.store(nbt.get_int().unwrap_or(6000), Ordering::Relaxed);
|
.store(nbt.get_int("EggLayTime").unwrap_or(6000), Ordering::Relaxed);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use crate::entity::{
|
|||||||
mob::{Mob, MobEntity},
|
mob::{Mob, MobEntity},
|
||||||
player::Player,
|
player::Player,
|
||||||
};
|
};
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
const TEMPT_ITEMS: &[&Item] = &[&Item::WHEAT];
|
const TEMPT_ITEMS: &[&Item] = &[&Item::WHEAT];
|
||||||
|
|
||||||
@ -58,11 +58,11 @@ impl CowEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for CowEntity {
|
impl NBTStorage for CowEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt)
|
self.mob_entity.living_entity.write_nbt(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::entity::{
|
|||||||
},
|
},
|
||||||
mob::{Mob, MobEntity},
|
mob::{Mob, MobEntity},
|
||||||
};
|
};
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
/// Represents an Iron Golem, a powerful neutral mob that protects villagers and players.
|
/// Represents an Iron Golem, a powerful neutral mob that protects villagers and players.
|
||||||
///
|
///
|
||||||
@ -58,11 +58,11 @@ impl IronGolemEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for IronGolemEntity {
|
impl NBTStorage for IronGolemEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt)
|
self.mob_entity.living_entity.write_nbt(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use crate::entity::{
|
|||||||
mob::{Mob, MobEntity},
|
mob::{Mob, MobEntity},
|
||||||
player::Player,
|
player::Player,
|
||||||
};
|
};
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
const PIG_FOOD: &[&Item] = &[
|
const PIG_FOOD: &[&Item] = &[
|
||||||
&Item::CARROT,
|
&Item::CARROT,
|
||||||
@ -63,11 +63,11 @@ impl PigEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for PigEntity {
|
impl NBTStorage for PigEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt)
|
self.mob_entity.living_entity.write_nbt(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ use std::sync::{
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
entity::EntityType, item::Item, meta_data_type::MetaDataType, tracked_data::TrackedData,
|
entity::EntityType, item::Item, meta_data_type::MetaDataType, tracked_data::TrackedData,
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_protocol::java::client::play::Metadata;
|
use pumpkin_protocol::java::client::play::Metadata;
|
||||||
|
|
||||||
use crate::entity::{
|
use crate::entity::{
|
||||||
@ -105,22 +106,24 @@ impl SheepEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
|
||||||
|
|
||||||
impl NBTStorage for SheepEntity {
|
impl NBTStorage for SheepEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.entity.write_nbt(nbt).await;
|
||||||
nbt.put_bool(self.is_sheared());
|
nbt.put_bool("Sheared", self.is_sheared());
|
||||||
nbt.put_byte(self.get_color() as i8);
|
nbt.put_byte("Color", self.get_color() as i8);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity
|
||||||
let sheared = nbt.get_bool().unwrap_or(false);
|
.living_entity
|
||||||
let color = nbt.get_byte().unwrap_or(0) as u8;
|
.entity
|
||||||
|
.read_nbt_non_mut(nbt)
|
||||||
|
.await;
|
||||||
|
let sheared = nbt.get_bool("Sheared").unwrap_or(false);
|
||||||
|
let color = nbt.get_byte("Color").unwrap_or(0) as u8;
|
||||||
let byte = (color & 0x0F) | if sheared { 0x10 } else { 0 };
|
let byte = (color & 0x0F) | if sheared { 0x10 } else { 0 };
|
||||||
self.color_and_sheared.store(byte, Ordering::Relaxed);
|
self.color_and_sheared.store(byte, Ordering::Relaxed);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use pumpkin_inventory::merchant::merchant_screen_handler::MerchantScreenHandler;
|
|||||||
use pumpkin_inventory::screen_handler::{
|
use pumpkin_inventory::screen_handler::{
|
||||||
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
|
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
|
||||||
};
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_protocol::codec::var_int::VarInt;
|
use pumpkin_protocol::codec::var_int::VarInt;
|
||||||
use pumpkin_protocol::java::client::play::{CMerchantOffers, Metadata};
|
use pumpkin_protocol::java::client::play::{CMerchantOffers, Metadata};
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
@ -318,109 +319,138 @@ impl ScreenHandlerFactory for VillagerEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for VillagerEntity {
|
impl NBTStorage for VillagerEntity {
|
||||||
fn write_nbt<'a>(
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> crate::entity::NbtFuture<'a, ()> {
|
||||||
&'a self,
|
|
||||||
nbt: &'a mut pumpkin_nbt::pnbt::PNbtCompound,
|
|
||||||
) -> crate::entity::NbtFuture<'a, ()> {
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.entity.write_nbt(nbt).await;
|
||||||
let data = self.villager_data.lock().await;
|
let data = self.villager_data.lock().await;
|
||||||
nbt.put_int(data.r#type as i32);
|
let mut villager_data_nbt = NbtCompound::new();
|
||||||
nbt.put_int(data.profession as i32);
|
villager_data_nbt.put_int("Type", data.r#type as i32);
|
||||||
nbt.put_int(data.level);
|
villager_data_nbt.put_int("Profession", data.profession as i32);
|
||||||
|
villager_data_nbt.put_int("Level", data.level);
|
||||||
|
nbt.put_compound("VillagerData", villager_data_nbt);
|
||||||
|
|
||||||
nbt.put_int(self.food_level.load(Ordering::Relaxed));
|
nbt.put_int("FoodLevel", self.food_level.load(Ordering::Relaxed));
|
||||||
nbt.put_int(self.xp.load(Ordering::Relaxed));
|
nbt.put_int("Xp", self.xp.load(Ordering::Relaxed));
|
||||||
nbt.put_long(self.last_restock_time.load(Ordering::Relaxed));
|
nbt.put_long(
|
||||||
nbt.put_int(self.restocks_today.load(Ordering::Relaxed));
|
"LastRestock",
|
||||||
|
self.last_restock_time.load(Ordering::Relaxed),
|
||||||
|
);
|
||||||
|
nbt.put_int("RestocksToday", self.restocks_today.load(Ordering::Relaxed));
|
||||||
|
|
||||||
// Inventory
|
// Inventory
|
||||||
let inventory = self.inventory.lock().await;
|
let inventory = self.inventory.lock().await;
|
||||||
nbt.put_int(inventory.len() as i32);
|
let mut inventory_list = Vec::new();
|
||||||
for stack_mutex in inventory.iter() {
|
for stack_mutex in inventory.iter() {
|
||||||
let stack = stack_mutex.lock().await;
|
let stack = stack_mutex.lock().await;
|
||||||
stack.write_item_stack_pnbt(nbt);
|
if !stack.is_empty() {
|
||||||
}
|
let mut item_nbt = NbtCompound::new();
|
||||||
|
stack.write_item_stack(&mut item_nbt);
|
||||||
// Gossips (Simplified: just save counts per UUID and type)
|
inventory_list.push(pumpkin_nbt::tag::NbtTag::Compound(item_nbt));
|
||||||
let gossips = self.gossips.lock().await;
|
|
||||||
nbt.put_int(gossips.len() as i32);
|
|
||||||
for (uuid, types) in gossips.iter() {
|
|
||||||
nbt.put_uuid(uuid);
|
|
||||||
nbt.put_int(types.len() as i32);
|
|
||||||
for (gtype, value) in types {
|
|
||||||
nbt.put_int(*gtype as i32);
|
|
||||||
nbt.put_int(*value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nbt.put("Inventory", pumpkin_nbt::tag::NbtTag::List(inventory_list));
|
||||||
|
|
||||||
|
// Gossips
|
||||||
|
let gossips = self.gossips.lock().await;
|
||||||
|
let mut gossip_list = Vec::new();
|
||||||
|
for (uuid, types) in gossips.iter() {
|
||||||
|
for (gtype, value) in types {
|
||||||
|
let mut gossip_nbt = NbtCompound::new();
|
||||||
|
let uuid_val = uuid.as_u128();
|
||||||
|
gossip_nbt.put(
|
||||||
|
"Target",
|
||||||
|
pumpkin_nbt::tag::NbtTag::IntArray(vec![
|
||||||
|
(uuid_val >> 96) as i32,
|
||||||
|
((uuid_val >> 64) & 0xFFFF_FFFF) as i32,
|
||||||
|
((uuid_val >> 32) & 0xFFFF_FFFF) as i32,
|
||||||
|
(uuid_val & 0xFFFF_FFFF) as i32,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
gossip_nbt.put_int("Type", *gtype as i32);
|
||||||
|
gossip_nbt.put_int("Value", *value);
|
||||||
|
gossip_list.push(pumpkin_nbt::tag::NbtTag::Compound(gossip_nbt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nbt.put("Gossips", pumpkin_nbt::tag::NbtTag::List(gossip_list));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> crate::entity::NbtFuture<'a, ()> {
|
||||||
&'a self,
|
|
||||||
nbt: &'a mut pumpkin_nbt::pnbt::PNbtCompound,
|
|
||||||
) -> crate::entity::NbtFuture<'a, ()> {
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
self.mob_entity
|
||||||
let mut data = self.villager_data.lock().await;
|
.living_entity
|
||||||
if let Ok(t) = nbt.get_int() {
|
.entity
|
||||||
data.r#type = VillagerType::try_from(t).unwrap_or(VillagerType::Plains);
|
.read_nbt_non_mut(nbt)
|
||||||
}
|
.await;
|
||||||
if let Ok(p) = nbt.get_int() {
|
if let Some(villager_data_nbt) = nbt.get_compound("VillagerData") {
|
||||||
data.profession =
|
let mut data = self.villager_data.lock().await;
|
||||||
VillagerProfession::try_from(p).unwrap_or(VillagerProfession::None);
|
if let Some(t) = villager_data_nbt.get_int("Type") {
|
||||||
}
|
data.r#type = VillagerType::try_from(t).unwrap_or(VillagerType::Plains);
|
||||||
if let Ok(l) = nbt.get_int() {
|
}
|
||||||
data.level = l;
|
if let Some(p) = villager_data_nbt.get_int("Profession") {
|
||||||
|
data.profession =
|
||||||
|
VillagerProfession::try_from(p).unwrap_or(VillagerProfession::None);
|
||||||
|
}
|
||||||
|
if let Some(l) = villager_data_nbt.get_int("Level") {
|
||||||
|
data.level = l;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(food) = nbt.get_int() {
|
if let Some(food) = nbt.get_int("FoodLevel") {
|
||||||
self.food_level.store(food, Ordering::Relaxed);
|
self.food_level.store(food, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if let Ok(xp) = nbt.get_int() {
|
if let Some(xp) = nbt.get_int("Xp") {
|
||||||
self.xp.store(xp, Ordering::Relaxed);
|
self.xp.store(xp, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if let Ok(restock) = nbt.get_long() {
|
if let Some(restock) = nbt.get_long("LastRestock") {
|
||||||
self.last_restock_time.store(restock, Ordering::Relaxed);
|
self.last_restock_time.store(restock, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if let Ok(today) = nbt.get_int() {
|
if let Some(today) = nbt.get_int("RestocksToday") {
|
||||||
self.restocks_today.store(today, Ordering::Relaxed);
|
self.restocks_today.store(today, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inventory
|
// Inventory
|
||||||
if let Ok(inv_len) = nbt.get_int() {
|
if let Some(inventory_list) = nbt.get_list("Inventory") {
|
||||||
let mut inventory = self.inventory.lock().await;
|
let mut inventory = self.inventory.lock().await;
|
||||||
inventory.clear();
|
inventory.clear();
|
||||||
for _ in 0..inv_len {
|
for tag in inventory_list {
|
||||||
let stack =
|
if let Some(item_compound) = tag.extract_compound()
|
||||||
ItemStack::read_item_stack_pnbt(nbt).unwrap_or(ItemStack::EMPTY.clone());
|
&& let Some(stack) = ItemStack::read_item_stack(item_compound)
|
||||||
inventory.push(Arc::new(Mutex::new(stack)));
|
{
|
||||||
|
inventory.push(Arc::new(Mutex::new(stack)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gossips
|
// Gossips
|
||||||
if let Ok(gossip_len) = nbt.get_int() {
|
if let Some(gossip_list) = nbt.get_list("Gossips") {
|
||||||
let mut gossips = self.gossips.lock().await;
|
let mut gossips = self.gossips.lock().await;
|
||||||
gossips.clear();
|
gossips.clear();
|
||||||
for _ in 0..gossip_len {
|
for tag in gossip_list {
|
||||||
if let Ok(uuid) = nbt.get_uuid()
|
if let Some(gossip_nbt) = tag.extract_compound() {
|
||||||
&& let Ok(types_len) = nbt.get_int()
|
let uuid = gossip_nbt.get_int_array("Target").map(|uuid_array| {
|
||||||
{
|
Uuid::from_u128(
|
||||||
let mut types = HashMap::new();
|
(uuid_array[0] as u128) << 96
|
||||||
for _ in 0..types_len {
|
| (uuid_array[1] as u128) << 64
|
||||||
if let (Ok(gtype), Ok(val)) = (nbt.get_int(), nbt.get_int()) {
|
| (uuid_array[2] as u128) << 32
|
||||||
let gossip_type = match gtype {
|
| (uuid_array[3] as u128),
|
||||||
0 => GossipType::MajorNegative,
|
)
|
||||||
1 => GossipType::MinorNegative,
|
});
|
||||||
2 => GossipType::MajorPositive,
|
if let (Some(uuid), Some(gtype), Some(val)) = (
|
||||||
3 => GossipType::MinorPositive,
|
uuid,
|
||||||
4 => GossipType::Trading,
|
gossip_nbt.get_int("Type"),
|
||||||
_ => continue,
|
gossip_nbt.get_int("Value"),
|
||||||
};
|
) {
|
||||||
types.insert(gossip_type, val);
|
let gossip_type = match gtype {
|
||||||
}
|
0 => GossipType::MajorNegative,
|
||||||
|
1 => GossipType::MinorNegative,
|
||||||
|
2 => GossipType::MajorPositive,
|
||||||
|
3 => GossipType::MinorPositive,
|
||||||
|
4 => GossipType::Trading,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
gossips.entry(uuid).or_default().insert(gossip_type, val);
|
||||||
}
|
}
|
||||||
gossips.insert(uuid, types);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,8 @@ use pumpkin_inventory::screen_handler::{
|
|||||||
};
|
};
|
||||||
use pumpkin_inventory::sync_handler::SyncHandler;
|
use pumpkin_inventory::sync_handler::SyncHandler;
|
||||||
use pumpkin_macros::send_cancellable;
|
use pumpkin_macros::send_cancellable;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use pumpkin_protocol::IdOr;
|
use pumpkin_protocol::IdOr;
|
||||||
use pumpkin_protocol::SoundEvent;
|
use pumpkin_protocol::SoundEvent;
|
||||||
use pumpkin_protocol::codec::var_int::VarInt;
|
use pumpkin_protocol::codec::var_int::VarInt;
|
||||||
@ -3696,10 +3697,9 @@ impl PartialEq for Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for Player {
|
impl NBTStorage for Player {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
nbt.put_int(DATA_VERSION);
|
nbt.put_int("DataVersion", DATA_VERSION);
|
||||||
nbt.put_string(self.world().dimension.minecraft_name);
|
|
||||||
self.living_entity.write_nbt(nbt).await;
|
self.living_entity.write_nbt(nbt).await;
|
||||||
self.inventory.write_nbt(nbt).await;
|
self.inventory.write_nbt(nbt).await;
|
||||||
self.ender_chest_inventory.write_nbt(nbt).await;
|
self.ender_chest_inventory.write_nbt(nbt).await;
|
||||||
@ -3709,44 +3709,48 @@ impl NBTStorage for Player {
|
|||||||
let total_exp =
|
let total_exp =
|
||||||
experience::points_to_level(self.experience_level.load(Ordering::Relaxed))
|
experience::points_to_level(self.experience_level.load(Ordering::Relaxed))
|
||||||
+ self.experience_points.load(Ordering::Relaxed);
|
+ self.experience_points.load(Ordering::Relaxed);
|
||||||
nbt.put_int(total_exp);
|
nbt.put_int("XpTotal", total_exp);
|
||||||
|
nbt.put_byte("playerGameType", self.gamemode.load() as i8);
|
||||||
nbt.put_byte(self.gamemode.load() as i8);
|
|
||||||
if let Some(previous_gamemode) = self.previous_gamemode.load() {
|
if let Some(previous_gamemode) = self.previous_gamemode.load() {
|
||||||
nbt.put_bool(true);
|
nbt.put_byte("previousPlayerGameType", previous_gamemode as i8);
|
||||||
nbt.put_byte(previous_gamemode as i8);
|
|
||||||
} else {
|
|
||||||
nbt.put_bool(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nbt.put_bool(self.has_played_before.load(Ordering::Relaxed));
|
nbt.put_bool(
|
||||||
|
"HasPlayedBefore",
|
||||||
|
self.has_played_before.load(Ordering::Relaxed),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store food level, saturation, exhaustion, and tick timer
|
||||||
self.hunger_manager.write_nbt(nbt).await;
|
self.hunger_manager.write_nbt(nbt).await;
|
||||||
|
|
||||||
|
nbt.put_string(
|
||||||
|
"Dimension",
|
||||||
|
self.world().dimension.minecraft_name.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(respawn) = self.respawn_point.lock().await.as_ref() {
|
if let Some(respawn) = self.respawn_point.lock().await.as_ref() {
|
||||||
nbt.put_bool(true);
|
nbt.put_int("SpawnX", respawn.position.0.x);
|
||||||
nbt.put_int(respawn.position.0.x);
|
nbt.put_int("SpawnY", respawn.position.0.y);
|
||||||
nbt.put_int(respawn.position.0.y);
|
nbt.put_int("SpawnZ", respawn.position.0.z);
|
||||||
nbt.put_int(respawn.position.0.z);
|
nbt.put_string(
|
||||||
nbt.put_string(respawn.dimension.minecraft_name);
|
"SpawnDimension",
|
||||||
nbt.put_bool(respawn.force);
|
respawn.dimension.minecraft_name.to_owned(),
|
||||||
} else {
|
);
|
||||||
nbt.put_bool(false);
|
nbt.put_bool("SpawnForced", respawn.force);
|
||||||
}
|
}
|
||||||
nbt.put_int(self.enchantment_seed.load(Ordering::Relaxed));
|
nbt.put_int("XpSeed", self.enchantment_seed.load(Ordering::Relaxed));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt<'a>(&'a mut self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let _version = nbt.get_int().unwrap_or(0);
|
|
||||||
let _dimension_name = nbt.get_string().unwrap_or_default();
|
|
||||||
|
|
||||||
self.living_entity.read_nbt(nbt).await;
|
self.living_entity.read_nbt(nbt).await;
|
||||||
self.inventory.read_nbt_non_mut(nbt).await;
|
self.inventory.read_nbt_non_mut(nbt).await;
|
||||||
self.ender_chest_inventory.read_nbt_non_mut(nbt).await;
|
self.ender_chest_inventory.read_nbt_non_mut(nbt).await;
|
||||||
self.abilities.lock().await.read_nbt(nbt).await;
|
self.abilities.lock().await.read_nbt(nbt).await;
|
||||||
|
|
||||||
let total_exp = nbt.get_int().unwrap_or(0);
|
// Load from total XP
|
||||||
|
let total_exp = nbt.get_int("XpTotal").unwrap_or(0);
|
||||||
let (level, points) = experience::total_to_level_and_points(total_exp);
|
let (level, points) = experience::total_to_level_and_points(total_exp);
|
||||||
let progress = experience::progress_in_level(level, points);
|
let progress = experience::progress_in_level(level, points);
|
||||||
self.experience_level.store(level, Ordering::Relaxed);
|
self.experience_level.store(level, Ordering::Relaxed);
|
||||||
@ -3754,29 +3758,33 @@ impl NBTStorage for Player {
|
|||||||
self.experience_points.store(points, Ordering::Relaxed);
|
self.experience_points.store(points, Ordering::Relaxed);
|
||||||
|
|
||||||
self.gamemode.store(
|
self.gamemode.store(
|
||||||
GameMode::try_from(nbt.get_byte().unwrap_or(0)).unwrap_or(GameMode::Survival),
|
GameMode::try_from(nbt.get_byte("playerGameType").unwrap_or(0))
|
||||||
|
.unwrap_or(GameMode::Survival),
|
||||||
);
|
);
|
||||||
|
|
||||||
if nbt.get_bool().unwrap_or(false) {
|
self.previous_gamemode.store(
|
||||||
self.previous_gamemode
|
nbt.get_byte("previousPlayerGameType")
|
||||||
.store(GameMode::try_from(nbt.get_byte().unwrap_or(0)).ok());
|
.and_then(|byte| GameMode::try_from(byte).ok()),
|
||||||
}
|
);
|
||||||
|
|
||||||
self.has_played_before
|
self.has_played_before.store(
|
||||||
.store(nbt.get_bool().unwrap_or(false), Ordering::Relaxed);
|
nbt.get_bool("HasPlayedBefore").unwrap_or(false),
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
|
||||||
self.hunger_manager.read_nbt(nbt).await;
|
self.hunger_manager.read_nbt(nbt).await;
|
||||||
|
|
||||||
if nbt.get_bool().unwrap_or(false) {
|
// Load any saved spawnpoint data (SpawnX/SpawnY/SpawnZ, SpawnDimension, SpawnForced)
|
||||||
let x = nbt.get_int().unwrap_or(0);
|
if let (Some(x), Some(y), Some(z)) = (
|
||||||
let y = nbt.get_int().unwrap_or(0);
|
nbt.get_int("SpawnX"),
|
||||||
let z = nbt.get_int().unwrap_or(0);
|
nbt.get_int("SpawnY"),
|
||||||
|
nbt.get_int("SpawnZ"),
|
||||||
|
) {
|
||||||
let dim = nbt
|
let dim = nbt
|
||||||
.get_string()
|
.get_string("SpawnDimension")
|
||||||
.ok()
|
.and_then(|s| Dimension::from_name(s).cloned())
|
||||||
.and_then(|s| Dimension::from_name(s.as_str()).cloned())
|
.unwrap_or_else(|| self.world().dimension.clone());
|
||||||
.unwrap_or(self.world().dimension.clone());
|
let force = nbt.get_bool("SpawnForced").unwrap_or(false);
|
||||||
let force = nbt.get_bool().unwrap_or(false);
|
|
||||||
*self.respawn_point.lock().await = Some(RespawnPoint {
|
*self.respawn_point.lock().await = Some(RespawnPoint {
|
||||||
dimension: dim,
|
dimension: dim,
|
||||||
position: BlockPos(Vector3::new(x, y, z)),
|
position: BlockPos(Vector3::new(x, y, z)),
|
||||||
@ -3784,8 +3792,10 @@ impl NBTStorage for Player {
|
|||||||
force,
|
force,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.enchantment_seed
|
self.enchantment_seed.store(
|
||||||
.store(nbt.get_int().unwrap_or(rand::random()), Ordering::Relaxed);
|
nbt.get_int("XpSeed").unwrap_or(rand::random()),
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3793,76 +3803,106 @@ impl NBTStorage for Player {
|
|||||||
impl NBTStorageInit for Player {}
|
impl NBTStorageInit for Player {}
|
||||||
|
|
||||||
impl NBTStorage for PlayerInventory {
|
impl NBTStorage for PlayerInventory {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// Save the selected slot (hotbar)
|
// Save the selected slot (hotbar)
|
||||||
nbt.put_u8(self.get_selected_slot());
|
nbt.put_int("SelectedItemSlot", i32::from(self.get_selected_slot()));
|
||||||
|
|
||||||
// Save items count then items
|
// Create inventory list with the correct capacity (inventory size)
|
||||||
let mut present_items = Vec::new();
|
let mut items: Vec<NbtTag> = Vec::with_capacity(41);
|
||||||
for (i, item) in self.main_inventory.iter().enumerate() {
|
for (i, item) in self.main_inventory.iter().enumerate() {
|
||||||
let stack = item.lock().await;
|
let stack = item.lock().await;
|
||||||
if !stack.is_empty() {
|
if !stack.is_empty() {
|
||||||
present_items.push((i as u8, stack.clone()));
|
let mut item_compound = NbtCompound::new();
|
||||||
|
item_compound.put_byte("Slot", i as i8);
|
||||||
|
stack.write_item_stack(&mut item_compound);
|
||||||
|
drop(stack);
|
||||||
|
items.push(NbtTag::Compound(item_compound));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nbt.put_u32(present_items.len() as u32);
|
|
||||||
for (slot, stack) in present_items {
|
|
||||||
nbt.put_u8(slot);
|
|
||||||
stack.write_item_stack_pnbt(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save equipment
|
let mut equipment_compound = NbtCompound::new();
|
||||||
#[allow(clippy::default_trait_access)]
|
for slot in self.equipment_slots.values() {
|
||||||
for slot in [
|
let stack_binding = self.entity_equipment.lock().await.get(slot);
|
||||||
EquipmentSlot::OffHand(Default::default()),
|
let stack = stack_binding.lock().await;
|
||||||
EquipmentSlot::Head(Default::default()),
|
if !stack.is_empty() {
|
||||||
EquipmentSlot::Chest(Default::default()),
|
let mut item_compound = NbtCompound::new();
|
||||||
EquipmentSlot::Legs(Default::default()),
|
stack.write_item_stack(&mut item_compound);
|
||||||
EquipmentSlot::Feet(Default::default()),
|
drop(stack);
|
||||||
] {
|
match slot {
|
||||||
// Find the actual slot in self.equipment_slots
|
EquipmentSlot::OffHand(_) => {
|
||||||
let mut found = false;
|
equipment_compound.put_compound("offhand", item_compound);
|
||||||
for s in self.equipment_slots.values() {
|
}
|
||||||
if mem::discriminant(s) == mem::discriminant(&slot) {
|
EquipmentSlot::Feet(_) => {
|
||||||
let stack_binding = self.entity_equipment.lock().await.get(s);
|
equipment_compound.put_compound("feet", item_compound);
|
||||||
let stack = stack_binding.lock().await;
|
}
|
||||||
if !stack.is_empty() {
|
EquipmentSlot::Legs(_) => {
|
||||||
nbt.put_bool(true);
|
equipment_compound.put_compound("legs", item_compound);
|
||||||
stack.write_item_stack_pnbt(nbt);
|
}
|
||||||
found = true;
|
EquipmentSlot::Chest(_) => {
|
||||||
|
equipment_compound.put_compound("chest", item_compound);
|
||||||
|
}
|
||||||
|
EquipmentSlot::Head(_) => {
|
||||||
|
equipment_compound.put_compound("head", item_compound);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!("Invalid equipment slot for a player");
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
|
||||||
nbt.put_bool(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
nbt.put_compound("equipment", equipment_compound);
|
||||||
|
nbt.put("Inventory", NbtTag::List(items));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async {
|
||||||
// Read selected hotbar slot
|
// Read selected hotbar slot
|
||||||
self.set_selected_slot(nbt.get_u8().unwrap_or(0));
|
self.set_selected_slot(nbt.get_int("SelectedItemSlot").unwrap_or(0) as u8);
|
||||||
|
|
||||||
// Process inventory list
|
// Process inventory list
|
||||||
let items_len = nbt.get_u32().unwrap_or(0) as usize;
|
if let Some(inventory_list) = nbt.get_list("Inventory") {
|
||||||
for _ in 0..items_len {
|
for tag in inventory_list {
|
||||||
let slot = nbt.get_u8().unwrap_or(0) as usize;
|
if let Some(item_compound) = tag.extract_compound()
|
||||||
if let Some(item_stack) = ItemStack::read_item_stack_pnbt(nbt) {
|
&& let Some(slot_byte) = item_compound.get_byte("Slot")
|
||||||
self.set_stack(slot, item_stack).await;
|
{
|
||||||
|
let slot = slot_byte as usize;
|
||||||
|
if let Some(item_stack) = ItemStack::read_item_stack(item_compound) {
|
||||||
|
self.set_stack(slot, item_stack).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read equipment
|
if let Some(equipment) = nbt.get_compound("equipment") {
|
||||||
for slot_idx in [40, 39, 38, 37, 36] {
|
if let Some(offhand) = equipment.get_compound("offhand")
|
||||||
// offhand, head, chest, legs, feet
|
&& let Some(item_stack) = ItemStack::read_item_stack(offhand)
|
||||||
if nbt.get_bool().unwrap_or(false)
|
|
||||||
&& let Some(item_stack) = ItemStack::read_item_stack_pnbt(nbt)
|
|
||||||
{
|
{
|
||||||
self.set_stack(slot_idx, item_stack).await;
|
self.set_stack(40, item_stack).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(head) = equipment.get_compound("head")
|
||||||
|
&& let Some(item_stack) = ItemStack::read_item_stack(head)
|
||||||
|
{
|
||||||
|
self.set_stack(39, item_stack).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(chest) = equipment.get_compound("chest")
|
||||||
|
&& let Some(item_stack) = ItemStack::read_item_stack(chest)
|
||||||
|
{
|
||||||
|
self.set_stack(38, item_stack).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(legs) = equipment.get_compound("legs")
|
||||||
|
&& let Some(item_stack) = ItemStack::read_item_stack(legs)
|
||||||
|
{
|
||||||
|
self.set_stack(37, item_stack).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(feet) = equipment.get_compound("feet")
|
||||||
|
&& let Some(item_stack) = ItemStack::read_item_stack(feet)
|
||||||
|
{
|
||||||
|
self.set_stack(36, item_stack).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -3872,32 +3912,38 @@ impl NBTStorage for PlayerInventory {
|
|||||||
impl NBTStorageInit for PlayerInventory {}
|
impl NBTStorageInit for PlayerInventory {}
|
||||||
|
|
||||||
impl NBTStorage for EnderChestInventory {
|
impl NBTStorage for EnderChestInventory {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
// Save items count then items
|
// Create item list with the correct capacity (inventory size)
|
||||||
let mut present_items = Vec::new();
|
let mut items: Vec<NbtTag> = Vec::with_capacity(Self::INVENTORY_SIZE);
|
||||||
for (i, item) in self.items.iter().enumerate() {
|
for (i, item) in self.items.iter().enumerate() {
|
||||||
let stack = item.lock().await;
|
let stack = item.lock().await;
|
||||||
if !stack.is_empty() {
|
if !stack.is_empty() {
|
||||||
present_items.push((i as u8, stack.clone()));
|
let mut item_compound = NbtCompound::new();
|
||||||
|
item_compound.put_byte("Slot", i as i8);
|
||||||
|
stack.write_item_stack(&mut item_compound);
|
||||||
|
drop(stack);
|
||||||
|
items.push(NbtTag::Compound(item_compound));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nbt.put_u32(present_items.len() as u32);
|
|
||||||
for (slot, stack) in present_items {
|
nbt.put("EnderItems", NbtTag::List(items));
|
||||||
nbt.put_u8(slot);
|
|
||||||
stack.write_item_stack_pnbt(nbt);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
// Process item list
|
// Process item list
|
||||||
let items_len = nbt.get_u32().unwrap_or(0) as usize;
|
if let Some(item_list) = nbt.get_list("EnderItems") {
|
||||||
for _ in 0..items_len {
|
for tag in item_list {
|
||||||
let slot = nbt.get_u8().unwrap_or(0) as usize;
|
if let Some(item_compound) = tag.extract_compound()
|
||||||
if let Some(item_stack) = ItemStack::read_item_stack_pnbt(nbt) {
|
&& let Some(slot_byte) = item_compound.get_byte("Slot")
|
||||||
self.set_stack(slot, item_stack).await;
|
{
|
||||||
|
let slot = slot_byte as usize;
|
||||||
|
if let Some(item_stack) = ItemStack::read_item_stack(item_compound) {
|
||||||
|
self.set_stack(slot, item_stack).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -4079,27 +4125,31 @@ pub struct Abilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NBTStorage for Abilities {
|
impl NBTStorage for Abilities {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
nbt.put_bool(self.invulnerable);
|
let mut component = NbtCompound::new();
|
||||||
nbt.put_bool(self.flying);
|
component.put_bool("invulnerable", self.invulnerable);
|
||||||
nbt.put_bool(self.allow_flying);
|
component.put_bool("flying", self.flying);
|
||||||
nbt.put_bool(self.creative);
|
component.put_bool("mayfly", self.allow_flying);
|
||||||
nbt.put_bool(self.allow_modify_world);
|
component.put_bool("instabuild", self.creative);
|
||||||
nbt.put_float(self.fly_speed);
|
component.put_bool("mayBuild", self.allow_modify_world);
|
||||||
nbt.put_float(self.walk_speed);
|
component.put_float("flySpeed", self.fly_speed);
|
||||||
|
component.put_float("walkSpeed", self.walk_speed);
|
||||||
|
nbt.put_compound("abilities", component);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt<'a>(&'a mut self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt<'a>(&'a mut self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.invulnerable = nbt.get_bool().unwrap_or(false);
|
if let Some(component) = nbt.get_compound("abilities") {
|
||||||
self.flying = nbt.get_bool().unwrap_or(false);
|
self.invulnerable = component.get_bool("invulnerable").unwrap_or(false);
|
||||||
self.allow_flying = nbt.get_bool().unwrap_or(false);
|
self.flying = component.get_bool("flying").unwrap_or(false);
|
||||||
self.creative = nbt.get_bool().unwrap_or(false);
|
self.allow_flying = component.get_bool("mayfly").unwrap_or(false);
|
||||||
self.allow_modify_world = nbt.get_bool().unwrap_or(false);
|
self.creative = component.get_bool("instabuild").unwrap_or(false);
|
||||||
self.fly_speed = nbt.get_float().unwrap_or(0.05);
|
self.allow_modify_world = component.get_bool("mayBuild").unwrap_or(false);
|
||||||
self.walk_speed = nbt.get_float().unwrap_or(0.1);
|
self.fly_speed = component.get_float("flySpeed").unwrap_or(0.05);
|
||||||
|
self.walk_speed = component.get_float("walkSpeed").unwrap_or(0.1);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -442,13 +442,10 @@ impl Server {
|
|||||||
let gamemode = self.defaultgamemode.lock().await.gamemode;
|
let gamemode = self.defaultgamemode.lock().await.gamemode;
|
||||||
|
|
||||||
let (world, nbt) =
|
let (world, nbt) =
|
||||||
if let Ok(Some(mut data)) = self.player_data_storage.load_data(&profile.id).await {
|
if let Ok(Some(data)) = self.player_data_storage.load_data(&profile.id).await {
|
||||||
let _version = data.get_int().unwrap_or(0);
|
if let Some(dimension_key) = data.get_string("Dimension") {
|
||||||
if let Ok(dimension_key) = data.get_string() {
|
if let Some(dimension) = Dimension::from_name(dimension_key) {
|
||||||
if let Some(dimension) = Dimension::from_name(&dimension_key) {
|
|
||||||
let world = self.get_world_from_dimension(dimension);
|
let world = self.get_world_from_dimension(dimension);
|
||||||
// Reset read position so player.read_nbt can read everything from start
|
|
||||||
data.read_pos = 0;
|
|
||||||
(world, Some(data))
|
(world, Some(data))
|
||||||
} else {
|
} else {
|
||||||
warn!("Invalid dimension key in player data: {dimension_key}");
|
warn!("Invalid dimension key in player data: {dimension_key}");
|
||||||
@ -458,18 +455,16 @@ impl Server {
|
|||||||
.first()
|
.first()
|
||||||
.expect("Default world should exist")
|
.expect("Default world should exist")
|
||||||
.clone();
|
.clone();
|
||||||
data.read_pos = 0;
|
|
||||||
(default_world, Some(data))
|
(default_world, Some(data))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Player data exists but doesn't have a dimension entry.
|
// Player data exists but doesn't have a "Dimension" key.
|
||||||
let default_world = self
|
let default_world = self
|
||||||
.worlds
|
.worlds
|
||||||
.load()
|
.load()
|
||||||
.first()
|
.first()
|
||||||
.expect("Default world should exist")
|
.expect("Default world should exist")
|
||||||
.clone();
|
.clone();
|
||||||
data.read_pos = 0;
|
|
||||||
(default_world, Some(data))
|
(default_world, Some(data))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -66,8 +66,7 @@ use pumpkin_data::{
|
|||||||
};
|
};
|
||||||
use pumpkin_data::{BlockDirection, BlockState, translation};
|
use pumpkin_data::{BlockDirection, BlockState, translation};
|
||||||
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed};
|
||||||
use pumpkin_nbt::to_bytes_unnamed;
|
|
||||||
use pumpkin_protocol::bedrock::client::set_actor_data::{
|
use pumpkin_protocol::bedrock::client::set_actor_data::{
|
||||||
CSetActorData, EntityMetadata, MetadataValue, PropertySyncData, entity_data_flag,
|
CSetActorData, EntityMetadata, MetadataValue, PropertySyncData, entity_data_flag,
|
||||||
entity_data_key,
|
entity_data_key,
|
||||||
@ -335,7 +334,7 @@ impl World {
|
|||||||
let base_entity = entity.get_entity();
|
let base_entity = entity.get_entity();
|
||||||
let uuid = base_entity.entity_uuid;
|
let uuid = base_entity.entity_uuid;
|
||||||
let current_chunk_coordinate = base_entity.block_pos.load().chunk_position();
|
let current_chunk_coordinate = base_entity.block_pos.load().chunk_position();
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
entity.write_nbt(&mut nbt).await;
|
entity.write_nbt(&mut nbt).await;
|
||||||
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
|
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
|
||||||
let old_chunk = old_chunk.to_vec2_i32();
|
let old_chunk = old_chunk.to_vec2_i32();
|
||||||
@ -2670,46 +2669,34 @@ impl World {
|
|||||||
};
|
};
|
||||||
let position = Vector2::new(chunk.x, chunk.z);
|
let position = Vector2::new(chunk.x, chunk.z);
|
||||||
|
|
||||||
let chunk = if level.is_chunk_watched(&position) {
|
if !level.is_chunk_watched(&position) {
|
||||||
chunk
|
|
||||||
} else {
|
|
||||||
trace!(
|
trace!(
|
||||||
"Received chunk {:?}, but it is no longer watched... cleaning",
|
"Received chunk {:?}, but it is no longer watched... cleaning",
|
||||||
&position
|
&position
|
||||||
);
|
);
|
||||||
let mut ids_to_remove = Vec::new();
|
let mut ids_to_remove = Vec::new();
|
||||||
|
|
||||||
let mut entities_to_load = Vec::new();
|
for (uuid, entity_nbt) in chunk.data.lock().await.iter() {
|
||||||
let mut entity_data = Vec::new();
|
let Some(id) = entity_nbt.get_string("id") else {
|
||||||
{
|
warn!("Entity has no ID");
|
||||||
let mut data = chunk.data.lock().await;
|
continue;
|
||||||
for (uuid, entity_nbt) in data.iter_mut() {
|
};
|
||||||
let Ok(id) = entity_nbt.get_string() else {
|
let Some(entity_type) =
|
||||||
warn!("Entity has no ID");
|
EntityType::from_name(id.strip_prefix("minecraft:").unwrap_or(id))
|
||||||
continue;
|
else {
|
||||||
};
|
warn!("Entity has no valid Entity Type {id}");
|
||||||
let Some(entity_type) = EntityType::from_name(&id) else {
|
continue;
|
||||||
warn!("Entity has no valid Entity Type {id}");
|
};
|
||||||
continue;
|
|
||||||
};
|
|
||||||
entity_data.push((*uuid, entity_type, entity_nbt.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uuid, entity_type, mut entity_nbt) in entity_data {
|
|
||||||
// Pos is zero since it will read from nbt
|
// Pos is zero since it will read from nbt
|
||||||
let entity =
|
let entity =
|
||||||
from_type(entity_type, Vector3::new(0.0, 0.0, 0.0), &world, uuid).await;
|
from_type(entity_type, Vector3::new(0.0, 0.0, 0.0), &world, *uuid)
|
||||||
entity_nbt.read_pos = 0;
|
.await;
|
||||||
entity.read_nbt_non_mut(&mut entity_nbt).await;
|
entity.read_nbt_non_mut(entity_nbt).await;
|
||||||
entities_to_load.push(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
for entity in entities_to_load {
|
|
||||||
let base_entity = entity.get_entity();
|
let base_entity = entity.get_entity();
|
||||||
|
|
||||||
ids_to_remove.push(VarInt(base_entity.entity_id));
|
ids_to_remove.push(VarInt(base_entity.entity_id));
|
||||||
|
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
entity.write_nbt(&mut nbt).await;
|
entity.write_nbt(&mut nbt).await;
|
||||||
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
|
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
|
||||||
let old_chunk = old_chunk.to_vec2_i32();
|
let old_chunk = old_chunk.to_vec2_i32();
|
||||||
@ -2721,65 +2708,40 @@ impl World {
|
|||||||
|
|
||||||
let mut data = chunk.data.lock().await;
|
let mut data = chunk.data.lock().await;
|
||||||
if old_chunk == current_chunk_coordinate {
|
if old_chunk == current_chunk_coordinate {
|
||||||
data.insert(base_entity.entity_uuid, nbt.clone());
|
data.insert(*uuid, nbt);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The chunk has changed, lets remove the entity from the old chunk
|
// The chunk has changed, lets remove the entity from the old chunk
|
||||||
data.remove(&base_entity.entity_uuid);
|
data.remove(uuid);
|
||||||
}
|
}
|
||||||
chunk
|
|
||||||
.data
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.insert(base_entity.entity_uuid, nbt.clone());
|
|
||||||
chunk.mark_dirty(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ids_to_remove.is_empty() {
|
if !ids_to_remove.is_empty() {
|
||||||
world.entities.rcu(|current_entities| {
|
|
||||||
let mut new_entities = (**current_entities).clone();
|
|
||||||
new_entities.retain(|e| {
|
|
||||||
!ids_to_remove.contains(&VarInt(e.get_entity().entity_id))
|
|
||||||
});
|
|
||||||
new_entities
|
|
||||||
});
|
|
||||||
player
|
player
|
||||||
.client
|
.client
|
||||||
.enqueue_packet(&CRemoveEntities::new(&ids_to_remove))
|
.enqueue_packet(&CRemoveEntities::new(&ids_to_remove))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
level.clean_entity_chunk(&position);
|
|
||||||
|
|
||||||
continue 'main;
|
continue 'main;
|
||||||
};
|
}
|
||||||
|
|
||||||
// Add all new Entities to the world
|
// Add all new Entities to the world
|
||||||
let mut entities_to_add: Vec<Arc<dyn EntityBase>> = Vec::new();
|
let mut entities_to_add: Vec<Arc<dyn EntityBase>> = Vec::new();
|
||||||
let mut entity_data = Vec::new();
|
for (uuid, entity_nbt) in chunk.data.lock().await.iter() {
|
||||||
{
|
let Some(id) = entity_nbt.get_string("id") else {
|
||||||
let mut data = chunk.data.lock().await;
|
debug!("Entity has no ID");
|
||||||
for (uuid, entity_nbt) in data.iter_mut() {
|
continue;
|
||||||
let Ok(id) = entity_nbt.get_string() else {
|
};
|
||||||
debug!("Entity has no ID");
|
let Some(entity_type) =
|
||||||
continue;
|
EntityType::from_name(id.strip_prefix("minecraft:").unwrap_or(id))
|
||||||
};
|
else {
|
||||||
let Some(entity_type) =
|
warn!("Entity has no valid Entity Type {id}");
|
||||||
EntityType::from_name(id.strip_prefix("minecraft:").unwrap_or(&id))
|
continue;
|
||||||
else {
|
};
|
||||||
warn!("Entity has no valid Entity Type {id}");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
entity_data.push((*uuid, entity_type, entity_nbt.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uuid, entity_type, mut entity_nbt) in entity_data {
|
|
||||||
// Pos is zero since it will read from nbt
|
// Pos is zero since it will read from nbt
|
||||||
let entity =
|
let entity =
|
||||||
from_type(entity_type, Vector3::new(0.0, 0.0, 0.0), &world, uuid).await;
|
from_type(entity_type, Vector3::new(0.0, 0.0, 0.0), &world, *uuid).await;
|
||||||
entity_nbt.read_pos = 0;
|
entity.read_nbt_non_mut(entity_nbt).await;
|
||||||
entity.read_nbt_non_mut(&mut entity_nbt).await;
|
|
||||||
let base_entity = entity.get_entity();
|
let base_entity = entity.get_entity();
|
||||||
player
|
player
|
||||||
.client
|
.client
|
||||||
@ -3215,7 +3177,7 @@ impl World {
|
|||||||
|
|
||||||
let block_pos = base_entity.block_pos.load();
|
let block_pos = base_entity.block_pos.load();
|
||||||
let chunk_coordinate = block_pos.chunk_position();
|
let chunk_coordinate = block_pos.chunk_position();
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
entity.write_nbt(&mut nbt).await;
|
entity.write_nbt(&mut nbt).await;
|
||||||
|
|
||||||
self.spawn_state.load().add_entity(self, entity.as_ref());
|
self.spawn_state.load().add_entity(self, entity.as_ref());
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use pumpkin_data::tag::Fluid::{MINECRAFT_LAVA, MINECRAFT_WATER};
|
|||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::tag::WorldgenBiome::MINECRAFT_REDUCE_WATER_AMBIENT_SPAWNS;
|
use pumpkin_data::tag::WorldgenBiome::MINECRAFT_REDUCE_WATER_AMBIENT_SPAWNS;
|
||||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||||
use pumpkin_nbt::pnbt::PNbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::GameMode;
|
use pumpkin_util::GameMode;
|
||||||
use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions};
|
use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions};
|
||||||
use pumpkin_util::math::get_section_cord;
|
use pumpkin_util::math::get_section_cord;
|
||||||
@ -716,7 +716,7 @@ pub async fn spawn_category_for_position(
|
|||||||
entity.init_data_tracker().await;
|
entity.init_data_tracker().await;
|
||||||
let base_entity = entity.get_entity();
|
let base_entity = entity.get_entity();
|
||||||
let packet = base_entity.create_spawn_packet();
|
let packet = base_entity.create_spawn_packet();
|
||||||
let mut nbt = PNbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
entity.write_nbt(&mut nbt).await;
|
entity.write_nbt(&mut nbt).await;
|
||||||
// Keep the entity reference here so we don't have to "find" it later
|
// Keep the entity reference here so we don't have to "find" it later
|
||||||
prepared_data.push((base_entity.entity_uuid, nbt, packet, entity.clone()));
|
prepared_data.push((base_entity.entity_uuid, nbt, packet, entity.clone()));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user