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"
unreachable = "deny"
unimplemented = "deny"
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
# Safety & Soundness
undocumented_unsafe_blocks = "deny"

View File

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

View File

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

View File

@ -26,9 +26,10 @@ macro_rules! impl_apply {
let result_1 = $self;
if !(result_1.is_error() $(|| $result.is_error())+) {
// All n results are successful.
#[allow(clippy::expect_used)]
return DataResult::new_success($f(
result_1.into_result().unwrap()
$( , $result.into_result().unwrap() )+
result_1.into_result().expect("checked is_error")
$( , $result.into_result().expect("checked is_error") )+
));
}
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.
#[allow(clippy::unwrap_used, clippy::expect_used)]
pub fn unwrap(self) -> R {
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.
#[allow(clippy::unwrap_used, clippy::expect_used)]
pub fn unwrap_or_partial(self) -> R {
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.
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
pub fn expect(self, message: &str) -> R {
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.
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
pub fn expect_or_partial(self, message: &str) -> R {
self.into_result_or_partial()
.unwrap_or_else(|| panic!("{}", message))
@ -614,7 +619,7 @@ macro_rules! assert_success {
result
);
assert_eq!(
result.unwrap(),
result.into_result().expect("DataResult success"),
$right,
"`DataResult` was successful but the value doesn't match"
);
@ -632,7 +637,7 @@ macro_rules! assert_encode_success {
result
);
assert_eq!(
result.unwrap(),
result.into_result().expect("DataResult success"),
$right,
"`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> {
if let Value::String(_) = input {
// Unwrapping is fine as only strings are possible here.
DataResult::new_success(Self::get_as_string(input).unwrap())
if let Value::String(s) = input {
DataResult::new_success(s.clone())
} else {
DataResult::new_error(format!("Not a string: {input}"))
}
@ -194,13 +193,14 @@ impl DynamicOps for JsonOps {
if !Self::is_valid_key(&key) {
return DataResult::new_partial_error(format!("Key is not a string: {key}"), map);
}
let mut output_map = Map::new();
if let Value::Object(mut m) = map {
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))
}
@ -225,23 +225,19 @@ impl DynamicOps for JsonOps {
let mut missed = vec![];
for entry in other_map_like.iter() {
if Self::is_valid_key(&entry.0) {
output_map.insert(Self::get_as_string(&entry.0).unwrap(), entry.1.clone());
if let Some(key_str) = Self::get_as_string(&entry.0) {
output_map.insert(key_str, entry.1.clone());
} else {
missed.push(entry.0);
}
}
let object = Value::Object(output_map);
let pretty_missed = serde_json::to_string_pretty(&missed);
if missed.is_empty() {
DataResult::new_success(object)
} else {
DataResult::new_partial_error(
format!(
"Some keys are not strings{}",
pretty_missed.map_or_else(|_| String::new(), |r| format!(": {r}"))
),
format!("Some keys are not valid: {missed:?}"),
object,
)
}
@ -375,10 +371,9 @@ impl StructBuilder for JsonStructBuilder {
impl StringStructBuilder for JsonStructBuilder {
fn append(&self, key: &str, value: Self::Value, mut builder: Self::Result) -> Self::Result {
builder
.as_object_mut()
.unwrap()
.insert(key.to_string(), value);
if let Some(obj) = builder.as_object_mut() {
obj.insert(key.to_string(), value);
}
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 dynamic_ops;
pub mod json_ops;

View File

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

View File

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

View File

@ -31,14 +31,19 @@ pub struct JavaConfig {
impl Default for JavaConfig {
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 {
enabled: true,
address: "0.0.0.0:25565".parse().unwrap(),
address,
encryption: true,
online_mode: true,
max_players: 1000,
view_distance: NonZeroU8::new(16).unwrap(),
simulation_distance: NonZeroU8::new(10).unwrap(),
view_distance,
simulation_distance,
compression: CompressionConfig::default(),
motd: "A blazingly fast Pumpkin server!".to_string(),
authentication: AuthenticationConfig::default(),

View File

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

View File

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

View File

@ -255,7 +255,6 @@ impl Default for BlockId {
impl std::fmt::Display for BlockId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
write!(
f,
"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::tag::NbtTag;
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::Digest;
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;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]

View File

@ -3,8 +3,8 @@ use crate::Enchantment;
use crate::attributes::Attributes;
use crate::data_component_impl::basic::SoundEvent;
use crate::data_component_impl::{
DataComponentImpl, EquipmentSlot, IDSet, IDSetContent, IdOr, default_impl, get_f32_hash,
get_i32_hash, get_idor, get_idor_hash, get_idset_hash, get_str_hash, put_idor,
DataComponentImpl, EquipmentSlot, IDSet, IdOr, get_i32_hash, get_idor, get_idor_hash,
get_idset_hash, get_str_hash, put_idor,
};
use crate::entity_type::EntityType;
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 std::borrow::Cow;

View File

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

View File

@ -3,7 +3,6 @@
use crate::Block;
use crate::BlockId;
use crate::data_component::DataComponent;
use crate::effect::StatusEffect;
use crate::entity_type::EntityType;
use crate::sound::Sound;
use crate::tag::Taggable;
@ -52,7 +51,13 @@ pub fn get<T: DataComponentImpl + 'static>(value: &dyn DataComponentImpl) -> &T
#[inline]
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 {
@ -85,8 +90,6 @@ macro_rules! default_impl {
};
}
pub(crate) use default_impl;
pub fn get_str_hash(val: &str) -> u32 {
let mut digest = Digest::new(Crc32Iscsi);
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);
IdOr::Id(Sound::from_name(sound).unwrap_or(default))
} else if let Some(sound_compound) = nbt.get_compound(key) {
let sound_name = sound_compound
.get_string("sound_id")
.expect("SoundEvent compound must have a 'sound_id' field");
let Some(sound_name) = sound_compound.get_string("sound_id") else {
return IdOr::Id(default);
};
let range = sound_compound.get_float("range");
IdOr::Value(basic::SoundEvent {
sound_name: sound_name.to_string(),

View File

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

View File

@ -4,7 +4,7 @@ use crate::item::Item;
use crate::item_stack::ItemStack;
use pumpkin_util::identifier::Identifier;
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::fmt::Display;
use std::hash::{Hash, Hasher};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -133,7 +133,10 @@ impl ItemStack {
item,
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));
return self
.patch
.last_mut()
.unwrap()
.last_mut()?
.1
.as_mut()
.map(|c| get_mut::<T>(c.as_mut()));
@ -209,7 +211,7 @@ impl ItemStack {
item: &Item::AIR,
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]
@ -731,7 +733,7 @@ impl From<&RecipeResultStruct> for ItemStack {
Self::new(
value.count,
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(
clippy::all,
clippy::pedantic,
clippy::nursery,
clippy::cargo,
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]

View File

@ -90,7 +90,10 @@ async fn recipe_matches(
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;
}
@ -120,10 +123,13 @@ async fn recipe_matches(
continue;
}
let ingredient = key
let Some(ingredient) = key
.iter()
.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) {
matched = false;
@ -136,7 +142,10 @@ async fn recipe_matches(
matched = true;
'outer: for y in 0..pattern.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
.get_stack(
(y + y_offset) * inventory.get_height()
@ -150,10 +159,13 @@ async fn recipe_matches(
}
continue;
}
let ingredient = key
let Some(ingredient) = key
.iter()
.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) {
matched = false;
break 'outer;
@ -242,7 +254,10 @@ async fn recipe_matches(
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;
}
if count
@ -268,11 +283,12 @@ async fn recipe_matches(
}
continue;
}
let ingredient = key
.iter()
.find(|(k, _)| *k == current_key)
.map(|(_, v)| v)
.expect("Crafting recipe used invalid key");
let Some(ingredient) =
key.iter().find(|(k, _)| *k == current_key).map(|(_, v)| v)
else {
matched = false;
break 'outer;
};
if !ingredient.match_item(slot.item) {
matched = false;
break 'outer;

View File

@ -13,6 +13,9 @@
//! - [`SyncHandler`] - Synchronizes inventory state between server and client
//!
//! # 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
//! - [`crafting`] - Crafting table and inventory crafting mechanics

View File

@ -116,7 +116,9 @@ impl PlayerInventory {
match hand {
Hand::Right => self.set_held_item(stack).await,
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);
}
}
@ -137,7 +139,9 @@ impl PlayerInventory {
///
/// Mojang name: `getOffHandStack`
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)
}
@ -146,7 +150,9 @@ impl PlayerInventory {
/// # Returns
/// The new main hand item and new off-hand item.
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 selected = self.get_selected_slot() as usize;
let mut main_inv = self.main_inventory.write().await;
@ -514,8 +520,6 @@ impl PlayerInventory {
pub fn set_selected_slot(&self, slot: u8) {
if Self::is_valid_hotbar_index(slot as usize) {
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())
&& slot.can_insert(&cursor_stack).await
{
let mut inserting_count = if drag_button == 0 {
initial_count / behaviour.drag_slots.len() as u8
} else if drag_button == 1 {
1
} else if drag_button == 2 {
let mut inserting_count = match drag_button {
0 => initial_count / behaviour.drag_slots.len() as u8,
1 => 1,
2 => {
cursor_stack.item_count = cursor_stack.get_max_stack_size();
cursor_stack.item_count
} else {
panic!("Invalid drag button: {drag_button}");
}
_ => 0,
};
inserting_count = inserting_count
.min(max(

View File

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

View File

@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use heck::ToShoutySnakeCase;
use proc_macro::TokenStream;
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 pumpkin_nbt::{Nbt, NbtCompound, deserializer, tag::NbtTag};
use std::io::Cursor;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,8 +7,10 @@
clippy::all,
clippy::pedantic,
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
//! 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.
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) {
handler.handle_erased(server, event)
} else {
@ -166,7 +168,7 @@ impl wit::Guest for Component {
server: Server,
args: command::ConsumedArgs,
) -> 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(
|| {
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`.
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);
}
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) {
goal.can_start(server, entity)
} else {
@ -197,7 +201,9 @@ impl wit::Guest for Component {
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) {
goal.should_continue(server, entity)
} else {
@ -206,21 +212,27 @@ impl wit::Guest for Component {
}
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) {
goal.start(server, 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) {
goal.tick(server, 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) {
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`.
fn plugin() -> &'static mut dyn Plugin {
#[expect(static_mut_refs)]
#[allow(clippy::unwrap_used)]
unsafe {
PLUGIN.as_deref_mut().unwrap()
}

View File

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

View File

@ -114,7 +114,10 @@ pub fn schedule_delayed_task<F>(delay_ticks: u64, handler: F) -> u32
where
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)
}
@ -124,7 +127,10 @@ pub fn schedule_repeating_task<F>(delay_ticks: u64, period_ticks: u64, handler:
where
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)
}

View File

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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@ pub struct VarULong(pub VarULongType);
impl VarULong {
/// The maximum number of bytes a `VarULong` can occupy.
#[allow(clippy::unwrap_used)]
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
#[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::{
io::{Error, Read, Write},
pin::Pin,

View File

@ -290,7 +290,10 @@ impl<'a> crate::serial::PacketReadSlice<'a> for i16 {
}
let (bytes, rest) = buf.split_at(2);
*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);
*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);
*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);
*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);
*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);
*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);
*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);
*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);
*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()))?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.map_err(|_| AuthError::PublicKeyBuild("Clock before UNIX epoch".into()))?
.as_secs();
if now > exp {
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 std::ops::{Index, IndexMut};
@ -57,13 +62,12 @@ macro_rules! global_path {
($path:expr) => {{
use std::path::Path;
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.ancestors()
.nth(2)
.unwrap_or_else(|| Path::new("."))
.join(file!())
.parent()
.unwrap()
.unwrap_or_else(|| Path::new("."))
.join($path)
}};
}
@ -188,11 +192,12 @@ impl<'a, T> MutableSplitSlice<'a, T> {
///
/// # Panics
/// * 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) {
let (start, end_inclusive) = base.split_at_mut(index);
let (value, end) = end_inclusive
.split_first_mut()
.expect("Index is not in base slice");
let Some((value, end)) = end_inclusive.split_first_mut() else {
panic!("Index is not in base slice");
};
(value, Self { start, end })
}
@ -214,6 +219,7 @@ impl<T> Index<usize> for MutableSplitSlice<'_, T> {
type Output = T;
#[expect(clippy::comparison_chain)]
#[allow(clippy::panic)]
fn index(&self, index: usize) -> &Self::Output {
if index < self.start.len() {
&self.start[index]
@ -239,6 +245,7 @@ pub struct DoublePerlinNoiseParametersCodec {
impl<T> IndexMut<usize> for MutableSplitSlice<'_, T> {
#[expect(clippy::comparison_chain)]
#[allow(clippy::panic)]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
if index < self.start.len() {
&mut self.start[index]

View File

@ -645,7 +645,9 @@ impl WeightedListIntProvider {
}
// 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.

View File

@ -317,8 +317,11 @@ impl OctavePerlinNoiseSampler {
let mut octaves = Vec::from_iter(octaves);
octaves.sort();
let i = -**octaves.first().expect("we should have some octaves");
let j = **octaves.last().expect("we should have some octaves");
let (Some(&&first), Some(&&last)) = (octaves.first(), octaves.last()) else {
return (0, Vec::new());
};
let i = -first;
let j = last;
let k = i + j + 1;
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);
octaves.sort();
let i = -**octaves.first().expect("Should have some octaves");
let j = **octaves.last().expect("Should have some octaves");
let (Some(&&first), Some(&&last)) = (octaves.first(), octaves.last()) else {
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 sampler = SimplexNoiseSampler::new(random);

View File

@ -107,6 +107,7 @@ impl PermissionRegistry {
///
/// # Parameters
/// - `permission`: The `Permission` instance to add.
#[allow(clippy::expect_used)]
pub fn register_permission_or_panic(&mut self, permission: Permission) {
self.register_permission(permission)
.expect("Permission should have been registered successfully");
@ -302,9 +303,9 @@ impl PermissionManager {
// Check for inherited permissions from parent nodes
for (node, value) in attachment.get_permissions() {
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| {
Some(val.wrapping_mul(1181783497276652981u64))
})
.unwrap();
.unwrap_or(0);
let nanos = time::SystemTime::now()
.duration_since(time::SystemTime::UNIX_EPOCH)
.unwrap()
.as_nanos();
.map_or(0, |d| d.as_nanos());
let nano_upper = (nanos >> 8) as u64;
let nano_lower = nanos as u64;

View File

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

View File

@ -76,7 +76,9 @@ impl SubstitutionRange {
/// * `translation`: The localized translation string.
/// * `locale`: The locale the translation belongs to.
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();
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.
pub fn add_translation_file<P: Into<String>>(namespace: P, file_path: P, locale: Locale) {
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() {
// TODO: Handle the case where the file is empty or not found properly
return;
}
let mut translations = TRANSLATIONS.lock().unwrap();
let mut translations = TRANSLATIONS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let namespace = namespace.into();
for (key, translation) in translations_map {
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
/// The localized translation. Falls back to `en_us` or the key itself if not found.
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();
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(|| {
let mut array: [HashMap<String, String>; Locale::COUNT] =
std::array::from_fn(|_| HashMap::new());
let vanilla_en_us: HashMap<String, String> =
serde_json::from_str(VANILLA_EN_US_JSON).expect("Could not parse en_us_java.json.");
let pumpkin_en_us: HashMap<String, String> =
serde_json::from_str(PUMPKIN_EN_US_JSON).expect("Could not parse en_us.json.");
let pumpkin_brb: HashMap<String, String> =
serde_json::from_str(PUMPKIN_BRB_JSON).expect("Could not parse brb.json.");
let pumpkin_de_de: HashMap<String, String> =
serde_json::from_str(PUMPKIN_DE_DE_JSON).expect("Could not parse de_de.json.");
let pumpkin_es_es: HashMap<String, String> =
serde_json::from_str(PUMPKIN_ES_ES_JSON).expect("Could not parse es_es.json.");
let pumpkin_fr_fr: HashMap<String, String> =
serde_json::from_str(PUMPKIN_FR_FR_JSON).expect("Could not parse fr_fr.json.");
let pumpkin_hr_hr: HashMap<String, String> =
serde_json::from_str(PUMPKIN_HR_HR_JSON).expect("Could not parse hr_hr.json.");
let pumpkin_it_it: HashMap<String, String> =
serde_json::from_str(PUMPKING_IT_IT_JSON).expect("Could not parse it_it.json.");
let pumpkin_ja_jp: HashMap<String, String> =
serde_json::from_str(PUMPKIN_JA_JP_JSON).expect("Could not parse ja_jp.json.");
let pumpkin_ka_ge: HashMap<String, String> =
serde_json::from_str(PUMPKIN_KA_GE_JSON).expect("Could not parse ka_ge.json.");
let pumpkin_ko_kr: HashMap<String, String> =
serde_json::from_str(PUMPKIN_KO_KR_JSON).expect("Could not parse ko_kr.json.");
let pumpkin_nds_de: HashMap<String, String> =
serde_json::from_str(PUMPKIN_NDS_DE_JSON).expect("Could not parse nds_de.json.");
let pumpkin_nl_be: HashMap<String, String> =
serde_json::from_str(PUMPKIN_NL_BE_JSON).expect("Could not parse nl_be.json.");
let pumpkin_nl_nl: HashMap<String, String> =
serde_json::from_str(PUMPKIN_NL_NL_JSON).expect("Could not parse nl_nl.json.");
let pumpkin_ro_ro: HashMap<String, String> =
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.");
let parse_json = |json: &str| -> HashMap<String, String> {
serde_json::from_str(json).unwrap_or_default()
};
let vanilla_en_us = parse_json(VANILLA_EN_US_JSON);
let pumpkin_en_us = parse_json(PUMPKIN_EN_US_JSON);
let pumpkin_brb = parse_json(PUMPKIN_BRB_JSON);
let pumpkin_de_de = parse_json(PUMPKIN_DE_DE_JSON);
let pumpkin_es_es = parse_json(PUMPKIN_ES_ES_JSON);
let pumpkin_fr_fr = parse_json(PUMPKIN_FR_FR_JSON);
let pumpkin_hr_hr = parse_json(PUMPKIN_HR_HR_JSON);
let pumpkin_it_it = parse_json(PUMPKING_IT_IT_JSON);
let pumpkin_ja_jp = parse_json(PUMPKIN_JA_JP_JSON);
let pumpkin_ka_ge = parse_json(PUMPKIN_KA_GE_JSON);
let pumpkin_ko_kr = parse_json(PUMPKIN_KO_KR_JSON);
let pumpkin_nds_de = parse_json(PUMPKIN_NDS_DE_JSON);
let pumpkin_nl_be = parse_json(PUMPKIN_NL_BE_JSON);
let pumpkin_nl_nl = parse_json(PUMPKIN_NL_NL_JSON);
let pumpkin_ro_ro = parse_json(PUMPKIN_RO_RO_JSON);
let pumpkin_ru_ru = parse_json(PUMPKIN_RU_RU_JSON);
let pumpkin_sq_al = parse_json(PUMPKIN_SQ_AL_JSON);
let pumpkin_zh_cn = parse_json(PUMPKIN_ZH_CN_JSON);
let pumpkin_zh_hk = parse_json(PUMPKIN_ZH_HK_JSON);
let pumpkin_zh_tw = parse_json(PUMPKIN_ZH_TW_JSON);
let pumpkin_lzh = parse_json(PUMPKIN_LZH_JSON);
let pumpkin_tr_tr = parse_json(PUMPKIN_TR_TR_JSON);
let pumpkin_uk_ua = parse_json(PUMPKIN_UK_UA_JSON);
let pumpkin_vi_vn = parse_json(PUMPKIN_VI_VN_JSON);
let pumpkin_pt_br = parse_json(PUMPKIN_PT_BR_JSON);
let pumpkin_pl_pl = parse_json(PUMPKIN_PL_PL_JSON);
for (key, value) in vanilla_en_us {
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 pumpkin_data::{
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 pumpkin_config::lighting::LightingEngineConfig;
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 pumpkin_data::BlockStateId;
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 pumpkin_data::{BlockStateId, dimension::Dimension};
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 pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
use pumpkin_world::generation::{

View File

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

View File

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

View File

@ -406,14 +406,31 @@ impl ChunkData {
.load(std::sync::atomic::Ordering::Relaxed);
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<_>>()
};
let light_lock = self.light_engine.lock().unwrap();
let heightmap_lock = self.heightmap.lock().unwrap();
let block_lock = self.section.block_sections.read().unwrap();
let biome_lock = self.section.biome_sections.read().unwrap();
let light_lock = self
.light_engine
.lock()
.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;

View File

@ -438,7 +438,7 @@ impl ChunkSections {
let relative_y = relative_y % BlockPalette::SIZE;
self.block_sections
.read()
.unwrap()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get(section_index)
.map(|section| section.get(relative_x, relative_y, relative_z))
}
@ -474,8 +474,14 @@ impl ChunkSections {
let relative_y = relative_y % BlockPalette::SIZE;
// Keep lock order consistent to avoid deadlocks: block sections first, then random-tick cache.
let mut sections = self.block_sections.write().unwrap();
let mut random_tick_sections_guard = self.random_tick_sections.write().unwrap();
let mut sections = self
.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) {
let replaced_block_state_id =
@ -546,7 +552,12 @@ impl ChunkSections {
let section_index = 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);
}
}
@ -563,7 +574,7 @@ impl ChunkSections {
debug_assert!(scale_z < BiomePalette::SIZE);
self.biome_sections
.read()
.unwrap()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get(index)
.map(|section| section.get(scale_x, scale_y, scale_z))
}
@ -587,6 +598,30 @@ impl ChunkSections {
}
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
pub fn set_block_absolute_y(
&self,
@ -622,7 +657,10 @@ impl ChunkData {
relative_z: usize,
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 x = relative_x as i32;
let y = relative_y as i32 + min_y;
@ -742,9 +780,9 @@ impl ChunkData {
}
for (idx, is_set) in has_found.iter().enumerate() {
if !(*is_set) {
if !(*is_set) && let Ok(hm_type) = idx.try_into() {
heightmaps.set(
idx.try_into().unwrap(),
hm_type,
x as i32,
z as i32,
self.section.min_y - 1,
@ -759,7 +797,7 @@ impl ChunkData {
self.section
.block_sections
.read()
.unwrap()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.iter()
.enumerate()
.rev()

View File

@ -63,7 +63,11 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> HeterogeneousPaletteData<V
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.
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 {
let mut indices = Box::new([[[0u8; DIM]; DIM]; DIM]);
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;
}
Self::Heterogeneous(Box::new(HeterogeneousPaletteData {
@ -259,7 +263,7 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
.map(|chunk| {
chunk.iter().enumerate().fold(0, |acc, (index, key)| {
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);
let packed_offset_index =
@ -578,7 +582,7 @@ impl BiomePalette {
for y in 0..16 {
for z in 0..16 {
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);
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
// Please test your code on bedrock before merging
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);
current_word |= (*key_index as u32)

View File

@ -39,7 +39,10 @@ impl LevelChannel {
pos: Vec<ChunkPos>,
) {
// 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);
if let Some(old) = &mut value.0 {
for (pos, change) in new_value.0 {
@ -73,7 +76,10 @@ impl LevelChannel {
),
) {
// 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 {
for (pos, change) in new_value.0 {
match old.0.entry(pos) {
@ -100,24 +106,36 @@ impl LevelChannel {
}
pub fn set_priority(&self, pos: Vec<ChunkPos>) {
// 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();
}
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);
swap(&mut ret, &mut *lock);
ret
}
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()
&& lock.1.is_none()
&& !level.should_unload.load(SeqCst)
&& !level.should_save.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);
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> {
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
}
pub fn add_global_chunk_listener(&self) -> Receiver<(ChunkPos, Weak<crate::chunk::ChunkData>)> {
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
}
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 len = single.len();
while i < len {
@ -56,7 +65,10 @@ impl ChunkListener {
}
{
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 len = global.len();
while i < len {

View File

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

View File

@ -50,10 +50,15 @@ pub struct DAG {
impl DAG {
pub fn fast_drop_node(&mut self, node: NodeKey) {
let mut edge = self.nodes.remove(node).unwrap().edge;
// debug!("drop node {node:?}");
if let Some(removed_node) = self.nodes.remove(node) {
let mut edge = removed_node.edge;
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) {

View File

@ -237,7 +237,10 @@ impl GenerationCache for Cache {
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
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;
heightmap.get(ChunkHeightmapType::MotionBlocking, x, z, min_y)
@ -253,7 +256,10 @@ impl GenerationCache for Cache {
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
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;
heightmap.get(ChunkHeightmapType::MotionBlockingNoLeaves, x, z, min_y)
}
@ -268,7 +274,10 @@ impl GenerationCache for Cache {
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
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;
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)
.unwrap_or(0),
)
.unwrap()
.unwrap_or(&Biome::PLAINS)
}
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
1. add proto chunk dirty flag

View File

@ -182,7 +182,10 @@ impl GenerationSchedule {
fn apply_lighting_override(&self, chunk: &SyncChunk) {
match self.lighting_config {
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 {
section.fill(15);
}
@ -192,7 +195,10 @@ impl GenerationSchedule {
chunk.dirty.store(true, Relaxed);
}
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 {
section.fill(0);
}
@ -512,7 +518,7 @@ impl GenerationSchedule {
self.unload_chunks.remove(&pos);
if holder.current_stage == StagedChunkEnum::Full && !holder.public {
holder.public = true;
match holder.chunk.as_ref().unwrap() {
match holder.chunk.as_ref().expect("chunk exists") {
Chunk::Level(chunk) => {
self.apply_lighting_override(chunk);
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 {
node.in_queue = true;
self.queue.push(TaskHeapNode(0, task));
@ -594,7 +600,7 @@ impl GenerationSchedule {
}
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) {
continue;
}
@ -605,7 +611,7 @@ impl GenerationSchedule {
let mut has_valid_task = false;
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) {
prev_edge = cur_edge;
cur_edge = edge.next;
@ -617,7 +623,11 @@ impl GenerationSchedule {
if prev_edge.is_null() {
change_head = Some(next);
} 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() {
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 {
*data.entry(*pos).or_insert(0) += 1;
}
@ -716,7 +730,7 @@ impl GenerationSchedule {
if should_save {
let chunk_to_save = match chunk {
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));
}
@ -733,7 +747,11 @@ impl GenerationSchedule {
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 {
*data.entry(*pos).or_insert(0) += 1;
}
@ -750,7 +768,7 @@ impl GenerationSchedule {
};
let mut edge = old.edge;
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) {
debug_assert!(node.in_degree >= 1);
node.in_degree -= 1;
@ -792,7 +810,7 @@ impl GenerationSchedule {
fn receive_chunk(&mut self, pos: ChunkPos, data: RecvChunk) {
match data {
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() {
warn!(
"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);
match 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;
if new_pos == pos {
if holder.current_stage != StagedChunkEnum::Spawn {
@ -926,7 +945,8 @@ impl GenerationSchedule {
self.chunk_map.insert(new_pos, holder);
}
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());
self.drop_satisfied_tasks(&mut holder, stage);
@ -1164,7 +1184,7 @@ impl GenerationSchedule {
if node.stage == StagedChunkEnum::Empty {
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_eq!(holder.current_stage, StagedChunkEnum::None);
let occupy = self.graph.nodes.insert(Node::new(
@ -1246,7 +1266,8 @@ impl GenerationSchedule {
for dx in -write_radius..=write_radius {
for dy in -write_radius..=write_radius {
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;
swap(&mut tmp, &mut holder.chunk);
let Some(tmp) = tmp else {
@ -1270,7 +1291,7 @@ impl GenerationSchedule {
let mut prev_edge = EdgeKey::null();
let mut change_head = None;
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) {
prev_edge = cur_edge;
cur_edge = edge.next;
@ -1282,8 +1303,11 @@ impl GenerationSchedule {
if prev_edge.is_null() {
change_head = Some(next);
} else {
self.graph.edges.get_mut(prev_edge).unwrap().next =
next;
self.graph
.edges
.get_mut(prev_edge)
.expect("edge exists")
.next = next;
}
}
}
@ -1455,7 +1479,7 @@ impl GenerationSchedule {
debug_assert!(holder.occupied.is_null());
if holder.current_stage != StagedChunkEnum::None {
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
);
}

View File

@ -67,7 +67,12 @@ pub async fn io_read_work(
// Lock handling
loop {
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;
}
notified.await;
@ -197,7 +202,10 @@ pub async fn io_write_work(recv: AsyncRx<Vec<(ChunkPos, Chunk)>>, level: Arc<Lev
}
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) {
Entry::Occupied(mut entry) => {
let rc = entry.get_mut();

View File

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

View File

@ -1,4 +1,4 @@
use pumpkin_data::BlockState;
use pumpkin_data::{Block, BlockState};
use pumpkin_util::{
DoublePerlinNoiseParametersCodec,
math::{
@ -199,7 +199,9 @@ pub struct WeightedBlockStateProvider {
impl WeightedBlockStateProvider {
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 =
Self::get_random_tag_entry_block(tag::Block::MINECRAFT_WALL_CORALS, random);
let original_props = &wall_coral
.properties(wall_coral.default_state.id)
.unwrap()
.to_props();
let Some(properties) = wall_coral.properties(wall_coral.default_state.id) else {
continue;
};
let original_props = &properties.to_props();
// Set the right Axis
let props: Vec<(&str, &str)> = original_props
.iter()
@ -111,7 +111,7 @@ impl CoralFeature {
) -> &'static Block {
let values = tag.1;
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()
}
}

View File

@ -157,7 +157,10 @@ impl FancyTrunkPlacer {
if TreeFeature::can_replace(block.to_state(), block.to_block_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();
// Set the right Axis
let props: Vec<(&'static str, &'static str)> = original_props

View File

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

View File

@ -121,7 +121,7 @@ fn find_nearest_concentric(
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(

View File

@ -251,7 +251,9 @@ impl ChunkNoiseFunctionComponent<'_> {
pos: &Vector3<i32>,
sample_options: &ChunkNoiseFunctionSampleOptions,
) -> 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
&& let ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::DensityInterpolator(interp),
@ -268,10 +270,11 @@ impl ChunkNoiseFunctionComponent<'_> {
mapper: &impl IndexToNoisePos,
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);
}
}
}
pub struct ChunkNoiseDensityFunction<'a> {
pub(crate) component_stack: &'a mut [ChunkNoiseFunctionComponent<'a>],

View File

@ -103,7 +103,9 @@ impl Spline {
let input_max = input_function.max() 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 {
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);
@ -113,7 +115,9 @@ impl Spline {
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 {
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);

View File

@ -260,16 +260,29 @@ impl ProtoChunk {
) -> Self {
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
.blending_data
.clone_from(&chunk_data.blending_data);
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 biome_sections_guard = section_data.biome_sections.read().unwrap();
let block_sections_guard = section_data
.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() {
let section_base_y = section_idx as i32 * 16;
@ -533,7 +546,7 @@ impl ProtoChunk {
#[inline]
#[must_use]
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]
@ -618,7 +631,9 @@ impl ProtoChunk {
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 {
let bounding_box = piece.get_structure_piece().bounding_box;
@ -933,7 +948,7 @@ impl ProtoChunk {
#[must_use]
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)]
@ -1223,7 +1238,9 @@ impl ProtoChunk {
let chunk = cache.get_center_chunk_mut();
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);
}
}

View File

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

View File

@ -370,9 +370,9 @@ impl MansionTemplatePiece {
"minecraft:chest".to_string(),
vec![("facing".to_string(), target.name().to_string())],
);
let state = BlockStateResolver::resolve_simple(&chest)
.expect("the vanilla chest state must resolve");
if let Some(state) = BlockStateResolver::resolve_simple(&chest) {
chunk.set_block_state(position.x, position.y, position.z, state);
}
let mut nbt = NbtCompound::new();
nbt.put_string("id", "minecraft:chest".to_string());
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()))
.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);
bbox
@ -656,7 +656,10 @@ pub struct StructurePosition {
impl StructurePosition {
#[must_use]
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 read_data(&self, 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;
}
}
}
}
sync_read_items_from_nbt(nbt, stacks);
}
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 + '_>>;
}
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) {
let mut slots = Vec::new();
for (i, stack) in items.iter().enumerate() {

View File

@ -165,9 +165,9 @@ impl Level {
let entities_folder = dim_folder.join("entities");
let poi_folder = dim_folder.join("poi");
std::fs::create_dir_all(&region_folder).expect("Failed to create Region folder");
std::fs::create_dir_all(&entities_folder).expect("Failed to create Entities folder");
std::fs::create_dir_all(&poi_folder).expect("Failed to create POI folder");
let _ = std::fs::create_dir_all(&region_folder);
let _ = std::fs::create_dir_all(&entities_folder);
let _ = std::fs::create_dir_all(&poi_folder);
let level_folder = Arc::new(LevelFolder {
root_folder,
@ -291,7 +291,11 @@ impl Level {
level_ref.clone(),
level_channel,
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,
);
@ -320,7 +324,7 @@ impl Level {
} else {
// Fallback to spawning a new thread if no pool is available (should not happen in production)
let level_clone = level;
thread::Builder::new()
let _ = thread::Builder::new()
.name(format!("Entity Gen {pos:?}"))
.spawn(move || {
let arc_chunk = Arc::new(ChunkEntityData {
@ -340,8 +344,7 @@ impl Level {
let _ = tx.send(arc_chunk.clone());
}
}
})
.expect("Failed to spawn entity generation thread");
});
}
}
@ -366,7 +369,10 @@ impl Level {
self.chunk_system_tasks.close();
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<_>>()
};
@ -530,7 +536,11 @@ impl Level {
// Use the bitmask to skip sections
let mask = chunk.section.randomly_ticking_mask.load(Ordering::Relaxed);
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;
for i in 0..section_count {
@ -641,17 +651,23 @@ impl Level {
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.send_change();
};
let chunk = recv
.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.send_change();
};
@ -768,7 +784,14 @@ impl Level {
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)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
use pumpkin_data::{
Block, BlockState, chunk_gen_settings::GenerationSettings, dimension::Dimension,

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