chore: clippy enforce unwrap_used, expect_used and panic

release is not far
This commit is contained in:
Alexander Medvedev 2026-08-08 13:44:51 +02:00
parent 0eb7b936db
commit 49360e53ef
291 changed files with 3200 additions and 1783 deletions

View File

@ -53,6 +53,9 @@ print_stderr = "deny"
todo = "deny" todo = "deny"
unreachable = "deny" unreachable = "deny"
unimplemented = "deny" unimplemented = "deny"
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
# Safety & Soundness # Safety & Soundness
undocumented_unsafe_blocks = "deny" undocumented_unsafe_blocks = "deny"

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro_error2::{abort, proc_macro_error}; use proc_macro_error2::{abort, proc_macro_error};
use proc_macro2::Ident; use proc_macro2::Ident;

View File

@ -67,8 +67,8 @@ impl<L: Decode, R: Decode> Decode for Either<L, R> {
DataResult::new_error(format!( DataResult::new_error(format!(
"Failed to parse either. First: {}; Second: {}", "Failed to parse either. First: {}; Second: {}",
left.get_message().unwrap(), left.get_message().as_deref().unwrap_or("unknown error"),
right.get_message().unwrap() right.get_message().as_deref().unwrap_or("unknown error")
)) ))
} }
} }

View File

@ -26,9 +26,10 @@ macro_rules! impl_apply {
let result_1 = $self; let result_1 = $self;
if !(result_1.is_error() $(|| $result.is_error())+) { if !(result_1.is_error() $(|| $result.is_error())+) {
// All n results are successful. // All n results are successful.
#[allow(clippy::expect_used)]
return DataResult::new_success($f( return DataResult::new_success($f(
result_1.into_result().unwrap() result_1.into_result().expect("checked is_error")
$( , $result.into_result().unwrap() )+ $( , $result.into_result().expect("checked is_error") )+
)); ));
} }
let mut messages: Vec<String> = vec![]; let mut messages: Vec<String> = vec![];
@ -231,21 +232,25 @@ impl<R> DataResult<R> {
} }
/// Tries to get a complete result from this `DataResult`. If no such result exists, this function panics. /// Tries to get a complete result from this `DataResult`. If no such result exists, this function panics.
#[allow(clippy::unwrap_used, clippy::expect_used)]
pub fn unwrap(self) -> R { pub fn unwrap(self) -> R {
self.expect("No complete result found for DataResult") self.expect("No complete result found for DataResult")
} }
/// Tries to get a complete or partial result from this `DataResult`. If no such result exists, this function panics. /// Tries to get a complete or partial result from this `DataResult`. If no such result exists, this function panics.
#[allow(clippy::unwrap_used, clippy::expect_used)]
pub fn unwrap_or_partial(self) -> R { pub fn unwrap_or_partial(self) -> R {
self.expect_or_partial("No complete or partial result found for DataResult") self.expect_or_partial("No complete or partial result found for DataResult")
} }
/// Tries to get a complete result from this `DataResult`. If no such result exists, this function panics with a custom message. /// Tries to get a complete result from this `DataResult`. If no such result exists, this function panics with a custom message.
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
pub fn expect(self, message: &str) -> R { pub fn expect(self, message: &str) -> R {
self.into_result().unwrap_or_else(|| panic!("{}", message)) self.into_result().unwrap_or_else(|| panic!("{}", message))
} }
/// Tries to get a complete or partial result from this `DataResult`. If no such result exists, this function panics with a custom message. /// Tries to get a complete or partial result from this `DataResult`. If no such result exists, this function panics with a custom message.
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
pub fn expect_or_partial(self, message: &str) -> R { pub fn expect_or_partial(self, message: &str) -> R {
self.into_result_or_partial() self.into_result_or_partial()
.unwrap_or_else(|| panic!("{}", message)) .unwrap_or_else(|| panic!("{}", message))
@ -614,7 +619,7 @@ macro_rules! assert_success {
result result
); );
assert_eq!( assert_eq!(
result.unwrap(), result.into_result().expect("DataResult success"),
$right, $right,
"`DataResult` was successful but the value doesn't match" "`DataResult` was successful but the value doesn't match"
); );
@ -632,7 +637,7 @@ macro_rules! assert_encode_success {
result result
); );
assert_eq!( assert_eq!(
result.unwrap(), result.into_result().expect("DataResult success"),
$right, $right,
"`DataResult` was successful but the value doesn't match" "`DataResult` was successful but the value doesn't match"
); );

View File

