chore: clippy enforce unwrap_used, expect_used and panic
release is not far
This commit is contained in:
parent
0eb7b936db
commit
49360e53ef
@ -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"
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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")
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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,31 +262,46 @@ 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!(
|
||||||
|
"{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!(
|
warn!(
|
||||||
"Couldn't write merged config to {}. Reason: {}",
|
"Couldn't write merged config to {}. Reason: {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
@ -289,17 +309,37 @@ pub trait LoadConfiguration {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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) {
|
||||||
|
Ok(toml_str) => {
|
||||||
|
if let Err(err) = fs::write(&path, toml_str) {
|
||||||
warn!(
|
warn!(
|
||||||
"Couldn't write default config to {:?}. Reason: {}",
|
"Couldn't write default config to {:?}. Reason: {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
err
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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};
|
||||||
|
|||||||
@ -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({} = \"{}\")",
|
||||||
|
|||||||
@ -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({} = \"{}\")",
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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)]
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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};
|
||||||
|
|||||||
@ -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
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.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]
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
}
|
||||||
panic!("Invalid drag button: {drag_button}");
|
_ => 0,
|
||||||
};
|
};
|
||||||
inserting_count = inserting_count
|
inserting_count = inserting_count
|
||||||
.min(max(
|
.min(max(
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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};
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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_error(format!("Key is not a string: {key}")),
|
||||||
|
|key_str| {
|
||||||
|
compound.put(key_str, value);
|
||||||
DataResult::new_success(compound.into())
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::panic)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,10 @@ 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.
|
||||||
fn enter(&self, _span: &tracing::span::Id) {}
|
fn enter(&self, _span: &tracing::span::Id) {}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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()));
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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];
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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};
|
||||||
|
|||||||
@ -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::{
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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,13 +753,14 @@ 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
|
||||||
// think that's fine
|
// think that's fine
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
|
||||||
.iter()
|
|
||||||
.find_position(|x| **x == pos)
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
self.high_priority.remove(index);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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,9 +270,10 @@ 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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChunkNoiseDensityFunction<'a> {
|
pub struct ChunkNoiseDensityFunction<'a> {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(¤t_biome) {
|
if biomes.contains(¤t_biome) {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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(®ion_folder).expect("Failed to create Region folder");
|
let _ = std::fs::create_dir_all(®ion_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),
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
Loading…
Reference in New Issue
Block a user