fix: disable cave carver for now

fixes some issues
This commit is contained in:
Alexander Medvedev 2026-05-23 16:36:08 +02:00
parent f19d84efef
commit 3629f43dcc
53 changed files with 403 additions and 212 deletions

6
Cargo.lock generated
View File

@ -876,9 +876,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]] [[package]]
name = "crossfire" name = "crossfire"
version = "3.1.15" version = "3.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7985ad34fb5a761ce21154d0e5a8c37ec6b50af61c90d594eb5cdeb934f91f7c" checksum = "4d8c4de3db833e7ef74050bae09d5f3fa8f9d1507d3c2689c6e8c50b71208b18"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
"embed-collections", "embed-collections",
@ -2155,7 +2155,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]

View File

@ -127,7 +127,7 @@ console-subscriber = { version = "0.5.0", default-features = false }
crc-fast = { version = "1.10.0", default-features = false, features = ["std"] } crc-fast = { version = "1.10.0", default-features = false, features = ["std"] }
criterion = { version = "0.8", default-features = false } criterion = { version = "0.8", default-features = false }
crossbeam-utils = { version = "0.8.21", default-features = false, features = ["std"] } crossbeam-utils = { version = "0.8.21", default-features = false, features = ["std"] }
crossfire = { version = "3.1.15", default-features = false, features = ["compat"] } crossfire = { version = "3.1.16", default-features = false, features = ["compat"] }
crypto-bigint = { version = "0.7.3", default-features = false } crypto-bigint = { version = "0.7.3", default-features = false }
dashmap = { version = "6.2", default-features = false } dashmap = { version = "6.2", default-features = false }
ecdsa = { version = "0.16.9", default-features = false, features = ["std"] } ecdsa = { version = "0.16.9", default-features = false, features = ["std"] }

View File

@ -1435,7 +1435,7 @@ fn get_be_data_from_nbt<R: Read + Seek>(
let raw_name = nbt.get_string("name").unwrap(); let raw_name = nbt.get_string("name").unwrap();
raw_name raw_name
.strip_prefix("minecraft:") .strip_prefix("minecraft:")
.unwrap_or(&raw_name) .unwrap_or(raw_name)
.to_string() .to_string()
}; };

View File