@ -106,9 +106,8 @@ impl DynamicOps for JsonOps {
} }
fn get_string(&self, input: &Self::Value) -> DataResult<String> { fn get_string(&self, input: &Self::Value) -> DataResult<String> {
if let Value::String(_) = input { if let Value::String(s) = input {
// Unwrapping is fine as only strings are possible here. DataResult::new_success(s.clone())
DataResult::new_success(Self::get_as_string(input).unwrap())
} else { } else {
DataResult::new_error(format!("Not a string: {input}")) DataResult::new_error(format!("Not a string: {input}"))
} }
@ -194,13 +193,14 @@ impl DynamicOps for JsonOps {
if !Self::is_valid_key(&key) { if !Self::is_valid_key(&key) {
return DataResult::new_partial_error(format!("Key is not a string: {key}"), map); return DataResult::new_partial_error(format!("Key is not a string: {key}"), map);
} }
let mut output_map = Map::new(); let mut output_map = Map::new();
if let Value::Object(mut m) = map { if let Value::Object(mut m) = map {
output_map.append(&mut m); output_map.append(&mut m);
} }
output_map.insert(Self::get_as_string(&key).unwrap(), value); if let Some(key_str) = Self::get_as_string(&key) {
output_map.insert(key_str, value);
}
DataResult::new_success(Value::Object(output_map)) DataResult::new_success(Value::Object(output_map))
} }
@ -225,23 +225,19 @@ impl DynamicOps for JsonOps {
let mut missed = vec![]; let mut missed = vec![];
for entry in other_map_like.iter() { for entry in other_map_like.iter() {
if Self::is_valid_key(&entry.0) { if let Some(key_str) = Self::get_as_string(&entry.0) {
output_map.insert(Self::get_as_string(&entry.0).unwrap(), entry.1.clone()); output_map.insert(key_str, entry.1.clone());
} else { } else {
missed.push(entry.0); missed.push(entry.0);
} }
} }
let object = Value::Object(output_map); let object = Value::Object(output_map);
let pretty_missed = serde_json::to_string_pretty(&missed);
if missed.is_empty() { if missed.is_empty() {
DataResult::new_success(object) DataResult::new_success(object)
} else { } else {
DataResult::new_partial_error( DataResult::new_partial_error(
format!( format!("Some keys are not valid: {missed:?}"),
"Some keys are not strings{}",
pretty_missed.map_or_else(|_| String::new(), |r| format!(": {r}"))
),
object, object,
) )
} }
@ -375,10 +371,9 @@ impl StructBuilder for JsonStructBuilder {
impl StringStructBuilder for JsonStructBuilder { impl StringStructBuilder for JsonStructBuilder {
fn append(&self, key: &str, value: Self::Value, mut builder: Self::Result) -> Self::Result { fn append(&self, key: &str, value: Self::Value, mut builder: Self::Result) -> Self::Result {
builder if let Some(obj) = builder.as_object_mut() {
.as_object_mut() obj.insert(key.to_string(), value);
.unwrap() }
.insert(key.to_string(), value);
builder builder
} }
} }

View File

@ -1,3 +1,6 @@
#![deny(clippy::unwrap_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
mod data_result; mod data_result;
mod dynamic_ops; mod dynamic_ops;
pub mod json_ops; pub mod json_ops;

View File

@ -1,7 +1,10 @@
//! Configuration management and serialization for the Pumpkin Minecraft server. //! Configuration management and serialization for the Pumpkin Minecraft server.
#![deny(missing_docs)] #![deny(missing_docs)]
#![deny(clippy::unwrap_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
use fun::FunConfig; use fun::FunConfig;
use logging::LoggingConfig; use logging::LoggingConfig;
use pumpkin_util::world_seed::Seed; use pumpkin_util::world_seed::Seed;
use pumpkin_util::{Difficulty, GameMode, PermissionLvl, random}; use pumpkin_util::{Difficulty, GameMode, PermissionLvl, random};
@ -10,7 +13,7 @@ use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::path::PathBuf; use std::path::PathBuf;
use std::{fs, num::NonZeroU8, path::Path}; use std::{fs, num::NonZeroU8, path::Path};
use tracing::{debug, warn}; use tracing::{debug, error, warn};
/// Fun and experimental configuration options. /// Fun and experimental configuration options.
pub mod fun; pub mod fun;
@ -84,8 +87,10 @@ impl LoadConfiguration for PumpkinConfig {
self.basic.validate(); self.basic.validate();
self.advanced.validate(); self.advanced.validate();
let min_vd = NonZeroU8::new(2).unwrap(); let min_vd = NonZeroU8::MIN;
let max_vd = NonZeroU8::new(64).unwrap(); let Some(max_vd) = NonZeroU8::new(64) else {
return;
};
// Validate Java // Validate Java
assert!( assert!(
@ -257,48 +262,83 @@ pub trait LoadConfiguration {
{ {
if !config_dir.exists() { if !config_dir.exists() {
debug!("creating new config root folder"); debug!("creating new config root folder");
fs::create_dir(config_dir).expect("Failed to create config root folder"); let _ = fs::create_dir(config_dir);
} }
let path = config_dir.join(Self::get_path()); let path = config_dir.join(Self::get_path());
let config = if path.exists() { let config = if path.exists() {
let file_content = fs::read_to_string(&path).unwrap_or_else(|_| { let file_content = match fs::read_to_string(&path) {
panic!("Couldn't read configuration file at {}", path.display()) Ok(content) => content,
}); Err(err) => {
error!(
"Couldn't read configuration file at {}: {err}",
path.display()
);
return Self::default();
}
};
let parsed_toml_value: toml::Value = toml::from_str(&file_content) let parsed_toml_value: toml::Value = match toml::from_str(&file_content) {
.unwrap_or_else(|err| { Ok(val) => val,
panic!( Err(err) => {
"Couldn't parse TOML at {}. Reason: {}. This is probably caused by invalid TOML syntax", error!(
path.display(), err "Couldn't parse TOML at {}. Reason: {err}. Using default config.",
) path.display()
}); );
return Self::default();
}
};
let (merged_config, changed) = Self::merge_with_default_toml(parsed_toml_value); let (merged_config, changed) = Self::merge_with_default_toml(parsed_toml_value);
if changed { if changed {
println!( let file_name = path.file_name().map_or_else(
"{} changed because values were missing. The missing values were filled with default values.", || path.display().to_string(),
path.file_name().unwrap().display() |f| f.to_string_lossy().into_owned(),
); );
if let Err(err) = fs::write(&path, toml::to_string(&merged_config).unwrap()) { println!(
warn!( "{file_name} changed because values were missing. The missing values were filled with default values."
"Couldn't write merged config to {}. Reason: {}", );
path.display(), match toml::to_string(&merged_config) {
err Ok(toml_str) => {
); if let Err(err) = fs::write(&path, toml_str) {
warn!(
"Couldn't write merged config to {}. Reason: {}",
path.display(),
err
);
}
}
Err(err) => {
warn!(
"Couldn't serialize merged config for {}. Reason: {}",
path.display(),
err
);
}
} }
} }
merged_config merged_config
} else { } else {
let content = Self::default(); let content = Self::default();
if let Err(err) = fs::write(&path, toml::to_string(&content).unwrap()) { match toml::to_string(&content) {
warn!( Ok(toml_str) => {
"Couldn't write default config to {:?}. Reason: {}", if let Err(err) = fs::write(&path, toml_str) {
path.display(), warn!(
err "Couldn't write default config to {:?}. Reason: {}",
); path.display(),
err
);
}
}
Err(err) => {
warn!(
"Couldn't serialize default config for {:?}. Reason: {}",
path.display(),
err
);
}
} }
content content
@ -318,14 +358,13 @@ pub trait LoadConfiguration {
{ {
let default_config = Self::default(); let default_config = Self::default();
let default_toml_value = let Ok(default_toml_value) = toml::Value::try_from(&default_config) else {
toml::Value::try_from(default_config).expect("Failed to parse default config"); return (default_config, false);
};
let (merged_value, changed) = Self::merge_toml_values(default_toml_value, parsed_toml); let (merged_value, changed) = Self::merge_toml_values(default_toml_value, parsed_toml);
let config = merged_value let config = merged_value.try_into().unwrap_or_else(|_| Self::default());
.try_into()
.expect("Failed to convert merged config");
(config, changed) (config, changed)
} }

View File

@ -34,9 +34,12 @@ pub struct NetherNetConfig {
impl Default for NetherNetConfig { impl Default for NetherNetConfig {
fn default() -> Self { fn default() -> Self {
let address = "0.0.0.0:19132"
.parse()
.unwrap_or_else(|_| std::net::SocketAddr::from(([0, 0, 0, 0], 19132)));
Self { Self {
enabled: true, enabled: true,
address: "0.0.0.0:19132".parse().unwrap(), address,
identity_key: "nethernet-key.der".into(), identity_key: "nethernet-key.der".into(),
stun_servers: Vec::new(), stun_servers: Vec::new(),
} }
@ -80,12 +83,14 @@ pub struct BedrockConfig {
impl Default for BedrockConfig { impl Default for BedrockConfig {
fn default() -> Self { fn default() -> Self {
let view_distance = NonZeroU8::new(16).unwrap_or(NonZeroU8::MIN);
let simulation_distance = NonZeroU8::new(10).unwrap_or(NonZeroU8::MIN);
Self { Self {
enabled: true, enabled: true,
online_mode: true, online_mode: true,
max_players: 1000, max_players: 1000,
view_distance: NonZeroU8::new(16).unwrap(), view_distance,
simulation_distance: NonZeroU8::new(10).unwrap(), simulation_distance,
compression: CompressionConfig::default(), compression: CompressionConfig::default(),
motd: "A blazingly fast Pumpkin server!".to_string(), motd: "A blazingly fast Pumpkin server!".to_string(),
authentication: BedrockAuthenticationConfig::default(), authentication: BedrockAuthenticationConfig::default(),

View File

@ -31,14 +31,19 @@ pub struct JavaConfig {
impl Default for JavaConfig { impl Default for JavaConfig {
fn default() -> Self { fn default() -> Self {
let address = "0.0.0.0:25565"
.parse()
.unwrap_or_else(|_| std::net::SocketAddr::from(([0, 0, 0, 0], 25565)));
let view_distance = NonZeroU8::new(16).unwrap_or(NonZeroU8::MIN);
let simulation_distance = NonZeroU8::new(10).unwrap_or(NonZeroU8::MIN);
Self { Self {
enabled: true, enabled: true,
address: "0.0.0.0:25565".parse().unwrap(), address,
encryption: true, encryption: true,
online_mode: true, online_mode: true,
max_players: 1000, max_players: 1000,
view_distance: NonZeroU8::new(16).unwrap(), view_distance,
simulation_distance: NonZeroU8::new(10).unwrap(), simulation_distance,
compression: CompressionConfig::default(), compression: CompressionConfig::default(),
motd: "A blazingly fast Pumpkin server!".to_string(), motd: "A blazingly fast Pumpkin server!".to_string(),
authentication: AuthenticationConfig::default(), authentication: AuthenticationConfig::default(),

View File

@ -5,11 +5,9 @@ use crate::translation::java::{
}; };
use crate::{ADVANCEMENT_TREE, Advancement}; use crate::{ADVANCEMENT_TREE, Advancement};
use pumpkin_util::identifier::Identifier; use pumpkin_util::identifier::Identifier;
use pumpkin_util::resource_location::ResourceLocation;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_util::text::color::{Color, NamedColor}; use pumpkin_util::text::color::NamedColor;
use serde::ser::SerializeStruct; use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Display; use std::fmt::Display;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};

View File

@ -250,7 +250,6 @@ impl Default for BlockStateId {
impl std::fmt::Display for BlockStateId { impl std::fmt::Display for BlockStateId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
write!( write!(
f, f,
"BlockStateId({} = \"{}\")", "BlockStateId({} = \"{}\")",

View File

@ -255,7 +255,6 @@ impl Default for BlockId {
impl std::fmt::Display for BlockId { impl std::fmt::Display for BlockId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
write!( write!(
f, f,
"BlockId({} = \"{}\")", "BlockId({} = \"{}\")",

View File

@ -1,4 +1,4 @@
use crate::data_component_impl::{DataComponentImpl, default_impl, get_i32_hash, get_str_hash}; use crate::data_component_impl::{DataComponentImpl, get_i32_hash, get_str_hash};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;

View File

@ -1,4 +1,4 @@
use crate::data_component_impl::{DataComponentImpl, default_impl, get_i32_hash, get_str_hash}; use crate::data_component_impl::DataComponentImpl;
use crc_fast::CrcAlgorithm::Crc32Iscsi; use crc_fast::CrcAlgorithm::Crc32Iscsi;
use crc_fast::Digest; use crc_fast::Digest;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;

View File

@ -1,4 +1,4 @@
use crate::data_component_impl::{DataComponentImpl, default_impl}; use crate::data_component_impl::DataComponentImpl;
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Debug, Hash, PartialEq, Eq)]

View File

@ -3,8 +3,8 @@ use crate::Enchantment;
use crate::attributes::Attributes; use crate::attributes::Attributes;
use crate::data_component_impl::basic::SoundEvent; use crate::data_component_impl::basic::SoundEvent;
use crate::data_component_impl::{ use crate::data_component_impl::{
DataComponentImpl, EquipmentSlot, IDSet, IDSetContent, IdOr, default_impl, get_f32_hash, DataComponentImpl, EquipmentSlot, IDSet, IdOr, get_i32_hash, get_idor, get_idor_hash,
get_i32_hash, get_idor, get_idor_hash, get_idset_hash, get_str_hash, put_idor, get_idset_hash, get_str_hash, put_idor,
}; };
use crate::entity_type::EntityType; use crate::entity_type::EntityType;
use crate::sound::Sound; use crate::sound::Sound;

View File

@ -1,4 +1,4 @@
use crate::data_component_impl::{DataComponentImpl, default_impl}; use crate::data_component_impl::DataComponentImpl;
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
use std::borrow::Cow; use std::borrow::Cow;

View File

@ -1,7 +1,7 @@
use crate::data_component_impl::basic::SoundEvent; use crate::data_component_impl::basic::SoundEvent;
use crate::data_component_impl::{ use crate::data_component_impl::{
DataComponentImpl, IDSet, IdOr, default_impl, get_f32_hash, get_i32_hash, get_idor, DataComponentImpl, IDSet, IdOr, get_f32_hash, get_i32_hash, get_idor, get_idor_hash,
get_idor_hash, get_idset_hash, get_str_hash, put_idor, get_idset_hash, get_str_hash, put_idor,
}; };
use crate::effect::StatusEffect; use crate::effect::StatusEffect;
use crate::sound::Sound; use crate::sound::Sound;

View File

@ -3,7 +3,6 @@
use crate::Block; use crate::Block;
use crate::BlockId; use crate::BlockId;
use crate::data_component::DataComponent; use crate::data_component::DataComponent;
use crate::effect::StatusEffect;
use crate::entity_type::EntityType; use crate::entity_type::EntityType;
use crate::sound::Sound; use crate::sound::Sound;
use crate::tag::Taggable; use crate::tag::Taggable;
@ -52,7 +51,13 @@ pub fn get<T: DataComponentImpl + 'static>(value: &dyn DataComponentImpl) -> &T
#[inline] #[inline]
pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T { pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T {
value.as_mut_any().downcast_mut::<T>().unwrap() let name = value.get_self_enum().to_name();
value.as_mut_any().downcast_mut::<T>().unwrap_or_else(|| {
panic!(
"you are trying to cast {name} to {}",
T::get_enum().to_name()
)
})
} }
macro_rules! default_impl { macro_rules! default_impl {
@ -85,8 +90,6 @@ macro_rules! default_impl {
}; };
} }
pub(crate) use default_impl;
pub fn get_str_hash(val: &str) -> u32 { pub fn get_str_hash(val: &str) -> u32 {
let mut digest = Digest::new(Crc32Iscsi); let mut digest = Digest::new(Crc32Iscsi);
digest.update(&[12u8]); digest.update(&[12u8]);
@ -154,9 +157,9 @@ pub fn get_idor(nbt: &NbtCompound, key: &str, default: Sound) -> IdOr<basic::Sou
let sound = sound.strip_prefix("minecraft:").unwrap_or(sound); let sound = sound.strip_prefix("minecraft:").unwrap_or(sound);
IdOr::Id(Sound::from_name(sound).unwrap_or(default)) IdOr::Id(Sound::from_name(sound).unwrap_or(default))
} else if let Some(sound_compound) = nbt.get_compound(key) { } else if let Some(sound_compound) = nbt.get_compound(key) {
let sound_name = sound_compound let Some(sound_name) = sound_compound.get_string("sound_id") else {
.get_string("sound_id") return IdOr::Id(default);
.expect("SoundEvent compound must have a 'sound_id' field"); };
let range = sound_compound.get_float("range"); let range = sound_compound.get_float("range");
IdOr::Value(basic::SoundEvent { IdOr::Value(basic::SoundEvent {
sound_name: sound_name.to_string(), sound_name: sound_name.to_string(),

View File

@ -1,6 +1,4 @@
use crate::data_component_impl::{ use crate::data_component_impl::{DataComponentImpl, get_i32_hash, get_str_hash};
DataComponentImpl, default_impl, get_f32_hash, get_i32_hash, get_str_hash,
};
use crc_fast::CrcAlgorithm::Crc32Iscsi; use crc_fast::CrcAlgorithm::Crc32Iscsi;
use crc_fast::Digest; use crc_fast::Digest;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;

View File

@ -4,7 +4,7 @@ use crate::item::Item;
use crate::item_stack::ItemStack; use crate::item_stack::ItemStack;
use pumpkin_util::identifier::Identifier; use pumpkin_util::identifier::Identifier;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_util::text::{color::Color, color::NamedColor, hover::HoverEvent, style::Style}; use pumpkin_util::text::{color::Color, hover::HoverEvent, style::Style};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Display; use std::fmt::Display;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};

View File

@ -1,6 +1,5 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use crate::biome::de::Deserialize; use crate::biome::de::Deserialize;
use crate::entity_type::EntityType;
use crate::tag::RegistryKey; use crate::tag::RegistryKey;
use crate::tag::Taggable; use crate::tag::Taggable;
use pumpkin_util::biome::{TemperatureModifier, Weather}; use pumpkin_util::biome::{TemperatureModifier, Weather};

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use pumpkin_util::math::float_provider::{ use pumpkin_util::math::float_provider::{
ClampedNormalFloatProvider, ConstantFloatProvider, FloatProvider, NormalFloatProvider, FloatProvider, NormalFloatProvider,
TrapezoidFloatProvider, UniformFloatProvider, TrapezoidFloatProvider, UniformFloatProvider,
}; };
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset}; use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
@ -63,7 +63,6 @@ pub struct CarverConfig {
pub replaceable: crate::tag::Tag, pub replaceable: crate::tag::Tag,
pub additional: CarverAdditionalConfig, pub additional: CarverAdditionalConfig,
} }
use super::*;
pub const CANYON: CarverConfig = CarverConfig { pub const CANYON: CarverConfig = CarverConfig {
probability: 0.01f32, probability: 0.01f32,
y: HeightProvider::Uniform(UniformHeightProvider { y: HeightProvider::Uniform(UniformHeightProvider {

View File

@ -3,10 +3,7 @@ use crate::BlockState;
use crate::biome::Biome; use crate::biome::Biome;
use crate::chunk::DoublePerlinNoiseParameters; use crate::chunk::DoublePerlinNoiseParameters;
use crate::dimension::Dimension; use crate::dimension::Dimension;
use pumpkin_util::random::RandomDeriver;
use pumpkin_util::y_offset::Absolute;
use pumpkin_util::y_offset::YOffset; use pumpkin_util::y_offset::YOffset;
use std::{cell::RefCell, num::NonZeroUsize};
pub struct GenerationSettings { pub struct GenerationSettings {
pub aquifers_enabled: bool, pub aquifers_enabled: bool,
pub ore_veins_enabled: bool, pub ore_veins_enabled: bool,

View File

@ -1,5 +1,5 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use crate::tag::{RegistryKey, Tag, Taggable}; use crate::tag::{RegistryKey, Taggable};
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub struct DamageType { pub struct DamageType {
pub death_message_type: DeathMessageType, pub death_message_type: DeathMessageType,

View File

@ -1,5 +1,4 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use crate::data_component_impl::*;
#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[derive(Copy, Clone, Hash, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
pub enum DataComponent { pub enum DataComponent {

View File

@ -1,8 +1,6 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use pumpkin_util::math::int_provider::{ use pumpkin_util::math::int_provider::{
BiasedToBottomIntProvider, ClampedIntProvider, ClampedNormalIntProvider, ConstantIntProvider, IntProvider, NormalIntProvider, UniformIntProvider,
IntProvider, NormalIntProvider, TrapezoidIntProvider, UniformIntProvider, WeightedEntry,
WeightedListIntProvider,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Dimension { pub struct Dimension {

View File

@ -883,7 +883,7 @@ fn default_water_source_conversion() -> bool {
GameRuleRegistry::default().water_source_conversion GameRuleRegistry::default().water_source_conversion
} }
mod as_string { mod as_string {
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serializer};
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
pub fn serialize<T: Display, S: Serializer>( pub fn serialize<T: Display, S: Serializer>(
value: &T, value: &T,

View File

@ -13,8 +13,7 @@ use crate::data_component_impl::*;
use crate::effect::StatusEffect; use crate::effect::StatusEffect;
use crate::sound::Sound; use crate::sound::Sound;
use crate::tag::{RegistryKey, Taggable}; use crate::tag::{RegistryKey, Taggable};
use crate::{AttributeModifierSlot, tag}; use crate::AttributeModifierSlot;
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use std::borrow::Cow; use std::borrow::Cow;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -64240,16 +64239,18 @@ impl Item {
}; };
#[must_use] #[must_use]
pub fn translated_name(&self) -> TextComponent { pub fn translated_name(&self) -> TextComponent {
TextComponent::translate( let name = self
self.components .components
.iter() .iter()
.find_map(|(id, data)| { .find_map(|(id, data)| {
(id == &ItemName) if id == &ItemName {
.then(|| data.as_any().downcast_ref::<ItemNameImpl>().unwrap().name) data.as_any().downcast_ref::<ItemNameImpl>().map(|n| n.name)
}) } else {
.unwrap(), None
&[], }
) })
.unwrap_or(self.registry_key);
TextComponent::translate(name, &[])
} }
#[doc = "Try to parse an item from a resource location string."] #[doc = "Try to parse an item from a resource location string."]
#[must_use] #[must_use]

View File

@ -1,6 +1,5 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use crate::effect::StatusEffect; use crate::effect::StatusEffect;
use std::hash::Hash;
pub struct Potion { pub struct Potion {
pub id: u8, pub id: u8,
pub name: &'static str, pub name: &'static str,

View File

@ -1,7 +1,7 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use crate::item::Item; use crate::item::Item;
use crate::tag::Taggable; use crate::tag::Taggable;
use serde::{Deserialize, Serialize}; use serde::Serialize;
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub enum CraftingRecipeTypes { pub enum CraftingRecipeTypes {
CraftingShaped { CraftingShaped {

View File

@ -1,8 +1,6 @@
/* This file is generated. Do not edit manually. */ /* This file is generated. Do not edit manually. */
use pumpkin_util::math::floor_div;
use pumpkin_util::random::{ use pumpkin_util::random::{
RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, legacy_rand::LegacyRand, RandomGenerator, RandomImpl,
xoroshiro128::Xoroshiro,
}; };
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum StructureKeys { pub enum StructureKeys {

View File

@ -133,7 +133,10 @@ impl ItemStack {
item, item,
patch: Vec::new(), patch: Vec::new(),
uid: NonZeroI32::new(1).unwrap(), uid: match NonZeroI32::new(1) {
Some(v) => v,
None => panic!("1 is non-zero"),
},
} }
} }
@ -176,8 +179,7 @@ impl ItemStack {
self.patch.push((id, component)); self.patch.push((id, component));
return self return self
.patch .patch
.last_mut() .last_mut()?
.unwrap()
.1 .1
.as_mut() .as_mut()
.map(|c| get_mut::<T>(c.as_mut())); .map(|c| get_mut::<T>(c.as_mut()));
@ -209,7 +211,7 @@ impl ItemStack {
item: &Item::AIR, item: &Item::AIR,
patch: Vec::new(), patch: Vec::new(),
uid: NonZeroI32::new(i32::MIN).unwrap(), // white lie - Bedrock `uid` is never sent if the stack is empty uid: NonZeroI32::MIN, // white lie - Bedrock `uid` is never sent if the stack is empty
}; };
#[must_use] #[must_use]
@ -731,7 +733,7 @@ impl From<&RecipeResultStruct> for ItemStack {
Self::new( Self::new(
value.count, value.count,
Item::from_registry_key(value.id.strip_prefix("minecraft:").unwrap_or(value.id)) Item::from_registry_key(value.id.strip_prefix("minecraft:").unwrap_or(value.id))
.expect("Crafting recipe gives invalid item"), .unwrap_or(&Item::AIR),
) )
} }
} }

View File

@ -1,11 +1,13 @@
#![allow(unused_imports)]
#![allow( #![allow(
clippy::all, clippy::all,
clippy::pedantic, clippy::pedantic,
clippy::nursery, clippy::nursery,
clippy::cargo, clippy::cargo,
clippy::undocumented_unsafe_blocks, clippy::undocumented_unsafe_blocks,
clippy::if_then_some_else_none clippy::if_then_some_else_none,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic
)] )]
#[rustfmt::skip] #[rustfmt::skip]

View File

@ -90,7 +90,10 @@ async fn recipe_matches(
result, result,
.. ..
}) => { }) => {
if pattern.len() != input_height || pattern.first().unwrap().len() != input_width { #[allow(clippy::redundant_closure_for_method_calls)]
if pattern.len() != input_height
|| pattern.first().map_or(0, |f| f.len()) != input_width
{
return None; return None;
} }
@ -120,10 +123,13 @@ async fn recipe_matches(
continue; continue;
} }
let ingredient = key let Some(ingredient) = key
.iter() .iter()
.find_map(|(k, v)| (*k == current_key).then_some(v)) .find_map(|(k, v)| (*k == current_key).then_some(v))
.expect("Crafting recipe used invalid key"); else {
matched = false;
break 'outer;
};
if !ingredient.match_item(slot.item) { if !ingredient.match_item(slot.item) {
matched = false; matched = false;
@ -136,7 +142,10 @@ async fn recipe_matches(
matched = true; matched = true;
'outer: for y in 0..pattern.len() { 'outer: for y in 0..pattern.len() {
for x in 0..pattern[y].len() { for x in 0..pattern[y].len() {
let current_key = pattern[y].chars().nth(x).unwrap(); let Some(current_key) = pattern[y].chars().nth(x) else {
matched = false;
break 'outer;
};
let slot = inventory let slot = inventory
.get_stack( .get_stack(
(y + y_offset) * inventory.get_height() (y + y_offset) * inventory.get_height()
@ -150,10 +159,13 @@ async fn recipe_matches(
} }
continue; continue;
} }
let ingredient = key let Some(ingredient) = key
.iter() .iter()
.find_map(|(k, v)| (*k == current_key).then_some(v)) .find_map(|(k, v)| (*k == current_key).then_some(v))
.expect("Crafting recipe used invalid key"); else {
matched = false;
break 'outer;
};
if !ingredient.match_item(slot.item) { if !ingredient.match_item(slot.item) {
matched = false; matched = false;
break 'outer; break 'outer;
@ -242,7 +254,10 @@ async fn recipe_matches(
result, result,
.. ..
}) => { }) => {
if pattern.len() != input_height || pattern.first().unwrap().len() != input_width { #[allow(clippy::redundant_closure_for_method_calls)]
if pattern.len() != input_height
|| pattern.first().map_or(0, |f| f.len()) != input_width
{
return None; return None;
} }
if count if count
@ -268,11 +283,12 @@ async fn recipe_matches(
} }
continue; continue;
} }
let ingredient = key let Some(ingredient) =
.iter() key.iter().find(|(k, _)| *k == current_key).map(|(_, v)| v)
.find(|(k, _)| *k == current_key) else {
.map(|(_, v)| v) matched = false;
.expect("Crafting recipe used invalid key"); break 'outer;
};
if !ingredient.match_item(slot.item) { if !ingredient.match_item(slot.item) {
matched = false; matched = false;
break 'outer; break 'outer;

View File

@ -13,6 +13,9 @@
//! - [`SyncHandler`] - Synchronizes inventory state between server and client //! - [`SyncHandler`] - Synchronizes inventory state between server and client
//! //!
//! # Module Structure //! # Module Structure
#![deny(clippy::unwrap_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
//! //!
//! - [`player`] - Player inventory and screen handler implementations //! - [`player`] - Player inventory and screen handler implementations
//! - [`crafting`] - Crafting table and inventory crafting mechanics //! - [`crafting`] - Crafting table and inventory crafting mechanics

View File

@ -116,7 +116,9 @@ impl PlayerInventory {
match hand { match hand {
Hand::Right => self.set_held_item(stack).await, Hand::Right => self.set_held_item(stack).await,
Hand::Left => { Hand::Left => {
let slot = self.equipment_slots.get(&Self::OFF_HAND_SLOT).unwrap(); let Some(slot) = self.equipment_slots.get(&Self::OFF_HAND_SLOT) else {
return;
};
self.entity_equipment.lock().await.put(slot, stack); self.entity_equipment.lock().await.put(slot, stack);
} }
} }
@ -137,7 +139,9 @@ impl PlayerInventory {
/// ///
/// Mojang name: `getOffHandStack` /// Mojang name: `getOffHandStack`
pub async fn off_hand_item(&self) -> ItemStack { pub async fn off_hand_item(&self) -> ItemStack {
let slot = self.equipment_slots.get(&Self::OFF_HAND_SLOT).unwrap(); let Some(slot) = self.equipment_slots.get(&Self::OFF_HAND_SLOT) else {
return ItemStack::EMPTY.clone();
};
self.entity_equipment.lock().await.get(slot) self.entity_equipment.lock().await.get(slot)
} }
@ -146,7 +150,9 @@ impl PlayerInventory {
/// # Returns /// # Returns
/// The new main hand item and new off-hand item. /// The new main hand item and new off-hand item.
pub async fn swap_item(&self) -> (ItemStack, ItemStack) { pub async fn swap_item(&self) -> (ItemStack, ItemStack) {
let slot = self.equipment_slots.get(&Self::OFF_HAND_SLOT).unwrap(); let Some(slot) = self.equipment_slots.get(&Self::OFF_HAND_SLOT) else {
return (ItemStack::EMPTY.clone(), ItemStack::EMPTY.clone());
};
let mut equipment = self.entity_equipment.lock().await; let mut equipment = self.entity_equipment.lock().await;
let selected = self.get_selected_slot() as usize; let selected = self.get_selected_slot() as usize;
let mut main_inv = self.main_inventory.write().await; let mut main_inv = self.main_inventory.write().await;
@ -514,8 +520,6 @@ impl PlayerInventory {
pub fn set_selected_slot(&self, slot: u8) { pub fn set_selected_slot(&self, slot: u8) {
if Self::is_valid_hotbar_index(slot as usize) { if Self::is_valid_hotbar_index(slot as usize) {
self.selected_slot.store(slot, Ordering::Relaxed); self.selected_slot.store(slot, Ordering::Relaxed);
} else {
panic!("Invalid hotbar slot: {slot}");
} }
} }

View File

@ -917,15 +917,14 @@ pub trait ScreenHandler: Send + Sync {
if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty())
&& slot.can_insert(&cursor_stack).await && slot.can_insert(&cursor_stack).await
{ {
let mut inserting_count = if drag_button == 0 { let mut inserting_count = match drag_button {
initial_count / behaviour.drag_slots.len() as u8 0 => initial_count / behaviour.drag_slots.len() as u8,
} else if drag_button == 1 { 1 => 1,
1 2 => {
} else if drag_button == 2 { cursor_stack.item_count = cursor_stack.get_max_stack_size();
cursor_stack.item_count = cursor_stack.get_max_stack_size(); cursor_stack.item_count
cursor_stack.item_count }
} else { _ => 0,
panic!("Invalid drag button: {drag_button}");
}; };
inserting_count = inserting_count inserting_count = inserting_count
.min(max( .min(max(

View File

@ -70,8 +70,7 @@ impl StonecutterScreenHandler {
if recipe_index != u8::MAX && (recipe_index as usize) < available_recipes.len() { if recipe_index != u8::MAX && (recipe_index as usize) < available_recipes.len() {
let recipe = available_recipes[recipe_index as usize]; let recipe = available_recipes[recipe_index as usize];
let item = let item = Item::from_registry_key(recipe.result.id).unwrap_or(&Item::AIR);
Item::from_registry_key(recipe.result.id).expect("Invalid recipe result item");
let result = ItemStack::new(recipe.result.count, item); let result = ItemStack::new(recipe.result.count, item);
self.output_inventory.set_stack(0, result).await; self.output_inventory.set_stack(0, result).await;
} else { } else {

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use heck::ToShoutySnakeCase; use heck::ToShoutySnakeCase;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro_error2::{abort, abort_call_site}; use proc_macro_error2::{abort, abort_call_site};

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use criterion::{Criterion, criterion_group, criterion_main}; use criterion::{Criterion, criterion_group, criterion_main};
use pumpkin_nbt::{Nbt, NbtCompound, deserializer, tag::NbtTag}; use pumpkin_nbt::{Nbt, NbtCompound, deserializer, tag::NbtTag};
use std::io::Cursor; use std::io::Cursor;

View File

@ -4,6 +4,9 @@
//! NBT, Bedrock network NBT, and gzip-compressed NBT. Data is handled directly //! NBT, Bedrock network NBT, and gzip-compressed NBT. Data is handled directly
//! via [`Nbt`], [`NbtCompound`], and [`NbtTag`]. //! via [`Nbt`], [`NbtCompound`], and [`NbtTag`].
#![deny(clippy::unwrap_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
use std::{ use std::{
io::{self, Write}, io::{self, Write},
ops::Deref, ops::Deref,
@ -156,11 +159,13 @@ impl Nbt {
pub fn write(self) -> Bytes { pub fn write(self) -> Bytes {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
let mut writer = NbtWriteHelperJava::new(&mut bytes); let mut writer = NbtWriteHelperJava::new(&mut bytes);
writer.write_u8(COMPOUND_ID).unwrap(); if writer.write_u8(COMPOUND_ID).is_ok()
NbtTag::String(self.name.into()) && NbtTag::String(self.name.into())
.serialize_data(&mut writer) .serialize_data(&mut writer)
.unwrap(); .is_ok()
self.root_tag.serialize_content(&mut writer).unwrap(); {
let _ = self.root_tag.serialize_content(&mut writer);
}
bytes.into() bytes.into()
} }
@ -170,11 +175,13 @@ impl Nbt {
pub fn write_bedrock(self) -> Bytes { pub fn write_bedrock(self) -> Bytes {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
let mut writer = NbtWriteHelperBedrock::new(&mut bytes); let mut writer = NbtWriteHelperBedrock::new(&mut bytes);
writer.write_u8(COMPOUND_ID).unwrap(); if writer.write_u8(COMPOUND_ID).is_ok()
NbtTag::String(self.name.into()) && NbtTag::String(self.name.into())
.serialize_data(&mut writer) .serialize_data(&mut writer)
.unwrap(); .is_ok()
self.root_tag.serialize_content(&mut writer).unwrap(); {
let _ = self.root_tag.serialize_content(&mut writer);
}
bytes.into() bytes.into()
} }
@ -197,8 +204,9 @@ impl Nbt {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
let mut writer = NbtWriteHelperJava::new(&mut bytes); let mut writer = NbtWriteHelperJava::new(&mut bytes);
writer.write_u8(COMPOUND_ID).unwrap(); if writer.write_u8(COMPOUND_ID).is_ok() {
self.root_tag.serialize_content(&mut writer).unwrap(); let _ = self.root_tag.serialize_content(&mut writer);
}
bytes.into() bytes.into()
} }

View File

@ -141,14 +141,12 @@ impl DynamicOps for NbtOps {
NbtTag::List(l) => { NbtTag::List(l) => {
// Check the type of this list. // Check the type of this list.
// If the list contains compounds, we try unwrapping them. // If the list contains compounds, we try unwrapping them.
if !l.is_empty() if let Some(NbtTag::Compound(_)) = l.first() {
&& let NbtTag::Compound(_) = l.first().unwrap()
{
DataResult::new_success(NbtIter::CompoundList(l.into_iter().map(|c| { DataResult::new_success(NbtIter::CompoundList(l.into_iter().map(|c| {
if let NbtTag::Compound(compound) = c { if let NbtTag::Compound(compound) = c {
Self::try_unwrap(compound) Self::try_unwrap(compound)
} else { } else {
c.clone() c
} }
}))) })))
} else { } else {
@ -242,8 +240,13 @@ impl DynamicOps for NbtOps {
} else { } else {
NbtCompound::new() NbtCompound::new()
}; };
compound.put(key.extract_string().unwrap(), value); key.extract_string().map_or_else(
DataResult::new_success(compound.into()) || DataResult::new_error(format!("Key is not a string: {key}")),
|key_str| {
compound.put(key_str, value);
DataResult::new_success(compound.into())
},
)
} }
} }
@ -334,7 +337,10 @@ impl NbtOps {
fn try_unwrap(mut compound: NbtCompound) -> NbtTag { fn try_unwrap(mut compound: NbtCompound) -> NbtTag {
if compound.child_tags.len() == 1 && compound.has("") { if compound.child_tags.len() == 1 && compound.has("") {
// Remove the element to own the contained tag. // Remove the element to own the contained tag.
compound.child_tags.remove("").unwrap() compound
.child_tags
.remove("")
.unwrap_or_else(|| NbtTag::from(compound))
} else { } else {
NbtTag::from(compound) NbtTag::from(compound)
} }

View File

@ -83,7 +83,10 @@ impl NbtTag {
if let Self::Compound(mut compound) = tag { if let Self::Compound(mut compound) = tag {
// Try to get the wrapped tag, stored by "". // Try to get the wrapped tag, stored by "".
if Self::is_wrapper_compound(&compound) { if Self::is_wrapper_compound(&compound) {
compound.child_tags.remove("").unwrap() compound
.child_tags
.remove("")
.unwrap_or(Self::Compound(compound))
} else { } else {
Self::Compound(compound) Self::Compound(compound)
} }

View File

@ -47,7 +47,7 @@ impl Command {
COMMAND_HANDLERS COMMAND_HANDLERS
.lock() .lock()
.unwrap() .unwrap_or_else(|e| e.into_inner())
.insert(id, Box::new(handler)); .insert(id, Box::new(handler));
self.execute_with_handler_id(id); self.execute_with_handler_id(id);
@ -67,7 +67,7 @@ impl CommandNode {
COMMAND_HANDLERS COMMAND_HANDLERS
.lock() .lock()
.unwrap() .unwrap_or_else(|e| e.into_inner())
.insert(id, Box::new(handler)); .insert(id, Box::new(handler));
self.execute_with_handler_id(id); self.execute_with_handler_id(id);

View File

@ -1,3 +1,5 @@
#![allow(clippy::panic)]
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
marker::PhantomData, marker::PhantomData,

View File

@ -213,12 +213,12 @@ impl FormResponse {
None => Self::Closed, None => Self::Closed,
Some(s) => { Some(s) => {
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&s) { if let Ok(val) = serde_json::from_str::<serde_json::Value>(&s) {
if val.is_u64() { if let Some(num) = val.as_u64() {
Self::Simple(val.as_u64().unwrap() as u32) Self::Simple(num as u32)
} else if val.is_boolean() { } else if let Some(b) = val.as_bool() {
Self::Modal(val.as_bool().unwrap()) Self::Modal(b)
} else if val.is_array() { } else if let Some(arr) = val.as_array() {
Self::Custom(val.as_array().unwrap().clone()) Self::Custom(arr.clone())
} else { } else {
Self::Closed // Or some error state Self::Closed // Or some error state
} }

View File

@ -7,8 +7,10 @@
clippy::all, clippy::all,
clippy::pedantic, clippy::pedantic,
clippy::nursery, clippy::nursery,
clippy::cargo clippy::cargo,
clippy::panic
)] )]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
//! //!
//! This crate provides everything needed to write a Pumpkin server plugin compiled //! This crate provides everything needed to write a Pumpkin server plugin compiled
//! to WebAssembly. A plugin consists of a type that implements [`Plugin`], registered //! to WebAssembly. A plugin consists of a type that implements [`Plugin`], registered
@ -149,7 +151,7 @@ impl wit::Guest for Component {
/// ///
/// Returns the event unchanged if no handler is registered for the given id. /// Returns the event unchanged if no handler is registered for the given id.
fn handle_event(event_id: u32, server: Server, event: events::Event) -> events::Event { fn handle_event(event_id: u32, server: Server, event: events::Event) -> events::Event {
let handlers = EVENT_HANDLERS.lock().unwrap(); let handlers = EVENT_HANDLERS.lock().unwrap_or_else(|e| e.into_inner());
if let Some(handler) = handlers.get(&event_id) { if let Some(handler) = handlers.get(&event_id) {
handler.handle_erased(server, event) handler.handle_erased(server, event)
} else { } else {
@ -166,7 +168,7 @@ impl wit::Guest for Component {
server: Server, server: Server,
args: command::ConsumedArgs, args: command::ConsumedArgs,
) -> Result<i32, command::CommandError> { ) -> Result<i32, command::CommandError> {
let handlers = COMMAND_HANDLERS.lock().unwrap(); let handlers = COMMAND_HANDLERS.lock().unwrap_or_else(|e| e.into_inner());
handlers.get(&command_id).map_or_else( handlers.get(&command_id).map_or_else(
|| { || {
Err(command::CommandError::CommandFailed(TextComponent::text( Err(command::CommandError::CommandFailed(TextComponent::text(
@ -179,12 +181,14 @@ impl wit::Guest for Component {
/// WIT entry point — dispatches a scheduled task invocation to the registered handler for `handler_id`. /// WIT entry point — dispatches a scheduled task invocation to the registered handler for `handler_id`.
fn handle_task(handler_id: u32, server: Server) { fn handle_task(handler_id: u32, server: Server) {
let mut handlers = TASK_HANDLERS.lock().unwrap(); let mut handlers = TASK_HANDLERS.lock().unwrap_or_else(|e| e.into_inner());
handlers.handle(handler_id, server); handlers.handle(handler_id, server);
} }
fn handle_ai_goal_can_start(goal_id: u32, server: Server, entity: entity::Entity) -> bool { fn handle_ai_goal_can_start(goal_id: u32, server: Server, entity: entity::Entity) -> bool {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap(); let mut handlers = crate::ai::AI_GOAL_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner());
if let Some(goal) = handlers.handlers.get_mut(&goal_id) { if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.can_start(server, entity) goal.can_start(server, entity)
} else { } else {
@ -197,7 +201,9 @@ impl wit::Guest for Component {
server: Server, server: Server,
entity: entity::Entity, entity: entity::Entity,
) -> bool { ) -> bool {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap(); let mut handlers = crate::ai::AI_GOAL_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner());
if let Some(goal) = handlers.handlers.get_mut(&goal_id) { if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.should_continue(server, entity) goal.should_continue(server, entity)
} else { } else {
@ -206,21 +212,27 @@ impl wit::Guest for Component {
} }
fn handle_ai_goal_start(goal_id: u32, server: Server, entity: entity::Entity) { fn handle_ai_goal_start(goal_id: u32, server: Server, entity: entity::Entity) {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap(); let mut handlers = crate::ai::AI_GOAL_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner());
if let Some(goal) = handlers.handlers.get_mut(&goal_id) { if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.start(server, entity); goal.start(server, entity);
} }
} }
fn handle_ai_goal_tick(goal_id: u32, server: Server, entity: entity::Entity) { fn handle_ai_goal_tick(goal_id: u32, server: Server, entity: entity::Entity) {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap(); let mut handlers = crate::ai::AI_GOAL_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner());
if let Some(goal) = handlers.handlers.get_mut(&goal_id) { if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.tick(server, entity); goal.tick(server, entity);
} }
} }
fn handle_ai_goal_stop(goal_id: u32, server: Server, entity: entity::Entity) { fn handle_ai_goal_stop(goal_id: u32, server: Server, entity: entity::Entity) {
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap(); let mut handlers = crate::ai::AI_GOAL_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner());
if let Some(goal) = handlers.handlers.get_mut(&goal_id) { if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
goal.stop(server, entity); goal.stop(server, entity);
} }
@ -271,6 +283,7 @@ pub fn register_plugin(build_plugin: fn() -> Box<dyn Plugin>) {
/// If called before [`register_plugin`] has initialized `PLUGIN`. /// If called before [`register_plugin`] has initialized `PLUGIN`.
fn plugin() -> &'static mut dyn Plugin { fn plugin() -> &'static mut dyn Plugin {
#[expect(static_mut_refs)] #[expect(static_mut_refs)]
#[allow(clippy::unwrap_used)]
unsafe { unsafe {
PLUGIN.as_deref_mut().unwrap() PLUGIN.as_deref_mut().unwrap()
} }

View File

@ -40,9 +40,9 @@ impl tracing::Subscriber for WitSubscriber {
/// Serialises the tracing event with `postcard` and sends it to the host via WIT. /// Serialises the tracing event with `postcard` and sends it to the host via WIT.
fn event(&self, event: &tracing::Event<'_>) { fn event(&self, event: &tracing::Event<'_>) {
let serialized = if let Ok(serialized) = postcard::to_allocvec(&event.as_serde()) {
postcard::to_allocvec(&event.as_serde()).expect("failed to serialize tracing event"); wit::pumpkin::plugin::logging::log_tracing(&serialized);
wit::pumpkin::plugin::logging::log_tracing(&serialized); }
} }
/// No-op — span entry is not tracked. /// No-op — span entry is not tracked.

View File

@ -114,7 +114,10 @@ pub fn schedule_delayed_task<F>(delay_ticks: u64, handler: F) -> u32
where where
F: FnMut(Server) + Send + 'static, F: FnMut(Server) + Send + 'static,
{ {
let handler_id = TASK_HANDLERS.lock().unwrap().register(Box::new(handler)); let handler_id = TASK_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner())
.register(Box::new(handler));
scheduler::schedule_delayed_task(handler_id, delay_ticks) scheduler::schedule_delayed_task(handler_id, delay_ticks)
} }
@ -124,7 +127,10 @@ pub fn schedule_repeating_task<F>(delay_ticks: u64, period_ticks: u64, handler:
where where
F: FnMut(Server) + Send + 'static, F: FnMut(Server) + Send + 'static,
{ {
let handler_id = TASK_HANDLERS.lock().unwrap().register(Box::new(handler)); let handler_id = TASK_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner())
.register(Box::new(handler));
scheduler::schedule_repeating_task(handler_id, delay_ticks, period_ticks) scheduler::schedule_repeating_task(handler_id, delay_ticks, period_ticks)
} }

View File

@ -18,9 +18,7 @@ impl OwnedRecipeIngredient {
let name = format!("minecraft:{}", item.registry_key); let name = format!("minecraft:{}", item.registry_key);
name == *id name == *id
} }
Self::Tagged(tag) => item Self::Tagged(tag) => item.is_tagged_with(tag).unwrap_or(false),
.is_tagged_with(tag)
.expect("Crafting recipe used invalid tag"),
Self::OneOf(ids) => { Self::OneOf(ids) => {
let name = format!("minecraft:{}", item.registry_key); let name = format!("minecraft:{}", item.registry_key);
ids.contains(&name) ids.contains(&name)

View File

@ -21,6 +21,7 @@ pub struct VarInt(pub VarIntType);
impl VarInt { impl VarInt {
/// The maximum number of bytes a `VarInt` can occupy. /// The maximum number of bytes a `VarInt` can occupy.
#[allow(clippy::unwrap_used)]
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap(); pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
#[must_use] #[must_use]

View File

@ -20,6 +20,7 @@ pub struct VarLong(pub VarLongType);
impl VarLong { impl VarLong {
/// The maximum number of bytes a `VarLong` can occupy. /// The maximum number of bytes a `VarLong` can occupy.
#[allow(clippy::unwrap_used)]
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap(); pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
#[must_use] #[must_use]

View File

@ -20,6 +20,7 @@ pub struct VarUInt(pub VarUIntType);
impl VarUInt { impl VarUInt {
/// The maximum number of bytes a `VarUInt` can occupy. /// The maximum number of bytes a `VarUInt` can occupy.
#[allow(clippy::unwrap_used)]
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap(); pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
#[must_use] #[must_use]

View File

@ -20,6 +20,7 @@ pub struct VarULong(pub VarULongType);
impl VarULong { impl VarULong {
/// The maximum number of bytes a `VarULong` can occupy. /// The maximum number of bytes a `VarULong` can occupy.
#[allow(clippy::unwrap_used)]
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap(); pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
#[must_use] #[must_use]

View File

@ -1,3 +1,6 @@
#![deny(clippy::unwrap_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
use std::{ use std::{
io::{Error, Read, Write}, io::{Error, Read, Write},
pin::Pin, pin::Pin,

View File

@ -290,7 +290,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for i16 {
} }
let (bytes, rest) = buf.split_at(2); let (bytes, rest) = buf.split_at(2);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid i16 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -301,7 +304,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for i32 {
} }
let (bytes, rest) = buf.split_at(4); let (bytes, rest) = buf.split_at(4);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid i32 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -312,7 +318,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for i64 {
} }
let (bytes, rest) = buf.split_at(8); let (bytes, rest) = buf.split_at(8);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid i64 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -323,7 +332,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for u16 {
} }
let (bytes, rest) = buf.split_at(2); let (bytes, rest) = buf.split_at(2);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid u16 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -334,7 +346,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for u32 {
} }
let (bytes, rest) = buf.split_at(4); let (bytes, rest) = buf.split_at(4);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid u32 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -345,7 +360,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for u64 {
} }
let (bytes, rest) = buf.split_at(8); let (bytes, rest) = buf.split_at(8);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid u64 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -356,7 +374,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for f32 {
} }
let (bytes, rest) = buf.split_at(4); let (bytes, rest) = buf.split_at(4);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid f32 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -367,7 +388,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for f64 {
} }
let (bytes, rest) = buf.split_at(8); let (bytes, rest) = buf.split_at(8);
*buf = rest; *buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid f64 slice"))?;
Ok(Self::from_le_bytes(arr))
} }
} }
@ -392,7 +416,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for uuid::Uuid {
} }
let (bytes, rest) = buf.split_at(16); let (bytes, rest) = buf.split_at(16);
*buf = rest; *buf = rest;
Ok(Self::from_bytes(bytes.try_into().unwrap())) let arr = bytes
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid Uuid slice"))?;
Ok(Self::from_bytes(arr))
} }
} }

View File

@ -474,7 +474,7 @@ fn verify_oidc_claims(payload: &Value, expected_issuer: Option<&str>) -> Result<
.ok_or_else(|| AuthError::PublicKeyBuild("OIDC payload missing exp".into()))?; .ok_or_else(|| AuthError::PublicKeyBuild("OIDC payload missing exp".into()))?;
let now = std::time::SystemTime::now() let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
.unwrap() .map_err(|_| AuthError::PublicKeyBuild("Clock before UNIX epoch".into()))?
.as_secs(); .as_secs();
if now > exp { if now > exp {
return Err(AuthError::PublicKeyBuild("OIDC token expired".into())); return Err(AuthError::PublicKeyBuild("OIDC token expired".into()));

View File

@ -1,3 +1,8 @@
//! Utility functions and shared types for the Pumpkin server.
#![deny(clippy::unwrap_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::panic))]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
@ -57,13 +62,12 @@ macro_rules! global_path {
($path:expr) => {{ ($path:expr) => {{
use std::path::Path; use std::path::Path;
Path::new(env!("CARGO_MANIFEST_DIR")) Path::new(env!("CARGO_MANIFEST_DIR"))
.parent() .ancestors()
.unwrap() .nth(2)
.parent() .unwrap_or_else(|| Path::new("."))
.unwrap()
.join(file!()) .join(file!())
.parent() .parent()
.unwrap() .unwrap_or_else(|| Path::new("."))
.join($path) .join($path)
}}; }};
} }
@ -188,11 +192,12 @@ impl<'a, T> MutableSplitSlice<'a, T> {
/// ///
/// # Panics /// # Panics
/// * if `index` is out of bounds of the base slice. /// * if `index` is out of bounds of the base slice.
#[allow(clippy::expect_used, clippy::panic)]
pub const fn extract_ith(base: &'a mut [T], index: usize) -> (&'a mut T, Self) { pub const fn extract_ith(base: &'a mut [T], index: usize) -> (&'a mut T, Self) {
let (start, end_inclusive) = base.split_at_mut(index); let (start, end_inclusive) = base.split_at_mut(index);
let (value, end) = end_inclusive let Some((value, end)) = end_inclusive.split_first_mut() else {
.split_first_mut() panic!("Index is not in base slice");
.expect("Index is not in base slice"); };
(value, Self { start, end }) (value, Self { start, end })
} }
@ -214,6 +219,7 @@ impl<T> Index<usize> for MutableSplitSlice<'_, T> {
type Output = T; type Output = T;
#[expect(clippy::comparison_chain)] #[expect(clippy::comparison_chain)]
#[allow(clippy::panic)]
fn index(&self, index: usize) -> &Self::Output { fn index(&self, index: usize) -> &Self::Output {
if index < self.start.len() { if index < self.start.len() {
&self.start[index] &self.start[index]
@ -239,6 +245,7 @@ pub struct DoublePerlinNoiseParametersCodec {
impl<T> IndexMut<usize> for MutableSplitSlice<'_, T> { impl<T> IndexMut<usize> for MutableSplitSlice<'_, T> {
#[expect(clippy::comparison_chain)] #[expect(clippy::comparison_chain)]
#[allow(clippy::panic)]
fn index_mut(&mut self, index: usize) -> &mut Self::Output { fn index_mut(&mut self, index: usize) -> &mut Self::Output {
if index < self.start.len() { if index < self.start.len() {
&mut self.start[index] &mut self.start[index]

View File

@ -645,7 +645,9 @@ impl WeightedListIntProvider {
} }
// Fallback to the last entry. // Fallback to the last entry.
self.distribution.last().unwrap().data.get(random) self.distribution
.last()
.map_or(0, |entry| entry.data.get(random))
} }
/// Returns the maximum possible value from all entries. /// Returns the maximum possible value from all entries.

View File

@ -317,8 +317,11 @@ impl OctavePerlinNoiseSampler {
let mut octaves = Vec::from_iter(octaves); let mut octaves = Vec::from_iter(octaves);
octaves.sort(); octaves.sort();
let i = -**octaves.first().expect("we should have some octaves"); let (Some(&&first), Some(&&last)) = (octaves.first(), octaves.last()) else {
let j = **octaves.last().expect("we should have some octaves"); return (0, Vec::new());
};
let i = -first;
let j = last;
let k = i + j + 1; let k = i + j + 1;
let mut double_list = vec![0.0; k as usize]; let mut double_list = vec![0.0; k as usize];

View File

@ -297,8 +297,15 @@ impl OctaveSimplexNoiseSampler {
let mut octaves = Vec::from_iter(octaves); let mut octaves = Vec::from_iter(octaves);
octaves.sort(); octaves.sort();
let i = -**octaves.first().expect("Should have some octaves"); let (Some(&&first), Some(&&last)) = (octaves.first(), octaves.last()) else {
let j = **octaves.last().expect("Should have some octaves"); return Self {
octave_samplers: Vec::new(),
persistence: 0.0,
lacunarity: 0.0,
};
};
let i = -first;
let j = last;
let k = i.wrapping_add(j).wrapping_add(1); let k = i.wrapping_add(j).wrapping_add(1);
let sampler = SimplexNoiseSampler::new(random); let sampler = SimplexNoiseSampler::new(random);

View File

@ -107,6 +107,7 @@ impl PermissionRegistry {
/// ///
/// # Parameters /// # Parameters
/// - `permission`: The `Permission` instance to add. /// - `permission`: The `Permission` instance to add.
#[allow(clippy::expect_used)]
pub fn register_permission_or_panic(&mut self, permission: Permission) { pub fn register_permission_or_panic(&mut self, permission: Permission) {
self.register_permission(permission) self.register_permission(permission)
.expect("Permission should have been registered successfully"); .expect("Permission should have been registered successfully");
@ -302,9 +303,9 @@ impl PermissionManager {
// Check for inherited permissions from parent nodes // Check for inherited permissions from parent nodes
for (node, value) in attachment.get_permissions() { for (node, value) in attachment.get_permissions() {
if let Some(permission) = reg.get_permission(node) if let Some(permission) = reg.get_permission(node)
&& permission.children.contains_key(permission_node) && let Some(child_val) = permission.children.get(permission_node)
{ {
return *value && *permission.children.get(permission_node).unwrap(); return *value && *child_val;
} }
} }
} }

View File

@ -18,12 +18,11 @@ pub fn get_seed() -> u64 {
.try_update(Ordering::Relaxed, Ordering::Relaxed, |val| { .try_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
Some(val.wrapping_mul(1181783497276652981u64)) Some(val.wrapping_mul(1181783497276652981u64))
}) })
.unwrap(); .unwrap_or(0);
let nanos = time::SystemTime::now() let nanos = time::SystemTime::now()
.duration_since(time::SystemTime::UNIX_EPOCH) .duration_since(time::SystemTime::UNIX_EPOCH)
.unwrap() .map_or(0, |d| d.as_nanos());
.as_nanos();
let nano_upper = (nanos >> 8) as u64; let nano_upper = (nanos >> 8) as u64;
let nano_lower = nanos as u64; let nano_lower = nanos as u64;

View File

@ -227,8 +227,8 @@ impl XoroshiroSplitter {
#[must_use] #[must_use]
pub fn split_string(&self, seed: &str) -> Xoroshiro { pub fn split_string(&self, seed: &str) -> Xoroshiro {
let bytes = md5::compute(seed.as_bytes()); let bytes = md5::compute(seed.as_bytes());
let l = u64::from_be_bytes(bytes[0..8].try_into().expect("incorrect length")); let l = u64::from_be_bytes(bytes[0..8].try_into().unwrap_or([0; 8]));
let m = u64::from_be_bytes(bytes[8..16].try_into().expect("incorrect length")); let m = u64::from_be_bytes(bytes[8..16].try_into().unwrap_or([0; 8]));
Xoroshiro::new(l ^ self.lo, m ^ self.hi) Xoroshiro::new(l ^ self.lo, m ^ self.hi)
} }

View File

@ -76,7 +76,9 @@ impl SubstitutionRange {
/// * `translation`: The localized translation string. /// * `translation`: The localized translation string.
/// * `locale`: The locale the translation belongs to. /// * `locale`: The locale the translation belongs to.
pub fn add_translation<P: Into<String>>(namespace: P, key: P, translation: P, locale: Locale) { pub fn add_translation<P: Into<String>>(namespace: P, key: P, translation: P, locale: Locale) {
let mut translations = TRANSLATIONS.lock().unwrap(); let mut translations = TRANSLATIONS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let namespaced_key = format!("{}:{}", namespace.into(), key.into()).to_lowercase(); let namespaced_key = format!("{}:{}", namespace.into(), key.into()).to_lowercase();
translations[locale as usize].insert(namespaced_key, translation.into()); translations[locale as usize].insert(namespaced_key, translation.into());
} }
@ -89,13 +91,15 @@ pub fn add_translation<P: Into<String>>(namespace: P, key: P, translation: P, lo
/// * `locale`: The locale the translations belong to. /// * `locale`: The locale the translations belong to.
pub fn add_translation_file<P: Into<String>>(namespace: P, file_path: P, locale: Locale) { pub fn add_translation_file<P: Into<String>>(namespace: P, file_path: P, locale: Locale) {
let translations_map: HashMap<String, String> = let translations_map: HashMap<String, String> =
serde_json::from_str(&file_path.into()).unwrap_or(HashMap::new()); serde_json::from_str(&file_path.into()).unwrap_or_default();
if translations_map.is_empty() { if translations_map.is_empty() {
// TODO: Handle the case where the file is empty or not found properly // TODO: Handle the case where the file is empty or not found properly
return; return;
} }
let mut translations = TRANSLATIONS.lock().unwrap(); let mut translations = TRANSLATIONS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let namespace = namespace.into(); let namespace = namespace.into();
for (key, translation) in translations_map { for (key, translation) in translations_map {
let namespaced_key = format!("{namespace}:{key}").to_lowercase(); let namespaced_key = format!("{namespace}:{key}").to_lowercase();
@ -112,7 +116,9 @@ pub fn add_translation_file<P: Into<String>>(namespace: P, file_path: P, locale:
/// # Returns /// # Returns
/// The localized translation. Falls back to `en_us` or the key itself if not found. /// The localized translation. Falls back to `en_us` or the key itself if not found.
pub fn get_translation(key: &str, locale: Locale) -> String { pub fn get_translation(key: &str, locale: Locale) -> String {
let translations = TRANSLATIONS.lock().unwrap(); let translations = TRANSLATIONS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let key = key.to_lowercase(); let key = key.to_lowercase();
translations[locale as usize].get(&key).map_or_else( translations[locale as usize].get(&key).map_or_else(
|| { || {
@ -273,58 +279,35 @@ pub static TRANSLATIONS: LazyLock<Mutex<[HashMap<String, String>; Locale::COUNT]
LazyLock::new(|| { LazyLock::new(|| {
let mut array: [HashMap<String, String>; Locale::COUNT] = let mut array: [HashMap<String, String>; Locale::COUNT] =
std::array::from_fn(|_| HashMap::new()); std::array::from_fn(|_| HashMap::new());
let vanilla_en_us: HashMap<String, String> = let parse_json = |json: &str| -> HashMap<String, String> {
serde_json::from_str(VANILLA_EN_US_JSON).expect("Could not parse en_us_java.json."); serde_json::from_str(json).unwrap_or_default()
let pumpkin_en_us: HashMap<String, String> = };
serde_json::from_str(PUMPKIN_EN_US_JSON).expect("Could not parse en_us.json."); let vanilla_en_us = parse_json(VANILLA_EN_US_JSON);
let pumpkin_brb: HashMap<String, String> = let pumpkin_en_us = parse_json(PUMPKIN_EN_US_JSON);
serde_json::from_str(PUMPKIN_BRB_JSON).expect("Could not parse brb.json."); let pumpkin_brb = parse_json(PUMPKIN_BRB_JSON);
let pumpkin_de_de: HashMap<String, String> = let pumpkin_de_de = parse_json(PUMPKIN_DE_DE_JSON);
serde_json::from_str(PUMPKIN_DE_DE_JSON).expect("Could not parse de_de.json."); let pumpkin_es_es = parse_json(PUMPKIN_ES_ES_JSON);
let pumpkin_es_es: HashMap<String, String> = let pumpkin_fr_fr = parse_json(PUMPKIN_FR_FR_JSON);
serde_json::from_str(PUMPKIN_ES_ES_JSON).expect("Could not parse es_es.json."); let pumpkin_hr_hr = parse_json(PUMPKIN_HR_HR_JSON);
let pumpkin_fr_fr: HashMap<String, String> = let pumpkin_it_it = parse_json(PUMPKING_IT_IT_JSON);
serde_json::from_str(PUMPKIN_FR_FR_JSON).expect("Could not parse fr_fr.json."); let pumpkin_ja_jp = parse_json(PUMPKIN_JA_JP_JSON);
let pumpkin_hr_hr: HashMap<String, String> = let pumpkin_ka_ge = parse_json(PUMPKIN_KA_GE_JSON);
serde_json::from_str(PUMPKIN_HR_HR_JSON).expect("Could not parse hr_hr.json."); let pumpkin_ko_kr = parse_json(PUMPKIN_KO_KR_JSON);
let pumpkin_it_it: HashMap<String, String> = let pumpkin_nds_de = parse_json(PUMPKIN_NDS_DE_JSON);
serde_json::from_str(PUMPKING_IT_IT_JSON).expect("Could not parse it_it.json."); let pumpkin_nl_be = parse_json(PUMPKIN_NL_BE_JSON);
let pumpkin_ja_jp: HashMap<String, String> = let pumpkin_nl_nl = parse_json(PUMPKIN_NL_NL_JSON);
serde_json::from_str(PUMPKIN_JA_JP_JSON).expect("Could not parse ja_jp.json."); let pumpkin_ro_ro = parse_json(PUMPKIN_RO_RO_JSON);
let pumpkin_ka_ge: HashMap<String, String> = let pumpkin_ru_ru = parse_json(PUMPKIN_RU_RU_JSON);
serde_json::from_str(PUMPKIN_KA_GE_JSON).expect("Could not parse ka_ge.json."); let pumpkin_sq_al = parse_json(PUMPKIN_SQ_AL_JSON);
let pumpkin_ko_kr: HashMap<String, String> = let pumpkin_zh_cn = parse_json(PUMPKIN_ZH_CN_JSON);
serde_json::from_str(PUMPKIN_KO_KR_JSON).expect("Could not parse ko_kr.json."); let pumpkin_zh_hk = parse_json(PUMPKIN_ZH_HK_JSON);
let pumpkin_nds_de: HashMap<String, String> = let pumpkin_zh_tw = parse_json(PUMPKIN_ZH_TW_JSON);
serde_json::from_str(PUMPKIN_NDS_DE_JSON).expect("Could not parse nds_de.json."); let pumpkin_lzh = parse_json(PUMPKIN_LZH_JSON);
let pumpkin_nl_be: HashMap<String, String> = let pumpkin_tr_tr = parse_json(PUMPKIN_TR_TR_JSON);
serde_json::from_str(PUMPKIN_NL_BE_JSON).expect("Could not parse nl_be.json."); let pumpkin_uk_ua = parse_json(PUMPKIN_UK_UA_JSON);
let pumpkin_nl_nl: HashMap<String, String> = let pumpkin_vi_vn = parse_json(PUMPKIN_VI_VN_JSON);
serde_json::from_str(PUMPKIN_NL_NL_JSON).expect("Could not parse nl_nl.json."); let pumpkin_pt_br = parse_json(PUMPKIN_PT_BR_JSON);
let pumpkin_ro_ro: HashMap<String, String> = let pumpkin_pl_pl = parse_json(PUMPKIN_PL_PL_JSON);
serde_json::from_str(PUMPKIN_RO_RO_JSON).expect("Could not parse ro_ro.json.");
let pumpkin_ru_ru: HashMap<String, String> =
serde_json::from_str(PUMPKIN_RU_RU_JSON).expect("Could not parse ru_ru.json.");
let pumpkin_sq_al: HashMap<String, String> =
serde_json::from_str(PUMPKIN_SQ_AL_JSON).expect("Could not parse sq_al.json.");
let pumpkin_zh_cn: HashMap<String, String> =
serde_json::from_str(PUMPKIN_ZH_CN_JSON).expect("Could not parse zh_cn.json.");
let pumpkin_zh_hk: HashMap<String, String> =
serde_json::from_str(PUMPKIN_ZH_HK_JSON).expect("Could not parse zh_hk.json.");
let pumpkin_zh_tw: HashMap<String, String> =
serde_json::from_str(PUMPKIN_ZH_TW_JSON).expect("Could not parse zh_tw.json.");
let pumpkin_lzh: HashMap<String, String> =
serde_json::from_str(PUMPKIN_LZH_JSON).expect("Could not parse lzh.json.");
let pumpkin_tr_tr: HashMap<String, String> =
serde_json::from_str(PUMPKIN_TR_TR_JSON).expect("Could not parse tr_tr.json.");
let pumpkin_uk_ua: HashMap<String, String> =
serde_json::from_str(PUMPKIN_UK_UA_JSON).expect("Could not parse uk_ua.json.");
let pumpkin_vi_vn: HashMap<String, String> =
serde_json::from_str(PUMPKIN_VI_VN_JSON).expect("Could not parse vi_vn.json.");
let pumpkin_pt_br: HashMap<String, String> =
serde_json::from_str(PUMPKIN_PT_BR_JSON).expect("Could not parse pt_br.json.");
let pumpkin_pl_pl: HashMap<String, String> =
serde_json::from_str(PUMPKIN_PL_PL_JSON).expect("Could not parse pl_pl.json.");
for (key, value) in vanilla_en_us { for (key, value) in vanilla_en_us {
array[Locale::EnUs as usize].insert(format!("minecraft:{key}"), value); array[Locale::EnUs as usize].insert(format!("minecraft:{key}"), value);

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use criterion::{Criterion, criterion_group, criterion_main}; use criterion::{Criterion, criterion_group, criterion_main};
use pumpkin_data::{ use pumpkin_data::{
chunk_gen_settings::GenerationSettings, dimension::Dimension, chunk_gen_settings::GenerationSettings, dimension::Dimension,

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use pumpkin_config::lighting::LightingEngineConfig; use pumpkin_config::lighting::LightingEngineConfig;
use pumpkin_data::BlockStateId; use pumpkin_data::BlockStateId;

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use criterion::{Criterion, criterion_group, criterion_main}; use criterion::{Criterion, criterion_group, criterion_main};
use pumpkin_data::BlockStateId; use pumpkin_data::BlockStateId;
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use criterion::{Criterion, criterion_group, criterion_main}; use criterion::{Criterion, criterion_group, criterion_main};
use pumpkin_data::{BlockStateId, dimension::Dimension}; use pumpkin_data::{BlockStateId, dimension::Dimension};
use pumpkin_util::{math::vector2::Vector2, world_seed::Seed}; use pumpkin_util::{math::vector2::Vector2, world_seed::Seed};

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use criterion::{Criterion, criterion_group, criterion_main}; use criterion::{Criterion, criterion_group, criterion_main};
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER; use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
use pumpkin_world::generation::{ use pumpkin_world::generation::{

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::env; use std::env;
use std::fmt::Write; use std::fmt::Write;
use std::fs; use std::fs;

View File

@ -395,14 +395,7 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
let mut chunks = indices let mut chunks = indices
.into_iter() .into_iter()
.map(|index| { .filter_map(|index| self.chunks_data[index].as_ref().map(|c| (index, c)))
(
index,
self.chunks_data[index]
.as_ref()
.expect("We are trying to write a chunk, but it does not exist!"),
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Sort such that writes are in order // Sort such that writes are in order
@ -726,13 +719,9 @@ impl<S: SingleChunkDataSerializer + 'static> ChunkSerializer for AnvilChunkFile<
timestamp: epoch, timestamp: epoch,
file_sector_offset: 0, file_sector_offset: 0,
}); });
} else { } else if let Some(swap) = chunks_to_shift.pop() {
// swap last element of the chunks to shift (the first because we // swap last element of the chunks to shift (the first because we
// reversed it) and shift the rest down // reversed it) and shift the rest down
let swap = chunks_to_shift
.pop()
.expect("We just checked that this exists");
let indices_to_shift = chunks_to_shift let indices_to_shift = chunks_to_shift
.iter() .iter()
.map(|(index, _)| index) .map(|(index, _)| index)
@ -749,10 +738,9 @@ impl<S: SingleChunkDataSerializer + 'static> ChunkSerializer for AnvilChunkFile<
}); });
write_action.maybe_update_chunk_index(index); write_action.maybe_update_chunk_index(index);
self.chunks_data[swapped_index] if let Some(swapped) = self.chunks_data[swapped_index].as_mut() {
.as_mut() swapped.file_sector_offset = old_offset;
.expect("We checked if this was none") }
.file_sector_offset = old_offset;
write_action.maybe_update_chunk_index(swapped_index); write_action.maybe_update_chunk_index(swapped_index);
// Then offset everything else // Then offset everything else
@ -765,12 +753,13 @@ impl<S: SingleChunkDataSerializer + 'static> ChunkSerializer for AnvilChunkFile<
); );
for shift_index in indices_to_shift { for shift_index in indices_to_shift {
let chunk_data = self.chunks_data[shift_index] if let Some(chunk_data) = self.chunks_data[shift_index].as_mut()
.as_mut() {
.expect("We checked if this was none"); let new_offset =
let new_offset = chunk_data.file_sector_offset as i64 + offset; chunk_data.file_sector_offset as i64 + offset;
chunk_data.file_sector_offset = new_offset as u32; chunk_data.file_sector_offset = new_offset as u32;
write_action.maybe_update_chunk_index(shift_index); write_action.maybe_update_chunk_index(shift_index);
}
} }
// If the shift is negative then there will be trailing data, but i // If the shift is negative then there will be trailing data, but i

View File

@ -406,14 +406,31 @@ impl ChunkData {
.load(std::sync::atomic::Ordering::Relaxed); .load(std::sync::atomic::Ordering::Relaxed);
let block_entities_nbt = { let block_entities_nbt = {
let entities_guard = self.pending_block_entities.lock().unwrap(); let entities_guard = self
.pending_block_entities
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
entities_guard.values().cloned().collect::<Vec<_>>() entities_guard.values().cloned().collect::<Vec<_>>()
}; };
let light_lock = self.light_engine.lock().unwrap(); let light_lock = self
let heightmap_lock = self.heightmap.lock().unwrap(); .light_engine
let block_lock = self.section.block_sections.read().unwrap(); .lock()
let biome_lock = self.section.biome_sections.read().unwrap(); .unwrap_or_else(std::sync::PoisonError::into_inner);
let heightmap_lock = self
.heightmap
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let block_lock = self
.section
.block_sections
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let biome_lock = self
.section
.biome_sections
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let min_section_y = (self.section.min_y >> 4) as i8; let min_section_y = (self.section.min_y >> 4) as i8;

View File

@ -438,7 +438,7 @@ impl ChunkSections {
let relative_y = relative_y % BlockPalette::SIZE; let relative_y = relative_y % BlockPalette::SIZE;
self.block_sections self.block_sections
.read() .read()
.unwrap() .unwrap_or_else(std::sync::PoisonError::into_inner)
.get(section_index) .get(section_index)
.map(|section| section.get(relative_x, relative_y, relative_z)) .map(|section| section.get(relative_x, relative_y, relative_z))
} }
@ -474,8 +474,14 @@ impl ChunkSections {
let relative_y = relative_y % BlockPalette::SIZE; let relative_y = relative_y % BlockPalette::SIZE;
// Keep lock order consistent to avoid deadlocks: block sections first, then random-tick cache. // Keep lock order consistent to avoid deadlocks: block sections first, then random-tick cache.
let mut sections = self.block_sections.write().unwrap(); let mut sections = self
let mut random_tick_sections_guard = self.random_tick_sections.write().unwrap(); .block_sections
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut random_tick_sections_guard = self
.random_tick_sections
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(section) = sections.get_mut(section_index) { if let Some(section) = sections.get_mut(section_index) {
let replaced_block_state_id = let replaced_block_state_id =
@ -546,7 +552,12 @@ impl ChunkSections {
let section_index = relative_y / BiomePalette::SIZE; let section_index = relative_y / BiomePalette::SIZE;
let relative_y = relative_y % BiomePalette::SIZE; let relative_y = relative_y % BiomePalette::SIZE;
if let Some(section) = self.biome_sections.write().unwrap().get_mut(section_index) { if let Some(section) = self
.biome_sections
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get_mut(section_index)
{
section.set(relative_x, relative_y, relative_z, biome_id); section.set(relative_x, relative_y, relative_z, biome_id);
} }
} }
@ -563,7 +574,7 @@ impl ChunkSections {
debug_assert!(scale_z < BiomePalette::SIZE); debug_assert!(scale_z < BiomePalette::SIZE);
self.biome_sections self.biome_sections
.read() .read()
.unwrap() .unwrap_or_else(std::sync::PoisonError::into_inner)
.get(index) .get(index)
.map(|section| section.get(scale_x, scale_y, scale_z)) .map(|section| section.get(scale_x, scale_y, scale_z))
} }
@ -587,6 +598,30 @@ impl ChunkSections {
} }
impl ChunkData { impl ChunkData {
#[must_use]
pub fn empty(x: i32, z: i32) -> Self {
Self {
section: ChunkSections::new(24, -64),
heightmap: std::sync::Mutex::new(ChunkHeightmaps::default()),
x,
z,
block_ticks: ChunkTickScheduler::default(),
fluid_ticks: ChunkTickScheduler::default(),
pending_block_entities: std::sync::Mutex::new(FxHashMap::default()),
light_engine: std::sync::Mutex::new(ChunkLight::default()),
light_populated: std::sync::atomic::AtomicBool::new(false),
status: ChunkStatus::Full,
blending_data: None,
dirty: std::sync::atomic::AtomicBool::new(false),
inhabited_time: std::sync::atomic::AtomicU64::new(0),
}
}
#[must_use]
pub fn empty_sync(x: i32, z: i32) -> std::sync::Arc<Self> {
std::sync::Arc::new(Self::empty(x, z))
}
/// Returns the replaced block state ID /// Returns the replaced block state ID
pub fn set_block_absolute_y( pub fn set_block_absolute_y(
&self, &self,
@ -622,7 +657,10 @@ impl ChunkData {
relative_z: usize, relative_z: usize,
block_state: &BlockState, block_state: &BlockState,
) { ) {
let mut heightmap = self.heightmap.lock().unwrap(); let mut heightmap = self
.heightmap
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let min_y = self.section.min_y; let min_y = self.section.min_y;
let x = relative_x as i32; let x = relative_x as i32;
let y = relative_y as i32 + min_y; let y = relative_y as i32 + min_y;
@ -742,9 +780,9 @@ impl ChunkData {
} }
for (idx, is_set) in has_found.iter().enumerate() { for (idx, is_set) in has_found.iter().enumerate() {
if !(*is_set) { if !(*is_set) && let Ok(hm_type) = idx.try_into() {
heightmaps.set( heightmaps.set(
idx.try_into().unwrap(), hm_type,
x as i32, x as i32,
z as i32, z as i32,
self.section.min_y - 1, self.section.min_y - 1,
@ -759,7 +797,7 @@ impl ChunkData {
self.section self.section
.block_sections .block_sections
.read() .read()
.unwrap() .unwrap_or_else(std::sync::PoisonError::into_inner)
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()

View File

@ -63,7 +63,11 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> HeterogeneousPaletteData<V
return original; return original;
} }
let original_index = self.palette.iter().position(|v| v == &original).unwrap(); let original_index = self
.palette
.iter()
.position(|v| v == &original)
.unwrap_or(0);
// Find or add the new value to the palette. // Find or add the new value to the palette.
let new_index = if let Some(new_index) = self.palette.iter().position(|v| v == &value) { let new_index = if let Some(new_index) = self.palette.iter().position(|v| v == &value) {
@ -172,7 +176,7 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
if palette.len() <= 256 && std::mem::size_of::<V>() > 1 { if palette.len() <= 256 && std::mem::size_of::<V>() > 1 {
let mut indices = Box::new([[[0u8; DIM]; DIM]; DIM]); let mut indices = Box::new([[[0u8; DIM]; DIM]; DIM]);
for (i, v) in cube.as_flattened().as_flattened().iter().enumerate() { for (i, v) in cube.as_flattened().as_flattened().iter().enumerate() {
let idx = palette.iter().position(|p| p == v).unwrap(); let idx = palette.iter().position(|p| p == v).unwrap_or(0);
indices.as_flattened_mut().as_flattened_mut()[i] = idx as u8; indices.as_flattened_mut().as_flattened_mut()[i] = idx as u8;
} }
Self::Heterogeneous(Box::new(HeterogeneousPaletteData { Self::Heterogeneous(Box::new(HeterogeneousPaletteData {
@ -259,7 +263,7 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
.map(|chunk| { .map(|chunk| {
chunk.iter().enumerate().fold(0, |acc, (index, key)| { chunk.iter().enumerate().fold(0, |acc, (index, key)| {
let key_index = let key_index =
data.palette.iter().position(|&x| x == *key).unwrap(); data.palette.iter().position(|&x| x == *key).unwrap_or(0);
debug_assert!((1 << bits_per_entry) > key_index); debug_assert!((1 << bits_per_entry) > key_index);
let packed_offset_index = let packed_offset_index =
@ -578,7 +582,7 @@ impl BiomePalette {
for y in 0..16 { for y in 0..16 {
for z in 0..16 { for z in 0..16 {
let key = self.get(x / 4, z / 4, y / 4); let key = self.get(x / 4, z / 4, y / 4);
let key_index = key_to_index_map.get(&key).unwrap(); let key_index = key_to_index_map.get(&key).unwrap_or(&0);
debug_assert!((1 << bits_per_entry) > *key_index); debug_assert!((1 << bits_per_entry) > *key_index);
current_word |= (*key_index as u32) current_word |= (*key_index as u32)
@ -728,7 +732,7 @@ impl BlockPalette {
// Java has it in y, z, x order, so we need to convert it back to x, y, z // Java has it in y, z, x order, so we need to convert it back to x, y, z
// Please test your code on bedrock before merging // Please test your code on bedrock before merging
let key = data.get(x, z, y); let key = data.get(x, z, y);
let key_index = key_to_index_map.get(&key).unwrap(); let key_index = key_to_index_map.get(&key).unwrap_or(&0);
debug_assert!((1 << bits_per_entry) > *key_index); debug_assert!((1 << bits_per_entry) > *key_index);
current_word |= (*key_index as u32) current_word |= (*key_index as u32)

View File

@ -39,7 +39,10 @@ impl LevelChannel {
pos: Vec<ChunkPos>, pos: Vec<ChunkPos>,
) { ) {
// debug!("set new level and priority"); // debug!("set new level and priority");
let mut value = self.value.lock().unwrap(); let mut value = self
.value
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
value.1 = Some(pos); value.1 = Some(pos);
if let Some(old) = &mut value.0 { if let Some(old) = &mut value.0 {
for (pos, change) in new_value.0 { for (pos, change) in new_value.0 {
@ -73,7 +76,10 @@ impl LevelChannel {
), ),
) { ) {
// debug!("set new level"); // debug!("set new level");
let mut value = self.value.lock().unwrap(); let mut value = self
.value
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(old) = &mut value.0 { if let Some(old) = &mut value.0 {
for (pos, change) in new_value.0 { for (pos, change) in new_value.0 {
match old.0.entry(pos) { match old.0.entry(pos) {
@ -100,24 +106,36 @@ impl LevelChannel {
} }
pub fn set_priority(&self, pos: Vec<ChunkPos>) { pub fn set_priority(&self, pos: Vec<ChunkPos>) {
// debug!("set new priority"); // debug!("set new priority");
self.value.lock().unwrap().1 = Some(pos); self.value
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.1 = Some(pos);
self.notify.notify_one(); self.notify.notify_one();
} }
pub fn get(&self) -> (Option<LevelChange>, Option<Vec<ChunkPos>>) { pub fn get(&self) -> (Option<LevelChange>, Option<Vec<ChunkPos>>) {
let mut lock = self.value.lock().unwrap(); let mut lock = self
.value
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut ret = (None, None); let mut ret = (None, None);
swap(&mut ret, &mut *lock); swap(&mut ret, &mut *lock);
ret ret
} }
pub fn wait_and_get(&self, level: &Arc<Level>) -> (Option<LevelChange>, Option<Vec<ChunkPos>>) { pub fn wait_and_get(&self, level: &Arc<Level>) -> (Option<LevelChange>, Option<Vec<ChunkPos>>) {
let mut lock = self.value.lock().unwrap(); let mut lock = self
.value
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
while lock.0.is_none() while lock.0.is_none()
&& lock.1.is_none() && lock.1.is_none()
&& !level.should_unload.load(SeqCst) && !level.should_unload.load(SeqCst)
&& !level.should_save.load(SeqCst) && !level.should_save.load(SeqCst)
&& !level.shut_down_chunk_system.load(SeqCst) && !level.shut_down_chunk_system.load(SeqCst)
{ {
lock = self.notify.wait(lock).unwrap(); lock = self
.notify
.wait(lock)
.unwrap_or_else(std::sync::PoisonError::into_inner);
} }
let mut ret = (None, None); let mut ret = (None, None);
swap(&mut ret, &mut *lock); swap(&mut ret, &mut *lock);

View File

@ -28,19 +28,28 @@ impl ChunkListener {
pub fn add_single_chunk_listener(&self, pos: ChunkPos) -> oneshot::Receiver<SyncChunk> { pub fn add_single_chunk_listener(&self, pos: ChunkPos) -> oneshot::Receiver<SyncChunk> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
self.single.lock().unwrap().push((pos, tx)); self.single
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.push((pos, tx));
rx rx
} }
pub fn add_global_chunk_listener(&self) -> Receiver<(ChunkPos, Weak<crate::chunk::ChunkData>)> { pub fn add_global_chunk_listener(&self) -> Receiver<(ChunkPos, Weak<crate::chunk::ChunkData>)> {
let (tx, rx) = crossbeam::channel::unbounded(); let (tx, rx) = crossbeam::channel::unbounded();
self.global.lock().unwrap().push(tx); self.global
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.push(tx);
rx rx
} }
pub fn process_new_chunk(&self, pos: ChunkPos, chunk: &SyncChunk) { pub fn process_new_chunk(&self, pos: ChunkPos, chunk: &SyncChunk) {
{ {
let mut single = self.single.lock().unwrap(); let mut single = self
.single
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut i = 0; let mut i = 0;
let mut len = single.len(); let mut len = single.len();
while i < len { while i < len {
@ -56,7 +65,10 @@ impl ChunkListener {
} }
{ {
let weak = Arc::downgrade(chunk); let weak = Arc::downgrade(chunk);
let mut global = self.global.lock().unwrap(); let mut global = self
.global
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut i = 0; let mut i = 0;
let mut len = global.len(); let mut len = global.len();
while i < len { while i < len {

View File

@ -107,7 +107,9 @@ impl ChunkLoading {
fn debug_check_error(&self) -> bool { fn debug_check_error(&self) -> bool {
let mut temp = ChunkLevel::default(); let mut temp = ChunkLevel::default();
for (ticket_pos, levels) in &self.ticket { for (ticket_pos, levels) in &self.ticket {
let level = *levels.iter().min().unwrap(); let Some(&level) = levels.iter().min() else {
continue;
};
let range = Self::MAX_LEVEL - level - 1; let range = Self::MAX_LEVEL - level - 1;
for dx in -range..=range { for dx in -range..=range {
for dy in -range..=range { for dy in -range..=range {
@ -124,7 +126,12 @@ impl ChunkLoading {
} }
assert_eq!(temp.len(), self.pos_level.len()); assert_eq!(temp.len(), self.pos_level.len());
for val in &temp { for val in &temp {
if val != self.pos_level.get_key_value(val.0).unwrap() { if val
!= self
.pos_level
.get_key_value(val.0)
.expect("key value exists")
{
Self::dump_level_debug( Self::dump_level_debug(
&self.high_priority, &self.high_priority,
&self.pos_level, &self.pos_level,
@ -134,7 +141,12 @@ impl ChunkLoading {
val.0.y + 40, val.0.y + 40,
); );
} }
assert_eq!(val, self.pos_level.get_key_value(val.0).unwrap()); assert_eq!(
val,
self.pos_level
.get_key_value(val.0)
.expect("key value exists")
);
} }
true true
} }
@ -279,7 +291,9 @@ impl ChunkLoading {
for (ticket_pos, levels) in &self.ticket { for (ticket_pos, levels) in &self.ticket {
if (ticket_pos.x - pos.x).abs() <= range && (ticket_pos.y - pos.y).abs() <= range { if (ticket_pos.x - pos.x).abs() <= range && (ticket_pos.y - pos.y).abs() <= range {
let level = *levels.iter().min().unwrap(); let Some(&level) = levels.iter().min() else {
continue;
};
debug_assert!(level < Self::MAX_LEVEL); debug_assert!(level < Self::MAX_LEVEL);
let old = self.cache.get(&self.pos_level, *ticket_pos); let old = self.cache.get(&self.pos_level, *ticket_pos);
if old <= level { if old <= level {
@ -300,13 +314,9 @@ impl ChunkLoading {
} }
pub fn remove_force_ticket(&mut self, pos: ChunkPos) { pub fn remove_force_ticket(&mut self, pos: ChunkPos) {
// debug!("remove force ticket at {pos:?}"); // debug!("remove force ticket at {pos:?}");
let index = self if let Some((index, _)) = self.high_priority.iter().find_position(|x| **x == pos) {
.high_priority self.high_priority.remove(index);
.iter() }
.find_position(|x| **x == pos)
.unwrap()
.0;
self.high_priority.remove(index);
self.is_priority_dirty = true; self.is_priority_dirty = true;
self.remove_ticket(pos, Self::FULL_CHUNK_LEVEL); self.remove_ticket(pos, Self::FULL_CHUNK_LEVEL);
} }

View File

@ -50,10 +50,15 @@ pub struct DAG {
impl DAG { impl DAG {
pub fn fast_drop_node(&mut self, node: NodeKey) { pub fn fast_drop_node(&mut self, node: NodeKey) {
let mut edge = self.nodes.remove(node).unwrap().edge; if let Some(removed_node) = self.nodes.remove(node) {
// debug!("drop node {node:?}"); let mut edge = removed_node.edge;
while !edge.is_null() { while !edge.is_null() {
edge = self.edges.remove(edge).unwrap().next; if let Some(removed_edge) = self.edges.remove(edge) {
edge = removed_edge.next;
} else {
break;
}
}
} }
} }
pub fn add_edge(&mut self, from: NodeKey, to: NodeKey) { pub fn add_edge(&mut self, from: NodeKey, to: NodeKey) {

View File

@ -237,7 +237,10 @@ impl GenerationCache for Cache {
debug_assert!(dx >= 0 && dy >= 0); debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] { match &self.chunks[(dx * self.size + dy) as usize] {
Chunk::Level(data) => { Chunk::Level(data) => {
let heightmap = data.heightmap.lock().unwrap(); let heightmap = data
.heightmap
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let min_y = data.section.min_y; let min_y = data.section.min_y;
heightmap.get(ChunkHeightmapType::MotionBlocking, x, z, min_y) heightmap.get(ChunkHeightmapType::MotionBlocking, x, z, min_y)
@ -253,7 +256,10 @@ impl GenerationCache for Cache {
debug_assert!(dx >= 0 && dy >= 0); debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] { match &self.chunks[(dx * self.size + dy) as usize] {
Chunk::Level(data) => { Chunk::Level(data) => {
let heightmap = data.heightmap.lock().unwrap(); let heightmap = data
.heightmap
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let min_y = data.section.min_y; let min_y = data.section.min_y;
heightmap.get(ChunkHeightmapType::MotionBlockingNoLeaves, x, z, min_y) heightmap.get(ChunkHeightmapType::MotionBlockingNoLeaves, x, z, min_y)
} }
@ -268,7 +274,10 @@ impl GenerationCache for Cache {
debug_assert!(dx >= 0 && dy >= 0); debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] { match &self.chunks[(dx * self.size + dy) as usize] {
Chunk::Level(data) => { Chunk::Level(data) => {
let heightmap = data.heightmap.lock().unwrap(); let heightmap = data
.heightmap
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let min_y = data.section.min_y; let min_y = data.section.min_y;
heightmap.get(ChunkHeightmapType::WorldSurface, x, z, min_y) // can we return this? heightmap.get(ChunkHeightmapType::WorldSurface, x, z, min_y) // can we return this?
} }
@ -308,7 +317,7 @@ impl GenerationCache for Cache {
.get_rough_biome_absolute_y((x & 15) as usize, y, (z & 15) as usize) .get_rough_biome_absolute_y((x & 15) as usize, y, (z & 15) as usize)
.unwrap_or(0), .unwrap_or(0),
) )
.unwrap() .unwrap_or(&Biome::PLAINS)
} }
Chunk::Proto(data) => data.get_terrain_gen_biome(x, y, z), Chunk::Proto(data) => data.get_terrain_gen_biome(x, y, z),
} }

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
/* /*
TODO TODO
1. add proto chunk dirty flag 1. add proto chunk dirty flag

View File

@ -182,7 +182,10 @@ impl GenerationSchedule {
fn apply_lighting_override(&self, chunk: &SyncChunk) { fn apply_lighting_override(&self, chunk: &SyncChunk) {
match self.lighting_config { match self.lighting_config {
LightingEngineConfig::Full => { LightingEngineConfig::Full => {
let mut engine = chunk.light_engine.lock().unwrap(); let mut engine = chunk
.light_engine
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for section in &mut engine.block_light { for section in &mut engine.block_light {
section.fill(15); section.fill(15);
} }
@ -192,7 +195,10 @@ impl GenerationSchedule {
chunk.dirty.store(true, Relaxed); chunk.dirty.store(true, Relaxed);
} }
LightingEngineConfig::Dark => { LightingEngineConfig::Dark => {
let mut engine = chunk.light_engine.lock().unwrap(); let mut engine = chunk
.light_engine
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for section in &mut engine.block_light { for section in &mut engine.block_light {
section.fill(0); section.fill(0);
} }
@ -512,7 +518,7 @@ impl GenerationSchedule {
self.unload_chunks.remove(&pos); self.unload_chunks.remove(&pos);
if holder.current_stage == StagedChunkEnum::Full && !holder.public { if holder.current_stage == StagedChunkEnum::Full && !holder.public {
holder.public = true; holder.public = true;
match holder.chunk.as_ref().unwrap() { match holder.chunk.as_ref().expect("chunk exists") {
Chunk::Level(chunk) => { Chunk::Level(chunk) => {
self.apply_lighting_override(chunk); self.apply_lighting_override(chunk);
self.public_chunk_map.insert(pos, chunk.clone()); self.public_chunk_map.insert(pos, chunk.clone());
@ -568,7 +574,7 @@ impl GenerationSchedule {
} }
} }
} }
let node = self.graph.nodes.get_mut(task).unwrap(); let node = self.graph.nodes.get_mut(task).expect("node exists");
if node.in_degree == 0 && !node.in_queue { if node.in_degree == 0 && !node.in_queue {
node.in_queue = true; node.in_queue = true;
self.queue.push(TaskHeapNode(0, task)); self.queue.push(TaskHeapNode(0, task));
@ -594,7 +600,7 @@ impl GenerationSchedule {
} }
for pos in stranded { for pos in stranded {
let holder = self.chunk_map.get_mut(&pos).unwrap(); let holder = self.chunk_map.get_mut(&pos).expect("holder exists");
if !holder.occupied.is_null() && self.graph.nodes.contains_key(holder.occupied) { if !holder.occupied.is_null() && self.graph.nodes.contains_key(holder.occupied) {
continue; continue;
} }
@ -605,7 +611,7 @@ impl GenerationSchedule {
let mut has_valid_task = false; let mut has_valid_task = false;
while !cur_edge.is_null() { while !cur_edge.is_null() {
let edge = self.graph.edges.get(cur_edge).unwrap(); let edge = self.graph.edges.get(cur_edge).expect("edge exists");
if self.graph.nodes.contains_key(edge.to) { if self.graph.nodes.contains_key(edge.to) {
prev_edge = cur_edge; prev_edge = cur_edge;
cur_edge = edge.next; cur_edge = edge.next;
@ -617,7 +623,11 @@ impl GenerationSchedule {
if prev_edge.is_null() { if prev_edge.is_null() {
change_head = Some(next); change_head = Some(next);
} else { } else {
self.graph.edges.get_mut(prev_edge).unwrap().next = next; self.graph
.edges
.get_mut(prev_edge)
.expect("edge exists")
.next = next;
} }
} }
} }
@ -683,7 +693,11 @@ impl GenerationSchedule {
if chunks.is_empty() { if chunks.is_empty() {
return; return;
} }
let mut data = self.io_lock.0.lock().unwrap(); let mut data = self
.io_lock
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for (pos, _chunk) in &chunks { for (pos, _chunk) in &chunks {
*data.entry(*pos).or_insert(0) += 1; *data.entry(*pos).or_insert(0) += 1;
} }
@ -716,7 +730,7 @@ impl GenerationSchedule {
if should_save { if should_save {
let chunk_to_save = match chunk { let chunk_to_save = match chunk {
Chunk::Level(sync_chunk) => Chunk::Level(sync_chunk.clone()), Chunk::Level(sync_chunk) => Chunk::Level(sync_chunk.clone()),
Chunk::Proto(_) => holder.chunk.take().unwrap(), Chunk::Proto(_) => holder.chunk.take().expect("proto chunk exists"),
}; };
chunks.push((*pos, chunk_to_save)); chunks.push((*pos, chunk_to_save));
} }
@ -733,7 +747,11 @@ impl GenerationSchedule {
self.chunk_map.len() self.chunk_map.len()
); );
let mut data = self.io_lock.0.lock().unwrap(); let mut data = self
.io_lock
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for (pos, _) in &chunks { for (pos, _) in &chunks {
*data.entry(*pos).or_insert(0) += 1; *data.entry(*pos).or_insert(0) += 1;
} }
@ -750,7 +768,7 @@ impl GenerationSchedule {
}; };
let mut edge = old.edge; let mut edge = old.edge;
while !edge.is_null() { while !edge.is_null() {
let cur = self.graph.edges.remove(edge).unwrap(); let cur = self.graph.edges.remove(edge).expect("edge exists");
if let Some(node) = self.graph.nodes.get_mut(cur.to) { if let Some(node) = self.graph.nodes.get_mut(cur.to) {
debug_assert!(node.in_degree >= 1); debug_assert!(node.in_degree >= 1);
node.in_degree -= 1; node.in_degree -= 1;
@ -792,7 +810,7 @@ impl GenerationSchedule {
fn receive_chunk(&mut self, pos: ChunkPos, data: RecvChunk) { fn receive_chunk(&mut self, pos: ChunkPos, data: RecvChunk) {
match data { match data {
RecvChunk::IO(chunk) => { RecvChunk::IO(chunk) => {
let mut holder = self.chunk_map.remove(&pos).unwrap(); let mut holder = self.chunk_map.remove(&pos).expect("holder exists");
if holder.chunk.is_some() { if holder.chunk.is_some() {
warn!( warn!(
"receive_chunk(IO): holder already has chunk at {:?}; replacing", "receive_chunk(IO): holder already has chunk at {:?}; replacing",
@ -849,7 +867,8 @@ impl GenerationSchedule {
let new_pos = ChunkPos::new(data.x + dx, data.z + dy); let new_pos = ChunkPos::new(data.x + dx, data.z + dy);
match chunk { match chunk {
Chunk::Level(chunk) => { Chunk::Level(chunk) => {
let mut holder = self.chunk_map.remove(&new_pos).unwrap(); let mut holder =
self.chunk_map.remove(&new_pos).expect("holder exists");
let stage = StagedChunkEnum::Full; let stage = StagedChunkEnum::Full;
if new_pos == pos { if new_pos == pos {
if holder.current_stage != StagedChunkEnum::Spawn { if holder.current_stage != StagedChunkEnum::Spawn {
@ -926,7 +945,8 @@ impl GenerationSchedule {
self.chunk_map.insert(new_pos, holder); self.chunk_map.insert(new_pos, holder);
} }
Chunk::Proto(chunk) => { Chunk::Proto(chunk) => {
let mut holder = self.chunk_map.remove(&new_pos).unwrap(); let mut holder =
self.chunk_map.remove(&new_pos).expect("holder exists");
let stage = StagedChunkEnum::from(chunk.stage_id()); let stage = StagedChunkEnum::from(chunk.stage_id());
self.drop_satisfied_tasks(&mut holder, stage); self.drop_satisfied_tasks(&mut holder, stage);
@ -1164,7 +1184,7 @@ impl GenerationSchedule {
if node.stage == StagedChunkEnum::Empty { if node.stage == StagedChunkEnum::Empty {
self.running_task_count += 1; self.running_task_count += 1;
let holder = self.chunk_map.get_mut(&node.pos).unwrap(); let holder = self.chunk_map.get_mut(&node.pos).expect("holder exists");
debug_assert!(holder.occupied.is_null()); debug_assert!(holder.occupied.is_null());
debug_assert_eq!(holder.current_stage, StagedChunkEnum::None); debug_assert_eq!(holder.current_stage, StagedChunkEnum::None);
let occupy = self.graph.nodes.insert(Node::new( let occupy = self.graph.nodes.insert(Node::new(
@ -1246,7 +1266,8 @@ impl GenerationSchedule {
for dx in -write_radius..=write_radius { for dx in -write_radius..=write_radius {
for dy in -write_radius..=write_radius { for dy in -write_radius..=write_radius {
let new_pos = node.pos.add_raw(dx, dy); let new_pos = node.pos.add_raw(dx, dy);
let holder = self.chunk_map.get_mut(&new_pos).unwrap(); let holder =
self.chunk_map.get_mut(&new_pos).expect("holder exists");
let mut tmp = None; let mut tmp = None;
swap(&mut tmp, &mut holder.chunk); swap(&mut tmp, &mut holder.chunk);
let Some(tmp) = tmp else { let Some(tmp) = tmp else {
@ -1270,7 +1291,7 @@ impl GenerationSchedule {
let mut prev_edge = EdgeKey::null(); let mut prev_edge = EdgeKey::null();
let mut change_head = None; let mut change_head = None;
while !cur_edge.is_null() { while !cur_edge.is_null() {
let edge = self.graph.edges.get(cur_edge).unwrap(); let edge = self.graph.edges.get(cur_edge).expect("edge exists");
if self.graph.nodes.contains_key(edge.to) { if self.graph.nodes.contains_key(edge.to) {
prev_edge = cur_edge; prev_edge = cur_edge;
cur_edge = edge.next; cur_edge = edge.next;
@ -1282,8 +1303,11 @@ impl GenerationSchedule {
if prev_edge.is_null() { if prev_edge.is_null() {
change_head = Some(next); change_head = Some(next);
} else { } else {
self.graph.edges.get_mut(prev_edge).unwrap().next = self.graph
next; .edges
.get_mut(prev_edge)
.expect("edge exists")
.next = next;
} }
} }
} }
@ -1455,7 +1479,7 @@ impl GenerationSchedule {
debug_assert!(holder.occupied.is_null()); debug_assert!(holder.occupied.is_null());
if holder.current_stage != StagedChunkEnum::None { if holder.current_stage != StagedChunkEnum::None {
debug_assert_eq!( debug_assert_eq!(
holder.chunk.as_ref().unwrap().get_stage_id(), holder.chunk.as_ref().expect("chunk exists").get_stage_id(),
holder.current_stage as u8 holder.current_stage as u8
); );
} }

View File

@ -67,7 +67,12 @@ pub async fn io_read_work(
// Lock handling // Lock handling
loop { loop {
let notified = lock.1.notified(); let notified = lock.1.notified();
if !lock.0.lock().unwrap().contains_key(pos) { if !lock
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.contains_key(pos)
{
break; break;
} }
notified.await; notified.await;
@ -197,7 +202,10 @@ pub async fn io_write_work(recv: AsyncRx<Vec<(ChunkPos, Chunk)>>, level: Arc<Lev
} }
for i in positions { for i in positions {
let mut data = lock.0.lock().unwrap(); let mut data = lock
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
match data.entry(i) { match data.entry(i) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
let rc = entry.get_mut(); let rc = entry.get_mut();

View File

@ -58,11 +58,14 @@ impl MatchingBlocksBlockPredicate {
let block = self.offset.get_block(chunk, pos); let block = self.offset.get_block(chunk, pos);
match &self.blocks { match &self.blocks {
MatchingBlocksWrapper::Single(single_block) => { MatchingBlocksWrapper::Single(single_block) => {
single_block.strip_prefix("minecraft:").unwrap() == block.name single_block
.strip_prefix("minecraft:")
.unwrap_or(single_block)
== block.name
} }
MatchingBlocksWrapper::Multiple(blocks) => blocks MatchingBlocksWrapper::Multiple(blocks) => blocks
.iter() .iter()
.map(|s| s.strip_prefix("minecraft:").unwrap()) .map(|s| s.strip_prefix("minecraft:").unwrap_or(s))
.contains(block.name), .contains(block.name),
} }
} }
@ -89,11 +92,14 @@ impl MatchingFluidsBlockPredicate {
let (fluid, _) = self.offset.get_fluid_and_fluid_state(chunk, pos); let (fluid, _) = self.offset.get_fluid_and_fluid_state(chunk, pos);
match &self.fluids { match &self.fluids {
MatchingBlocksWrapper::Single(single_block) => { MatchingBlocksWrapper::Single(single_block) => {
single_block.strip_prefix("minecraft:").unwrap() == fluid.name single_block
.strip_prefix("minecraft:")
.unwrap_or(single_block)
== fluid.name
} }
MatchingBlocksWrapper::Multiple(blocks) => blocks MatchingBlocksWrapper::Multiple(blocks) => blocks
.iter() .iter()
.map(|s| s.strip_prefix("minecraft:").unwrap()) .map(|s| s.strip_prefix("minecraft:").unwrap_or(s))
.contains(fluid.name), .contains(fluid.name),
} }
} }

View File

@ -1,4 +1,4 @@
use pumpkin_data::BlockState; use pumpkin_data::{Block, BlockState};
use pumpkin_util::{ use pumpkin_util::{
DoublePerlinNoiseParametersCodec, DoublePerlinNoiseParametersCodec,
math::{ math::{
@ -199,7 +199,9 @@ pub struct WeightedBlockStateProvider {
impl WeightedBlockStateProvider { impl WeightedBlockStateProvider {
pub fn get(&self, random: &mut RandomGenerator) -> &'static BlockState { pub fn get(&self, random: &mut RandomGenerator) -> &'static BlockState {
Pool::get(&self.entries, random).unwrap() Pool::get(&self.entries, random)
.copied()
.unwrap_or(Block::AIR.default_state)
} }
} }

View File

@ -64,10 +64,10 @@ impl CoralFeature {
} }
let wall_coral = let wall_coral =
Self::get_random_tag_entry_block(tag::Block::MINECRAFT_WALL_CORALS, random); Self::get_random_tag_entry_block(tag::Block::MINECRAFT_WALL_CORALS, random);
let original_props = &wall_coral let Some(properties) = wall_coral.properties(wall_coral.default_state.id) else {
.properties(wall_coral.default_state.id) continue;
.unwrap() };
.to_props(); let original_props = &properties.to_props();
// Set the right Axis // Set the right Axis
let props: Vec<(&str, &str)> = original_props let props: Vec<(&str, &str)> = original_props
.iter() .iter()
@ -111,7 +111,7 @@ impl CoralFeature {
) -> &'static Block { ) -> &'static Block {
let values = tag.1; let values = tag.1;
let value = values[random.next_bounded_i32(values.len() as i32) as usize]; let value = values[random.next_bounded_i32(values.len() as i32) as usize];
let id = BlockId::new(value).expect("Invalid block id in tag"); let id = BlockId::new(value).unwrap_or(BlockId::AIR);
id.to_block() id.to_block()
} }
} }

View File

@ -157,7 +157,10 @@ impl FancyTrunkPlacer {
if TreeFeature::can_replace(block.to_state(), block.to_block_id()) { if TreeFeature::can_replace(block.to_state(), block.to_block_id()) {
let block = Block::from_state_id(trunk_provider.id); let block = Block::from_state_id(trunk_provider.id);
let original_props = &block.properties(trunk_provider.id).unwrap().to_props(); let Some(properties) = block.properties(trunk_provider.id) else {
continue;
};
let original_props = &properties.to_props();
let axis = axis.to_value(); let axis = axis.to_value();
// Set the right Axis // Set the right Axis
let props: Vec<(&'static str, &'static str)> = original_props let props: Vec<(&'static str, &'static str)> = original_props

View File

@ -28,9 +28,10 @@ pub enum PlacedFeatureWrapper {
} }
impl PlacedFeatureWrapper { impl PlacedFeatureWrapper {
#[allow(clippy::expect_used)]
pub fn get(&self) -> &PlacedFeature { pub fn get(&self) -> &PlacedFeature {
match self { match self {
Self::Named(name) => PLACED_FEATURES.get(name).unwrap(), Self::Named(name) => PLACED_FEATURES.get(name).expect("Unknown placed feature"),
Self::Direct(feature) => feature, Self::Direct(feature) => feature,
} }
} }
@ -56,11 +57,11 @@ impl PlacedFeature {
random: &mut RandomGenerator, random: &mut RandomGenerator,
pos: BlockPos, pos: BlockPos,
) -> bool { ) -> bool {
let feature = match &self.feature { let Some(feature) = (match &self.feature {
Feature::Named(name) => CONFIGURED_FEATURES Feature::Named(name) => CONFIGURED_FEATURES.get(name),
.get(name) Feature::Inlined(feature) => Some(feature.as_ref()),
.expect("Name: {name:?} not found"), }) else {
Feature::Inlined(feature) => feature, return false;
}; };
if let ConfiguredFeature::SculkPatch(feature) = feature { if let ConfiguredFeature::SculkPatch(feature) = feature {
feature.generate_in_proto_chunk(chunk, random, pos) feature.generate_in_proto_chunk(chunk, random, pos)
@ -110,11 +111,11 @@ impl PlacedFeature {
stream = new_stream; stream = new_stream;
} }
let feature = match &self.feature { let Some(feature) = (match &self.feature {
Feature::Named(name) => CONFIGURED_FEATURES Feature::Named(name) => CONFIGURED_FEATURES.get(name),
.get(name) Feature::Inlined(feature) => Some(feature.as_ref()),
.expect("Name: {name:?} not found"), }) else {
Feature::Inlined(feature) => feature, return false;
}; };
let mut ret = false; let mut ret = false;

View File

@ -121,7 +121,7 @@ fn find_nearest_concentric(
distance_sq: dx * dx + dz * dz, distance_sq: dx * dx + dz * dz,
} }
}) })
.min_by(|a, b| a.distance_sq.partial_cmp(&b.distance_sq).unwrap()) .min_by(|a, b| a.distance_sq.total_cmp(&b.distance_sq))
} }
fn find_nearest_random_spread_at_radius( fn find_nearest_random_spread_at_radius(

View File

@ -251,7 +251,9 @@ impl ChunkNoiseFunctionComponent<'_> {
pos: &Vector3<i32>, pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions, sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 { ) -> f64 {
let (top_component, component_stack) = component_stack.split_last_mut().unwrap(); let Some((top_component, component_stack)) = component_stack.split_last_mut() else {
return 0.0;
};
if !sample_options.populating_caches if !sample_options.populating_caches
&& let ChunkNoiseFunctionComponent::Chunk( && let ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::DensityInterpolator(interp), ChunkSpecificNoiseFunctionComponent::DensityInterpolator(interp),
@ -268,8 +270,9 @@ impl ChunkNoiseFunctionComponent<'_> {
mapper: &impl IndexToNoisePos, mapper: &impl IndexToNoisePos,
sample_options: &mut ChunkNoiseFunctionSampleOptions, sample_options: &mut ChunkNoiseFunctionSampleOptions,
) { ) {
let (top_component, component_stack) = component_stack.split_last_mut().unwrap(); if let Some((top_component, component_stack)) = component_stack.split_last_mut() {
top_component.fill(component_stack, array, mapper, sample_options); top_component.fill(component_stack, array, mapper, sample_options);
}
} }
} }

View File

@ -103,7 +103,9 @@ impl Spline {
let input_max = input_function.max() as f32; let input_max = input_function.max() as f32;
let input_min = input_function.min() as f32; let input_min = input_function.min() as f32;
let first_point = self.points.first().expect("A spline with no values?"); let Some(first_point) = self.points.first() else {
return (0.0, 0.0);
};
if input_min < first_point.location { if input_min < first_point.location {
let (point_min, point_max) = first_point.value.calculate_min_and_max(component_stack); let (point_min, point_max) = first_point.value.calculate_min_and_max(component_stack);
let sample_min = first_point.sample_outside_range(input_min, point_min); let sample_min = first_point.sample_outside_range(input_min, point_min);
@ -113,7 +115,9 @@ impl Spline {
max = max.max(sample_min.max(sample_max)); max = max.max(sample_min.max(sample_max));
} }
let last_point = self.points.last().expect("A spline with no values?"); let Some(last_point) = self.points.last() else {
return (min, max);
};
if input_max > last_point.location { if input_max > last_point.location {
let (point_min, point_max) = last_point.value.calculate_min_and_max(component_stack); let (point_min, point_max) = last_point.value.calculate_min_and_max(component_stack);
let sample_min = last_point.sample_outside_range(input_max, point_min); let sample_min = last_point.sample_outside_range(input_max, point_min);

View File

@ -260,16 +260,29 @@ impl ProtoChunk {
) -> Self { ) -> Self {
let mut proto_chunk = Self::new(chunk_data.x, chunk_data.z, generator); let mut proto_chunk = Self::new(chunk_data.x, chunk_data.z, generator);
proto_chunk.light = chunk_data.light_engine.lock().unwrap().clone(); proto_chunk.light = chunk_data
.light_engine
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
proto_chunk proto_chunk
.blending_data .blending_data
.clone_from(&chunk_data.blending_data); .clone_from(&chunk_data.blending_data);
let section_data = &chunk_data.section; let section_data = &chunk_data.section;
let heightmap_data = chunk_data.heightmap.lock().unwrap(); let heightmap_data = chunk_data
.heightmap
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let block_sections_guard = section_data.block_sections.read().unwrap(); let block_sections_guard = section_data
let biome_sections_guard = section_data.biome_sections.read().unwrap(); .block_sections
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let biome_sections_guard = section_data
.biome_sections
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for (section_idx, block_palette) in block_sections_guard.iter().enumerate() { for (section_idx, block_palette) in block_sections_guard.iter().enumerate() {
let section_base_y = section_idx as i32 * 16; let section_base_y = section_idx as i32 * 16;
@ -533,7 +546,7 @@ impl ProtoChunk {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn get_biome(&self, x: i32, y: i32, z: i32) -> &'static Biome { pub fn get_biome(&self, x: i32, y: i32, z: i32) -> &'static Biome {
Biome::from_id(self.get_biome_id(x, y, z)).unwrap() Biome::from_id(self.get_biome_id(x, y, z)).unwrap_or(&Biome::PLAINS)
} }
#[inline] #[inline]
@ -618,7 +631,9 @@ impl ProtoChunk {
StructureInstance::Reference(collector) => collector, StructureInstance::Reference(collector) => collector,
}; };
let collector = collector.lock().unwrap(); let collector = collector
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
for piece in &collector.pieces { for piece in &collector.pieces {
let bounding_box = piece.get_structure_piece().bounding_box; let bounding_box = piece.get_structure_piece().bounding_box;
@ -933,7 +948,7 @@ impl ProtoChunk {
#[must_use] #[must_use]
pub fn get_terrain_gen_biome(&self, x: i32, y: i32, z: i32) -> &'static Biome { pub fn get_terrain_gen_biome(&self, x: i32, y: i32, z: i32) -> &'static Biome {
Biome::from_id(self.get_terrain_gen_biome_id(x, y, z)).unwrap() Biome::from_id(self.get_terrain_gen_biome_id(x, y, z)).unwrap_or(&Biome::PLAINS)
} }
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
@ -1223,7 +1238,9 @@ impl ProtoChunk {
let chunk = cache.get_center_chunk_mut(); let chunk = cache.get_center_chunk_mut();
for collector_arc in tasks { for collector_arc in tasks {
let mut collector = collector_arc.lock().unwrap(); let mut collector = collector_arc
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
collector.generate_in_chunk(chunk, block_registry, &mut random, world_seed); collector.generate_in_chunk(chunk, block_registry, &mut random, world_seed);
} }
} }

View File

@ -64,12 +64,10 @@ pub fn generate_structure_position(
| StructureKeys::VillageSavanna | StructureKeys::VillageSavanna
| StructureKeys::VillageSnowy | StructureKeys::VillageSnowy
| StructureKeys::VillageTaiga => { | StructureKeys::VillageTaiga => {
let generator = JigsawGenerator::new( let (Some(start_pool), Some(size)) = (structure.start_pool, structure.size) else {
structure return None;
.start_pool };
.expect("Jigsaw structure must have a start pool"), let generator = JigsawGenerator::new(start_pool, size);
structure.size.expect("Jigsaw structure must have a size"),
);
generator.get_structure_position(context) generator.get_structure_position(context)
} }
StructureKeys::AncientCity StructureKeys::AncientCity
@ -77,12 +75,10 @@ pub fn generate_structure_position(
| StructureKeys::PillagerOutpost | StructureKeys::PillagerOutpost
| StructureKeys::TrailRuins | StructureKeys::TrailRuins
| StructureKeys::TrialChambers => { | StructureKeys::TrialChambers => {
let mut generator = JigsawGenerator::new( let (Some(start_pool), Some(size)) = (structure.start_pool, structure.size) else {
structure return None;
.start_pool };
.expect("Jigsaw structure must have a start pool"), let mut generator = JigsawGenerator::new(start_pool, size);
structure.size.expect("Jigsaw structure must have a size"),
);
if *key == StructureKeys::PillagerOutpost { if *key == StructureKeys::PillagerOutpost {
generator = generator.with_expansion_hack(true); generator = generator.with_expansion_hack(true);
} }
@ -169,8 +165,7 @@ pub fn try_generate_structure(
.biomes .biomes
.strip_prefix('#') .strip_prefix('#')
.unwrap_or(structure.biomes), .unwrap_or(structure.biomes),
) )?;
.unwrap();
// Check if the biome is allowed for this structure // Check if the biome is allowed for this structure
if biomes.contains(&current_biome) { if biomes.contains(&current_biome) {

View File

@ -370,9 +370,9 @@ impl MansionTemplatePiece {
"minecraft:chest".to_string(), "minecraft:chest".to_string(),
vec![("facing".to_string(), target.name().to_string())], vec![("facing".to_string(), target.name().to_string())],
); );
let state = BlockStateResolver::resolve_simple(&chest) if let Some(state) = BlockStateResolver::resolve_simple(&chest) {
.expect("the vanilla chest state must resolve"); chunk.set_block_state(position.x, position.y, position.z, state);
chunk.set_block_state(position.x, position.y, position.z, state); }
let mut nbt = NbtCompound::new(); let mut nbt = NbtCompound::new();
nbt.put_string("id", "minecraft:chest".to_string()); nbt.put_string("id", "minecraft:chest".to_string());
nbt.put_int("x", position.x); nbt.put_int("x", position.x);

View File

@ -631,7 +631,7 @@ impl StructurePiecesCollector {
} }
let bbox = BlockBox::encompass_all(self.pieces.iter().map(|p| p.bounding_box())) let bbox = BlockBox::encompass_all(self.pieces.iter().map(|p| p.bounding_box()))
.expect("Structure must have at least one piece to calculate a bounding box"); .unwrap_or_else(|| BlockBox::new(0, 0, 0, 0, 0, 0));
self.cached_box = Some(bbox); self.cached_box = Some(bbox);
bbox bbox
@ -656,7 +656,10 @@ pub struct StructurePosition {
impl StructurePosition { impl StructurePosition {
#[must_use] #[must_use]
pub fn get_bounding_box(&self) -> BlockBox { pub fn get_bounding_box(&self) -> BlockBox {
self.collector.lock().unwrap().get_bounding_box() self.collector
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get_bounding_box()
} }
} }

View File

@ -103,20 +103,7 @@ pub trait Inventory: Send + Sync + Clearable {
fn mark_dirty(&self) {} fn mark_dirty(&self) {}
fn read_data(&self, nbt: &NbtCompound, stacks: &mut [ItemStack]) { fn read_data(&self, nbt: &NbtCompound, stacks: &mut [ItemStack]) {
if let Some(inventory_list) = nbt.get_list("Items") { sync_read_items_from_nbt(nbt, stacks);
for tag in inventory_list {
if let Some(item_compound) = tag.extract_compound()
&& let Some(slot_byte) = item_compound.get_byte("Slot")
{
let slot = slot_byte as usize;
if slot < stacks.len()
&& let Some(item_stack) = ItemStack::read_item_stack(item_compound)
{
stacks[slot] = item_stack;
}
}
}
}
} }
fn is_valid_slot_for(&self, _slot: usize, _stack: &ItemStack) -> bool { fn is_valid_slot_for(&self, _slot: usize, _stack: &ItemStack) -> bool {
@ -139,6 +126,23 @@ pub trait Clearable {
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>; fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
} }
pub fn sync_read_items_from_nbt(nbt: &NbtCompound, stacks: &mut [ItemStack]) {
if let Some(inventory_list) = nbt.get_list("Items") {
for tag in inventory_list {
if let Some(item_compound) = tag.extract_compound()
&& let Some(slot_byte) = item_compound.get_byte("Slot")
{
let slot = slot_byte as usize;
if slot < stacks.len()
&& let Some(item_stack) = ItemStack::read_item_stack(item_compound)
{
stacks[slot] = item_stack;
}
}
}
}
}
pub fn sync_write_items_to_nbt(items: &[ItemStack], nbt: &mut NbtCompound) { pub fn sync_write_items_to_nbt(items: &[ItemStack], nbt: &mut NbtCompound) {
let mut slots = Vec::new(); let mut slots = Vec::new();
for (i, stack) in items.iter().enumerate() { for (i, stack) in items.iter().enumerate() {

View File

@ -165,9 +165,9 @@ impl Level {
let entities_folder = dim_folder.join("entities"); let entities_folder = dim_folder.join("entities");
let poi_folder = dim_folder.join("poi"); let poi_folder = dim_folder.join("poi");
std::fs::create_dir_all(&region_folder).expect("Failed to create Region folder"); let _ = std::fs::create_dir_all(&region_folder);
std::fs::create_dir_all(&entities_folder).expect("Failed to create Entities folder"); let _ = std::fs::create_dir_all(&entities_folder);
std::fs::create_dir_all(&poi_folder).expect("Failed to create POI folder"); let _ = std::fs::create_dir_all(&poi_folder);
let level_folder = Arc::new(LevelFolder { let level_folder = Arc::new(LevelFolder {
root_folder, root_folder,
@ -291,7 +291,11 @@ impl Level {
level_ref.clone(), level_ref.clone(),
level_channel, level_channel,
listener, listener,
level_ref.thread_tracker.lock().unwrap().as_mut(), level_ref
.thread_tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.as_mut(),
gen_pool, gen_pool,
); );
@ -320,7 +324,7 @@ impl Level {
} else { } else {
// Fallback to spawning a new thread if no pool is available (should not happen in production) // Fallback to spawning a new thread if no pool is available (should not happen in production)
let level_clone = level; let level_clone = level;
thread::Builder::new() let _ = thread::Builder::new()
.name(format!("Entity Gen {pos:?}")) .name(format!("Entity Gen {pos:?}"))
.spawn(move || { .spawn(move || {
let arc_chunk = Arc::new(ChunkEntityData { let arc_chunk = Arc::new(ChunkEntityData {
@ -340,8 +344,7 @@ impl Level {
let _ = tx.send(arc_chunk.clone()); let _ = tx.send(arc_chunk.clone());
} }
} }
}) });
.expect("Failed to spawn entity generation thread");
} }
} }
@ -366,7 +369,10 @@ impl Level {
self.chunk_system_tasks.close(); self.chunk_system_tasks.close();
let handles = { let handles = {
let mut lock = self.thread_tracker.lock().unwrap(); let mut lock = self
.thread_tracker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
lock.drain(..).collect::<Vec<_>>() lock.drain(..).collect::<Vec<_>>()
}; };
@ -530,7 +536,11 @@ impl Level {
// Use the bitmask to skip sections // Use the bitmask to skip sections
let mask = chunk.section.randomly_ticking_mask.load(Ordering::Relaxed); let mask = chunk.section.randomly_ticking_mask.load(Ordering::Relaxed);
if mask != 0 { if mask != 0 {
let sections = chunk.section.block_sections.read().unwrap(); let sections = chunk
.section
.block_sections
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let min_y = chunk.section.min_y; let min_y = chunk.section.min_y;
for i in 0..section_count { for i in 0..section_count {
@ -641,17 +651,23 @@ impl Level {
let recv = self.chunk_listener.add_single_chunk_listener(pos); let recv = self.chunk_listener.add_single_chunk_listener(pos);
{ {
let mut lock = self.chunk_loading.lock().unwrap(); let mut lock = self
.chunk_loading
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
lock.add_ticket(pos, 31); lock.add_ticket(pos, 31);
lock.send_change(); lock.send_change();
}; };
let chunk = recv let chunk = recv
.await .await
.expect("Chunk listener dropped without sending chunk"); .unwrap_or_else(|_| ChunkData::empty_sync(pos.x, pos.y));
{ {
let mut lock = self.chunk_loading.lock().unwrap(); let mut lock = self
.chunk_loading
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
lock.remove_ticket(pos, 31); lock.remove_ticket(pos, 31);
lock.send_change(); lock.send_change();
}; };
@ -768,7 +784,14 @@ impl Level {
self.spawn_entity_generation(pos); self.spawn_entity_generation(pos);
} }
} }
rx.await.expect("Entity generation worker dropped") rx.await.unwrap_or_else(|_| {
Arc::new(ChunkEntityData {
x: pos.x,
z: pos.y,
data: tokio::sync::Mutex::new(Vec::new()),
dirty: AtomicBool::new(false),
})
})
} }
} }

View File

@ -1,4 +1,5 @@
#![allow(clippy::unreachable)] #![allow(clippy::unreachable)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
use pumpkin_data::{ use pumpkin_data::{
Block, BlockState, chunk_gen_settings::GenerationSettings, dimension::Dimension, Block, BlockState, chunk_gen_settings::GenerationSettings, dimension::Dimension,

Some files were not shown because too many files have changed in this diff Show More