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]]
name = "crossfire"
version = "3.1.15"
version = "3.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7985ad34fb5a761ce21154d0e5a8c37ec6b50af61c90d594eb5cdeb934f91f7c"
checksum = "4d8c4de3db833e7ef74050bae09d5f3fa8f9d1507d3c2689c6e8c50b71208b18"
dependencies = [
"crossbeam-utils",
"embed-collections",
@ -2155,7 +2155,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[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"] }
criterion = { version = "0.8", default-features = false }
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 }
dashmap = { version = "6.2", default-features = false }
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();
raw_name
.strip_prefix("minecraft:")
.unwrap_or(&raw_name)
.unwrap_or(raw_name)
.to_string()
};

View File

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

View File

@ -13,14 +13,35 @@ const LATEST_VERSION: JavaMinecraftVersion = JavaMinecraftVersion::V_26_1;
/// static registry data, and the `Registry::get_synced` method.
pub(crate) fn build() -> TokenStream {
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_2, "1_21_2_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_2,
"1_21_2_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,
"1_21_11_synced_registries.json",

View File

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

View File

@ -23,8 +23,7 @@ pub fn build() -> String {
let name = raw_name
.strip_prefix("minecraft:")
.unwrap_or(raw_name)
.replace('_', "-")
.replace('/', "-");
.replace(['_', '/'], "-");
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::Reference(r) => match &*r.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(),
},
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) {
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) => {
let (name, _, is_slice) = get_type_info(&tr.elem);
(name, true, is_slice)

View File

@ -17,16 +17,15 @@ pub fn map_type(ty: &Type) -> WitType {
"u8" | "i8" => WitType::U8,
"u16" | "i16" => WitType::S32,
"Option" => {
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments {
if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
return WitType::option(map_type(inner_ty));
}
}
WitType::String
}
"Vec" | "Box" => {
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments {
if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
// Check if the inner type is u8
if let Type::Path(tp) = inner_ty
&& 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));
}
}
WitType::String
}
"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(
"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(
"int_array",

View File