@ -91,7 +91,6 @@ impl ToTokens for LootPoolStruct {
} }
} }
/// Deserialized single-item loot entry holding the item's registry key. /// Deserialized single-item loot entry holding the item's registry key.
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
pub struct ItemEntryStruct { pub struct ItemEntryStruct {
@ -428,9 +427,7 @@ pub enum LootConditionStruct {
}, },
/// References an external predicate by ID. /// References an external predicate by ID.
#[serde(rename = "minecraft:reference")] #[serde(rename = "minecraft:reference")]
Reference { Reference { name: String },
name: String,
},
/// Passes based on the current in-game time. /// Passes based on the current in-game time.
#[serde(rename = "minecraft:time_check")] #[serde(rename = "minecraft:time_check")]
TimeCheck { TimeCheck {
@ -446,9 +443,7 @@ pub enum LootConditionStruct {
}, },
/// Passes if an enchantment is currently active. /// Passes if an enchantment is currently active.
#[serde(rename = "minecraft:enchantment_active_check")] #[serde(rename = "minecraft:enchantment_active_check")]
EnchantmentActiveCheck { EnchantmentActiveCheck { active: bool },
active: bool,
},
} }
impl ToTokens for LootConditionStruct { impl ToTokens for LootConditionStruct {
@ -461,7 +456,10 @@ impl ToTokens for LootConditionStruct {
Self::RandomChance { chance } => { Self::RandomChance { chance } => {
quote! { LootCondition::RandomChance { chance: #chance } } quote! { LootCondition::RandomChance { chance: #chance } }
} }
Self::RandomChanceWithEnchantedBonus { enchantment, chances } => { Self::RandomChanceWithEnchantedBonus {
enchantment,
chances,
} => {
let e = LitStr::new(enchantment, Span::call_site()); let e = LitStr::new(enchantment, Span::call_site());
if let Some(chances) = chances { if let Some(chances) = chances {
quote! { LootCondition::RandomChanceWithEnchantedBonus { enchantment: #e, chances: Some(&[#(#chances),*]) } } quote! { LootCondition::RandomChanceWithEnchantedBonus { enchantment: #e, chances: Some(&[#(#chances),*]) } }
@ -472,15 +470,17 @@ impl ToTokens for LootConditionStruct {
Self::EntityProperties { entity, predicate } => { Self::EntityProperties { entity, predicate } => {
let entity = entity.as_deref().unwrap_or("this"); let entity = entity.as_deref().unwrap_or("this");
let e = LitStr::new(entity, Span::call_site()); let e = LitStr::new(entity, Span::call_site());
let expected_type = predicate.as_ref().and_then(|p| p.entity_type.as_ref()).map(|t| { let expected_type = predicate
match t { .as_ref()
.and_then(|p| p.entity_type.as_ref())
.map(|t| match t {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => { StringOrVec::Vec(v) => {
let s = &v[0]; let s = &v[0];
quote! { Some(#s) } quote! { Some(#s) }
} }
} })
}).unwrap_or(quote! { None }); .unwrap_or(quote! { None });
quote! { LootCondition::EntityProperties { entity: #e, expected_type: #expected_type } } quote! { LootCondition::EntityProperties { entity: #e, expected_type: #expected_type } }
} }
Self::KilledByPlayer => quote! { LootCondition::KilledByPlayer }, Self::KilledByPlayer => quote! { LootCondition::KilledByPlayer },
@ -516,7 +516,10 @@ impl ToTokens for LootConditionStruct {
quote! { LootCondition::MatchTool { items: None } } quote! { LootCondition::MatchTool { items: None } }
} }
} }
Self::TableBonus { enchantment, chances } => { Self::TableBonus {
enchantment,
chances,
} => {
let e = LitStr::new(enchantment, Span::call_site()); let e = LitStr::new(enchantment, Span::call_site());
quote! { LootCondition::TableBonus { enchantment: #e, chances: &[#(#chances),*] } } quote! { LootCondition::TableBonus { enchantment: #e, chances: &[#(#chances),*] } }
} }
@ -559,20 +562,29 @@ impl ToTokens for LootConditionStruct {
let ox = offset_x.unwrap_or(0); let ox = offset_x.unwrap_or(0);
let oy = offset_y.unwrap_or(0); let oy = offset_y.unwrap_or(0);
let oz = offset_z.unwrap_or(0); let oz = offset_z.unwrap_or(0);
let expected_biome = predicate.as_ref().and_then(|p| p.biome.as_ref()).map(|b| { let expected_biome = predicate
match b { .as_ref()
.and_then(|p| p.biome.as_ref())
.map(|b| match b {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => { StringOrVec::Vec(v) => {
let s = &v[0]; let s = &v[0];
quote! { Some(#s) } quote! { Some(#s) }
} }
} })
}).unwrap_or(quote! { None }); .unwrap_or(quote! { None });
quote! { LootCondition::LocationCheck { offset_x: #ox, offset_y: #oy, offset_z: #oz, expected_biome: #expected_biome } } quote! { LootCondition::LocationCheck { offset_x: #ox, offset_y: #oy, offset_z: #oz, expected_biome: #expected_biome } }
} }
Self::WeatherCheck { raining, thundering } => { Self::WeatherCheck {
let r = raining.map(|b| quote! { Some(#b) }).unwrap_or(quote! { None }); raining,
let t = thundering.map(|b| quote! { Some(#b) }).unwrap_or(quote! { None }); thundering,
} => {
let r = raining
.map(|b| quote! { Some(#b) })
.unwrap_or(quote! { None });
let t = thundering
.map(|b| quote! { Some(#b) })
.unwrap_or(quote! { None });
quote! { LootCondition::WeatherCheck { raining: #r, thundering: #t } } quote! { LootCondition::WeatherCheck { raining: #r, thundering: #t } }
} }
Self::Reference { name } => { Self::Reference { name } => {
@ -581,7 +593,9 @@ impl ToTokens for LootConditionStruct {
} }
Self::TimeCheck { range, period } => { Self::TimeCheck { range, period } => {
let r = range.to_token_stream(); let r = range.to_token_stream();
let p = period.map(|val| quote! { Some(#val) }).unwrap_or(quote! { None }); let p = period
.map(|val| quote! { Some(#val) })
.unwrap_or(quote! { None });
quote! { LootCondition::TimeCheck { range: #r, period: #p } } quote! { LootCondition::TimeCheck { range: #r, period: #p } }
} }
Self::ValueCheck { value, range } => { Self::ValueCheck { value, range } => {
@ -824,8 +838,14 @@ pub struct LootFunctionLimitCountStruct {
impl ToTokens for LootFunctionLimitCountStruct { impl ToTokens for LootFunctionLimitCountStruct {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let min = self.min.map(|val| quote! { Some(#val) }).unwrap_or(quote! { None }); let min = self
let max = self.max.map(|val| quote! { Some(#val) }).unwrap_or(quote! { None }); .min
.map(|val| quote! { Some(#val) })
.unwrap_or(quote! { None });
let max = self
.max
.map(|val| quote! { Some(#val) })
.unwrap_or(quote! { None });
tokens.extend(quote! { (#min, #max) }); tokens.extend(quote! { (#min, #max) });
} }
} }
@ -918,7 +938,6 @@ impl ToTokens for LootPoolEntryStruct {
} }
} }
/// Deserialized loot table category, tagged by its `"type"` field. /// Deserialized loot table category, tagged by its `"type"` field.
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
#[serde(rename = "snake_case")] #[serde(rename = "snake_case")]

View File

@ -15,7 +15,10 @@ pub fn build() -> TokenStream {
(JavaMinecraftVersion::V_1_21_6, "1_21_6_meta_data_type.json"), (JavaMinecraftVersion::V_1_21_6, "1_21_6_meta_data_type.json"),
(JavaMinecraftVersion::V_1_21_7, "1_21_7_meta_data_type.json"), (JavaMinecraftVersion::V_1_21_7, "1_21_7_meta_data_type.json"),
(JavaMinecraftVersion::V_1_21_9, "1_21_9_meta_data_type.json"), (JavaMinecraftVersion::V_1_21_9, "1_21_9_meta_data_type.json"),
(JavaMinecraftVersion::V_1_21_11, "1_21_11_meta_data_type.json"), (
JavaMinecraftVersion::V_1_21_11,
"1_21_11_meta_data_type.json",
),
(JavaMinecraftVersion::V_26_1, "26_1_meta_data_type.json"), (JavaMinecraftVersion::V_26_1, "26_1_meta_data_type.json"),
]; ];

View File

@ -13,14 +13,35 @@ const LATEST_VERSION: JavaMinecraftVersion = JavaMinecraftVersion::V_26_1;
/// static registry data, and the `Registry::get_synced` method. /// static registry data, and the `Registry::get_synced` method.
pub(crate) fn build() -> TokenStream { pub(crate) fn build() -> TokenStream {
let assets = [ let assets = [
(JavaMinecraftVersion::V_1_20_5, "1_21_synced_registries.json"), (
JavaMinecraftVersion::V_1_20_5,
"1_21_synced_registries.json",
),
(JavaMinecraftVersion::V_1_21, "1_21_synced_registries.json"), (JavaMinecraftVersion::V_1_21, "1_21_synced_registries.json"),
(JavaMinecraftVersion::V_1_21_2, "1_21_2_synced_registries.json"), (
(JavaMinecraftVersion::V_1_21_4, "1_21_4_synced_registries.json"), JavaMinecraftVersion::V_1_21_2,
(JavaMinecraftVersion::V_1_21_5, "1_21_5_synced_registries.json"), "1_21_2_synced_registries.json",
(JavaMinecraftVersion::V_1_21_6, "1_21_6_synced_registries.json"), ),
(JavaMinecraftVersion::V_1_21_7, "1_21_7_synced_registries.json"), (
(JavaMinecraftVersion::V_1_21_9, "1_21_9_synced_registries.json"), JavaMinecraftVersion::V_1_21_4,
"1_21_4_synced_registries.json",
),
(
JavaMinecraftVersion::V_1_21_5,
"1_21_5_synced_registries.json",
),
(
JavaMinecraftVersion::V_1_21_6,
"1_21_6_synced_registries.json",
),
(
JavaMinecraftVersion::V_1_21_7,
"1_21_7_synced_registries.json",
),
(
JavaMinecraftVersion::V_1_21_9,
"1_21_9_synced_registries.json",
),
( (
JavaMinecraftVersion::V_1_21_11, JavaMinecraftVersion::V_1_21_11,
"1_21_11_synced_registries.json", "1_21_11_synced_registries.json",

View File

@ -27,10 +27,7 @@ pub fn build() -> String {
biome_enum.case(name); biome_enum.case(name);
} }
interface.type_def(TypeDef::new( interface.type_def(TypeDef::new("biome", TypeDefKind::Enum(biome_enum)));
"biome",
TypeDefKind::Enum(biome_enum),
));
package.interface(interface); package.interface(interface);
package.to_string() package.to_string()

View File

@ -23,8 +23,7 @@ pub fn build() -> String {
let name = raw_name let name = raw_name
.strip_prefix("minecraft:") .strip_prefix("minecraft:")
.unwrap_or(raw_name) .unwrap_or(raw_name)
.replace('_', "-") .replace(['_', '/'], "-");
.replace('/', "-");
component_enum.case(name); component_enum.case(name);
} }

View File

@ -88,7 +88,9 @@ fn parse_packet_file(path: &Path, interface: &mut Interface, variant: &mut Varia
syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(), syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(),
syn::Type::Reference(r) => match &*r.elem { syn::Type::Reference(r) => match &*r.elem {
syn::Type::Slice(s) => match &*s.elem { syn::Type::Slice(s) => match &*s.elem {
syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(), syn::Type::Path(p) => {
p.path.segments.last().unwrap().ident.to_string()
}
_ => String::new(), _ => String::new(),
}, },
syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(), syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(),

View File

@ -235,7 +235,11 @@ fn parse_packet_file(
fn get_type_info(ty: &syn::Type) -> (String, bool, bool) { fn get_type_info(ty: &syn::Type) -> (String, bool, bool) {
match ty { match ty {
syn::Type::Path(tp) => (tp.path.segments.last().unwrap().ident.to_string(), false, false), syn::Type::Path(tp) => (
tp.path.segments.last().unwrap().ident.to_string(),
false,
false,
),
syn::Type::Reference(tr) => { syn::Type::Reference(tr) => {
let (name, _, is_slice) = get_type_info(&tr.elem); let (name, _, is_slice) = get_type_info(&tr.elem);
(name, true, is_slice) (name, true, is_slice)

View File

@ -17,16 +17,15 @@ pub fn map_type(ty: &Type) -> WitType {
"u8" | "i8" => WitType::U8, "u8" | "i8" => WitType::U8,
"u16" | "i16" => WitType::S32, "u16" | "i16" => WitType::S32,
"Option" => { "Option" => {
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments { if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
return WitType::option(map_type(inner_ty)); return WitType::option(map_type(inner_ty));
} }
}
WitType::String WitType::String
} }
"Vec" | "Box" => { "Vec" | "Box" => {
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments { if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
// Check if the inner type is u8 // Check if the inner type is u8
if let Type::Path(tp) = inner_ty if let Type::Path(tp) = inner_ty
&& tp.path.segments.last().unwrap().ident == "u8" && tp.path.segments.last().unwrap().ident == "u8"
@ -35,7 +34,6 @@ pub fn map_type(ty: &Type) -> WitType {
} }
return WitType::list(map_type(inner_ty)); return WitType::list(map_type(inner_ty));
} }
}
WitType::String WitType::String
} }
"Vector3" | "BlockPos" => { "Vector3" | "BlockPos" => {

View File

@ -47,7 +47,7 @@ fn create_large_compound(depth: usize) -> NbtCompound {
compound.put_string("string", "Hello test benchmark data".to_string()); compound.put_string("string", "Hello test benchmark data".to_string());
compound.put( compound.put(
"byte_array", "byte_array",
NbtTag::ByteArray(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), NbtTag::ByteArray(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
); );
compound.put( compound.put(
"int_array", "int_array",

View File

@ -166,14 +166,14 @@ impl DynamicOps for NbtOps {
fn get_byte_list(&self, input: Self::Value) -> DataResult<Vec<i8>> { fn get_byte_list(&self, input: Self::Value) -> DataResult<Vec<i8>> {
if let NbtTag::ByteArray(b) = input { if let NbtTag::ByteArray(b) = input {
DataResult::new_success(b) DataResult::new_success(b.into())
} else { } else {
impl_get_list!(self, input, "bytes") impl_get_list!(self, input, "bytes")
} }
} }
fn create_byte_list(&self, buffer: Vec<i8>) -> Self::Value { fn create_byte_list(&self, buffer: Vec<i8>) -> Self::Value {
NbtTag::ByteArray(buffer) NbtTag::ByteArray(buffer.into())
} }
fn get_int_list(&self, input: Self::Value) -> DataResult<Vec<i32>> { fn get_int_list(&self, input: Self::Value) -> DataResult<Vec<i32>> {
@ -301,7 +301,7 @@ impl DynamicOps for NbtOps {
NbtTag::Long(l) => out_ops.create_long(l), NbtTag::Long(l) => out_ops.create_long(l),
NbtTag::Float(f) => out_ops.create_float(f), NbtTag::Float(f) => out_ops.create_float(f),
NbtTag::Double(d) => out_ops.create_double(d), NbtTag::Double(d) => out_ops.create_double(d),
NbtTag::ByteArray(b) => out_ops.create_byte_list(b), NbtTag::ByteArray(b) => out_ops.create_byte_list(b.to_vec()),
NbtTag::String(s) => out_ops.create_string(&s), NbtTag::String(s) => out_ops.create_string(&s),
NbtTag::List(_) => self.convert_list(out_ops, input), NbtTag::List(_) => self.convert_list(out_ops, input),
NbtTag::Compound(_) => self.convert_map(out_ops, input), NbtTag::Compound(_) => self.convert_map(out_ops, input),
@ -477,7 +477,9 @@ impl ListCollector {
// From this point onwards, we know that the list is not empty. // From this point onwards, we know that the list is not empty.
match tag { match tag {
NbtTag::List(list) => Some(Self::Generic(InnerGenericListCollector::new(list))), NbtTag::List(list) => Some(Self::Generic(InnerGenericListCollector::new(list))),
NbtTag::ByteArray(list) => Some(Self::Byte(InnerByteListCollector::new(list))), NbtTag::ByteArray(list) => {
Some(Self::Byte(InnerByteListCollector::new(list.into())))
}
NbtTag::IntArray(list) => Some(Self::Int(InnerIntListCollector::new(list))), NbtTag::IntArray(list) => Some(Self::Int(InnerIntListCollector::new(list))),
NbtTag::LongArray(list) => Some(Self::Long(InnerLongListCollector::new(list))), NbtTag::LongArray(list) => Some(Self::Long(InnerLongListCollector::new(list))),
@ -592,7 +594,7 @@ impl InnerListCollector for InnerByteListCollector {
} }
fn result(self) -> NbtTag { fn result(self) -> NbtTag {
NbtTag::ByteArray(self.list) NbtTag::ByteArray(self.list.into())
} }
} }
@ -669,13 +671,13 @@ mod test {
); );
// Byte list collector // Byte list collector
let tag = NbtTag::ByteArray(vec![-1, 45, 100]); let tag = NbtTag::ByteArray(vec![-1, 45, 100].into());
assert_eq!( assert_eq!(
ListCollector::new(tag) ListCollector::new(tag)
.expect("List collector should exist") .expect("List collector should exist")
.result(), .result(),
NbtTag::ByteArray(vec![-1, 45, 100]) NbtTag::ByteArray(vec![-1, 45, 100].into())
); );
// Long list // Long list

View File

@ -19,7 +19,7 @@ pub enum NbtTag {
Long(i64) = LONG_ID, Long(i64) = LONG_ID,
Float(f32) = FLOAT_ID, Float(f32) = FLOAT_ID,
Double(f64) = DOUBLE_ID, Double(f64) = DOUBLE_ID,
ByteArray(Vec<i8>) = BYTE_ARRAY_ID, ByteArray(Box<[i8]>) = BYTE_ARRAY_ID,
String(Box<str>) = STRING_ID, String(Box<str>) = STRING_ID,
List(Vec<Self>) = LIST_ID, List(Vec<Self>) = LIST_ID,
Compound(NbtCompound) = COMPOUND_ID, Compound(NbtCompound) = COMPOUND_ID,
@ -281,7 +281,7 @@ impl NbtTag {
let byte = reader.get_i8()?; let byte = reader.get_i8()?;
byte_array.push(byte); byte_array.push(byte);
} }
Ok(Self::ByteArray(byte_array)) Ok(Self::ByteArray(byte_array.into()))
} }
STRING_ID => Ok(Self::String(reader.get_string()?.into())), STRING_ID => Ok(Self::String(reader.get_string()?.into())),
LIST_ID => { LIST_ID => {
@ -457,7 +457,7 @@ impl From<&str> for NbtTag {
impl From<&[i8]> for NbtTag { impl From<&[i8]> for NbtTag {
fn from(value: &[i8]) -> Self { fn from(value: &[i8]) -> Self {
Self::ByteArray(value.to_vec()) Self::ByteArray(value.into())
} }
} }
@ -575,7 +575,7 @@ impl<'de> Deserialize<'de> for NbtTag {
while let Some(value) = seq.next_element()? { while let Some(value) = seq.next_element()? {
vec.push(value); vec.push(value);
} }
Ok(NbtTag::ByteArray(vec)) Ok(NbtTag::ByteArray(vec.into()))
} }
_ => { _ => {
let mut vec = Vec::new(); let mut vec = Vec::new();

View File

@ -87,7 +87,7 @@ impl UDPNetworkDecoder {
pub async fn get_packet_payload( pub async fn get_packet_payload(
&mut self, &mut self,
mut full_packet: Vec<u8>, full_packet: Vec<u8>,
) -> Result<Vec<u8>, PacketDecodeError> { ) -> Result<Vec<u8>, PacketDecodeError> {
if full_packet.is_empty() { if full_packet.is_empty() {
return Err(PacketDecodeError::MalformedLength("Empty packet".into())); return Err(PacketDecodeError::MalformedLength("Empty packet".into()));
@ -104,7 +104,7 @@ impl UDPNetworkDecoder {
// If compression is NOT enabled yet, the payload starts at index 1. // If compression is NOT enabled yet, the payload starts at index 1.
if self.compression.is_none() { if self.compression.is_none() {
return Ok(full_packet.split_off(1)); return Ok(full_packet[1..].to_vec());
} }
// If compression IS enabled, Bedrock expects a compression method byte at index 1. // If compression IS enabled, Bedrock expects a compression method byte at index 1.
@ -125,7 +125,7 @@ impl UDPNetworkDecoder {
} }
0xff => { 0xff => {
// None (Compression enabled but this specific packet is raw) // None (Compression enabled but this specific packet is raw)
Ok(full_packet.split_off(2)) Ok(full_packet[data_start..].to_vec())
} }
_ => Err(PacketDecodeError::FailedDecompression(format!( _ => Err(PacketDecodeError::FailedDecompression(format!(
"Unsupported compression method: 0x{compression_method:02x}" "Unsupported compression method: 0x{compression_method:02x}"

View File

@ -35,7 +35,7 @@ pub struct CSetPlayerTeam<'a> {
pub team_name: String, pub team_name: String,
pub method: TeamMethod, pub method: TeamMethod,
pub parameters: Option<TeamParameters<'a>>, pub parameters: Option<TeamParameters<'a>>,
pub players: Vec<String>, pub players: Box<[String]>,
} }
impl ClientPacket for CSetPlayerTeam<'_> { impl ClientPacket for CSetPlayerTeam<'_> {

View File

@ -1,8 +1,8 @@
use super::Carver; use super::Carver;
use crate::ProtoChunk; use crate::ProtoChunk;
use pumpkin_data::block_state::BlockState;
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig, HeightProvider}; use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig, HeightProvider};
use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector2::Vector2;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::random::{RandomGenerator, RandomImpl}; use pumpkin_util::random::{RandomGenerator, RandomImpl};
use std::f32::consts::PI; use std::f32::consts::PI;
@ -352,8 +352,8 @@ impl CaveCarver {
has_grass: &mut bool, has_grass: &mut bool,
) -> bool { ) -> bool {
let local_y = y - chunk.bottom_y() as i32; let local_y = y - chunk.bottom_y() as i32;
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15); let state = chunk.get_block_state(&Vector3::new(x, y, z));
let block = pumpkin_data::Block::from_state_id(state_id); let block = state.to_block();
if block.id == pumpkin_data::Block::GRASS_BLOCK.id if block.id == pumpkin_data::Block::GRASS_BLOCK.id
|| block.id == pumpkin_data::Block::MYCELIUM.id || block.id == pumpkin_data::Block::MYCELIUM.id
@ -361,12 +361,11 @@ impl CaveCarver {
*has_grass = true; *has_grass = true;
} }
// Only carve if it's replaceable if !config.replaceable.1.contains(&block.id) {
if config.replaceable.1.contains(&block.id) { return false;
// Replace with air or lava }
let air = BlockState::from_id(pumpkin_data::Block::AIR.default_state.id);
let lava = BlockState::from_id(pumpkin_data::Block::LAVA.default_state.id);
let carve_state = {
let lava_y = if is_nether { let lava_y = if is_nether {
chunk.bottom_y() as i32 + 31 chunk.bottom_y() as i32 + 31
} else { } else {
@ -376,26 +375,44 @@ impl CaveCarver {
}; };
if y <= lava_y { if y <= lava_y {
chunk.set_block_state(x & 15, local_y, z & 15, lava); Some(pumpkin_data::Block::LAVA.default_state)
} else { } else {
chunk.set_block_state(x & 15, local_y, z & 15, air); // TODO: Aquifer logic goes here.
// BlockState state = aquifer.computeSubstance(...)
// return state (or debug barrier if null)
if block.id == pumpkin_data::Block::WATER.id
|| block.id == pumpkin_data::Block::LAVA.id
{
None
} else {
Some(pumpkin_data::Block::AIR.default_state)
} }
}
};
// TODO: fix this if let Some(state) = carve_state {
chunk.set_block_state(x, local_y, z, state);
// TODO: Fluid scheduling
// if aquifer.should_schedule_fluid_update() && !state.fluid_state().is_empty() {
// chunk.mark_pos_for_postprocessing(x, y, z);
// }
// TODO: fix this (grass block survival logic)
// if *has_grass { // if *has_grass {
// let below_state_id = chunk.get_block_state_raw(x & 15, local_y - 1, z & 15); // let below_state_id = chunk.get_block_state_raw(x, local_y - 1, z);
// let below_block = pumpkin_data::Block::from_state_id(below_state_id); // let below_block = pumpkin_data::Block::from_state_id(below_state_id);
// if below_block.id == pumpkin_data::Block::DIRT.id { // if below_block.id == pumpkin_data::Block::DIRT.id {
// // TODO: Java uses Biome top material here, defaulting to Grass for now
// let top_material = // let top_material =
// BlockState::from_id(pumpkin_data::Block::GRASS_BLOCK.default_state.id); // pumpkin_data::Block::GRASS_BLOCK.default_state;
// chunk.set_block_state(x & 15, local_y - 1, z & 15, top_material); // chunk.set_block_state(x, local_y - 1, z, top_material);
// } // }
// } // }
return true; return true;
} }
false false
} }
} }

View File

@ -39,7 +39,7 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
&[] &[]
}; };
let cave_carver = cave::CaveCarver; let _cave_carver = cave::CaveCarver;
let canyon_carver = canyon::CanyonCarver; let canyon_carver = canyon::CanyonCarver;
for dx in -radius..=radius { for dx in -radius..=radius {
@ -69,14 +69,14 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
if should_carve(config, &mut carver_random) { if should_carve(config, &mut carver_random) {
match config.additional { match config.additional {
CarverAdditionalConfig::Cave(_) | CarverAdditionalConfig::NetherCave(_) => { CarverAdditionalConfig::Cave(_) | CarverAdditionalConfig::NetherCave(_) => {
cave_carver.carve( // cave_carver.carve(
config, // config,
chunk, // chunk,
&mut carver_random, // &mut carver_random,
&chunk_pos, // &chunk_pos,
&carver_chunk_pos, // &carver_chunk_pos,
generator.settings.legacy_random_source, // generator.settings.legacy_random_source,
); // );
} }
CarverAdditionalConfig::Canyon(_) => { CarverAdditionalConfig::Canyon(_) => {
canyon_carver.carve( canyon_carver.carve(

View File

@ -61,7 +61,11 @@ impl CommandBlock {
warn!("Command block has no matching entity"); warn!("Command block has no matching entity");
return false; return false;
}; };
let command_entity: &CommandBlockEntity = before_entity.as_any().downcast_ref().unwrap(); let Some(command_entity) = before_entity.as_any().downcast_ref::<CommandBlockEntity>()
else {
warn!("Block entity at {} is not a command block", before.0);
return false;
};
command_entity.success_count.load(Ordering::Relaxed) > 0 command_entity.success_count.load(Ordering::Relaxed) > 0
} }
@ -122,7 +126,10 @@ impl CommandBlock {
return; return;
} }
let command_entity: Arc<CommandBlockEntity> = Arc::downcast(block_entity).unwrap(); let Ok(command_entity) = Arc::downcast::<CommandBlockEntity>(block_entity) else {
warn!("Failed to downcast block entity to CommandBlockEntity");
return;
};
if command.is_empty() { if command.is_empty() {
command_entity.success_count.store(0, Ordering::Release); command_entity.success_count.store(0, Ordering::Release);
@ -164,7 +171,11 @@ impl CommandBlock {
break; break;
}; };
let command_entity: &CommandBlockEntity = block_entity.as_any().downcast_ref().unwrap(); let Some(command_entity) = block_entity.as_any().downcast_ref::<CommandBlockEntity>()
else {
warn!("Block entity at {} is not a command block", pos);
break;
};
let powered = command_entity.powered.load(Ordering::Relaxed); let powered = command_entity.powered.load(Ordering::Relaxed);
let auto = command_entity.auto.load(Ordering::Relaxed); let auto = command_entity.auto.load(Ordering::Relaxed);
let state_id = world.get_block_state_id(&pos); let state_id = world.get_block_state_id(&pos);
@ -174,7 +185,10 @@ impl CommandBlock {
let conditions_met = Self::conditions_met(&world, &pos, direction); let conditions_met = Self::conditions_met(&world, &pos, direction);
if conditions_met { if conditions_met {
let command = command_entity.command.lock().await; let command = command_entity.command.lock().await;
let entity = world.get_block_entity(&pos).unwrap(); let Some(entity) = world.get_block_entity(&pos) else {
warn!("Command block entity disappeared during execution");
break;
};
Self::execute(server, world.clone(), entity, &command).await; Self::execute(server, world.clone(), entity, &command).await;
} else if props.conditional { } else if props.conditional {
command_entity.success_count.store(0, Ordering::Release); command_entity.success_count.store(0, Ordering::Release);
@ -238,10 +252,12 @@ impl BlockBehaviour for CommandBlock {
if block_entity.resource_location() != CommandBlockEntity::ID { if block_entity.resource_location() != CommandBlockEntity::ID {
return; return;
} }
let command_entity = block_entity let Some(command_entity) =
.as_any() block_entity.as_any().downcast_ref::<CommandBlockEntity>()
.downcast_ref::<CommandBlockEntity>() else {
.unwrap(); warn!("Block entity at {} is not a command block", args.position);
return;
};
Self::update( Self::update(
args.world, args.world,
@ -268,7 +284,11 @@ impl BlockBehaviour for CommandBlock {
return; return;
} }
let command_entity: &CommandBlockEntity = block_entity.as_any().downcast_ref().unwrap(); let Some(command_entity) = block_entity.as_any().downcast_ref::<CommandBlockEntity>()
else {
warn!("Block entity at {} is not a command block", args.position);
return;
};
let Some(server) = args.world.server.upgrade() else { let Some(server) = args.world.server.upgrade() else {
return; return;
}; };

View File

@ -109,7 +109,13 @@ impl FireBlockBase {
Xoroshiro::from_seed(rand::rng().random()), Xoroshiro::from_seed(rand::rng().random()),
)) ))
}; };
NetherPortal::get_new_portal(world, block_pos, dir.to_horizontal_axis().unwrap()).is_some() NetherPortal::get_new_portal(
world,
block_pos,
dir.to_horizontal_axis()
.expect("Direction should be horizontal"),
)
.is_some()
} }
/// Shared fire collision behavior used by `fire` and `soul_fire`. /// Shared fire collision behavior used by `fire` and `soul_fire`.

View File

@ -26,8 +26,10 @@ impl BlockBehaviour for LadderBlock {
let props = let props =
LadderLikeProperties::from_state_id(clicked_block_state_id, clicked_block); LadderLikeProperties::from_state_id(clicked_block_state_id, clicked_block);
let sub = args.position.0.sub(&clicked_pos.0); let sub = args.position.0.sub(&clicked_pos.0);
let dir = horizontal_facing_from_offset(sub).unwrap(); if let Some(dir) = horizontal_facing_from_offset(sub)
if props.facing == dir.to_horizontal_facing().unwrap() { && let Some(horizontal_facing) = dir.to_horizontal_facing()
&& props.facing == horizontal_facing
{
return Block::AIR.default_state.id; return Block::AIR.default_state.id;
} }
} }
@ -45,7 +47,7 @@ impl BlockBehaviour for LadderBlock {
.opposite() .opposite()
.to_block_direction() .to_block_direction()
.to_horizontal_facing() .to_horizontal_facing()
.unwrap(); .expect("Opposite of horizontal direction should be horizontal");
return props.to_state_id(args.block); return props.to_state_id(args.block);
} }
Block::AIR.default_state.id Block::AIR.default_state.id

View File

@ -48,7 +48,8 @@ impl BlockBehaviour for LanternBlock {
} }
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
can_place_at(args.world.unwrap(), args.position) args.world
.is_some_and(|world| can_place_at(world, args.position))
} }
fn get_state_for_neighbor_update<'a>( fn get_state_for_neighbor_update<'a>(

View File

@ -216,7 +216,7 @@ impl BlockBehaviour for PistonBlock {
let mut props = PistonProps::default(block); let mut props = PistonProps::default(block);
props.facing = BlockDirection::by_index((data & 7) as usize) props.facing = BlockDirection::by_index((data & 7) as usize)
.unwrap() .expect("Invalid block direction index")
.to_facing(); .to_facing();
world.add_block_entity(Arc::new(PistonBlockEntity { world.add_block_entity(Arc::new(PistonBlockEntity {
@ -343,7 +343,10 @@ pub async fn try_move(world: &Arc<World>, block: &Block, block_pos: &BlockPos) {
if new_props.facing == props.facing if new_props.facing == props.facing
&& let Some(entity) = world.get_block_entity(&new_pos) && let Some(entity) = world.get_block_entity(&new_pos)
{ {
let piston = entity.as_any().downcast_ref::<PistonBlockEntity>().unwrap(); let piston = entity
.as_any()
.downcast_ref::<PistonBlockEntity>()
.expect("Block entity at MOVING_PISTON should be PistonBlockEntity");
if piston.extending && piston.current_progress.load() < 0.5 if piston.extending && piston.current_progress.load() < 0.5
// TODO: more stuff... // TODO: more stuff...
{ {

View File

@ -87,7 +87,8 @@ impl BlockBehaviour for PressurePlateBlock {
} }
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
Self::can_pressure_plate_place_at(args.world.unwrap(), args.position) args.world
.is_some_and(|world| Self::can_pressure_plate_place_at(world, args.position))
} }
} }

View File

@ -86,7 +86,8 @@ impl BlockBehaviour for WeightedPressurePlateBlock {
} }
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
Self::can_pressure_plate_place_at(args.world.unwrap(), args.position) args.world
.is_some_and(|world| Self::can_pressure_plate_place_at(world, args.position))
} }
} }

View File

@ -53,7 +53,7 @@ impl BrewingStandBlockEntity {
fn ingredient_matches(&self, ingredient: &ItemStack) -> bool { fn ingredient_matches(&self, ingredient: &ItemStack) -> bool {
self.ingredient_item self.ingredient_item
.lock() .lock()
.unwrap() .expect("Ingredient item mutex should not be poisoned")
.is_some_and(|stored| !ingredient.is_empty() && ingredient.get_item().id == stored.id) .is_some_and(|stored| !ingredient.is_empty() && ingredient.get_item().id == stored.id)
} }
@ -350,7 +350,10 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
if let Ok(guard) = entity.items[3].try_lock() if let Ok(guard) = entity.items[3].try_lock()
&& !guard.is_empty() && !guard.is_empty()
{ {
*entity.ingredient_item.lock().unwrap() = Some(guard.get_item()); *entity
.ingredient_item
.lock()
.expect("Ingredient item mutex should not be poisoned") = Some(guard.get_item());
} }
// Recompute last_potion_count so visuals are correct after load // Recompute last_potion_count so visuals are correct after load
@ -362,7 +365,10 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
|| slot.get_item().id == pumpkin_data::item::Item::GLASS_BOTTLE.id); || slot.get_item().id == pumpkin_data::item::Item::GLASS_BOTTLE.id);
} }
} }
*entity.last_potion_count.lock().unwrap() = Some(current); *entity
.last_potion_count
.lock()
.expect("Last potion count mutex should not be poisoned") = Some(current);
entity entity
} }
@ -455,7 +461,11 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
// Start new brewing cycle // Start new brewing cycle
self.fuel.fetch_sub(1, Ordering::Relaxed); self.fuel.fetch_sub(1, Ordering::Relaxed);
self.brew_time.store(400, Ordering::Relaxed); self.brew_time.store(400, Ordering::Relaxed);
*self.ingredient_item.lock().unwrap() = Some(ingredient.get_item()); *self
.ingredient_item
.lock()
.expect("Ingredient item mutex should not be poisoned") =
Some(ingredient.get_item());
self.mark_dirty(); self.mark_dirty();
} else if fuel_refilled { } else if fuel_refilled {
// Mark dirty if fuel was refilled to update fuel indicator // Mark dirty if fuel was refilled to update fuel indicator
@ -474,8 +484,11 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
// If potion presence changed, update last_potion_count and update block state so clients // If potion presence changed, update last_potion_count and update block state so clients
let mut needs_update = false; let mut needs_update = false;
{ {
let mut last_guard = self.last_potion_count.lock().unwrap(); let mut last_guard = self
if last_guard.is_none() || last_guard.as_ref().unwrap() != &current { .last_potion_count
.lock()
.expect("Last potion count mutex should not be poisoned");
if last_guard.as_ref() != Some(&current) {
*last_guard = Some(current); *last_guard = Some(current);
needs_update = true; needs_update = true;
} }

View File

@ -34,7 +34,7 @@ macro_rules! impl_block_entity_for_chest {
}; };
// Only read saved items when there is no pending loot table. // Only read saved items when there is no pending loot table.
if chest.loot_table.lock().unwrap().is_none() { if chest.loot_table.lock().expect("Loot table mutex should not be poisoned").is_none() {
chest.read_data(nbt, &chest.items); chest.read_data(nbt, &chest.items);
} }
@ -50,7 +50,7 @@ macro_rules! impl_block_entity_for_chest {
Box::pin(async move { Box::pin(async move {
// Clone the loot table key without holding the lock across an await. // Clone the loot table key without holding the lock across an await.
let loot_table_key = { let loot_table_key = {
let guard = self.loot_table.lock().unwrap(); let guard = self.loot_table.lock().expect("Loot table mutex should not be poisoned");
guard.clone() guard.clone()
}; };
@ -100,12 +100,12 @@ macro_rules! impl_block_entity_for_chest {
} }
fn take_loot_table(&self) -> Option<(String, i64)> { fn take_loot_table(&self) -> Option<(String, i64)> {
let mut guard = self.loot_table.lock().unwrap(); let mut guard = self.loot_table.lock().expect("Loot table mutex should not be poisoned");
guard.take().map(|key| (key, self.loot_table_seed)) guard.take().map(|key| (key, self.loot_table_seed))
} }
fn has_loot_table(&self) -> bool { fn has_loot_table(&self) -> bool {
self.loot_table.lock().unwrap().is_some() self.loot_table.lock().expect("Loot table mutex should not be poisoned").is_some()
} }
} }
}; };

View File

@ -135,7 +135,11 @@ macro_rules! impl_cooking_block_entity_base {
} }
if let Some(recipe_output_item) = pumpkin_data::item::Item::from_registry_key( if let Some(recipe_output_item) = pumpkin_data::item::Item::from_registry_key(
recipe.result.id.strip_prefix("minecraft:").unwrap(), recipe
.result
.id
.strip_prefix("minecraft:")
.expect("Recipe ID should have minecraft: prefix"),
) && !is_top_items_empty ) && !is_top_items_empty
&& recipe_output_item.id == side_item_stack.item.id && recipe_output_item.id == side_item_stack.item.id
&& side_item_stack.item_count < max_count && side_item_stack.item_count < max_count
@ -156,7 +160,11 @@ macro_rules! impl_cooking_block_entity_base {
if can_accept_output { if can_accept_output {
let mut side_items = self.items[2].lock().await; let mut side_items = self.items[2].lock().await;
let Some(output_item) = pumpkin_data::item::Item::from_registry_key( let Some(output_item) = pumpkin_data::item::Item::from_registry_key(
recipe.result.id.strip_prefix("minecraft:").unwrap(), recipe
.result
.id
.strip_prefix("minecraft:")
.expect("Recipe ID should have minecraft: prefix"),
) else { ) else {
return false; return false;
}; };

View File

@ -86,9 +86,14 @@ pub trait BlockEntity: Any + Send + Sync {
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
.iter() .iter()
.position(|block_entity_name| { .position(|block_entity_name| {
*block_entity_name == self.resource_location().split(':').next_back().unwrap() *block_entity_name
== self
.resource_location()
.split(':')
.next_back()
.expect("Resource location should have a name")
}) })
.unwrap() as u32 .expect("Block entity type should be registered") as u32
} }
/// Obtain NBT data for sending to the client in [`ChunkData`](crate::chunk::ChunkData) /// Obtain NBT data for sending to the client in [`ChunkData`](crate::chunk::ChunkData)
@ -135,9 +140,9 @@ pub trait BlockEntity: Any + Send + Sync {
#[must_use] #[must_use]
pub fn block_entity_from_generic<T: BlockEntity>(nbt: &NbtCompound) -> T { pub fn block_entity_from_generic<T: BlockEntity>(nbt: &NbtCompound) -> T {
let x = nbt.get_int("x").unwrap(); let x = nbt.get_int("x").expect("NBT should have x coordinate");
let y = nbt.get_int("y").unwrap(); let y = nbt.get_int("y").expect("NBT should have y coordinate");
let z = nbt.get_int("z").unwrap(); let z = nbt.get_int("z").expect("NBT should have z coordinate");
T::from_nbt(nbt, BlockPos::new(x, y, z)) T::from_nbt(nbt, BlockPos::new(x, y, z))
} }

View File

@ -158,7 +158,7 @@ impl From<Text> for NbtTag {
value value
.messages .messages
.lock() .lock()
.unwrap() .expect("Text messages mutex should not be poisoned")
.iter() .iter()
.map(|s| Self::String(s.clone())) .map(|s| Self::String(s.clone()))
.collect(), .collect(),
@ -172,7 +172,7 @@ impl From<NbtTag> for Text {
fn from(tag: NbtTag) -> Self { fn from(tag: NbtTag) -> Self {
let nbt = tag.extract_compound().unwrap(); let nbt = tag.extract_compound().unwrap();
let has_glowing_text = nbt.get_bool("has_glowing_text").unwrap_or(false); let has_glowing_text = nbt.get_bool("has_glowing_text").unwrap_or(false);
let color = nbt.get_string("color").unwrap(); let color = nbt.get_string("color").unwrap_or("black");
let messages: Vec<Box<str>> = nbt let messages: Vec<Box<str>> = nbt
.get_list("messages") .get_list("messages")
.unwrap() .unwrap()
@ -226,8 +226,16 @@ impl BlockEntity for SignBlockEntity {
where where
Self: Sized, Self: Sized,
{ {
let front_text = Text::from(nbt.get("front_text").unwrap().clone()); let front_text = nbt
let back_text = Text::from(nbt.get("back_text").unwrap().clone()); .get("front_text")
.cloned()
.map(Text::from)
.unwrap_or_default();
let back_text = nbt
.get("back_text")
.cloned()
.map(Text::from)
.unwrap_or_default();
let is_waxed = nbt.get_bool("is_waxed").unwrap_or(false); let is_waxed = nbt.get_bool("is_waxed").unwrap_or(false);
Self { Self {
position, position,

View File

@ -471,7 +471,7 @@ mod test {
let Err(error) = parse_target_selector_with_context(raw_arg) else { let Err(error) = parse_target_selector_with_context(raw_arg) else {
panic!("expected selector parsing to fail"); panic!("expected selector parsing to fail");
}; };
let cursor = error.context.unwrap().cursor; let cursor = error.context.expect("Error should have context").cursor;
assert_eq!(cursor, 7); assert_eq!(cursor, 7);
} }
@ -484,7 +484,9 @@ mod test {
end: input.len(), end: input.len(),
input, input,
}; };
let selector = "@e".parse::<TargetSelector>().unwrap(); let selector = "@e"
.parse::<TargetSelector>()
.expect("Selector should be valid");
let error = ensure_player_only_selector(&selector, raw_arg).unwrap_err(); let error = ensure_player_only_selector(&selector, raw_arg).unwrap_err();
let translate_key = match error.message.0.content.as_ref() { let translate_key = match error.message.0.content.as_ref() {
@ -492,6 +494,6 @@ mod test {
_ => "", _ => "",
}; };
assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_ENTITIES); assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_ENTITIES);
assert_eq!(error.context.unwrap().cursor, 4); assert_eq!(error.context.expect("Error should have context").cursor, 4);
} }
} }

View File

@ -423,6 +423,6 @@ mod test {
_ => "", _ => "",
}; };
assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_UNKNOWN); assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_UNKNOWN);
assert_eq!(error.context.unwrap().cursor, 4); assert_eq!(error.context.expect("Error should have context").cursor, 4);
} }
} }

View File

@ -162,7 +162,10 @@ impl EntitySelector {
) -> Result<Arc<Player>, CommandSyntaxError> { ) -> Result<Arc<Player>, CommandSyntaxError> {
let list = self.find_players(source).await?; let list = self.find_players(source).await?;
if list.len() == 1 { if list.len() == 1 {
Ok(list.first().unwrap().clone()) Ok(list
.first()
.expect("List length is 1, so first should exist")
.clone())
} else { } else {
Err(entity::NO_PLAYERS_ERROR_TYPE.create_without_context()) Err(entity::NO_PLAYERS_ERROR_TYPE.create_without_context())
} }

View File

@ -250,7 +250,7 @@ impl<'b, 'a> EntitySelectorParser<'b, 'a> {
return Err(MISSING_SELECTOR_TYPE_ERROR_TYPE.create(self.reader)); return Err(MISSING_SELECTOR_TYPE_ERROR_TYPE.create(self.reader));
} }
let i = self.reader.cursor(); let i = self.reader.cursor();
let char = self.reader.read().unwrap(); let char = self.reader.read().expect("can_read_char is true");
let mut add_alive_predicate = false; let mut add_alive_predicate = false;
match char { match char {
'a' => { 'a' => {

View File

@ -93,7 +93,7 @@ impl CommandExecutor for GiveExecutor {
.living_entity .living_entity
.get_effect(effect) .get_effect(effect)
.await .await
.unwrap() .expect("Effect should exist because has_effect returned true")
.amplifier .amplifier
>= amplifier >= amplifier
{ {

View File

@ -65,7 +65,7 @@ impl CommandExecutor for Executor {
.find_map(|(id, component)| { .find_map(|(id, component)| {
(id == &MaxStackSize).then(|| get::<MaxStackSizeImpl>(*component).size) (id == &MaxStackSize).then(|| get::<MaxStackSizeImpl>(*component).size)
}) })
.unwrap(), .expect("Item should have MaxStackSize component"),
); );
let mut remaining = item_count; let mut remaining = item_count;

View File

@ -176,7 +176,7 @@ fn register_permissions(registry: &mut PermissionRegistry) {
"Allows a player to use selector variables", "Allows a player to use selector variables",
PermissionDefault::Allow, PermissionDefault::Allow,
)) ))
.unwrap(); .expect("Permission already registered");
} }
fn register_level_0_permissions(registry: &mut PermissionRegistry) { fn register_level_0_permissions(registry: &mut PermissionRegistry) {
@ -187,21 +187,21 @@ fn register_level_0_permissions(registry: &mut PermissionRegistry) {
"Shows information about the Pumpkin server", "Shows information about the Pumpkin server",
PermissionDefault::Allow, PermissionDefault::Allow,
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.me", "minecraft:command.me",
"Broadcasts a narrative message about the player", "Broadcasts a narrative message about the player",
PermissionDefault::Allow, PermissionDefault::Allow,
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.msg", "minecraft:command.msg",
"Sends a private message to another player", "Sends a private message to another player",
PermissionDefault::Allow, PermissionDefault::Allow,
)) ))
.unwrap(); .expect("Permission already registered");
} }
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
@ -213,189 +213,189 @@ fn register_level_2_permissions(registry: &mut PermissionRegistry) {
"Manages the world border", "Manages the world border",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.effect", "minecraft:command.effect",
"Adds or removes status effects", "Adds or removes status effects",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.teleport", "minecraft:command.teleport",
"Teleports entities to other locations", "Teleports entities to other locations",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.time", "minecraft:command.time",
"Changes or queries the world's game time", "Changes or queries the world's game time",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.give", "minecraft:command.give",
"Gives an item to a player", "Gives an item to a player",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.clear", "minecraft:command.clear",
"Clears items from player inventory", "Clears items from player inventory",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.setblock", "minecraft:command.setblock",
"Changes a block to another block", "Changes a block to another block",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.fill", "minecraft:command.fill",
"Fills a region with a specific block", "Fills a region with a specific block",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.playsound", "minecraft:command.playsound",
"Plays a sound to players", "Plays a sound to players",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.tellraw", "minecraft:command.tellraw",
"Displays a JSON message to players", "Displays a JSON message to players",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.title", "minecraft:command.title",
"Controls screen titles displayed to players", "Controls screen titles displayed to players",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.summon", "minecraft:command.summon",
"Summons an entity", "Summons an entity",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.experience", "minecraft:command.experience",
"Adds, removes or queries player experience", "Adds, removes or queries player experience",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.weather", "minecraft:command.weather",
"Sets the weather in the server", "Sets the weather in the server",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.particle", "minecraft:command.particle",
"Creates particles in the world", "Creates particles in the world",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.rotate", "minecraft:command.rotate",
"Changes the rotation of an entity", "Changes the rotation of an entity",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.damage", "minecraft:command.damage",
"Damages entities", "Damages entities",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.bossbar", "minecraft:command.bossbar",
"Creates and manages boss bars", "Creates and manages boss bars",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.say", "minecraft:command.say",
"Broadcasts a message to multiple players", "Broadcasts a message to multiple players",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.gamemode", "minecraft:command.gamemode",
"Sets a player's game mode", "Sets a player's game mode",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.gamerule", "minecraft:command.gamerule",
"Sets a player's game mode", "Sets a player's game mode",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.stopsound", "minecraft:command.stopsound",
"Stops sounds from playing", "Stops sounds from playing",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.defaultgamemode", "minecraft:command.defaultgamemode",
"Sets the default game mode for new players", "Sets the default game mode for new players",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.data", "minecraft:command.data",
"Query and modify data of entities and blocks", "Query and modify data of entities and blocks",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.enchant", "minecraft:command.enchant",
"Adds an enchantment to a player's selected item, subject to the same restrictions as an anvil. Also works on any mob or entity holding a weapon/tool/armor in its main hand.", "Adds an enchantment to a player's selected item, subject to the same restrictions as an anvil. Also works on any mob or entity holding a weapon/tool/armor in its main hand.",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.spawnpoint", "minecraft:command.spawnpoint",
"Sets the spawn point for a player", "Sets the spawn point for a player",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"pumpkin:command.tps", "pumpkin:command.tps",
"Displays the server TPS and MSPT", "Displays the server TPS and MSPT",
PermissionDefault::Op(PermissionLvl::Two), PermissionDefault::Op(PermissionLvl::Two),
)) ))
.unwrap(); .expect("Permission already registered");
} }
fn register_level_3_permissions(registry: &mut PermissionRegistry) { fn register_level_3_permissions(registry: &mut PermissionRegistry) {
@ -406,75 +406,75 @@ fn register_level_3_permissions(registry: &mut PermissionRegistry) {
"Sets the world spawn point", "Sets the world spawn point",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.deop", "minecraft:command.deop",
"Revokes operator status from a player", "Revokes operator status from a player",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.kick", "minecraft:command.kick",
"Removes players from the server", "Removes players from the server",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"pumpkin:command.plugin", "pumpkin:command.plugin",
"Manages server plugins", "Manages server plugins",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"pumpkin:command.plugins", "pumpkin:command.plugins",
"Lists all plugins loaded on the server", "Lists all plugins loaded on the server",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.ban", "minecraft:command.ban",
"Adds players to banlist", "Adds players to banlist",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.banip", "minecraft:command.banip",
"Adds IP addresses to banlist", "Adds IP addresses to banlist",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.pardon", "minecraft:command.pardon",
"Removes entries from the player banlist", "Removes entries from the player banlist",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.pardonip", "minecraft:command.pardonip",
"Removes entries from the IP banlist", "Removes entries from the IP banlist",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.whitelist", "minecraft:command.whitelist",
"Manages server whitelist", "Manages server whitelist",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
registry registry
.register_permission(Permission::new( .register_permission(Permission::new(
"minecraft:command.transfer", "minecraft:command.transfer",
"Transfers the player to another server", "Transfers the player to another server",
PermissionDefault::Op(PermissionLvl::Three), PermissionDefault::Op(PermissionLvl::Three),
)) ))
.unwrap(); .expect("Permission already registered");
} }

View File

@ -57,9 +57,14 @@ fn resolve_sender_world(
sender: &CommandSender, sender: &CommandSender,
server: &crate::server::Server, server: &crate::server::Server,
) -> std::sync::Arc<World> { ) -> std::sync::Arc<World> {
sender sender.world().unwrap_or_else(|| {
.world() server
.unwrap_or_else(|| server.worlds.load().first().unwrap().clone()) .worlds
.load()
.first()
.expect("Server should have at least one world")
.clone()
})
} }
struct EntitiesToEntityExecutor; struct EntitiesToEntityExecutor;

View File

@ -99,7 +99,9 @@ impl CommandSender {
let now = time::OffsetDateTime::now_utc(); let now = time::OffsetDateTime::now_utc();
let format = time::macros::format_description!("[hour]:[minute]:[second]"); let format = time::macros::format_description!("[hour]:[minute]:[second]");
let timestamp = now.format(&format).unwrap(); let timestamp = now
.format(&format)
.expect("Failed to format timestamp for command block output");
*last_output = format!("[{}] {}", timestamp, text.get_text()); *last_output = format!("[{}] {}", timestamp, text.get_text());
} }

View File

@ -224,7 +224,12 @@ impl CommandDispatcher {
pub async fn execute(&self, parsed: ParsingResult<'_>) -> Result<i32, CommandSyntaxError> { pub async fn execute(&self, parsed: ParsingResult<'_>) -> Result<i32, CommandSyntaxError> {
if parsed.reader.peek().is_some() { if parsed.reader.peek().is_some() {
return if parsed.errors.len() == 1 { return if parsed.errors.len() == 1 {
Err(parsed.errors.values().next().unwrap().clone()) Err(parsed
.errors
.values()
.next()
.expect("Errors length is 1, so next should exist")
.clone())
} else if parsed.context.range.is_empty() { } else if parsed.context.range.is_empty() {
Err(DISPATCHER_UNKNOWN_COMMAND.create(&parsed.reader)) Err(DISPATCHER_UNKNOWN_COMMAND.create(&parsed.reader))
} else { } else {
@ -358,7 +363,7 @@ impl CommandDispatcher {
(a_reader_remaining, a_has_errors).cmp(&(b_reader_remaining, b_has_errors)) (a_reader_remaining, a_has_errors).cmp(&(b_reader_remaining, b_has_errors))
}) })
.unwrap() .expect("Potentials list is not empty")
} }
} }
@ -897,7 +902,10 @@ impl CommandDispatcher {
} }
} }
if child_usages.len() == 1 { if child_usages.len() == 1 {
let mut child_usage = child_usages.into_iter().next().unwrap(); let mut child_usage = child_usages
.into_iter()
.next()
.expect("Child usages length is 1, so next should exist");
if is_optional { if is_optional {
child_usage = format!( child_usage = format!(
"{USAGE_OPTIONAL_OPEN}{child_usage}{USAGE_OPTIONAL_CLOSE}" "{USAGE_OPTIONAL_OPEN}{child_usage}{USAGE_OPTIONAL_CLOSE}"

View File

@ -17,7 +17,7 @@ use std::ops::{Index, IndexMut};
use std::sync::Arc; use std::sync::Arc;
/// The constant local ID occupied by the root node. /// The constant local ID occupied by the root node.
pub const ROOT_NODE_ID: NodeId = NodeId(NonZero::new(1).unwrap()); pub const ROOT_NODE_ID: NodeId = NodeId(NonZero::new(1).expect("1 is non-zero"));
/// A consumer which takes ambiguity of input (when two or more nodes are satisfied) /// A consumer which takes ambiguity of input (when two or more nodes are satisfied)
pub trait AmbiguityConsumer { pub trait AmbiguityConsumer {

View File

@ -439,7 +439,7 @@ impl SnbtParser<'_, '_> {
} }
bytes.push(self.parse_integer_literal(value, TypeSuffix::Byte)?.into()); bytes.push(self.parse_integer_literal(value, TypeSuffix::Byte)?.into());
} }
Some(NbtTag::ByteArray(bytes)) Some(NbtTag::ByteArray(bytes.into()))
} }
fn create_int_array(&mut self, values: &[IntegerLiteral]) -> Option<NbtTag> { fn create_int_array(&mut self, values: &[IntegerLiteral]) -> Option<NbtTag> {

View File

@ -76,7 +76,10 @@ macro_rules! assert_parse_err {
); );
// There should always be a context in SNBT parsing. // There should always be a context in SNBT parsing.
assert_eq!( assert_eq!(
error.context.unwrap().cursor, error
.context
.expect("There should always be a context in SNBT parsing")
.cursor,
$cursor, $cursor,
"Cursor positions for error don't match" "Cursor positions for error don't match"
); );
@ -453,7 +456,7 @@ fn lists() {
#[test] #[test]
fn arrays() { fn arrays() {
assert_parse_ok!("[B;]", NbtTag::ByteArray(vec![])); assert_parse_ok!("[B;]", NbtTag::ByteArray(vec![].into()));
assert_parse_ok!("[I ;1 ,2 , 3,]", NbtTag::IntArray(vec![1, 2, 3])); assert_parse_ok!("[I ;1 ,2 , 3,]", NbtTag::IntArray(vec![1, 2, 3]));
assert_parse_ok!("[L;1 ,2 , 3, 4]", NbtTag::LongArray(vec![1, 2, 3, 4])); assert_parse_ok!("[L;1 ,2 , 3, 4]", NbtTag::LongArray(vec![1, 2, 3, 4]));
@ -481,7 +484,7 @@ fn arrays() {
assert_parse_err!("[I;{}]", "Expected literal +", 3, []); assert_parse_err!("[I;{}]", "Expected literal +", 3, []);
assert_parse_err!("[i;4]", "Expected literal (", 2, []); assert_parse_err!("[i;4]", "Expected literal (", 2, []);
assert_parse_ok!("[B; 0b11111111]", NbtTag::ByteArray(vec![-1])); assert_parse_ok!("[B; 0b11111111]", NbtTag::ByteArray(vec![-1].into()));
assert_parse_ok!("[L; 0xFFFFFFFFFFFFFFFF]", NbtTag::LongArray(vec![-1])); assert_parse_ok!("[L; 0xFFFFFFFFFFFFFFFF]", NbtTag::LongArray(vec![-1]));
assert_parse_err!( assert_parse_err!(
"[L; 0xFFFFFFFFFFFFFFFFF]", "[L; 0xFFFFFFFFFFFFFFFFF]",

View File

@ -381,9 +381,9 @@ mod test {
assert!(reader.can_read_chars(7)); assert!(reader.can_read_chars(7));
assert!(!reader.can_read_chars(8)); assert!(!reader.can_read_chars(8));
reader.expect('l').unwrap(); reader.expect('l').expect("Expected 'l'");
reader.skip(); reader.skip();
reader.expect('o').unwrap(); reader.expect('o').expect("Expected 'o'");
// Note: 🎃 carries 4 bytes in UTF-8 // Note: 🎃 carries 4 bytes in UTF-8
assert!(reader.can_read_bytes(6)); assert!(reader.can_read_bytes(6));
@ -422,10 +422,10 @@ mod test {
assert_eq!(reader.read_long(), Ok(7890123456)); assert_eq!(reader.read_long(), Ok(7890123456));
reader.skip_whitespace(); reader.skip_whitespace();
assert!((reader.read_float().unwrap() - 1.233f32).abs() < 1e-07); assert!((reader.read_float().expect("Expected float") - 1.233f32).abs() < 1e-07);
reader.skip_whitespace(); reader.skip_whitespace();
assert!((reader.read_double().unwrap() - 1.592394582f64).abs() < 1e-15); assert!((reader.read_double().expect("Expected double") - 1.592394582f64).abs() < 1e-15);
reader.skip_whitespace(); reader.skip_whitespace();
assert_eq!(reader.read_bool(), Ok(false)); assert_eq!(reader.read_bool(), Ok(false));

View File

@ -335,7 +335,7 @@ impl Suggestions {
.iter() .iter()
.map(|s| s.borrow().range) .map(|s| s.borrow().range)
.reduce(StringRange::encompass) .reduce(StringRange::encompass)
.unwrap(); .expect("Suggestions list is not empty, so range should exist");
let mut texts = Vec::new(); let mut texts = Vec::new();
for suggestion in &suggestions { for suggestion in &suggestions {
@ -412,7 +412,9 @@ impl Suggestions {
match side { match side {
PushSide::Text => { PushSide::Text => {
let text = text_iter.next().unwrap(); let text = text_iter.next().expect(
"text_iter should have a next value because side is PushSide::Text",
);
suggestions.push(Suggestion { suggestions.push(Suggestion {
text: SuggestionText::Text(text.0), text: SuggestionText::Text(text.0),
tooltip: text.1, tooltip: text.1,
@ -420,7 +422,9 @@ impl Suggestions {
}); });
} }
PushSide::Integer => { PushSide::Integer => {
let text = integer_iter.next().unwrap(); let text = integer_iter.next().expect(
"integer_iter should have a next value because side is PushSide::Integer",
);
suggestions.push(Suggestion { suggestions.push(Suggestion {
text: SuggestionText::Integer { text: SuggestionText::Integer {
cached_text: text.0, cached_text: text.0,

View File

@ -42,7 +42,7 @@ pub trait LoadJSONConfiguration {
where where
Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, Self: Sized + Default + Serialize + for<'de> Deserialize<'de>,
{ {
let exe_dir = env::current_dir().unwrap(); let exe_dir = env::current_dir().expect("Failed to get current directory");
let data_dir = exe_dir.join(DATA_FOLDER); let data_dir = exe_dir.join(DATA_FOLDER);
if !data_dir.exists() { if !data_dir.exists() {
debug!("creating new data root folder"); debug!("creating new data root folder");
@ -63,7 +63,11 @@ pub trait LoadJSONConfiguration {
} else { } else {
let content = Self::default(); let content = Self::default();
if let Err(err) = fs::write(&path, serde_json::to_string_pretty(&content).unwrap()) { if let Err(err) = fs::write(
&path,
serde_json::to_string_pretty(&content)
.expect("Failed to serialize default data config"),
) {
error!( error!(
"Couldn't write default data config to {}. Reason: {err}. This is probably caused by a config update. Just delete the old data config and restart.", "Couldn't write default data config to {}. Reason: {err}. This is probably caused by a config update. Just delete the old data config and restart.",
path.display(), path.display(),
@ -87,7 +91,7 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration {
where where
Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, Self: Sized + Default + Serialize + for<'de> Deserialize<'de>,
{ {
let exe_dir = env::current_dir().unwrap(); let exe_dir = env::current_dir().expect("Failed to get current directory");
let data_dir = exe_dir.join(DATA_FOLDER); let data_dir = exe_dir.join(DATA_FOLDER);
if !data_dir.exists() { if !data_dir.exists() {
debug!("creating new data root folder"); debug!("creating new data root folder");

View File

@ -18,7 +18,7 @@ pub struct AxeItem;
impl ItemMetadata for AxeItem { impl ItemMetadata for AxeItem {
fn ids() -> Box<[u16]> { fn ids() -> Box<[u16]> {
tag::Item::MINECRAFT_AXES.1.to_vec().into_boxed_slice() tag::Item::MINECRAFT_AXES.1.into()
} }
} }

View File

@ -17,7 +17,7 @@ pub struct DyeItem;
impl ItemMetadata for DyeItem { impl ItemMetadata for DyeItem {
fn ids() -> Box<[u16]> { fn ids() -> Box<[u16]> {
tag::Item::C_DYES.1.to_vec().into_boxed_slice() tag::Item::C_DYES.1.into()
} }
} }

View File

@ -19,7 +19,7 @@ pub struct HoeItem;
impl ItemMetadata for HoeItem { impl ItemMetadata for HoeItem {
fn ids() -> Box<[u16]> { fn ids() -> Box<[u16]> {
tag::Item::MINECRAFT_HOES.1.to_vec().into_boxed_slice() tag::Item::MINECRAFT_HOES.1.into()
} }
} }

View File

@ -19,7 +19,7 @@ pub struct ShovelItem;
impl ItemMetadata for ShovelItem { impl ItemMetadata for ShovelItem {
fn ids() -> Box<[u16]> { fn ids() -> Box<[u16]> {
tag::Item::MINECRAFT_SHOVELS.1.to_vec().into_boxed_slice() tag::Item::MINECRAFT_SHOVELS.1.into()
} }
} }

View File

@ -7,7 +7,7 @@ pub struct SwordItem;
impl ItemMetadata for SwordItem { impl ItemMetadata for SwordItem {
fn ids() -> Box<[u16]> { fn ids() -> Box<[u16]> {
tag::Item::MINECRAFT_SWORDS.1.to_vec().into_boxed_slice() tag::Item::MINECRAFT_SWORDS.1.into()
} }
} }

View File

@ -697,6 +697,26 @@ pub fn serialize_bedrock_packet(packet: &BClientboundPacket) -> Option<Bytes> {
crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap(); crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap();
Some(buf.into()) Some(buf.into())
} }
BClientboundPacket::CSetDisplayObjective(data) => {
let p = pumpkin_protocol::bedrock::client::CSetDisplayObjective {
display_slot: data.display_slot.clone(),
objective_name: data.objective_name.clone(),
display_name: data.display_name.clone(),
criteria_name: data.criteria_name.clone(),
sort_order: VarInt(data.sort_order),
};
let mut buf = Vec::new();
crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap();
Some(buf.into())
}
BClientboundPacket::CRemoveObjective(data) => {
let p = pumpkin_protocol::bedrock::client::CRemoveObjective {
objective_name: data.objective_name.clone(),
};
let mut buf = Vec::new();
crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap();
Some(buf.into())
}
BClientboundPacket::CSetHealth(data) => { BClientboundPacket::CSetHealth(data) => {
let p = pumpkin_protocol::bedrock::client::CSetHealth { let p = pumpkin_protocol::bedrock::client::CSetHealth {
health: VarInt(data.health), health: VarInt(data.health),
@ -728,6 +748,16 @@ pub fn serialize_bedrock_packet(packet: &BClientboundPacket) -> Option<Bytes> {
crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap(); crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap();
Some(buf.into()) Some(buf.into())
} }
BClientboundPacket::CTransfer(data) => {
let p = pumpkin_protocol::bedrock::client::CTransfer {
address: data.address.clone(),
port: data.port.try_into().unwrap(),
reload_world: data.reload_world.try_into().unwrap(),
};
let mut buf = Vec::new();
crate::net::bedrock::BedrockClient::write_raw_packet(&p, &mut buf).unwrap();
Some(buf.into())
}
_ => None, _ => None,
} }
} }

View File

@ -181,7 +181,7 @@ impl Scoreboard {
team_name: team.name.clone(), team_name: team.name.clone(),
method: TeamMethod::Create, method: TeamMethod::Create,
parameters: Some(parameters), parameters: Some(parameters),
players: team.players.clone(), players: team.players.clone().into(),
}); });
self.teams.insert(team.name.clone(), team); self.teams.insert(team.name.clone(), team);
@ -207,7 +207,7 @@ impl Scoreboard {
team_name: team.name.clone(), team_name: team.name.clone(),
method: TeamMethod::Update, method: TeamMethod::Update,
parameters: Some(parameters), parameters: Some(parameters),
players: Vec::new(), players: Box::new([]),
}); });
self.teams.insert(team.name.clone(), team); self.teams.insert(team.name.clone(), team);
@ -223,7 +223,7 @@ impl Scoreboard {
team_name: name.to_string(), team_name: name.to_string(),
method: TeamMethod::Remove, method: TeamMethod::Remove,
parameters: None, parameters: None,
players: Vec::new(), players: Box::new([]),
}); });
self.teams.remove(name); self.teams.remove(name);
@ -246,7 +246,7 @@ impl Scoreboard {
team_name: team_name.to_string(), team_name: team_name.to_string(),
method: TeamMethod::AddPlayers, method: TeamMethod::AddPlayers,
parameters: None, parameters: None,
players: vec![player.clone()], players: vec![player.clone()].into(),
}); });
team.players.push(player); team.players.push(player);
@ -269,7 +269,7 @@ impl Scoreboard {
team_name: team_name.to_string(), team_name: team_name.to_string(),
method: TeamMethod::RemovePlayers, method: TeamMethod::RemovePlayers,
parameters: None, parameters: None,
players: vec![player.to_string()], players: vec![player.to_string()].into(),
}); });
team.players.retain(|p| p != player); team.players.retain(|p| p != player);