@ -166,14 +166,14 @@ impl DynamicOps for NbtOps {
fn get_byte_list(&self, input: Self::Value) -> DataResult<Vec<i8>> {
if let NbtTag::ByteArray(b) = input {
DataResult::new_success(b)
DataResult::new_success(b.into())
} else {
impl_get_list!(self, input, "bytes")
}
}
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>> {
@ -301,7 +301,7 @@ impl DynamicOps for NbtOps {
NbtTag::Long(l) => out_ops.create_long(l),
NbtTag::Float(f) => out_ops.create_float(f),
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::List(_) => self.convert_list(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.
match tag {
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::LongArray(list) => Some(Self::Long(InnerLongListCollector::new(list))),
@ -592,7 +594,7 @@ impl InnerListCollector for InnerByteListCollector {
}
fn result(self) -> NbtTag {
NbtTag::ByteArray(self.list)
NbtTag::ByteArray(self.list.into())
}
}
@ -669,13 +671,13 @@ mod test {
);
// Byte list collector
let tag = NbtTag::ByteArray(vec![-1, 45, 100]);
let tag = NbtTag::ByteArray(vec![-1, 45, 100].into());
assert_eq!(
ListCollector::new(tag)
.expect("List collector should exist")
.result(),
NbtTag::ByteArray(vec![-1, 45, 100])
NbtTag::ByteArray(vec![-1, 45, 100].into())
);
// Long list

View File

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

View File

@ -87,7 +87,7 @@ impl UDPNetworkDecoder {
pub async fn get_packet_payload(
&mut self,
mut full_packet: Vec<u8>,
full_packet: Vec<u8>,
) -> Result<Vec<u8>, PacketDecodeError> {
if full_packet.is_empty() {
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 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.
@ -125,7 +125,7 @@ impl UDPNetworkDecoder {
}
0xff => {
// 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!(
"Unsupported compression method: 0x{compression_method:02x}"

View File

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

View File

@ -1,8 +1,8 @@
use super::Carver;
use crate::ProtoChunk;
use pumpkin_data::block_state::BlockState;
use pumpkin_data::carver::{CarverAdditionalConfig, CarverConfig, HeightProvider};
use pumpkin_util::math::vector2::Vector2;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::random::{RandomGenerator, RandomImpl};
use std::f32::consts::PI;
@ -352,8 +352,8 @@ impl CaveCarver {
has_grass: &mut bool,
) -> bool {
let local_y = y - chunk.bottom_y() as i32;
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
let block = pumpkin_data::Block::from_state_id(state_id);
let state = chunk.get_block_state(&Vector3::new(x, y, z));
let block = state.to_block();
if block.id == pumpkin_data::Block::GRASS_BLOCK.id
|| block.id == pumpkin_data::Block::MYCELIUM.id
@ -361,12 +361,11 @@ impl CaveCarver {
*has_grass = true;
}
// Only carve if it's replaceable
if config.replaceable.1.contains(&block.id) {
// 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);
if !config.replaceable.1.contains(&block.id) {
return false;
}
let carve_state = {
let lava_y = if is_nether {
chunk.bottom_y() as i32 + 31
} else {
@ -376,26 +375,44 @@ impl CaveCarver {
};
if y <= lava_y {
chunk.set_block_state(x & 15, local_y, z & 15, lava);
Some(pumpkin_data::Block::LAVA.default_state)
} 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 {
// 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);
// if below_block.id == pumpkin_data::Block::DIRT.id {
// // TODO: Java uses Biome top material here, defaulting to Grass for now
// let top_material =
// BlockState::from_id(pumpkin_data::Block::GRASS_BLOCK.default_state.id);
// chunk.set_block_state(x & 15, local_y - 1, z & 15, top_material);
// pumpkin_data::Block::GRASS_BLOCK.default_state;
// chunk.set_block_state(x, local_y - 1, z, top_material);
// }
// }
return true;
}
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;
for dx in -radius..=radius {
@ -69,14 +69,14 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
if should_carve(config, &mut carver_random) {
match config.additional {
CarverAdditionalConfig::Cave(_) | CarverAdditionalConfig::NetherCave(_) => {
cave_carver.carve(
config,
chunk,
&mut carver_random,
&chunk_pos,
&carver_chunk_pos,
generator.settings.legacy_random_source,
);
// cave_carver.carve(
// config,
// chunk,
// &mut carver_random,
// &chunk_pos,
// &carver_chunk_pos,
// generator.settings.legacy_random_source,
// );
}
CarverAdditionalConfig::Canyon(_) => {
canyon_carver.carve(

View File

@ -61,7 +61,11 @@ impl CommandBlock {
warn!("Command block has no matching entity");
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
}
@ -122,7 +126,10 @@ impl CommandBlock {
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() {
command_entity.success_count.store(0, Ordering::Release);
@ -164,7 +171,11 @@ impl CommandBlock {
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 auto = command_entity.auto.load(Ordering::Relaxed);
let state_id = world.get_block_state_id(&pos);
@ -174,7 +185,10 @@ impl CommandBlock {
let conditions_met = Self::conditions_met(&world, &pos, direction);
if conditions_met {
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;
} else if props.conditional {
command_entity.success_count.store(0, Ordering::Release);
@ -238,10 +252,12 @@ impl BlockBehaviour for CommandBlock {
if block_entity.resource_location() != CommandBlockEntity::ID {
return;
}
let command_entity = block_entity
.as_any()
.downcast_ref::<CommandBlockEntity>()
.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;
};
Self::update(
args.world,
@ -268,7 +284,11 @@ impl BlockBehaviour for CommandBlock {
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 {
return;
};

View File

@ -109,7 +109,13 @@ impl FireBlockBase {
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`.

View File

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

View File

@ -48,7 +48,8 @@ impl BlockBehaviour for LanternBlock {
}
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>(

View File

@ -216,7 +216,7 @@ impl BlockBehaviour for PistonBlock {
let mut props = PistonProps::default(block);
props.facing = BlockDirection::by_index((data & 7) as usize)
.unwrap()
.expect("Invalid block direction index")
.to_facing();
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
&& 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
// TODO: more stuff...
{

View File

@ -87,7 +87,8 @@ impl BlockBehaviour for PressurePlateBlock {
}
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 {
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 {
self.ingredient_item
.lock()
.unwrap()
.expect("Ingredient item mutex should not be poisoned")
.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()
&& !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
@ -362,7 +365,10 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
|| 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
}
@ -455,7 +461,11 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
// Start new brewing cycle
self.fuel.fetch_sub(1, 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();
} else if fuel_refilled {
// 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
let mut needs_update = false;
{
let mut last_guard = self.last_potion_count.lock().unwrap();
if last_guard.is_none() || last_guard.as_ref().unwrap() != &current {
let mut last_guard = self
.last_potion_count
.lock()
.expect("Last potion count mutex should not be poisoned");
if last_guard.as_ref() != Some(&current) {
*last_guard = Some(current);
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.
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);
}
@ -50,7 +50,7 @@ macro_rules! impl_block_entity_for_chest {
Box::pin(async move {
// Clone the loot table key without holding the lock across an await.
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()
};
@ -100,12 +100,12 @@ macro_rules! impl_block_entity_for_chest {
}
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))
}
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(
recipe.result.id.strip_prefix("minecraft:").unwrap(),
recipe
.result
.id
.strip_prefix("minecraft:")
.expect("Recipe ID should have minecraft: prefix"),
) && !is_top_items_empty
&& recipe_output_item.id == side_item_stack.item.id
&& side_item_stack.item_count < max_count
@ -156,7 +160,11 @@ macro_rules! impl_cooking_block_entity_base {
if can_accept_output {
let mut side_items = self.items[2].lock().await;
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 {
return false;
};

View File

@ -86,9 +86,14 @@ pub trait BlockEntity: Any + Send + Sync {
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
.iter()
.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)
@ -135,9 +140,9 @@ pub trait BlockEntity: Any + Send + Sync {
#[must_use]
pub fn block_entity_from_generic<T: BlockEntity>(nbt: &NbtCompound) -> T {
let x = nbt.get_int("x").unwrap();
let y = nbt.get_int("y").unwrap();
let z = nbt.get_int("z").unwrap();
let x = nbt.get_int("x").expect("NBT should have x coordinate");
let y = nbt.get_int("y").expect("NBT should have y coordinate");
let z = nbt.get_int("z").expect("NBT should have z coordinate");
T::from_nbt(nbt, BlockPos::new(x, y, z))
}

View File

@ -158,7 +158,7 @@ impl From<Text> for NbtTag {
value
.messages
.lock()
.unwrap()
.expect("Text messages mutex should not be poisoned")
.iter()
.map(|s| Self::String(s.clone()))
.collect(),
@ -172,7 +172,7 @@ impl From<NbtTag> for Text {
fn from(tag: NbtTag) -> Self {
let nbt = tag.extract_compound().unwrap();
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
.get_list("messages")
.unwrap()
@ -226,8 +226,16 @@ impl BlockEntity for SignBlockEntity {
where
Self: Sized,
{
let front_text = Text::from(nbt.get("front_text").unwrap().clone());
let back_text = Text::from(nbt.get("back_text").unwrap().clone());
let front_text = nbt
.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);
Self {
position,

View File

@ -471,7 +471,7 @@ mod test {
let Err(error) = parse_target_selector_with_context(raw_arg) else {
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);
}
@ -484,7 +484,9 @@ mod test {
end: input.len(),
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 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!(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!(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> {
let list = self.find_players(source).await?;
if list.len() == 1 {
Ok(list.first().unwrap().clone())
Ok(list
.first()
.expect("List length is 1, so first should exist")
.clone())
} else {
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));
}
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;
match char {
'a' => {

View File

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

View File

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

View File

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

View File

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

View File

@ -99,7 +99,9 @@ impl CommandSender {
let now = time::OffsetDateTime::now_utc();
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());
}

View File

@ -224,7 +224,12 @@ impl CommandDispatcher {
pub async fn execute(&self, parsed: ParsingResult<'_>) -> Result<i32, CommandSyntaxError> {
if parsed.reader.peek().is_some() {
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() {
Err(DISPATCHER_UNKNOWN_COMMAND.create(&parsed.reader))
} else {
@ -358,7 +363,7 @@ impl CommandDispatcher {
(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 {
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 {
child_usage = format!(
"{USAGE_OPTIONAL_OPEN}{child_usage}{USAGE_OPTIONAL_CLOSE}"

View File

@ -17,7 +17,7 @@ use std::ops::{Index, IndexMut};
use std::sync::Arc;
/// 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)
pub trait AmbiguityConsumer {

View File

@ -439,7 +439,7 @@ impl SnbtParser<'_, '_> {
}
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> {

View File

@ -76,7 +76,10 @@ macro_rules! assert_parse_err {
);
// There should always be a context in SNBT parsing.
assert_eq!(
error.context.unwrap().cursor,
error
.context
.expect("There should always be a context in SNBT parsing")
.cursor,
$cursor,
"Cursor positions for error don't match"
);
@ -453,7 +456,7 @@ fn lists() {
#[test]
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!("[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;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_err!(
"[L; 0xFFFFFFFFFFFFFFFFF]",

View File

@ -381,9 +381,9 @@ mod test {
assert!(reader.can_read_chars(7));
assert!(!reader.can_read_chars(8));
reader.expect('l').unwrap();
reader.expect('l').expect("Expected 'l'");
reader.skip();
reader.expect('o').unwrap();
reader.expect('o').expect("Expected 'o'");
// Note: 🎃 carries 4 bytes in UTF-8
assert!(reader.can_read_bytes(6));
@ -422,10 +422,10 @@ mod test {
assert_eq!(reader.read_long(), Ok(7890123456));
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();
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();
assert_eq!(reader.read_bool(), Ok(false));

View File

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

View File

@ -42,7 +42,7 @@ pub trait LoadJSONConfiguration {
where
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);
if !data_dir.exists() {
debug!("creating new data root folder");
@ -63,7 +63,11 @@ pub trait LoadJSONConfiguration {
} else {
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!(
"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(),
@ -87,7 +91,7 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration {
where
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);
if !data_dir.exists() {
debug!("creating new data root folder");

View File

@ -18,7 +18,7 @@ pub struct AxeItem;
impl ItemMetadata for AxeItem {
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 {
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 {
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 {
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 {
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();
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) => {
let p = pumpkin_protocol::bedrock::client::CSetHealth {
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();
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,
}
}

View File

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