chore: Remove Integer0ToX
This commit is contained in:
parent
6f2c3cd5e3
commit
fdf8ff5fd8
@ -67,6 +67,8 @@ fn property_group_name_from_derived_name(name: &str) -> String {
|
|||||||
enum PropertyType {
|
enum PropertyType {
|
||||||
/// The property is a simple boolean (`true`/`false`).
|
/// The property is a simple boolean (`true`/`false`).
|
||||||
Bool,
|
Bool,
|
||||||
|
/// The property is an integer with an inclusive range.
|
||||||
|
Int { min: u8, max: u8 },
|
||||||
/// The property is an enum with a generated Rust type identified by `name`.
|
/// The property is an enum with a generated Rust type identified by `name`.
|
||||||
Enum { name: String },
|
Enum { name: String },
|
||||||
}
|
}
|
||||||
@ -211,6 +213,7 @@ impl ToTokens for BlockPropertyStruct {
|
|||||||
let key = Ident::new_raw(&entry.original_name, Span::call_site());
|
let key = Ident::new_raw(&entry.original_name, Span::call_site());
|
||||||
match &entry.property_type {
|
match &entry.property_type {
|
||||||
PropertyType::Bool => quote! { pub #key: bool },
|
PropertyType::Bool => quote! { pub #key: bool },
|
||||||
|
PropertyType::Int { .. } => quote! { pub #key: u8 },
|
||||||
PropertyType::Enum { name } => {
|
PropertyType::Enum { name } => {
|
||||||
let value = Ident::new(name, Span::call_site());
|
let value = Ident::new(name, Span::call_site());
|
||||||
quote! { pub #key: #value }
|
quote! { pub #key: #value }
|
||||||
@ -229,6 +232,10 @@ impl ToTokens for BlockPropertyStruct {
|
|||||||
let field = Ident::new_raw(&entry.original_name, Span::call_site());
|
let field = Ident::new_raw(&entry.original_name, Span::call_site());
|
||||||
match &entry.property_type {
|
match &entry.property_type {
|
||||||
PropertyType::Bool => quote! { (!self.#field as u16, 2) },
|
PropertyType::Bool => quote! { (!self.#field as u16, 2) },
|
||||||
|
PropertyType::Int { min, max } => {
|
||||||
|
let count = (max - min + 1) as u16;
|
||||||
|
quote! { ((self.#field - #min) as u16, #count) }
|
||||||
|
}
|
||||||
PropertyType::Enum { name } => {
|
PropertyType::Enum { name } => {
|
||||||
let ty = Ident::new(name, Span::call_site());
|
let ty = Ident::new(name, Span::call_site());
|
||||||
quote! { (self.#field.to_index(), #ty::variant_count()) }
|
quote! { (self.#field.to_index(), #ty::variant_count()) }
|
||||||
@ -251,6 +258,16 @@ impl ToTokens for BlockPropertyStruct {
|
|||||||
value == 0
|
value == 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
PropertyType::Int { min, max } => {
|
||||||
|
let count = (max - min + 1) as u16;
|
||||||
|
quote! {
|
||||||
|
#field_name: {
|
||||||
|
let value = (index % #count) as u8;
|
||||||
|
index /= #count;
|
||||||
|
value + #min
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
PropertyType::Enum { name } => {
|
PropertyType::Enum { name } => {
|
||||||
let enum_ident = Ident::new(name, Span::call_site());
|
let enum_ident = Ident::new(name, Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
@ -272,6 +289,19 @@ impl ToTokens for BlockPropertyStruct {
|
|||||||
PropertyType::Bool => quote! {
|
PropertyType::Bool => quote! {
|
||||||
(#key_str, if self.#field { "true" } else { "false" })
|
(#key_str, if self.#field { "true" } else { "false" })
|
||||||
},
|
},
|
||||||
|
PropertyType::Int { min, max } => {
|
||||||
|
let mut arms = Vec::new();
|
||||||
|
for i in *min..=*max {
|
||||||
|
let i_str = i.to_string();
|
||||||
|
arms.push(quote! { #i => #i_str });
|
||||||
|
}
|
||||||
|
quote! {
|
||||||
|
(#key_str, match self.#field {
|
||||||
|
#(#arms,)*
|
||||||
|
_ => unreachable!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
PropertyType::Enum { .. } => quote! {
|
PropertyType::Enum { .. } => quote! {
|
||||||
(#key_str, self.#field.to_value())
|
(#key_str, self.#field.to_value())
|
||||||
},
|
},
|
||||||
@ -287,6 +317,21 @@ impl ToTokens for BlockPropertyStruct {
|
|||||||
block_props.#field_name = matches!(*value, "true")
|
block_props.#field_name = matches!(*value, "true")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
PropertyType::Int { min, max } => {
|
||||||
|
let mut arms = Vec::new();
|
||||||
|
for i in *min..=*max {
|
||||||
|
let i_str = i.to_string();
|
||||||
|
arms.push(quote! { #i_str => #i });
|
||||||
|
}
|
||||||
|
quote! {
|
||||||
|
#key => {
|
||||||
|
block_props.#field_name = match *value {
|
||||||
|
#(#arms,)*
|
||||||
|
_ => #min,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
PropertyType::Enum { name } => {
|
PropertyType::Enum { name } => {
|
||||||
let enum_ident = Ident::new(name, Span::call_site());
|
let enum_ident = Ident::new(name, Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
@ -596,6 +641,8 @@ pub struct Block {
|
|||||||
pub states: Vec<BlockState>,
|
pub states: Vec<BlockState>,
|
||||||
/// Experience points dropped when the block is mined, if any.
|
/// Experience points dropped when the block is mined, if any.
|
||||||
pub experience: Option<Experience>,
|
pub experience: Option<Experience>,
|
||||||
|
/// Calculated property descriptors for the block.
|
||||||
|
pub property_descriptors: Vec<TokenStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for Block {
|
impl ToTokens for Block {
|
||||||
@ -638,6 +685,7 @@ impl ToTokens for Block {
|
|||||||
} else {
|
} else {
|
||||||
quote! { None }
|
quote! { None }
|
||||||
};
|
};
|
||||||
|
let property_descriptors = &self.property_descriptors;
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
Block {
|
Block {
|
||||||
id: #id,
|
id: #id,
|
||||||
@ -650,6 +698,7 @@ impl ToTokens for Block {
|
|||||||
item_id: #item_id,
|
item_id: #item_id,
|
||||||
default_state: &#default_state,
|
default_state: &#default_state,
|
||||||
states: &[#(#states),*],
|
states: &[#(#states),*],
|
||||||
|
properties: &[#(#property_descriptors),*],
|
||||||
flammable: #flammable,
|
flammable: #flammable,
|
||||||
loot_table: #loot_table,
|
loot_table: #loot_table,
|
||||||
experience: #experience,
|
experience: #experience,
|
||||||
@ -800,15 +849,14 @@ pub fn build() -> TokenStream {
|
|||||||
let property = generated_property.to_property();
|
let property = generated_property.to_property();
|
||||||
let renamed_property = property.enum_name.to_upper_camel_case();
|
let renamed_property = property.enum_name.to_upper_camel_case();
|
||||||
|
|
||||||
let property_type = if property.values.len() == 2
|
let property_type = match &generated_property.property_type {
|
||||||
&& property.values.contains(&"true".to_string())
|
GeneratedPropertyType::Boolean => PropertyType::Bool,
|
||||||
&& property.values.contains(&"false".to_string())
|
GeneratedPropertyType::Int { min, max } => {
|
||||||
{
|
PropertyType::Int { min: *min, max: *max }
|
||||||
PropertyType::Bool
|
|
||||||
} else {
|
|
||||||
PropertyType::Enum {
|
|
||||||
name: renamed_property.clone(),
|
|
||||||
}
|
}
|
||||||
|
GeneratedPropertyType::Enum { .. } => PropertyType::Enum {
|
||||||
|
name: renamed_property.clone(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let PropertyType::Enum { name } = &property_type {
|
if let PropertyType::Enum { name } = &property_type {
|
||||||
@ -826,13 +874,35 @@ pub fn build() -> TokenStream {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut multiplier = 1;
|
||||||
|
let mut property_descriptors = Vec::new();
|
||||||
|
for hash in block.properties.iter().rev() {
|
||||||
|
let gen_prop = generated_prop_map.get(hash).unwrap();
|
||||||
|
let variant_count = match &gen_prop.property_type {
|
||||||
|
GeneratedPropertyType::Boolean => 2,
|
||||||
|
GeneratedPropertyType::Int { min, max } => (max - min + 1) as u16,
|
||||||
|
GeneratedPropertyType::Enum { values } => values.len() as u16,
|
||||||
|
};
|
||||||
|
property_descriptors.push(quote! {
|
||||||
|
PropertyDescriptor {
|
||||||
|
hash_key: #hash,
|
||||||
|
multiplier: #multiplier,
|
||||||
|
variant_count: #variant_count,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
multiplier *= variant_count;
|
||||||
|
}
|
||||||
|
|
||||||
let const_ident = format_ident!("{}", const_block_name_from_block_name(&block.name));
|
let const_ident = format_ident!("{}", const_block_name_from_block_name(&block.name));
|
||||||
let name_str = &block.name;
|
let name_str = &block.name;
|
||||||
let id_lit = LitInt::new(&block.id.to_string(), Span::call_site());
|
let id_lit = LitInt::new(&block.id.to_string(), Span::call_site());
|
||||||
let item_id = block.item_id;
|
let item_id = block.item_id;
|
||||||
|
|
||||||
|
let mut block_with_descriptors = block.clone();
|
||||||
|
block_with_descriptors.property_descriptors = property_descriptors;
|
||||||
|
|
||||||
constants_list.push(quote! {
|
constants_list.push(quote! {
|
||||||
pub const #const_ident: Block = #block;
|
pub const #const_ident: Block = #block_with_descriptors;
|
||||||
});
|
});
|
||||||
|
|
||||||
type_from_raw_id_array.push((block.id, quote! { &Self::#const_ident }));
|
type_from_raw_id_array.push((block.id, quote! { &Self::#const_ident }));
|
||||||
@ -887,6 +957,15 @@ pub fn build() -> TokenStream {
|
|||||||
if val == 0 { "true" } else { "false" }.to_string(),
|
if val == 0 { "true" } else { "false" }.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
PropertyType::Int { min, max } => {
|
||||||
|
let count = (*max - *min + 1) as u16;
|
||||||
|
let val = (temp_index % count) as u8;
|
||||||
|
temp_index /= count;
|
||||||
|
java_props_for_this_state.insert(
|
||||||
|
mapping.original_name.clone(),
|
||||||
|
(val + *min).to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
PropertyType::Enum { name } => {
|
PropertyType::Enum { name } => {
|
||||||
let enum_info = property_enums.get(name).unwrap();
|
let enum_info = property_enums.get(name).unwrap();
|
||||||
let count = enum_info.values.len() as u16;
|
let count = enum_info.values.len() as u16;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -464,11 +464,4 @@ impl DataComponent {
|
|||||||
Self::ShulkerColor => "minecraft:shulker/color",
|
Self::ShulkerColor => "minecraft:shulker/color",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn try_from_u8(v: u8) -> Option<Self> {
|
|
||||||
if v <= 106 {
|
|
||||||
Some(unsafe { std::mem::transmute::<u8, Self>(v) })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -549,7 +549,7 @@ impl ItemStack {
|
|||||||
let patch_len = nbt.get_u32().ok()? as usize;
|
let patch_len = nbt.get_u32().ok()? as usize;
|
||||||
for _ in 0..patch_len {
|
for _ in 0..patch_len {
|
||||||
let component_id_raw = nbt.get_u8().ok()?;
|
let component_id_raw = nbt.get_u8().ok()?;
|
||||||
let id = DataComponent::try_from_u8(component_id_raw)?;
|
let id = DataComponent::try_from_id(component_id_raw)?;
|
||||||
let is_present = nbt.get_bool().ok()?;
|
let is_present = nbt.get_bool().ok()?;
|
||||||
|
|
||||||
if is_present {
|
if is_present {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, EnumVariants, Integer0To15};
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ impl DaylightDetectorBlockEntity {
|
|||||||
power = (power as f32 * sun_angle_radians.cos()).round() as u8;
|
power = (power as f32 * sun_angle_radians.cos()).round() as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
let power = Integer0To15::from_index(power.clamp(0, 15).into());
|
let power = power.clamp(0, 15);
|
||||||
if power != props.power {
|
if power != props.power {
|
||||||
props.power = power;
|
props.power = power;
|
||||||
let state = props.to_state_id(block);
|
let state = props.to_state_id(block);
|
||||||
|
|||||||
@ -22,24 +22,29 @@ use crate::{
|
|||||||
|
|
||||||
use super::{ChunkSerializer, FileIO, LoadedData};
|
use super::{ChunkSerializer, FileIO, LoadedData};
|
||||||
|
|
||||||
/// A simple implementation of the `ChunkSerializer` trait
|
/// A simple implementation of the `ChunkSerializer` trait that loads and saves data
|
||||||
/// that load and save the data from a file in the disk
|
/// to disk using parallelism and a lazy-loading cache keyed by file path.
|
||||||
/// using parallelism and a cache for the files with ongoing IO operations.
|
|
||||||
///
|
///
|
||||||
/// It also avoid IO operations that could produce dataraces thanks to the
|
/// ### Concurrency model
|
||||||
/// custom *`DashMap`*-like implementation.
|
///
|
||||||
|
/// * `file_locks` — one `Arc<RwLock<S>>` per on-disk file, created lazily.
|
||||||
|
/// All readers/writers for the same region file share this lock, so there
|
||||||
|
/// are never two concurrent writers for the same file.
|
||||||
|
/// * `watchers` — a ref-count per path. While a path has active watchers the
|
||||||
|
/// serializer is **not** evicted from the cache and the file is **not**
|
||||||
|
/// flushed to disk (the caller owns the flush lifecycle).
|
||||||
|
///
|
||||||
|
/// ### Lock ordering (must never be violated to avoid deadlocks)
|
||||||
|
///
|
||||||
|
/// 1. `file_locks` (outer)
|
||||||
|
/// 2. individual `RwLock<S>` inside each loader (inner)
|
||||||
|
/// 3. `watchers` (independent — never held at the same time as either above)
|
||||||
|
///
|
||||||
|
/// `watchers` is always acquired in its own critical section, after all
|
||||||
|
/// serializer locks are released, which keeps it strictly independent.
|
||||||
pub struct ChunkFileManager<S: ChunkSerializer<WriteBackend = PathBuf>> {
|
pub struct ChunkFileManager<S: ChunkSerializer<WriteBackend = PathBuf>> {
|
||||||
// Dashmap has rw-locks on shards, but we want per-serializer.
|
|
||||||
//
|
|
||||||
// Lock lookups need to be:
|
|
||||||
// - Relatively quick insertion / lookup as to not extraneously block other concurrent hashmap
|
|
||||||
// ops
|
|
||||||
// - Guarantee that there is only one serializer per file at a time
|
|
||||||
// - Lazily load files as to support point 1
|
|
||||||
// - Allow for ease of usage (able to return the serializer from a function)
|
|
||||||
file_locks: RwLock<BTreeMap<PathBuf, Arc<ChunkSerializerLazyLoader<S>>>>,
|
file_locks: RwLock<BTreeMap<PathBuf, Arc<ChunkSerializerLazyLoader<S>>>>,
|
||||||
watchers: RwLock<BTreeMap<PathBuf, usize>>,
|
watchers: RwLock<BTreeMap<PathBuf, usize>>,
|
||||||
|
|
||||||
chunk_config: S::ChunkConfig,
|
chunk_config: S::ChunkConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +54,7 @@ pub(crate) trait PathFromLevelFolder {
|
|||||||
|
|
||||||
struct ChunkSerializerLazyLoader<S: ChunkSerializer<WriteBackend = PathBuf>> {
|
struct ChunkSerializerLazyLoader<S: ChunkSerializer<WriteBackend = PathBuf>> {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
/// Initialised at most once; subsequent calls reuse the same Arc.
|
||||||
internal: OnceCell<Arc<RwLock<S>>>,
|
internal: OnceCell<Arc<RwLock<S>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,21 +66,28 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkSerializerLazyLoader<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We can only remove this entry from the map if we are the only ones with a reference to it
|
/// Returns `true` only when no outside caller still holds a clone of this
|
||||||
/// IMPORTANT: This must be called within the write lock of the parent map
|
/// loader *or* the inner serializer.
|
||||||
|
///
|
||||||
|
/// # Safety requirement
|
||||||
|
/// **Must be called while the write-lock on the parent `file_locks` map is
|
||||||
|
/// held.** That guarantees no new `Arc` clones can be issued while we
|
||||||
|
/// inspect the strong counts.
|
||||||
fn can_remove(loader: &Arc<Self>) -> bool {
|
fn can_remove(loader: &Arc<Self>) -> bool {
|
||||||
|
// The map itself holds 1 strong count; anything above that means an
|
||||||
|
// active caller still has a handle.
|
||||||
if Arc::strong_count(loader) > 1 {
|
if Arc::strong_count(loader) > 1 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
match loader.internal.get() {
|
match loader.internal.get() {
|
||||||
Some(arc) => {
|
// The loader holds 1 strong count; anything above that means
|
||||||
// A strong count of 1 means it's only in the map
|
// a read/write guard is still live somewhere.
|
||||||
Arc::strong_count(arc) == 1
|
Some(arc) => Arc::strong_count(arc) == 1,
|
||||||
}
|
|
||||||
None => true,
|
None => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the serializer, initialising it from disk on the first call.
|
||||||
async fn get(&self) -> Result<Arc<RwLock<S>>, ChunkReadingError> {
|
async fn get(&self) -> Result<Arc<RwLock<S>>, ChunkReadingError> {
|
||||||
self.internal
|
self.internal
|
||||||
.get_or_try_init(|| async {
|
.get_or_try_init(|| async {
|
||||||
@ -86,30 +99,19 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkSerializerLazyLoader<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn read_from_disk(&self) -> Result<S, ChunkReadingError> {
|
async fn read_from_disk(&self) -> Result<S, ChunkReadingError> {
|
||||||
trace!("Opening file from Disk: {}", self.path.display());
|
trace!("Opening file from disk: {}", self.path.display());
|
||||||
|
|
||||||
let file_bytes = tokio::fs::read(&self.path).await.map_err(|err| {
|
match tokio::fs::read(&self.path).await {
|
||||||
if err.kind() == std::io::ErrorKind::NotFound {
|
|
||||||
ChunkReadingError::ChunkNotExist
|
|
||||||
} else {
|
|
||||||
ChunkReadingError::IoError(err.kind())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match file_bytes {
|
|
||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
let value = S::read(bytes.into())?;
|
let value = S::read(bytes.into())?;
|
||||||
trace!("Successfully read file from Disk: {}", self.path.display());
|
trace!("Successfully read file from disk: {}", self.path.display());
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
Err(ChunkReadingError::ChunkNotExist) => {
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
trace!(
|
trace!("File not found, using default for: {}", self.path.display());
|
||||||
"Chunk not found, using default for: {}",
|
|
||||||
self.path.display()
|
|
||||||
);
|
|
||||||
Ok(S::default())
|
Ok(S::default())
|
||||||
}
|
}
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(ChunkReadingError::IoError(err.kind())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,23 +127,63 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkFileManager<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkFileManager<S> {
|
impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkFileManager<S> {
|
||||||
|
/// Returns the serializer for `path`, inserting a lazy-loader if absent.
|
||||||
|
///
|
||||||
|
/// Uses an optimistic read-first pattern: in the common case (cache hit)
|
||||||
|
/// we never need a write-lock on the map.
|
||||||
async fn get_serializer(&self, path: &Path) -> Result<Arc<RwLock<S>>, ChunkReadingError> {
|
async fn get_serializer(&self, path: &Path) -> Result<Arc<RwLock<S>>, ChunkReadingError> {
|
||||||
// We get the entry from the DashMap and try to insert a new lock if it doesn't exist
|
{
|
||||||
// using dead-lock safe methods like `or_try_insert_with`
|
let locks = self.file_locks.read().await;
|
||||||
|
if let Some(loader) = locks.get(path) {
|
||||||
|
// Clone the Arc *before* releasing the lock so it stays alive.
|
||||||
|
let loader = loader.clone();
|
||||||
|
drop(locks);
|
||||||
|
return loader.get().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We use a lazy loader here to quickly make an insertion into the map without holding the
|
let loader = {
|
||||||
// lock for too long starving other threads
|
|
||||||
let loader = self.file_locks.read().await.get(path).cloned();
|
|
||||||
if let Some(serializer_loader) = loader {
|
|
||||||
serializer_loader.get().await
|
|
||||||
} else {
|
|
||||||
let mut locks = self.file_locks.write().await;
|
let mut locks = self.file_locks.write().await;
|
||||||
let loader = locks
|
locks
|
||||||
.entry(path.into())
|
.entry(path.into())
|
||||||
.or_insert_with(|| Arc::new(ChunkSerializerLazyLoader::new(path.into())))
|
.or_insert_with(|| Arc::new(ChunkSerializerLazyLoader::new(path.into())))
|
||||||
.clone();
|
.clone()
|
||||||
drop(locks);
|
// Write-lock dropped here — `loader.get()` may block on I/O and
|
||||||
loader.get().await
|
// must not hold the map lock.
|
||||||
|
};
|
||||||
|
|
||||||
|
loader.get().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to evict the cached serializer for `path`.
|
||||||
|
///
|
||||||
|
/// The entry is only removed when *both* conditions hold:
|
||||||
|
/// 1. No watcher still references the path.
|
||||||
|
/// 2. No other `Arc` clone is live (ensured via `can_remove`).
|
||||||
|
async fn maybe_evict(&self, path: &PathBuf) {
|
||||||
|
// Check watchers independently of file_locks to honour lock ordering.
|
||||||
|
let still_watched = {
|
||||||
|
let watchers = self.watchers.read().await;
|
||||||
|
watchers.get(path).is_some_and(|&c| c > 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if still_watched {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut locks = self.file_locks.write().await;
|
||||||
|
let removable = locks
|
||||||
|
.get(path)
|
||||||
|
.is_some_and(ChunkSerializerLazyLoader::can_remove);
|
||||||
|
|
||||||
|
if removable {
|
||||||
|
locks.remove(path);
|
||||||
|
trace!("Evicted serializer cache for {}", path.display());
|
||||||
|
} else {
|
||||||
|
trace!(
|
||||||
|
"Skipping eviction for {} — references still live",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,16 +201,15 @@ where
|
|||||||
folder: &'a LevelFolder,
|
folder: &'a LevelFolder,
|
||||||
chunks: &'a [Vector2<i32>],
|
chunks: &'a [Vector2<i32>],
|
||||||
) -> BoxFuture<'a, ()> {
|
) -> BoxFuture<'a, ()> {
|
||||||
let paths = chunks
|
|
||||||
.iter()
|
|
||||||
.map(|chunk| P::file_path(folder, &S::get_chunk_key(chunk)));
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
let paths: Vec<_> = chunks
|
||||||
|
.iter()
|
||||||
|
.map(|c| P::file_path(folder, &S::get_chunk_key(c)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut watchers = self.watchers.write().await;
|
let mut watchers = self.watchers.write().await;
|
||||||
for path in paths {
|
for path in paths {
|
||||||
watchers
|
*watchers.entry(path).or_insert(0) += 1;
|
||||||
.entry(path)
|
|
||||||
.and_modify(|count| *count += 1)
|
|
||||||
.or_insert(1);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -178,21 +219,19 @@ where
|
|||||||
folder: &'a LevelFolder,
|
folder: &'a LevelFolder,
|
||||||
chunks: &'a [Vector2<i32>],
|
chunks: &'a [Vector2<i32>],
|
||||||
) -> BoxFuture<'a, ()> {
|
) -> BoxFuture<'a, ()> {
|
||||||
let paths = chunks
|
|
||||||
.iter()
|
|
||||||
.map(|chunk| P::file_path(folder, &S::get_chunk_key(chunk)));
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
let paths: Vec<_> = chunks
|
||||||
|
.iter()
|
||||||
|
.map(|c| P::file_path(folder, &S::get_chunk_key(c)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut watchers = self.watchers.write().await;
|
let mut watchers = self.watchers.write().await;
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
if let std::collections::btree_map::Entry::Occupied(mut occupied) =
|
if let std::collections::btree_map::Entry::Occupied(mut e) = watchers.entry(path) {
|
||||||
watchers.entry(path)
|
let count = e.get_mut();
|
||||||
{
|
|
||||||
let count = occupied.get_mut();
|
|
||||||
*count = count.saturating_sub(1);
|
*count = count.saturating_sub(1);
|
||||||
|
|
||||||
if *count == 0 {
|
if *count == 0 {
|
||||||
occupied.remove();
|
e.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,64 +248,63 @@ where
|
|||||||
&'a self,
|
&'a self,
|
||||||
folder: &'a LevelFolder,
|
folder: &'a LevelFolder,
|
||||||
chunk_coords: &'a [Vector2<i32>],
|
chunk_coords: &'a [Vector2<i32>],
|
||||||
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
|
stream: mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
|
||||||
) -> BoxFuture<'a, ()> {
|
) -> BoxFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
// Group requested chunk coords by their region file.
|
||||||
let mut regions_chunks: BTreeMap<String, Vec<Vector2<i32>>> = BTreeMap::new();
|
let mut regions_chunks: BTreeMap<String, Vec<Vector2<i32>>> = BTreeMap::new();
|
||||||
|
|
||||||
for at in chunk_coords {
|
for at in chunk_coords {
|
||||||
let key = S::get_chunk_key(at);
|
|
||||||
|
|
||||||
regions_chunks
|
regions_chunks
|
||||||
.entry(key)
|
.entry(S::get_chunk_key(at))
|
||||||
.and_modify(|chunks| chunks.push(*at))
|
.or_default()
|
||||||
.or_insert(vec![*at]);
|
.push(*at);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we use a Sync Closure with an Async Block to execute the tasks concurrently
|
let region_tasks = regions_chunks.into_iter().map(|(file_name, chunks)| {
|
||||||
// Also improves File Cache utilizations.
|
|
||||||
let region_read_tasks = regions_chunks.into_iter().map(move |(file_name, chunks)| {
|
|
||||||
let task_stream = stream.clone();
|
let task_stream = stream.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let path = P::file_path(folder, &file_name);
|
let path = P::file_path(folder, &file_name);
|
||||||
|
|
||||||
let chunk_serializer = match self.get_serializer(&path).await {
|
let chunk_serializer = match self.get_serializer(&path).await {
|
||||||
Ok(chunk_serializer) => chunk_serializer,
|
Ok(s) => s,
|
||||||
Err(ChunkReadingError::ChunkNotExist) => {
|
Err(ChunkReadingError::ChunkNotExist) => {
|
||||||
unreachable!("Default Serializer must be created")
|
unreachable!("get_serializer always returns a default serializer")
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
// Best-effort: report the error for the first coord in the batch.
|
||||||
let _ = task_stream.send(LoadedData::Error((chunks[0], err))).await;
|
let _ = task_stream.send(LoadedData::Error((chunks[0], err))).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A bounded channel of 1 keeps backpressure between the
|
||||||
|
// serializer and the caller without unbounded buffering.
|
||||||
let (send, mut recv) =
|
let (send, mut recv) =
|
||||||
mpsc::channel::<LoadedData<S::Data, ChunkReadingError>>(1);
|
mpsc::channel::<LoadedData<S::Data, ChunkReadingError>>(1);
|
||||||
|
|
||||||
let intermediary = async move {
|
// Forward received chunks, wrapping them in `Arc`.
|
||||||
// Note: 'task_stream' is captured and moved here
|
// Captured move is intentional — `task_stream` is consumed here.
|
||||||
|
let forward = async move {
|
||||||
while let Some(data) = recv.recv().await {
|
while let Some(data) = recv.recv().await {
|
||||||
let wrapped_data = data.map_loaded(|data| Arc::new(data));
|
let wrapped = data.map_loaded(Arc::new);
|
||||||
if task_stream.send(wrapped_data).await.is_err() {
|
if task_stream.send(wrapped).await.is_err() {
|
||||||
// Stream is closed, so stop unneeded computation and io
|
// Receiver dropped; abort early to avoid wasted work.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// We need to hold the read lock only for the duration of get_chunks
|
// Hold the read lock only for the duration of `get_chunks`.
|
||||||
// This minimizes the time we block other operations
|
let read = async move {
|
||||||
let reader = async move {
|
|
||||||
let serializer = chunk_serializer.read().await;
|
let serializer = chunk_serializer.read().await;
|
||||||
serializer.get_chunks(chunks, send).await;
|
serializer.get_chunks(chunks, send).await;
|
||||||
};
|
};
|
||||||
|
|
||||||
join!(intermediary, reader);
|
join!(forward, read);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = join_all(region_read_tasks).await;
|
join_all(region_tasks).await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,133 +314,115 @@ where
|
|||||||
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
|
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
|
||||||
) -> BoxFuture<'a, Result<(), ChunkWritingError>> {
|
) -> BoxFuture<'a, Result<(), ChunkWritingError>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
// Group chunks by region file.
|
||||||
let mut regions_chunks: BTreeMap<String, Vec<Self::Data>> = BTreeMap::new();
|
let mut regions_chunks: BTreeMap<String, Vec<Self::Data>> = BTreeMap::new();
|
||||||
|
|
||||||
for (at, chunk) in chunks_data {
|
for (at, chunk) in chunks_data {
|
||||||
let key = S::get_chunk_key(&at);
|
regions_chunks
|
||||||
|
.entry(S::get_chunk_key(&at))
|
||||||
match regions_chunks.entry(key) {
|
.or_default()
|
||||||
std::collections::btree_map::Entry::Occupied(mut occupied) => {
|
.push(chunk);
|
||||||
occupied.get_mut().push(chunk);
|
|
||||||
}
|
|
||||||
std::collections::btree_map::Entry::Vacant(vacant) => {
|
|
||||||
vacant.insert(vec![chunk]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we use a Sync Closure with an Async Block to execute the tasks in parallel
|
|
||||||
// with out waiting the future. Also it improve we File Cache utilizations.
|
|
||||||
let tasks = regions_chunks
|
let tasks = regions_chunks
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// The closure now needs `move` to capture `self` and `folder` by value
|
.map(|(file_name, chunk_locks)| async move {
|
||||||
.map(move |(file_name, chunk_locks)| async move {
|
|
||||||
let path = P::file_path(folder, &file_name);
|
let path = P::file_path(folder, &file_name);
|
||||||
trace!("Updating data for file {}", path.display());
|
trace!("Saving chunks into {}", path.display());
|
||||||
|
|
||||||
let chunk_serializer = match self.get_serializer(&path).await {
|
let chunk_serializer = match self.get_serializer(&path).await {
|
||||||
Ok(file) => Ok(file),
|
Ok(s) => s,
|
||||||
Err(ChunkReadingError::ChunkNotExist) => {
|
Err(ChunkReadingError::ChunkNotExist) => {
|
||||||
unreachable!("Must be managed by the cache")
|
unreachable!("get_serializer always returns a default serializer")
|
||||||
}
|
}
|
||||||
Err(ChunkReadingError::IoError(err)) => {
|
Err(ChunkReadingError::IoError(err)) => {
|
||||||
error!("Error reading the data before write: {err}");
|
error!("I/O error reading region before write: {err}");
|
||||||
Err(ChunkWritingError::IoError(err))
|
return Err(ChunkWritingError::IoError(err));
|
||||||
}
|
}
|
||||||
Err(_) => Err(ChunkWritingError::IoError(std::io::ErrorKind::Other)),
|
Err(_) => {
|
||||||
}?;
|
return Err(ChunkWritingError::IoError(std::io::ErrorKind::Other));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Create a task for each chunk_lock
|
{
|
||||||
let update_tasks = chunk_locks.into_iter().map(|chunk_lock| {
|
let mut writer = chunk_serializer.write().await;
|
||||||
let chunk_serializer = chunk_serializer.clone();
|
for chunk in &chunk_locks {
|
||||||
async move {
|
// Atomically snapshot and clear the dirty flag before we
|
||||||
let chunk = chunk_lock;
|
// write so that any mutation that races in *during* this
|
||||||
let chunk_is_dirty = chunk.is_dirty();
|
// serialisation round will mark dirty again correctly.
|
||||||
// Edge case: this chunk is loaded while we were saving, mark it as cleaned since we are
|
let was_dirty = chunk.is_dirty();
|
||||||
// updating what we will write here
|
|
||||||
chunk.mark_dirty(false);
|
chunk.mark_dirty(false);
|
||||||
|
|
||||||
// We only need to update the chunk if it is dirty
|
if was_dirty {
|
||||||
if chunk_is_dirty {
|
writer.update_chunk(&**chunk, &self.chunk_config).await?;
|
||||||
let mut writer = chunk_serializer.write().await;
|
|
||||||
writer.update_chunk(&*chunk, &self.chunk_config).await?;
|
|
||||||
}
|
}
|
||||||
Ok::<(), ChunkWritingError>(())
|
|
||||||
}
|
}
|
||||||
});
|
// Write-lock released here — flush can proceed under a read-lock.
|
||||||
// Run all update tasks concurrently and propagate any error
|
}
|
||||||
futures::future::try_join_all(update_tasks).await?;
|
|
||||||
trace!("Updated data for file {}", path.display());
|
|
||||||
|
|
||||||
|
trace!("Chunk data updated for {}", path.display());
|
||||||
|
|
||||||
|
// We check watchers *after* releasing the write-lock to honour
|
||||||
|
// lock ordering (serializer lock → watchers, never the reverse).
|
||||||
let is_watched = {
|
let is_watched = {
|
||||||
let watchers = self.watchers.read().await;
|
let watchers = self.watchers.read().await;
|
||||||
watchers.get(&path).is_some_and(|count| count != &0)
|
watchers.get(&path).is_some_and(|&c| c > 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !is_watched {
|
if !is_watched {
|
||||||
// With the modification done, we can drop the write lock but keep the read lock
|
// A read-lock suffices for `write()` since we have already
|
||||||
// to avoid other threads to write/modify the data, but allow other threads to read it
|
// applied all mutations above.
|
||||||
let serializer = chunk_serializer.read().await;
|
{
|
||||||
|
let serializer = chunk_serializer.read().await;
|
||||||
|
debug!("Flushing {} to disk", path.display());
|
||||||
|
serializer
|
||||||
|
.write(&path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ChunkWritingError::IoError(e.kind()))?;
|
||||||
|
// Read-lock released here.
|
||||||
|
}
|
||||||
|
|
||||||
debug!("Writing file for {}", path.display());
|
// Drop our handle so `can_remove` may succeed.
|
||||||
serializer
|
|
||||||
.write(&path)
|
|
||||||
.await
|
|
||||||
.map_err(|err| ChunkWritingError::IoError(err.kind()))?;
|
|
||||||
|
|
||||||
// Remove lock
|
|
||||||
drop(serializer);
|
|
||||||
// Decrement strong count
|
|
||||||
drop(chunk_serializer);
|
drop(chunk_serializer);
|
||||||
|
|
||||||
let should_remove = {
|
// Evict the cache entry when no longer needed.
|
||||||
let watchers = self.watchers.read().await;
|
self.maybe_evict(&path).await;
|
||||||
watchers.get(&path).is_none_or(|count| count == &0)
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_remove {
|
|
||||||
let mut locks = self.file_locks.write().await;
|
|
||||||
|
|
||||||
let can_remove = if let Some(loader) = locks.get(&path) {
|
|
||||||
ChunkSerializerLazyLoader::can_remove(loader)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
if can_remove {
|
|
||||||
locks.remove(&path);
|
|
||||||
trace!("Removed lockfile cache {}", path.display());
|
|
||||||
} else {
|
|
||||||
trace!("Wanted to remove lockfile cache {} but someone still holds a reference to it!", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: we need to handle the errors and return the result
|
// Collect all region results; surface the first error encountered.
|
||||||
// files to save
|
let results: Vec<Result<(), ChunkWritingError>> = join_all(tasks).await;
|
||||||
let _test: Vec<Result<(), ChunkWritingError>> = join_all(tasks).await;
|
results.into_iter().find(Result::is_err).unwrap_or(Ok(()))
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocks until all in-flight serialiser operations have completed by
|
||||||
|
/// acquiring (and immediately releasing) a write-lock on every cached
|
||||||
|
/// serialiser.
|
||||||
|
///
|
||||||
|
/// This is a linearisation point: after this future resolves no mutation
|
||||||
|
/// started before the call is still running.
|
||||||
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
|
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
//we need to block any other operation
|
// Snapshot the current set of loaders under a read-lock so we do
|
||||||
let serializer_cache = self.file_locks.read().await;
|
// not block new insertions longer than necessary.
|
||||||
|
let loaders: Vec<Arc<ChunkSerializerLazyLoader<S>>> =
|
||||||
|
{ self.file_locks.read().await.values().cloned().collect() };
|
||||||
|
|
||||||
// Acquire a write lock on all entries to verify they are complete
|
// For each loader that has been initialised, acquire a write-lock
|
||||||
let _tasks = serializer_cache
|
// and release it immediately. This guarantees that any concurrent
|
||||||
.values()
|
// read or write operation that was in progress has finished.
|
||||||
.map(ChunkSerializerLazyLoader::can_remove);
|
let drain_tasks = loaders.into_iter().map(|loader| async move {
|
||||||
|
if let Some(serializer_arc) = loader.internal.get() {
|
||||||
|
// Acquiring + immediately dropping the write-lock acts as a
|
||||||
|
// barrier: it can only succeed once all current lock holders
|
||||||
|
// have released their guards.
|
||||||
|
let _guard = serializer_arc.write().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// We need to wait to ensure that all the locks are acquired
|
join_all(drain_tasks).await;
|
||||||
// so there is no **operation** ongoing
|
|
||||||
// TODO
|
|
||||||
// let _ = join_all(tasks);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockState,
|
Block, BlockState,
|
||||||
block_properties::{BambooLeaves, BambooLikeProperties, BlockProperties, Integer0To1},
|
block_properties::{BambooLeaves, BambooLikeProperties, BlockProperties},
|
||||||
tag,
|
tag,
|
||||||
};
|
};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
@ -60,13 +60,13 @@ impl BambooFeature {
|
|||||||
if bpos.0.y - pos.0.y >= 3 {
|
if bpos.0.y - pos.0.y >= 3 {
|
||||||
let mut props = BambooLikeProperties::default(&Block::BAMBOO);
|
let mut props = BambooLikeProperties::default(&Block::BAMBOO);
|
||||||
props.leaves = BambooLeaves::Large;
|
props.leaves = BambooLeaves::Large;
|
||||||
props.stage = Integer0To1::L1;
|
props.stage = 1;
|
||||||
|
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&bpos.0,
|
&bpos.0,
|
||||||
BlockState::from_id(props.to_state_id(&Block::BAMBOO)),
|
BlockState::from_id(props.to_state_id(&Block::BAMBOO)),
|
||||||
);
|
);
|
||||||
props.stage = Integer0To1::L0;
|
props.stage = 0;
|
||||||
|
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&bpos.down().0,
|
&bpos.down().0,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection,
|
Block, BlockDirection,
|
||||||
block_properties::{
|
block_properties::{
|
||||||
BlockProperties, BrownMushroomBlockLikeProperties, ChorusFlowerLikeProperties, Integer0To5,
|
BlockProperties, BrownMushroomBlockLikeProperties, ChorusFlowerLikeProperties,
|
||||||
},
|
},
|
||||||
tag,
|
tag,
|
||||||
};
|
};
|
||||||
@ -100,9 +100,7 @@ fn set_chorus_plant<T: GenerationCache>(chunk: &mut T, pos: &BlockPos) {
|
|||||||
|
|
||||||
/// Places a dead chorus flower (age 5) at `pos`.
|
/// Places a dead chorus flower (age 5) at `pos`.
|
||||||
fn place_dead_flower<T: GenerationCache>(chunk: &mut T, pos: &BlockPos) {
|
fn place_dead_flower<T: GenerationCache>(chunk: &mut T, pos: &BlockPos) {
|
||||||
let props = ChorusFlowerLikeProperties {
|
let props = ChorusFlowerLikeProperties { age: 5 };
|
||||||
age: Integer0To5::L5,
|
|
||||||
};
|
|
||||||
let state_id = props.to_state_id(&Block::CHORUS_FLOWER);
|
let state_id = props.to_state_id(&Block::CHORUS_FLOWER);
|
||||||
chunk.set_block_state(&pos.0, pumpkin_data::BlockState::from_id(state_id));
|
chunk.set_block_state(&pos.0, pumpkin_data::BlockState::from_id(state_id));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection, BlockState,
|
Block, BlockDirection, BlockState,
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer1To4, SeaPickleLikeProperties},
|
block_properties::{BlockProperties, EnumVariants, SeaPickleLikeProperties},
|
||||||
tag,
|
tag,
|
||||||
};
|
};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
@ -38,7 +38,7 @@ impl CoralFeature {
|
|||||||
);
|
);
|
||||||
} else if random.next_f32() < 0.05 {
|
} else if random.next_f32() < 0.05 {
|
||||||
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
||||||
props.pickles = Integer1To4::from_index(random.next_bounded_i32(4) as u16); // TODO: vanilla adds + 1, but this can crash
|
props.pickles = (random.next_bounded_i32(4) as u8) + 1;
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&pos.0,
|
&pos.0,
|
||||||
BlockState::from_id(props.to_state_id(&Block::SEA_PICKLE)),
|
BlockState::from_id(props.to_state_id(&Block::SEA_PICKLE)),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockState,
|
Block, BlockState,
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer1To4, SeaPickleLikeProperties},
|
block_properties::{BlockProperties, SeaPickleLikeProperties},
|
||||||
};
|
};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{int_provider::IntProvider, position::BlockPos},
|
math::{int_provider::IntProvider, position::BlockPos},
|
||||||
@ -32,7 +32,7 @@ impl SeaPickleFeature {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
||||||
props.pickles = Integer1To4::from_index(random.next_bounded_i32(4) as u16); // TODO: vanilla adds + 1, but this can crash
|
props.pickles = (random.next_bounded_i32(4) as u8) + 1;
|
||||||
let pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
let pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&pos.0,
|
&pos.0,
|
||||||
|
|||||||
@ -14,7 +14,6 @@ pub mod generation;
|
|||||||
pub mod inventory;
|
pub mod inventory;
|
||||||
pub mod level;
|
pub mod level;
|
||||||
pub mod lighting;
|
pub mod lighting;
|
||||||
pub mod lock;
|
|
||||||
pub mod poi;
|
pub mod poi;
|
||||||
pub mod tick;
|
pub mod tick;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
use super::{LevelLocker, LockError};
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
fs::{File, TryLockError},
|
|
||||||
io::Write,
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct AnvilLevelLocker {
|
|
||||||
_locked_file: File,
|
|
||||||
}
|
|
||||||
|
|
||||||
const SESSION_LOCK_FILE_NAME: &str = "session.lock";
|
|
||||||
|
|
||||||
const SNOWMAN: &[u8] = "☃".as_bytes();
|
|
||||||
|
|
||||||
impl LevelLocker<Self> for AnvilLevelLocker {
|
|
||||||
fn lock(folder: &Path) -> Result<Self, LockError> {
|
|
||||||
std::fs::create_dir_all(folder).map_err(|_| LockError::FailedWrite)?;
|
|
||||||
let file_path = folder.join(SESSION_LOCK_FILE_NAME);
|
|
||||||
let mut file = File::options()
|
|
||||||
.create(true)
|
|
||||||
.truncate(false)
|
|
||||||
.write(true)
|
|
||||||
.open(file_path)
|
|
||||||
.unwrap();
|
|
||||||
// im not joking, mojang writes a snowman into the lock file
|
|
||||||
file.write_all(SNOWMAN)
|
|
||||||
.map_err(|_| LockError::FailedWrite)?;
|
|
||||||
file.try_lock().map_err(|e| match e {
|
|
||||||
TryLockError::WouldBlock => {
|
|
||||||
LockError::AlreadyLocked(SESSION_LOCK_FILE_NAME.to_string())
|
|
||||||
}
|
|
||||||
TryLockError::Error(io_err) => LockError::Error(io_err),
|
|
||||||
})?;
|
|
||||||
Ok(Self { _locked_file: file })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
pub mod anvil;
|
|
||||||
|
|
||||||
// Gets unlocked when dropped
|
|
||||||
pub trait LevelLocker<T>: Send + Sync {
|
|
||||||
fn lock(folder: &Path) -> Result<T, LockError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum LockError {
|
|
||||||
#[error("Oh no, Level is already locked by {0}")]
|
|
||||||
AlreadyLocked(String),
|
|
||||||
#[error("{0}")]
|
|
||||||
Error(std::io::Error),
|
|
||||||
#[error("Failed to write into lock file")]
|
|
||||||
FailedWrite,
|
|
||||||
}
|
|
||||||
@ -12,7 +12,7 @@ use crate::{
|
|||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, CakeLikeProperties, EnumVariants, Integer0To6},
|
block_properties::{BlockProperties, CakeLikeProperties},
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -52,9 +52,9 @@ impl CakeBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut properties = CakeLikeProperties::from_state_id(state_id, block);
|
let mut properties = CakeLikeProperties::from_state_id(state_id, block);
|
||||||
match properties.bites.to_index() {
|
match properties.bites {
|
||||||
0..=5 => {
|
0..=5 => {
|
||||||
properties.bites = Integer0To6::from_index(properties.bites.to_index() + 1);
|
properties.bites += 1;
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
location,
|
location,
|
||||||
@ -107,7 +107,7 @@ impl BlockBehaviour for CakeBlock {
|
|||||||
drop(item_lock);
|
drop(item_lock);
|
||||||
match item.id {
|
match item.id {
|
||||||
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => {
|
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => {
|
||||||
if properties.bites.to_index() != 0 {
|
if properties.bites != 0 {
|
||||||
return Self::consume_if_hungry(
|
return Self::consume_if_hungry(
|
||||||
args.world,
|
args.world,
|
||||||
args.player,
|
args.player,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
BlockDirection,
|
BlockDirection,
|
||||||
block_properties::{BlockProperties, CandleLikeProperties, EnumVariants, Integer1To4},
|
block_properties::{BlockProperties, CandleLikeProperties},
|
||||||
entity::EntityPose,
|
entity::EntityPose,
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
@ -33,8 +33,8 @@ impl BlockBehaviour for CandleBlock {
|
|||||||
&& let BlockIsReplacing::Itself(state_id) = args.replacing
|
&& let BlockIsReplacing::Itself(state_id) = args.replacing
|
||||||
{
|
{
|
||||||
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
|
let mut properties = CandleLikeProperties::from_state_id(state_id, args.block);
|
||||||
if properties.candles.to_index() < 3 {
|
if properties.candles < 4 {
|
||||||
properties.candles = Integer1To4::from_index(properties.candles.to_index() + 1);
|
properties.candles += 1;
|
||||||
}
|
}
|
||||||
return properties.to_state_id(args.block);
|
return properties.to_state_id(args.block);
|
||||||
}
|
}
|
||||||
@ -63,9 +63,8 @@ impl BlockBehaviour for CandleBlock {
|
|||||||
{
|
{
|
||||||
let was_lit = properties.lit;
|
let was_lit = properties.lit;
|
||||||
|
|
||||||
if properties.candles.to_index() < 3 {
|
if properties.candles < 4 {
|
||||||
properties.candles =
|
properties.candles += 1;
|
||||||
Integer1To4::from_index(properties.candles.to_index() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
properties.lit = was_lit;
|
properties.lit = was_lit;
|
||||||
@ -130,8 +129,7 @@ impl BlockBehaviour for CandleBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let b = args.world.get_block(args.position).await;
|
let b = args.world.get_block(args.position).await;
|
||||||
args.player.get_entity().pose.load() != EntityPose::Crouching
|
args.player.get_entity().pose.load() != EntityPose::Crouching
|
||||||
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles
|
&& CandleLikeProperties::from_state_id(args.state_id, args.block).candles != 4
|
||||||
!= Integer1To4::L4
|
|
||||||
&& args.block.id == b.id
|
&& args.block.id == b.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, ComposterLikeProperties, EnumVariants, Integer0To8},
|
block_properties::{BlockProperties, ComposterLikeProperties},
|
||||||
composter_increase_chance::get_composter_increase_chance_from_item_id,
|
composter_increase_chance::get_composter_increase_chance_from_item_id,
|
||||||
entity::EntityType,
|
entity::EntityType,
|
||||||
item::Item,
|
item::Item,
|
||||||
@ -31,7 +31,7 @@ impl BlockBehaviour for ComposterBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
||||||
if props.get_level() == 8 {
|
if props.level == 8 {
|
||||||
self.clear_composter(args.world, args.position, state_id, args.block)
|
self.clear_composter(args.world, args.position, state_id, args.block)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ impl BlockBehaviour for ComposterBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
||||||
let level = props.get_level();
|
let level = props.level;
|
||||||
|
|
||||||
// Check if the composter is full
|
// Check if the composter is full
|
||||||
if level == 8 {
|
if level == 8 {
|
||||||
@ -93,7 +93,7 @@ impl BlockBehaviour for ComposterBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
let props = ComposterLikeProperties::from_state_id(state_id, args.block);
|
||||||
let level = props.get_level();
|
let level = props.level;
|
||||||
if level == 7 {
|
if level == 7 {
|
||||||
self.update_level_composter(
|
self.update_level_composter(
|
||||||
args.world,
|
args.world,
|
||||||
@ -113,7 +113,7 @@ impl BlockBehaviour for ComposterBlock {
|
|||||||
) -> BlockFuture<'a, Option<u8>> {
|
) -> BlockFuture<'a, Option<u8>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block);
|
let props = ComposterLikeProperties::from_state_id(args.state.id, args.block);
|
||||||
Some(props.get_level())
|
Some(props.level)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ impl ComposterBlock {
|
|||||||
level: u8,
|
level: u8,
|
||||||
) {
|
) {
|
||||||
let mut props = ComposterLikeProperties::from_state_id(state_id, block);
|
let mut props = ComposterLikeProperties::from_state_id(state_id, block);
|
||||||
props.set_level(level);
|
props.level = level;
|
||||||
world
|
world
|
||||||
.set_block_state(location, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
.set_block_state(location, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
||||||
.await;
|
.await;
|
||||||
@ -167,17 +167,3 @@ impl ComposterBlock {
|
|||||||
world.spawn_entity(Arc::new(item_entity)).await;
|
world.spawn_entity(Arc::new(item_entity)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ComposterPropertiesEx {
|
|
||||||
fn get_level(&self) -> u8;
|
|
||||||
fn set_level(&mut self, level: u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComposterPropertiesEx for ComposterLikeProperties {
|
|
||||||
fn get_level(&self) -> u8 {
|
|
||||||
self.level.to_index() as u8
|
|
||||||
}
|
|
||||||
fn set_level(&mut self, level: u8) {
|
|
||||||
self.level = Integer0To8::from_index(u16::from(level));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -11,9 +11,7 @@ use crate::world::World;
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::block_properties::EnumVariants;
|
|
||||||
use pumpkin_data::block_properties::FarmlandLikeProperties;
|
use pumpkin_data::block_properties::FarmlandLikeProperties;
|
||||||
use pumpkin_data::block_properties::Integer0To7;
|
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -77,7 +75,7 @@ impl BlockBehaviour for FarmlandBlock {
|
|||||||
// TODO: add rain check. Remember to check which one is most optimized.
|
// TODO: add rain check. Remember to check which one is most optimized.
|
||||||
if is_water_nearby(args.world, args.position).await {
|
if is_water_nearby(args.world, args.position).await {
|
||||||
let mut props = FarmlandProperties::default(args.block);
|
let mut props = FarmlandProperties::default(args.block);
|
||||||
props.moisture = Integer0To7::L7;
|
props.moisture = 7;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -88,7 +86,7 @@ impl BlockBehaviour for FarmlandBlock {
|
|||||||
} else {
|
} else {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let mut props = FarmlandProperties::from_state_id(state_id, args.block);
|
let mut props = FarmlandProperties::from_state_id(state_id, args.block);
|
||||||
if props.moisture == Integer0To7::L0 {
|
if props.moisture == 0 {
|
||||||
if !args
|
if !args
|
||||||
.world
|
.world
|
||||||
.get_block(&args.position.up())
|
.get_block(&args.position.up())
|
||||||
@ -105,7 +103,7 @@ impl BlockBehaviour for FarmlandBlock {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
props.moisture = Integer0To7::from_index(props.moisture.to_index() - 1);
|
props.moisture -= 1;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use pumpkin_data::biome::Biome;
|
use pumpkin_data::biome::Biome;
|
||||||
use pumpkin_data::block_properties::{BlockProperties, EnumVariants, HorizontalAxis};
|
use pumpkin_data::block_properties::{BlockProperties, HorizontalAxis};
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
use pumpkin_data::fluid::Fluid;
|
use pumpkin_data::fluid::Fluid;
|
||||||
use pumpkin_data::tag::{self, Taggable};
|
use pumpkin_data::tag::{self, Taggable};
|
||||||
@ -152,7 +152,7 @@ impl FireBlock {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_spreading_fire(&self, world: &Arc<World>, pos: &BlockPos, chance: i32, age: u16) {
|
async fn try_spreading_fire(&self, world: &Arc<World>, pos: &BlockPos, chance: i32, age: u8) {
|
||||||
let block = world.get_block(pos).await;
|
let block = world.get_block(pos).await;
|
||||||
let odds = Self::get_burn_odds(block);
|
let odds = Self::get_burn_odds(block);
|
||||||
if rand::rng().random_range(0..chance) < odds {
|
if rand::rng().random_range(0..chance) < odds {
|
||||||
@ -160,12 +160,12 @@ impl FireBlock {
|
|||||||
if rand::rng().random_range(0..(age + 10) as i32) < 5
|
if rand::rng().random_range(0..(age + 10) as i32) < 5
|
||||||
&& !Self::is_near_rain(world.as_ref(), pos)
|
&& !Self::is_near_rain(world.as_ref(), pos)
|
||||||
{
|
{
|
||||||
let new_age = (age + (rand::rng().random_range(0..5) / 4)).min(15);
|
let new_age = (age + (rand::rng().random_range(0..5) / 4)).min(15) as u8;
|
||||||
let state_id = self
|
let state_id = self
|
||||||
.get_state_for_position(world.as_ref(), &Block::FIRE, pos)
|
.get_state_for_position(world.as_ref(), &Block::FIRE, pos)
|
||||||
.await;
|
.await;
|
||||||
let mut fire_props = FireProperties::from_state_id(state_id, &Block::FIRE);
|
let mut fire_props = FireProperties::from_state_id(state_id, &Block::FIRE);
|
||||||
fire_props.age = EnumVariants::from_index(new_age);
|
fire_props.age = new_age;
|
||||||
let new_state_id = fire_props.to_state_id(&Block::FIRE);
|
let new_state_id = fire_props.to_state_id(&Block::FIRE);
|
||||||
world
|
world
|
||||||
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
|
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
|
||||||
@ -244,7 +244,7 @@ impl BlockBehaviour for FireBlock {
|
|||||||
.get_state_for_position(args.world, &Block::FIRE, args.position)
|
.get_state_for_position(args.world, &Block::FIRE, args.position)
|
||||||
.await;
|
.await;
|
||||||
let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
let mut fire_props = FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
||||||
fire_props.age = EnumVariants::from_index(old_fire_props.age.to_index());
|
fire_props.age = old_fire_props.age;
|
||||||
return fire_props.to_state_id(&Block::FIRE);
|
return fire_props.to_state_id(&Block::FIRE);
|
||||||
}
|
}
|
||||||
Block::AIR.default_state.id
|
Block::AIR.default_state.id
|
||||||
@ -323,7 +323,7 @@ impl BlockBehaviour for FireBlock {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE);
|
let mut fire_props = FireProperties::from_state_id(block_state.id, &Block::FIRE);
|
||||||
let age = fire_props.age.to_index();
|
let age = fire_props.age;
|
||||||
|
|
||||||
// Check if rain should extinguish the fire
|
// Check if rain should extinguish the fire
|
||||||
if !infiniburn && Self::is_near_rain(world.as_ref(), pos) {
|
if !infiniburn && Self::is_near_rain(world.as_ref(), pos) {
|
||||||
@ -341,10 +341,10 @@ impl BlockBehaviour for FireBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increment age
|
// Increment age
|
||||||
let random = rand::rng().random_range(0..3) / 2;
|
let random = (rand::rng().random_range(0..3) / 2) as u8;
|
||||||
let new_age = (age + random).min(15);
|
let new_age = (age + random).min(15);
|
||||||
if new_age != age {
|
if new_age != age {
|
||||||
fire_props.age = EnumVariants::from_index(new_age);
|
fire_props.age = new_age;
|
||||||
let new_state_id = fire_props.to_state_id(&Block::FIRE);
|
let new_state_id = fire_props.to_state_id(&Block::FIRE);
|
||||||
world
|
world
|
||||||
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
|
.set_block_state(pos, new_state_id, BlockFlags::NOTIFY_NEIGHBORS)
|
||||||
@ -481,14 +481,15 @@ impl BlockBehaviour for FireBlock {
|
|||||||
&& rand::rng().random_range(0..rate) <= odds
|
&& rand::rng().random_range(0..rate) <= odds
|
||||||
&& !Self::is_near_rain(world.as_ref(), &offset_pos)
|
&& !Self::is_near_rain(world.as_ref(), &offset_pos)
|
||||||
{
|
{
|
||||||
let spread_age =
|
let spread_age = (new_age + rand::rng().random_range(0..5) / 4)
|
||||||
(new_age + rand::rng().random_range(0..5) / 4).min(15);
|
.min(15)
|
||||||
|
as u8;
|
||||||
let fire_state_id = self
|
let fire_state_id = self
|
||||||
.get_state_for_position(world.as_ref(), block, &offset_pos)
|
.get_state_for_position(world.as_ref(), block, &offset_pos)
|
||||||
.await;
|
.await;
|
||||||
let mut new_fire_props =
|
let mut new_fire_props =
|
||||||
FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
FireProperties::from_state_id(fire_state_id, &Block::FIRE);
|
||||||
new_fire_props.age = EnumVariants::from_index(spread_age);
|
new_fire_props.age = spread_age;
|
||||||
|
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use pumpkin_data::block_properties::{Axis, NoteblockInstrument};
|
|||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer0To24, NoteBlockLikeProperties},
|
block_properties::{BlockProperties, NoteBlockLikeProperties},
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -85,13 +85,7 @@ impl BlockBehaviour for NoteBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let block_state = args.world.get_block_state(args.position).await;
|
let block_state = args.world.get_block_state(args.position).await;
|
||||||
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id, args.block);
|
||||||
let next_index = note_props.note.to_index() + 1;
|
note_props.note = (note_props.note + 1) % 25;
|
||||||
// Increment and check if max
|
|
||||||
note_props.note = if next_index >= Integer0To24::variant_count() {
|
|
||||||
Integer0To24::from_index(0)
|
|
||||||
} else {
|
|
||||||
Integer0To24::from_index(next_index)
|
|
||||||
};
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -125,7 +119,7 @@ impl BlockBehaviour for NoteBlock {
|
|||||||
let instrument = note_props.instrument;
|
let instrument = note_props.instrument;
|
||||||
let pitch = if is_base_block(instrument) {
|
let pitch = if is_base_block(instrument) {
|
||||||
// checks if can be pitched
|
// checks if can be pitched
|
||||||
Self::get_note_pitch(note_props.note.to_index())
|
Self::get_note_pitch(u16::from(note_props.note))
|
||||||
} else {
|
} else {
|
||||||
1.0 // default pitch
|
1.0 // default pitch
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{BambooLeaves, BambooLikeProperties, BlockProperties};
|
||||||
BambooLeaves, BambooLikeProperties, BlockProperties, EnumVariants, Integer0To1,
|
|
||||||
};
|
|
||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_data::tag::Block::MINECRAFT_SUPPORTS_BAMBOO;
|
use pumpkin_data::tag::Block::MINECRAFT_SUPPORTS_BAMBOO;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
@ -44,8 +42,8 @@ impl BlockBehaviour for BambooBlock {
|
|||||||
} else if block_below == &Block::BAMBOO {
|
} else if block_below == &Block::BAMBOO {
|
||||||
let props_below =
|
let props_below =
|
||||||
BambooLikeProperties::from_state_id(state_id_below, block_below);
|
BambooLikeProperties::from_state_id(state_id_below, block_below);
|
||||||
if props_below.age.to_index() > 0 {
|
if props_below.age > 0 {
|
||||||
props.age = Integer0To1::L1;
|
props.age = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let (block_above, state_id_above) =
|
let (block_above, state_id_above) =
|
||||||
@ -119,10 +117,10 @@ impl BlockBehaviour for BambooBlock {
|
|||||||
let neighbor_props =
|
let neighbor_props =
|
||||||
BambooLikeProperties::from_state_id(args.neighbor_state_id, neighbor_block);
|
BambooLikeProperties::from_state_id(args.neighbor_state_id, neighbor_block);
|
||||||
let mut props = BambooLikeProperties::from_state_id(args.state_id, args.block);
|
let mut props = BambooLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
if neighbor_props.age.to_index() > props.age.to_index() {
|
if neighbor_props.age > props.age {
|
||||||
props.age = match props.age {
|
props.age = match props.age {
|
||||||
Integer0To1::L0 => Integer0To1::L1,
|
0 => 1,
|
||||||
Integer0To1::L1 => Integer0To1::L0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
return props.to_state_id(args.block);
|
return props.to_state_id(args.block);
|
||||||
}
|
}
|
||||||
@ -153,7 +151,7 @@ async fn update_leaves_and_grow(world: Arc<World>, position: &BlockPos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut props = BambooLikeProperties::from_state_id(state_id, block);
|
let mut props = BambooLikeProperties::from_state_id(state_id, block);
|
||||||
if props.stage != Integer0To1::L0 {
|
if props.stage != 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,18 +198,11 @@ async fn update_leaves_and_grow(world: Arc<World>, position: &BlockPos) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
props.age = if props.age != Integer0To1::L1 && block_two_below == &Block::BAMBOO {
|
props.age = u8::from(!(props.age != 1 && block_two_below == &Block::BAMBOO));
|
||||||
Integer0To1::L0
|
|
||||||
} else {
|
|
||||||
Integer0To1::L1
|
|
||||||
};
|
|
||||||
|
|
||||||
props.stage =
|
props.stage = u8::from(
|
||||||
if (bamboo_count < 11 || rand::rng().random::<f32>() >= 0.25) && bamboo_count != 15 {
|
!((bamboo_count < 11 || rand::rng().random::<f32>() >= 0.25) && bamboo_count != 15),
|
||||||
Integer0To1::L0
|
);
|
||||||
} else {
|
|
||||||
Integer0To1::L1
|
|
||||||
};
|
|
||||||
|
|
||||||
world
|
world
|
||||||
.set_block_state(&above_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
.set_block_state(&above_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
||||||
@ -265,7 +256,7 @@ async fn bone_meal(world: Arc<World>, position: &BlockPos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let next_props = BambooLikeProperties::from_state_id(next_state.id, &Block::BAMBOO);
|
let next_props = BambooLikeProperties::from_state_id(next_state.id, &Block::BAMBOO);
|
||||||
if next_props.stage == Integer0To1::L1 {
|
if next_props.stage == 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{BlockProperties, CactusLikeProperties};
|
||||||
BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15,
|
|
||||||
};
|
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::{Block, BlockDirection, tag};
|
use pumpkin_data::{Block, BlockDirection, tag};
|
||||||
@ -40,12 +38,12 @@ impl BlockBehaviour for CactusBlock {
|
|||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
while args.world.get_block(&args.position.down_height(i)).await == &Block::CACTUS {
|
while args.world.get_block(&args.position.down_height(i)).await == &Block::CACTUS {
|
||||||
i += 1;
|
i += 1;
|
||||||
if 1 == 3 && age == Integer0To15::L15 {
|
if 1 == 3 && age == 15 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if age == Integer0To15::L8 && can_place_at(args.world.as_ref(), &block_up).await {
|
if age == 8 && can_place_at(args.world.as_ref(), &block_up).await {
|
||||||
let d = if i >= 3 { 0.25 } else { 0.1 };
|
let d = if i >= 3 { 0.25 } else { 0.1 };
|
||||||
if rand::rng().random_range(0.0..1.0) <= d {
|
if rand::rng().random_range(0.0..1.0) <= d {
|
||||||
args.world
|
args.world
|
||||||
@ -56,7 +54,7 @@ impl BlockBehaviour for CactusBlock {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
} else if age == Integer0To15::L15 && i < 3 {
|
} else if age == 15 && i < 3 {
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&block_up,
|
&block_up,
|
||||||
@ -65,7 +63,7 @@ impl BlockBehaviour for CactusBlock {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let mut new_props = CactusLikeProperties::default(&Block::CACTUS);
|
let mut new_props = CactusLikeProperties::default(&Block::CACTUS);
|
||||||
new_props.age = Integer0To15::L0;
|
new_props.age = 0;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -77,8 +75,8 @@ impl BlockBehaviour for CactusBlock {
|
|||||||
.update_neighbor(args.position, &Block::CACTUS)
|
.update_neighbor(args.position, &Block::CACTUS)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
if age.to_index() < 15 {
|
if age < 15 {
|
||||||
props.age = Integer0To15::from_index(age.to_index() + 1);
|
props.age = age + 1;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{BlockProperties, NetherWartLikeProperties};
|
||||||
BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties,
|
|
||||||
};
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
@ -62,12 +60,12 @@ impl CropBlockBase for BeetrootBlock {
|
|||||||
|
|
||||||
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
||||||
let props = BeetrootProperties::from_state_id(state, block);
|
let props = BeetrootProperties::from_state_id(state, block);
|
||||||
i32::from(props.age.to_index())
|
i32::from(props.age)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
||||||
let mut props = BeetrootProperties::from_state_id(state, block);
|
let mut props = BeetrootProperties::from_state_id(state, block);
|
||||||
props.age = Integer0To3::from_index(age as u16);
|
props.age = age as u8;
|
||||||
props.to_state_id(block)
|
props.to_state_id(block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{
|
block_properties::{BlockProperties, WallTorchLikeProperties, WheatLikeProperties},
|
||||||
BlockProperties, EnumVariants, Integer0To7, WallTorchLikeProperties, WheatLikeProperties,
|
|
||||||
},
|
|
||||||
tag::{self, Taggable},
|
tag::{self, Taggable},
|
||||||
};
|
};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -63,7 +61,7 @@ impl BlockBehaviour for AttachedStemBlock {
|
|||||||
&& args.neighbor_state_id != Self::get_gourd(args.block).default_state.id
|
&& args.neighbor_state_id != Self::get_gourd(args.block).default_state.id
|
||||||
{
|
{
|
||||||
let mut props = StemProperties::default(Self::get_stem(args.block));
|
let mut props = StemProperties::default(Self::get_stem(args.block));
|
||||||
props.age = Integer0To7::from_index(7);
|
props.age = 7;
|
||||||
return props.to_state_id(Self::get_stem(args.block));
|
return props.to_state_id(Self::get_stem(args.block));
|
||||||
}
|
}
|
||||||
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
<Self as PlantBlockBase>::get_state_for_neighbor_update(
|
||||||
|
|||||||
@ -5,9 +5,7 @@ use crate::block::{
|
|||||||
};
|
};
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection,
|
Block, BlockDirection,
|
||||||
block_properties::{
|
block_properties::{BlockProperties, WallTorchLikeProperties, WheatLikeProperties},
|
||||||
BlockProperties, EnumVariants, Integer0To7, WallTorchLikeProperties, WheatLikeProperties,
|
|
||||||
},
|
|
||||||
tag,
|
tag,
|
||||||
tag::Taggable,
|
tag::Taggable,
|
||||||
};
|
};
|
||||||
@ -35,7 +33,7 @@ impl BlockMetadata for StemBlock {
|
|||||||
impl StemBlock {
|
impl StemBlock {
|
||||||
fn state_with_age(block: &Block, state: u16, age: i32) -> BlockStateId {
|
fn state_with_age(block: &Block, state: u16, age: i32) -> BlockStateId {
|
||||||
let mut props = StemProperties::from_state_id(state, block);
|
let mut props = StemProperties::from_state_id(state, block);
|
||||||
props.age = Integer0To7::from_index(age as u16);
|
props.age = age as u8;
|
||||||
props.to_state_id(block)
|
props.to_state_id(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +86,7 @@ impl BlockBehaviour for StemBlock {
|
|||||||
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
|
if rand::rng().random_range(0..=(25.0 / f).floor() as i32) == 0 {
|
||||||
let (block, state) = args.world.get_block_and_state_id(args.position).await;
|
let (block, state) = args.world.get_block_and_state_id(args.position).await;
|
||||||
let props = StemProperties::from_state_id(state, block);
|
let props = StemProperties::from_state_id(state, block);
|
||||||
let age = i32::from(props.age.to_index());
|
let age = i32::from(props.age);
|
||||||
if age < 7 {
|
if age < 7 {
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
|
|||||||
@ -3,9 +3,7 @@ use std::sync::Arc;
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
BlockDirection::{East, North, South, West},
|
BlockDirection::{East, North, South, West},
|
||||||
block_properties::{
|
block_properties::{BlockProperties, FarmlandLikeProperties, WheatLikeProperties},
|
||||||
BlockProperties, EnumVariants, FarmlandLikeProperties, Integer0To7, WheatLikeProperties,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||||
use pumpkin_world::{
|
use pumpkin_world::{
|
||||||
@ -43,12 +41,12 @@ trait CropBlockBase: PlantBlockBase {
|
|||||||
|
|
||||||
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
||||||
let props = CropProperties::from_state_id(state, block);
|
let props = CropProperties::from_state_id(state, block);
|
||||||
i32::from(props.age.to_index())
|
i32::from(props.age)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
||||||
let mut props = CropProperties::from_state_id(state, block);
|
let mut props = CropProperties::from_state_id(state, block);
|
||||||
props.age = Integer0To7::from_index(age as u16);
|
props.age = age as u8;
|
||||||
props.to_state_id(block)
|
props.to_state_id(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +96,7 @@ pub async fn get_available_moisture(world: &Arc<World>, pos: &BlockPos, block: &
|
|||||||
if block == &Block::FARMLAND {
|
if block == &Block::FARMLAND {
|
||||||
local_moisture = 1.0;
|
local_moisture = 1.0;
|
||||||
let props = FarmlandProperties::from_state_id(block_state, block);
|
let props = FarmlandProperties::from_state_id(block_state, block);
|
||||||
if props.moisture != Integer0To7::L0 {
|
if props.moisture != 0 {
|
||||||
local_moisture = 3.0;
|
local_moisture = 3.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties},
|
block_properties::{BlockProperties, NetherWartLikeProperties},
|
||||||
tag::{self, Taggable},
|
tag::{self, Taggable},
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -75,12 +75,12 @@ impl CropBlockBase for NetherWartBlock {
|
|||||||
|
|
||||||
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
||||||
let props = NetherWartLikeProperties::from_state_id(state, block);
|
let props = NetherWartLikeProperties::from_state_id(state, block);
|
||||||
i32::from(props.age.to_index())
|
i32::from(props.age)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
||||||
let mut props = NetherWartLikeProperties::from_state_id(state, block);
|
let mut props = NetherWartLikeProperties::from_state_id(state, block);
|
||||||
props.age = Integer0To3::from_index(age as u16);
|
props.age = age as u8;
|
||||||
props.to_state_id(block)
|
props.to_state_id(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties},
|
block_properties::{BlockProperties, NetherWartLikeProperties},
|
||||||
damage::DamageType,
|
damage::DamageType,
|
||||||
entity::EntityType,
|
entity::EntityType,
|
||||||
item::Item,
|
item::Item,
|
||||||
@ -35,9 +35,9 @@ impl BlockBehaviour for SweetBerryBushBlock {
|
|||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let mut props = NetherWartLikeProperties::from_state_id(state_id, args.block);
|
let mut props = NetherWartLikeProperties::from_state_id(state_id, args.block);
|
||||||
match props.age {
|
match props.age {
|
||||||
Integer0To3::L2 | Integer0To3::L3 => {
|
2 | 3 => {
|
||||||
let index = props.age.to_index() as u8;
|
let index = props.age;
|
||||||
props.age = Integer0To3::L1;
|
props.age = 1;
|
||||||
let count: u8 = rand::rng().random_range((index - 1)..=(index));
|
let count: u8 = rand::rng().random_range((index - 1)..=(index));
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
args.world
|
args.world
|
||||||
@ -68,9 +68,7 @@ impl BlockBehaviour for SweetBerryBushBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let props = NetherWartLikeProperties::from_state_id(state_id, &Block::SWEET_BERRY_BUSH);
|
let props = NetherWartLikeProperties::from_state_id(state_id, &Block::SWEET_BERRY_BUSH);
|
||||||
if props.age != Integer0To3::L3
|
if props.age != 3 && args.item_stack.lock().await.get_item() == &Item::BONE_MEAL {
|
||||||
&& args.item_stack.lock().await.get_item() == &Item::BONE_MEAL
|
|
||||||
{
|
|
||||||
BlockActionResult::Pass
|
BlockActionResult::Pass
|
||||||
} else {
|
} else {
|
||||||
BlockActionResult::PassToDefaultBlockAction
|
BlockActionResult::PassToDefaultBlockAction
|
||||||
@ -123,7 +121,7 @@ impl BlockBehaviour for SweetBerryBushBlock {
|
|||||||
|
|
||||||
let state_id = args.world.get_block_state_id(args.position).await;
|
let state_id = args.world.get_block_state_id(args.position).await;
|
||||||
let props = NetherWartLikeProperties::from_state_id(state_id, args.block);
|
let props = NetherWartLikeProperties::from_state_id(state_id, args.block);
|
||||||
if props.age == Integer0To3::L0 {
|
if props.age == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,12 +175,12 @@ impl CropBlockBase for SweetBerryBushBlock {
|
|||||||
|
|
||||||
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
||||||
let props = NetherWartLikeProperties::from_state_id(state, block);
|
let props = NetherWartLikeProperties::from_state_id(state, block);
|
||||||
i32::from(props.age.to_index())
|
i32::from(props.age)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
||||||
let mut props = NetherWartLikeProperties::from_state_id(state, block);
|
let mut props = NetherWartLikeProperties::from_state_id(state, block);
|
||||||
props.age = Integer0To3::from_index(age as u16);
|
props.age = age as u8;
|
||||||
props.to_state_id(block)
|
props.to_state_id(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{BlockProperties, TorchflowerCropLikeProperties};
|
||||||
BlockProperties, EnumVariants, Integer0To1, TorchflowerCropLikeProperties,
|
|
||||||
};
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
@ -62,13 +60,13 @@ impl CropBlockBase for TorchFlowerBlock {
|
|||||||
|
|
||||||
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
fn get_age(&self, state: u16, block: &Block) -> i32 {
|
||||||
let props = TorchFlowerProperties::from_state_id(state, block);
|
let props = TorchFlowerProperties::from_state_id(state, block);
|
||||||
i32::from(props.age.to_index())
|
i32::from(props.age)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
|
||||||
if age == 1 {
|
if age == 1 {
|
||||||
let mut properties = TorchFlowerProperties::from_state_id(state, block);
|
let mut properties = TorchFlowerProperties::from_state_id(state, block);
|
||||||
properties.age = Integer0To1::L1;
|
properties.age = 1;
|
||||||
properties.to_state_id(block)
|
properties.to_state_id(block)
|
||||||
} else {
|
} else {
|
||||||
Block::TORCHFLOWER.default_state.id
|
Block::TORCHFLOWER.default_state.id
|
||||||
|
|||||||
@ -93,7 +93,7 @@ impl PlantBlockBase for KelpBlock {
|
|||||||
WaterLikeProperties::from_state_id(replacing_block_state.id, replacing_block);
|
WaterLikeProperties::from_state_id(replacing_block_state.id, replacing_block);
|
||||||
|
|
||||||
//Only allow placing kelp on either full water or downward flowing water
|
//Only allow placing kelp on either full water or downward flowing water
|
||||||
if water_props.level as u8 != 0 && water_props.level as u8 != 8 {
|
if water_props.level != 0 && water_props.level != 8 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use pumpkin_data::block_properties::{BlockProperties, Integer0To1};
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
@ -20,8 +20,8 @@ impl SaplingBlock {
|
|||||||
async fn generate(&self, world: &Arc<World>, pos: &BlockPos) {
|
async fn generate(&self, world: &Arc<World>, pos: &BlockPos) {
|
||||||
let (block, state) = world.get_block_and_state_id(pos).await;
|
let (block, state) = world.get_block_and_state_id(pos).await;
|
||||||
let mut props = SaplingProperties::from_state_id(state, block);
|
let mut props = SaplingProperties::from_state_id(state, block);
|
||||||
if props.stage == Integer0To1::L0 {
|
if props.stage == 0 {
|
||||||
props.stage = Integer0To1::L1;
|
props.stage = 1;
|
||||||
world
|
world
|
||||||
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::block::{
|
|||||||
};
|
};
|
||||||
use crate::block::{BlockFuture, BlockIsReplacing};
|
use crate::block::{BlockFuture, BlockIsReplacing};
|
||||||
use crate::entity::EntityBase;
|
use crate::entity::EntityBase;
|
||||||
use pumpkin_data::block_properties::{BlockProperties, Integer1To4};
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::entity::EntityPose;
|
use pumpkin_data::entity::EntityPose;
|
||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
@ -73,12 +73,7 @@ impl BlockBehaviour for SeaPickleBlock {
|
|||||||
}
|
}
|
||||||
let mut sea_pickle_prop = SeaPickleProperties::default(args.block);
|
let mut sea_pickle_prop = SeaPickleProperties::default(args.block);
|
||||||
|
|
||||||
sea_pickle_prop.pickles = match rand::rng().random_range(0..4) + 1 {
|
sea_pickle_prop.pickles = rand::rng().random_range(1..=4);
|
||||||
1 => Integer1To4::L1,
|
|
||||||
2 => Integer1To4::L2,
|
|
||||||
3 => Integer1To4::L3,
|
|
||||||
_ => Integer1To4::L4,
|
|
||||||
};
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&lv,
|
&lv,
|
||||||
@ -98,7 +93,7 @@ impl BlockBehaviour for SeaPickleBlock {
|
|||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
let mut sea_pickle_prop = SeaPickleProperties::default(args.block);
|
let mut sea_pickle_prop = SeaPickleProperties::default(args.block);
|
||||||
sea_pickle_prop.pickles = Integer1To4::L4;
|
sea_pickle_prop.pickles = 4;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -117,12 +112,8 @@ impl BlockBehaviour for SeaPickleBlock {
|
|||||||
&& let BlockIsReplacing::Itself(state_id) = args.replacing
|
&& let BlockIsReplacing::Itself(state_id) = args.replacing
|
||||||
{
|
{
|
||||||
let mut sea_pickle_prop = SeaPickleProperties::from_state_id(state_id, args.block);
|
let mut sea_pickle_prop = SeaPickleProperties::from_state_id(state_id, args.block);
|
||||||
if sea_pickle_prop.pickles != Integer1To4::L4 {
|
if sea_pickle_prop.pickles < 4 {
|
||||||
sea_pickle_prop.pickles = match sea_pickle_prop.pickles {
|
sea_pickle_prop.pickles += 1;
|
||||||
Integer1To4::L1 => Integer1To4::L2,
|
|
||||||
Integer1To4::L2 => Integer1To4::L3,
|
|
||||||
_ => Integer1To4::L4,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return sea_pickle_prop.to_state_id(args.block);
|
return sea_pickle_prop.to_state_id(args.block);
|
||||||
}
|
}
|
||||||
@ -146,8 +137,7 @@ impl BlockBehaviour for SeaPickleBlock {
|
|||||||
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
|
fn can_update_at<'a>(&'a self, args: CanUpdateAtArgs<'a>) -> BlockFuture<'a, bool> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
args.player.get_entity().pose.load() != EntityPose::Crouching
|
args.player.get_entity().pose.load() != EntityPose::Crouching
|
||||||
&& SeaPickleProperties::from_state_id(args.state_id, args.block).pickles
|
&& SeaPickleProperties::from_state_id(args.state_id, args.block).pickles < 4
|
||||||
!= Integer1To4::L4
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing, Integer1To4};
|
use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing};
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
|
||||||
use crate::block::{BlockBehaviour, BlockFuture, CanUpdateAtArgs};
|
use crate::block::{BlockBehaviour, BlockFuture, CanUpdateAtArgs};
|
||||||
use crate::block::{BlockIsReplacing, OnPlaceArgs};
|
use crate::block::{BlockIsReplacing, OnPlaceArgs};
|
||||||
|
|
||||||
pub trait SegmentProperties {
|
pub trait SegmentProperties {
|
||||||
fn get_segment_amount(&self) -> Integer1To4;
|
fn get_segment_amount(&self) -> u8;
|
||||||
fn set_segment_amount(&mut self, amount: Integer1To4);
|
fn set_segment_amount(&mut self, amount: u8);
|
||||||
fn get_facing(&self) -> HorizontalFacing;
|
fn get_facing(&self) -> HorizontalFacing;
|
||||||
fn set_facing(&mut self, facing: HorizontalFacing);
|
fn set_facing(&mut self, facing: HorizontalFacing);
|
||||||
}
|
}
|
||||||
@ -14,11 +14,11 @@ pub trait SegmentProperties {
|
|||||||
macro_rules! impl_segment_properties {
|
macro_rules! impl_segment_properties {
|
||||||
($type:ty, $amount_field:ident) => {
|
($type:ty, $amount_field:ident) => {
|
||||||
impl SegmentProperties for $type {
|
impl SegmentProperties for $type {
|
||||||
fn get_segment_amount(&self) -> Integer1To4 {
|
fn get_segment_amount(&self) -> u8 {
|
||||||
self.$amount_field
|
self.$amount_field
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_segment_amount(&mut self, amount: Integer1To4) {
|
fn set_segment_amount(&mut self, amount: u8) {
|
||||||
self.$amount_field = amount;
|
self.$amount_field = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,28 +46,23 @@ pub trait Segmented: BlockBehaviour {
|
|||||||
type Properties: BlockProperties + SegmentProperties;
|
type Properties: BlockProperties + SegmentProperties;
|
||||||
|
|
||||||
fn can_add_segment(&self, props: &Self::Properties) -> bool {
|
fn can_add_segment(&self, props: &Self::Properties) -> bool {
|
||||||
let amount = props.get_segment_amount();
|
props.get_segment_amount() < 4
|
||||||
matches!(amount, Integer1To4::L1 | Integer1To4::L2 | Integer1To4::L3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_next_segment_amount(&self, current: Integer1To4) -> Integer1To4 {
|
fn get_next_segment_amount(&self, current: u8) -> u8 {
|
||||||
match current {
|
(current + 1).min(4)
|
||||||
Integer1To4::L1 => Integer1To4::L2,
|
|
||||||
Integer1To4::L2 => Integer1To4::L3,
|
|
||||||
Integer1To4::L3 | Integer1To4::L4 => Integer1To4::L4,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_facing_for_segment(
|
fn get_facing_for_segment(
|
||||||
&self,
|
&self,
|
||||||
player_facing: HorizontalFacing,
|
player_facing: HorizontalFacing,
|
||||||
segment_amount: Integer1To4,
|
segment_amount: u8,
|
||||||
) -> HorizontalFacing {
|
) -> HorizontalFacing {
|
||||||
let base_facing = match segment_amount {
|
let base_facing = match segment_amount {
|
||||||
Integer1To4::L1 => HorizontalFacing::South,
|
1 => HorizontalFacing::South,
|
||||||
Integer1To4::L2 => HorizontalFacing::East,
|
2 => HorizontalFacing::East,
|
||||||
Integer1To4::L3 => HorizontalFacing::North,
|
3 => HorizontalFacing::North,
|
||||||
Integer1To4::L4 => HorizontalFacing::West,
|
_ => HorizontalFacing::West,
|
||||||
};
|
};
|
||||||
|
|
||||||
match player_facing {
|
match player_facing {
|
||||||
@ -102,8 +97,8 @@ pub trait Segmented: BlockBehaviour {
|
|||||||
// Set first segment orientation based on player direction
|
// Set first segment orientation based on player direction
|
||||||
let player_facing = args.player.living_entity.entity.get_horizontal_facing();
|
let player_facing = args.player.living_entity.entity.get_horizontal_facing();
|
||||||
let mut props = Self::Properties::default(args.block);
|
let mut props = Self::Properties::default(args.block);
|
||||||
props.set_segment_amount(Integer1To4::L1);
|
props.set_segment_amount(1);
|
||||||
props.set_facing(self.get_facing_for_segment(player_facing, Integer1To4::L1));
|
props.set_facing(self.get_facing_for_segment(player_facing, 1));
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use pumpkin_data::block_properties::HorizontalFacing;
|
|||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block,
|
Block,
|
||||||
block_properties::{BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15},
|
block_properties::{BlockProperties, CactusLikeProperties},
|
||||||
tag,
|
tag,
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -43,13 +43,11 @@ impl BlockBehaviour for SugarCaneBlock {
|
|||||||
{
|
{
|
||||||
let state_id = args.world.get_block_state(args.position).await.id;
|
let state_id = args.world.get_block_state(args.position).await.id;
|
||||||
let age = CactusLikeProperties::from_state_id(state_id, args.block).age;
|
let age = CactusLikeProperties::from_state_id(state_id, args.block).age;
|
||||||
if age == Integer0To15::L15 {
|
if age == 15 {
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(&args.position.up(), state_id, BlockFlags::empty())
|
.set_block_state(&args.position.up(), state_id, BlockFlags::empty())
|
||||||
.await;
|
.await;
|
||||||
let props = CactusLikeProperties {
|
let props = CactusLikeProperties { age: 0 };
|
||||||
age: Integer0To15::L0,
|
|
||||||
};
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -58,9 +56,7 @@ impl BlockBehaviour for SugarCaneBlock {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
let props = CactusLikeProperties {
|
let props = CactusLikeProperties { age: age + 1 };
|
||||||
age: Integer0To15::from_index(age.to_index() + 1),
|
|
||||||
};
|
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
|
|||||||
@ -3,8 +3,8 @@ use std::sync::Arc;
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection, BlockState, HorizontalFacingExt,
|
Block, BlockDirection, BlockState, HorizontalFacingExt,
|
||||||
block_properties::{
|
block_properties::{
|
||||||
BlockProperties, ComparatorLikeProperties, EnumVariants, HorizontalFacing,
|
BlockProperties, ComparatorLikeProperties, HorizontalFacing, RedstoneWireLikeProperties,
|
||||||
RedstoneWireLikeProperties, RepeaterLikeProperties,
|
RepeaterLikeProperties,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -300,7 +300,7 @@ pub async fn get_power<T: BlockProperties + RedstoneGateBlockProperties + Send>(
|
|||||||
} else {
|
} else {
|
||||||
source_level.max(if source_block == &Block::REDSTONE_WIRE {
|
source_level.max(if source_block == &Block::REDSTONE_WIRE {
|
||||||
let props = RedstoneWireLikeProperties::from_state_id(source_state.id, source_block);
|
let props = RedstoneWireLikeProperties::from_state_id(source_state.id, source_block);
|
||||||
props.power.to_index() as u8
|
props.power
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{Block, block_properties::BlockProperties};
|
||||||
Block,
|
|
||||||
block_properties::{BlockProperties, EnumVariants},
|
|
||||||
};
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::block::entities::daylight_detector::DaylightDetectorBlockEntity;
|
use pumpkin_world::block::entities::daylight_detector::DaylightDetectorBlockEntity;
|
||||||
@ -61,7 +58,7 @@ impl BlockBehaviour for DaylightDetectorBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let props = DaylightDetectorProperties::from_state_id(args.state.id, args.block);
|
let props = DaylightDetectorProperties::from_state_id(args.state.id, args.block);
|
||||||
|
|
||||||
props.power.to_index() as u8
|
props.power
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::BlockProperties};
|
||||||
Block, BlockDirection, BlockState,
|
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer0To15},
|
|
||||||
};
|
|
||||||
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
|
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
|
||||||
use pumpkin_world::{BlockStateId, world::BlockFlags};
|
use pumpkin_world::{BlockStateId, world::BlockFlags};
|
||||||
|
|
||||||
@ -98,7 +95,7 @@ impl BlockBehaviour for WeightedPressurePlateBlock {
|
|||||||
impl PressurePlate for WeightedPressurePlateBlock {
|
impl PressurePlate for WeightedPressurePlateBlock {
|
||||||
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8 {
|
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8 {
|
||||||
let props = PressurePlateProps::from_state_id(state, block);
|
let props = PressurePlateProps::from_state_id(state, block);
|
||||||
props.power.to_index() as u8
|
props.power
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8 {
|
async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8 {
|
||||||
@ -129,7 +126,7 @@ impl PressurePlate for WeightedPressurePlateBlock {
|
|||||||
output: u8,
|
output: u8,
|
||||||
) -> pumpkin_world::BlockStateId {
|
) -> pumpkin_world::BlockStateId {
|
||||||
let mut props = PressurePlateProps::from_state_id(state.id, block);
|
let mut props = PressurePlateProps::from_state_id(state.id, block);
|
||||||
props.power = Integer0To15::from_index(u16::from(output));
|
props.power = output;
|
||||||
props.to_state_id(block)
|
props.to_state_id(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::block_properties::{
|
use pumpkin_data::block_properties::{
|
||||||
BlockProperties, EastRedstone, EnumVariants, Integer0To15, NorthRedstone,
|
BlockProperties, EastRedstone, NorthRedstone, ObserverLikeProperties,
|
||||||
ObserverLikeProperties, RedstoneWireLikeProperties, RepeaterLikeProperties, SouthRedstone,
|
RedstoneWireLikeProperties, RepeaterLikeProperties, SouthRedstone, WestRedstone,
|
||||||
WestRedstone,
|
|
||||||
};
|
};
|
||||||
use pumpkin_data::{Block, BlockDirection, BlockState, HorizontalFacingExt};
|
use pumpkin_data::{Block, BlockDirection, BlockState, HorizontalFacingExt};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
@ -37,8 +36,7 @@ impl BlockBehaviour for RedstoneWireBlock {
|
|||||||
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut wire = RedstoneWireProperties::default(args.block);
|
let mut wire = RedstoneWireProperties::default(args.block);
|
||||||
wire.power =
|
wire.power = calculate_power(args.world, args.position).await;
|
||||||
Integer0To15::from_index(calculate_power(args.world, args.position).await.into());
|
|
||||||
wire = get_regulated_sides(wire, args.world, args.position).await;
|
wire = get_regulated_sides(wire, args.world, args.position).await;
|
||||||
if is_dot(wire) {
|
if is_dot(wire) {
|
||||||
wire = make_cross(wire.power);
|
wire = make_cross(wire.power);
|
||||||
@ -89,8 +87,7 @@ impl BlockBehaviour for RedstoneWireBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wire = get_regulated_sides(wire, args.world, args.position).await;
|
wire = get_regulated_sides(wire, args.world, args.position).await;
|
||||||
wire.power =
|
wire.power = calculate_power(args.world, args.position).await;
|
||||||
Integer0To15::from_index(calculate_power(args.world, args.position).await.into());
|
|
||||||
|
|
||||||
if is_cross(old_state) && new_side.is_none() {
|
if is_cross(old_state) && new_side.is_none() {
|
||||||
return wire.to_state_id(args.block);
|
return wire.to_state_id(args.block);
|
||||||
@ -177,8 +174,8 @@ impl BlockBehaviour for RedstoneWireBlock {
|
|||||||
let state = args.world.get_block_state(args.position).await;
|
let state = args.world.get_block_state(args.position).await;
|
||||||
let mut wire = RedstoneWireProperties::from_state_id(state.id, args.block);
|
let mut wire = RedstoneWireProperties::from_state_id(state.id, args.block);
|
||||||
let new_power = calculate_power(args.world, args.position).await;
|
let new_power = calculate_power(args.world, args.position).await;
|
||||||
if wire.power.to_index() as u8 != new_power {
|
if wire.power != new_power {
|
||||||
wire.power = Integer0To15::from_index(new_power.into());
|
wire.power = new_power;
|
||||||
args.world
|
args.world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
args.position,
|
args.position,
|
||||||
@ -206,7 +203,7 @@ impl BlockBehaviour for RedstoneWireBlock {
|
|||||||
if args.direction == BlockDirection::Up
|
if args.direction == BlockDirection::Up
|
||||||
|| wire.is_side_connected(args.direction.opposite())
|
|| wire.is_side_connected(args.direction.opposite())
|
||||||
{
|
{
|
||||||
wire.power.to_index() as u8
|
wire.power
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -222,7 +219,7 @@ impl BlockBehaviour for RedstoneWireBlock {
|
|||||||
if args.direction == BlockDirection::Up
|
if args.direction == BlockDirection::Up
|
||||||
|| wire.is_side_connected(args.direction.opposite())
|
|| wire.is_side_connected(args.direction.opposite())
|
||||||
{
|
{
|
||||||
wire.power.to_index() as u8
|
wire.power
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -273,7 +270,7 @@ async fn on_use(wire: RedstoneWireProperties, world: &Arc<World>, block_pos: &Bl
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn make_cross(power: Integer0To15) -> RedstoneWireProperties {
|
pub const fn make_cross(power: u8) -> RedstoneWireProperties {
|
||||||
RedstoneWireProperties {
|
RedstoneWireProperties {
|
||||||
north: NorthRedstone::Side,
|
north: NorthRedstone::Side,
|
||||||
south: SouthRedstone::Side,
|
south: SouthRedstone::Side,
|
||||||
@ -529,7 +526,7 @@ async fn max_wire_power(wire_power: u8, world: &World, pos: BlockPos) -> u8 {
|
|||||||
let (block, block_state) = world.get_block_and_state(&pos).await;
|
let (block, block_state) = world.get_block_and_state(&pos).await;
|
||||||
if block == &Block::REDSTONE_WIRE {
|
if block == &Block::REDSTONE_WIRE {
|
||||||
let wire = RedstoneWireProperties::from_state_id(block_state.id, block);
|
let wire = RedstoneWireProperties::from_state_id(block_state.id, block);
|
||||||
wire_power.max(wire.power.to_index() as u8)
|
wire_power.max(wire.power)
|
||||||
} else {
|
} else {
|
||||||
wire_power
|
wire_power
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection, BlockState, HorizontalFacingExt,
|
Block, BlockDirection, BlockState, HorizontalFacingExt,
|
||||||
block_properties::{BlockProperties, EnumVariants, HorizontalFacing, Integer1To4},
|
block_properties::{BlockProperties, HorizontalFacing},
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -275,7 +275,7 @@ impl RedstoneGateBlock<RepeaterProperties> for RepeaterBlock {
|
|||||||
|
|
||||||
fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8 {
|
fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8 {
|
||||||
let props = RepeaterProperties::from_state_id(state_id, block);
|
let props = RepeaterProperties::from_state_id(state_id, block);
|
||||||
(props.delay.to_index() as u8 + 1) * 2
|
props.delay * 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,12 +288,7 @@ impl RepeaterBlock {
|
|||||||
block: &Block,
|
block: &Block,
|
||||||
) {
|
) {
|
||||||
let mut props = props;
|
let mut props = props;
|
||||||
props.delay = match props.delay {
|
props.delay = if props.delay == 4 { 1 } else { props.delay + 1 };
|
||||||
Integer1To4::L1 => Integer1To4::L2,
|
|
||||||
Integer1To4::L2 => Integer1To4::L3,
|
|
||||||
Integer1To4::L3 => Integer1To4::L4,
|
|
||||||
Integer1To4::L4 => Integer1To4::L1,
|
|
||||||
};
|
|
||||||
let state = props.to_state_id(block);
|
let state = props.to_state_id(block);
|
||||||
world
|
world
|
||||||
.set_block_state(&block_pos, state, BlockFlags::empty())
|
.set_block_state(&block_pos, state, BlockFlags::empty())
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockDirection, BlockState,
|
Block, BlockDirection, BlockState,
|
||||||
block_properties::{BlockProperties, EnumVariants, Integer0To15, RedstoneWireLikeProperties},
|
block_properties::{BlockProperties, RedstoneWireLikeProperties},
|
||||||
};
|
};
|
||||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||||
use pumpkin_world::world::BlockFlags;
|
use pumpkin_world::world::BlockFlags;
|
||||||
@ -402,8 +402,8 @@ impl RedstoneWireTurbo {
|
|||||||
if wire_power > j {
|
if wire_power > j {
|
||||||
j = wire_power;
|
j = wire_power;
|
||||||
}
|
}
|
||||||
if i.to_index() as u8 != j {
|
if i != j {
|
||||||
wire.power = Integer0To15::from_index(j.into());
|
wire.power = j;
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
&pos,
|
&pos,
|
||||||
@ -419,7 +419,7 @@ impl RedstoneWireTurbo {
|
|||||||
let node = &self.nodes[upd.index];
|
let node = &self.nodes[upd.index];
|
||||||
let block = Block::from_state_id(node.state.id);
|
let block = Block::from_state_id(node.state.id);
|
||||||
if block == &Block::REDSTONE_WIRE {
|
if block == &Block::REDSTONE_WIRE {
|
||||||
(unwrap_wire(node.state).power.to_index() as u8).max(strength)
|
unwrap_wire(node.state).power.max(strength)
|
||||||
} else {
|
} else {
|
||||||
strength
|
strength
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ use std::sync::atomic::Ordering;
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::BlockDirection;
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::block_properties::EnumVariants;
|
use pumpkin_data::block_properties::EnumVariants;
|
||||||
use pumpkin_data::block_properties::Integer0To15;
|
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
use pumpkin_inventory::screen_handler::InventoryPlayer;
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
@ -47,7 +46,7 @@ struct SupportInfo {
|
|||||||
struct SignPlacement {
|
struct SignPlacement {
|
||||||
block_id: u16,
|
block_id: u16,
|
||||||
facing: Option<String>,
|
facing: Option<String>,
|
||||||
rotation: Option<Integer0To15>,
|
rotation: Option<u8>,
|
||||||
attached: bool,
|
attached: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,30 +112,21 @@ impl SignBlock {
|
|||||||
/// Calculates rotation for wall-hanging signs.
|
/// Calculates rotation for wall-hanging signs.
|
||||||
fn calculate_wall_hanging_rotation(
|
fn calculate_wall_hanging_rotation(
|
||||||
wall_dir: BlockDirection,
|
wall_dir: BlockDirection,
|
||||||
player_rot: Integer0To15,
|
player_rot: u8,
|
||||||
is_sneaking: bool,
|
is_sneaking: bool,
|
||||||
) -> Integer0To15 {
|
) -> u8 {
|
||||||
if is_sneaking {
|
if is_sneaking {
|
||||||
return player_rot;
|
return player_rot;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = player_rot.to_index();
|
|
||||||
match wall_dir {
|
match wall_dir {
|
||||||
BlockDirection::North | BlockDirection::South => {
|
BlockDirection::North | BlockDirection::South => {
|
||||||
// Snap to North-South axis (0 or 8)
|
// Snap to North-South axis (0 or 8)
|
||||||
if (4..12).contains(&idx) {
|
if (4..12).contains(&player_rot) { 8 } else { 0 }
|
||||||
Integer0To15::from_index(8)
|
|
||||||
} else {
|
|
||||||
Integer0To15::from_index(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BlockDirection::East | BlockDirection::West => {
|
BlockDirection::East | BlockDirection::West => {
|
||||||
// Snap to East-West axis (4 or 12)
|
// Snap to East-West axis (4 or 12)
|
||||||
if (2..10).contains(&idx) {
|
if (2..10).contains(&player_rot) { 4 } else { 12 }
|
||||||
Integer0To15::from_index(4)
|
|
||||||
} else {
|
|
||||||
Integer0To15::from_index(12)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => player_rot,
|
_ => player_rot,
|
||||||
}
|
}
|
||||||
@ -205,7 +195,7 @@ impl SignBlock {
|
|||||||
args: &OnPlaceArgs,
|
args: &OnPlaceArgs,
|
||||||
support: &SupportInfo,
|
support: &SupportInfo,
|
||||||
is_sneaking: bool,
|
is_sneaking: bool,
|
||||||
) -> (Option<String>, Option<Integer0To15>, bool) {
|
) -> (Option<String>, Option<u8>, bool) {
|
||||||
let wall_dir = if args.direction.is_horizontal() {
|
let wall_dir = if args.direction.is_horizontal() {
|
||||||
args.direction
|
args.direction
|
||||||
} else {
|
} else {
|
||||||
@ -218,7 +208,7 @@ impl SignBlock {
|
|||||||
let player_rot = args.player.get_entity().get_flipped_rotation_16();
|
let player_rot = args.player.get_entity().get_flipped_rotation_16();
|
||||||
let rotation = Self::calculate_wall_hanging_rotation(wall_dir, player_rot, is_sneaking);
|
let rotation = Self::calculate_wall_hanging_rotation(wall_dir, player_rot, is_sneaking);
|
||||||
|
|
||||||
let is_angled = rotation.to_index() % 4 != 0;
|
let is_angled = rotation % 4 != 0;
|
||||||
let attached = is_angled || is_sneaking;
|
let attached = is_angled || is_sneaking;
|
||||||
|
|
||||||
(Some(facing.to_string()), Some(rotation), attached)
|
(Some(facing.to_string()), Some(rotation), attached)
|
||||||
@ -228,37 +218,29 @@ impl SignBlock {
|
|||||||
fn calculate_ceiling_orientation(
|
fn calculate_ceiling_orientation(
|
||||||
args: &OnPlaceArgs,
|
args: &OnPlaceArgs,
|
||||||
is_sneaking: bool,
|
is_sneaking: bool,
|
||||||
) -> (Option<String>, Option<Integer0To15>, bool) {
|
) -> (Option<String>, Option<u8>, bool) {
|
||||||
let rotation = if is_sneaking {
|
let rotation = if is_sneaking {
|
||||||
args.player.get_entity().get_flipped_rotation_16()
|
args.player.get_entity().get_flipped_rotation_16()
|
||||||
} else {
|
} else {
|
||||||
// Snap to nearest cardinal
|
// Snap to nearest cardinal
|
||||||
let index = args
|
let index = args.player.get_entity().get_flipped_rotation_16();
|
||||||
.player
|
((index + 2) / 4 * 4) % 16
|
||||||
.get_entity()
|
|
||||||
.get_flipped_rotation_16()
|
|
||||||
.to_index();
|
|
||||||
Integer0To15::from_index(((index + 2) / 4 * 4) % 16)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_angled = rotation.to_index() % 4 != 0;
|
let is_angled = rotation % 4 != 0;
|
||||||
let attached = is_angled || is_sneaking;
|
let attached = is_angled || is_sneaking;
|
||||||
|
|
||||||
(None, Some(rotation), attached)
|
(None, Some(rotation), attached)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates orientation for wall signs.
|
/// Calculates orientation for wall signs.
|
||||||
fn calculate_wall_orientation(
|
fn calculate_wall_orientation(args: &OnPlaceArgs) -> (Option<String>, Option<u8>, bool) {
|
||||||
args: &OnPlaceArgs,
|
|
||||||
) -> (Option<String>, Option<Integer0To15>, bool) {
|
|
||||||
let facing = args.direction.opposite().to_cardinal_direction().to_value();
|
let facing = args.direction.opposite().to_cardinal_direction().to_value();
|
||||||
(Some(facing.to_string()), None, false)
|
(Some(facing.to_string()), None, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates orientation for standing signs.
|
/// Calculates orientation for standing signs.
|
||||||
fn calculate_standing_orientation(
|
fn calculate_standing_orientation(args: &OnPlaceArgs) -> (Option<String>, Option<u8>, bool) {
|
||||||
args: &OnPlaceArgs,
|
|
||||||
) -> (Option<String>, Option<Integer0To15>, bool) {
|
|
||||||
let rotation = args.player.get_entity().get_flipped_rotation_16();
|
let rotation = args.player.get_entity().get_flipped_rotation_16();
|
||||||
(None, Some(rotation), false)
|
(None, Some(rotation), false)
|
||||||
}
|
}
|
||||||
@ -279,7 +261,24 @@ impl SignBlock {
|
|||||||
if let Some(rotation) = placement.rotation
|
if let Some(rotation) = placement.rotation
|
||||||
&& let Some(prop) = props.iter_mut().find(|(k, _)| *k == "rotation")
|
&& let Some(prop) = props.iter_mut().find(|(k, _)| *k == "rotation")
|
||||||
{
|
{
|
||||||
prop.1 = rotation.to_value();
|
prop.1 = match rotation {
|
||||||
|
1 => "1",
|
||||||
|
2 => "2",
|
||||||
|
3 => "3",
|
||||||
|
4 => "4",
|
||||||
|
5 => "5",
|
||||||
|
6 => "6",
|
||||||
|
7 => "7",
|
||||||
|
8 => "8",
|
||||||
|
9 => "9",
|
||||||
|
10 => "10",
|
||||||
|
11 => "11",
|
||||||
|
12 => "12",
|
||||||
|
13 => "13",
|
||||||
|
14 => "14",
|
||||||
|
15 => "15",
|
||||||
|
_ => "0",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prop) = props.iter_mut().find(|(k, _)| *k == "attached") {
|
if let Some(prop) = props.iter_mut().find(|(k, _)| *k == "attached") {
|
||||||
@ -596,7 +595,7 @@ async fn is_facing_front_text(
|
|||||||
if let Some(props) = block.properties(state_id) {
|
if let Some(props) = block.properties(state_id) {
|
||||||
let prop_map = props.to_props();
|
let prop_map = props.to_props();
|
||||||
if let Some((_, val)) = prop_map.iter().find(|(k, _)| k == &"rotation") {
|
if let Some((_, val)) = prop_map.iter().find(|(k, _)| k == &"rotation") {
|
||||||
let r = Integer0To15::from_value(val);
|
let r = val.parse().unwrap_or(0);
|
||||||
rotation = get_yaw_from_rotation_16(r);
|
rotation = get_yaw_from_rotation_16(r);
|
||||||
} else if let Some((_, val)) = prop_map.iter().find(|(k, _)| k == &"facing") {
|
} else if let Some((_, val)) = prop_map.iter().find(|(k, _)| k == &"facing") {
|
||||||
rotation = match &val[..] {
|
rotation = match &val[..] {
|
||||||
@ -618,10 +617,8 @@ async fn is_facing_front_text(
|
|||||||
diff.abs() <= 90.0
|
diff.abs() <= 90.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_yaw_from_rotation_16(rotation: Integer0To15) -> f32 {
|
fn get_yaw_from_rotation_16(rotation: u8) -> f32 {
|
||||||
let index = rotation.to_index();
|
f32::from(rotation) * 22.5
|
||||||
|
|
||||||
f32::from(index) * 22.5
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_claim_sign(
|
fn try_claim_sign(
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
use pumpkin_data::block_properties::BlockProperties;
|
use pumpkin_data::block_properties::BlockProperties;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{Block, BlockDirection, block_properties::SnowLikeProperties, item::Item};
|
||||||
Block, BlockDirection,
|
|
||||||
block_properties::{Integer1To8, SnowLikeProperties},
|
|
||||||
item::Item,
|
|
||||||
};
|
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::{
|
use pumpkin_world::{
|
||||||
@ -27,7 +23,7 @@ impl BlockBehaviour for LayeredSnowBlock {
|
|||||||
return Block::AIR.default_state.id;
|
return Block::AIR.default_state.id;
|
||||||
}
|
}
|
||||||
let mut props = SnowLikeProperties::default(args.block);
|
let mut props = SnowLikeProperties::default(args.block);
|
||||||
props.layers = Integer1To8::L1;
|
props.layers = 1;
|
||||||
props.to_state_id(&Block::SNOW)
|
props.to_state_id(&Block::SNOW)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -58,25 +54,17 @@ impl BlockBehaviour for LayeredSnowBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut props = SnowLikeProperties::from_state_id(state_id, &Block::SNOW);
|
let mut props = SnowLikeProperties::from_state_id(state_id, &Block::SNOW);
|
||||||
props.layers = match props.layers {
|
if props.layers >= 8 {
|
||||||
Integer1To8::L1 => Integer1To8::L2,
|
args.world
|
||||||
Integer1To8::L2 => Integer1To8::L3,
|
.set_block_state(
|
||||||
Integer1To8::L3 => Integer1To8::L4,
|
pos,
|
||||||
Integer1To8::L4 => Integer1To8::L5,
|
Block::SNOW_BLOCK.default_state.id,
|
||||||
Integer1To8::L5 => Integer1To8::L6,
|
BlockFlags::NOTIFY_ALL,
|
||||||
Integer1To8::L6 => Integer1To8::L7,
|
)
|
||||||
Integer1To8::L7 => Integer1To8::L8,
|
.await;
|
||||||
Integer1To8::L8 => {
|
return BlockActionResult::Success;
|
||||||
args.world
|
}
|
||||||
.set_block_state(
|
props.layers += 1;
|
||||||
pos,
|
|
||||||
Block::SNOW_BLOCK.default_state.id,
|
|
||||||
BlockFlags::NOTIFY_ALL,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
return BlockActionResult::Success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let state_id = props.to_state_id(&Block::SNOW);
|
let state_id = props.to_state_id(&Block::SNOW);
|
||||||
args.world
|
args.world
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use pumpkin_data::block_properties::Integer0To15;
|
|
||||||
use pumpkin_data::{Block, BlockState};
|
use pumpkin_data::{Block, BlockState};
|
||||||
|
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
@ -465,7 +464,7 @@ pub async fn calc_block_breaking(
|
|||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub enum BlockIsReplacing {
|
pub enum BlockIsReplacing {
|
||||||
Itself(BlockStateId),
|
Itself(BlockStateId),
|
||||||
Water(Integer0To15),
|
Water(u8),
|
||||||
Other,
|
Other,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
@ -473,10 +472,10 @@ pub enum BlockIsReplacing {
|
|||||||
impl BlockIsReplacing {
|
impl BlockIsReplacing {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Returns true if the block was a water source block.
|
/// Returns true if the block was a water source block.
|
||||||
pub fn water_source(&self) -> bool {
|
pub const fn water_source(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
// Level 0 means the water is a source block
|
// Level 0 means the water is a source block
|
||||||
Self::Water(level) => *level == Integer0To15::L0,
|
Self::Water(level) => *level == 0,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use living::LivingEntity;
|
|||||||
use player::Player;
|
use player::Player;
|
||||||
use pumpkin_data::BlockState;
|
use pumpkin_data::BlockState;
|
||||||
use pumpkin_data::biome::Biome;
|
use pumpkin_data::biome::Biome;
|
||||||
use pumpkin_data::block_properties::{EnumVariants, Integer0To15, blocks_movement};
|
use pumpkin_data::block_properties::blocks_movement;
|
||||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
use pumpkin_data::entity::EntityStatus;
|
use pumpkin_data::entity::EntityStatus;
|
||||||
@ -2082,33 +2082,14 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rotation_16(&self) -> Integer0To15 {
|
pub fn get_rotation_16(&self) -> u8 {
|
||||||
let adjusted_yaw = self.yaw.load().rem_euclid(360.0);
|
let adjusted_yaw = self.yaw.load().rem_euclid(360.0);
|
||||||
|
|
||||||
let index = (adjusted_yaw / 22.5).round() as u16 % 16;
|
((adjusted_yaw / 22.5).round() as u8) % 16
|
||||||
|
|
||||||
Integer0To15::from_index(index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_flipped_rotation_16(&self) -> Integer0To15 {
|
pub fn get_flipped_rotation_16(&self) -> u8 {
|
||||||
match self.get_rotation_16() {
|
(self.get_rotation_16() + 8) % 16
|
||||||
Integer0To15::L0 => Integer0To15::L8,
|
|
||||||
Integer0To15::L1 => Integer0To15::L9,
|
|
||||||
Integer0To15::L2 => Integer0To15::L10,
|
|
||||||
Integer0To15::L3 => Integer0To15::L11,
|
|
||||||
Integer0To15::L4 => Integer0To15::L12,
|
|
||||||
Integer0To15::L5 => Integer0To15::L13,
|
|
||||||
Integer0To15::L6 => Integer0To15::L14,
|
|
||||||
Integer0To15::L7 => Integer0To15::L15,
|
|
||||||
Integer0To15::L8 => Integer0To15::L0,
|
|
||||||
Integer0To15::L9 => Integer0To15::L1,
|
|
||||||
Integer0To15::L10 => Integer0To15::L2,
|
|
||||||
Integer0To15::L11 => Integer0To15::L3,
|
|
||||||
Integer0To15::L12 => Integer0To15::L4,
|
|
||||||
Integer0To15::L13 => Integer0To15::L5,
|
|
||||||
Integer0To15::L14 => Integer0To15::L6,
|
|
||||||
Integer0To15::L15 => Integer0To15::L7,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_facing(&self) -> Facing {
|
pub fn get_facing(&self) -> Facing {
|
||||||
|
|||||||
@ -29,7 +29,7 @@ use tracing::{debug, warn};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use pumpkin_data::attributes::Attributes;
|
use pumpkin_data::attributes::Attributes;
|
||||||
use pumpkin_data::block_properties::{BlockProperties, EnumVariants, HorizontalFacing};
|
use pumpkin_data::block_properties::{BlockProperties, HorizontalFacing};
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
use pumpkin_data::data_component_impl::{AttributeModifiersImpl, Operation};
|
use pumpkin_data::data_component_impl::{AttributeModifiersImpl, Operation};
|
||||||
use pumpkin_data::data_component_impl::{EquipmentSlot, EquippableImpl, ToolImpl, WeaponImpl};
|
use pumpkin_data::data_component_impl::{EquipmentSlot, EquippableImpl, ToolImpl, WeaponImpl};
|
||||||
@ -1106,10 +1106,8 @@ impl Player {
|
|||||||
|
|
||||||
// Handle respawn anchor (Nether)
|
// Handle respawn anchor (Nether)
|
||||||
if block == &Block::RESPAWN_ANCHOR {
|
if block == &Block::RESPAWN_ANCHOR {
|
||||||
use pumpkin_data::block_properties::Integer0To4;
|
|
||||||
|
|
||||||
let anchor_props = AnchorProperties::from_state_id(state_id, block);
|
let anchor_props = AnchorProperties::from_state_id(state_id, block);
|
||||||
let charges = anchor_props.charges.to_index();
|
let charges = anchor_props.charges;
|
||||||
|
|
||||||
// Anchor needs at least 1 charge to work
|
// Anchor needs at least 1 charge to work
|
||||||
if charges == 0 {
|
if charges == 0 {
|
||||||
@ -1121,7 +1119,7 @@ impl Player {
|
|||||||
// Decrement charges after successful respawn position found
|
// Decrement charges after successful respawn position found
|
||||||
let new_charges = charges - 1;
|
let new_charges = charges - 1;
|
||||||
let mut new_props = anchor_props;
|
let mut new_props = anchor_props;
|
||||||
new_props.charges = Integer0To4::from_index(new_charges);
|
new_props.charges = new_charges;
|
||||||
world
|
world
|
||||||
.set_block_state(
|
.set_block_state(
|
||||||
pos,
|
pos,
|
||||||
|
|||||||
@ -31,8 +31,6 @@ use pumpkin_protocol::java::client::play::{CChangeDifficulty, CTabList};
|
|||||||
use pumpkin_protocol::{ClientPacket, java::client::config::CPluginMessage};
|
use pumpkin_protocol::{ClientPacket, java::client::config::CPluginMessage};
|
||||||
use pumpkin_util::Difficulty;
|
use pumpkin_util::Difficulty;
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
use pumpkin_world::lock::LevelLocker;
|
|
||||||
use pumpkin_world::lock::anvil::AnvilLevelLocker;
|
|
||||||
use pumpkin_world::world_info::anvil::{
|
use pumpkin_world::world_info::anvil::{
|
||||||
AnvilLevelInfo, LEVEL_DAT_BACKUP_FILE_NAME, LEVEL_DAT_FILE_NAME,
|
AnvilLevelInfo, LEVEL_DAT_BACKUP_FILE_NAME, LEVEL_DAT_FILE_NAME,
|
||||||
};
|
};
|
||||||
@ -124,9 +122,6 @@ pub struct Server {
|
|||||||
// world stuff which maybe should be put into a struct
|
// world stuff which maybe should be put into a struct
|
||||||
pub level_info: Arc<ArcSwap<LevelData>>,
|
pub level_info: Arc<ArcSwap<LevelData>>,
|
||||||
world_info_writer: Arc<dyn WorldInfoWriter>,
|
world_info_writer: Arc<dyn WorldInfoWriter>,
|
||||||
// Gets unlocked when dropped
|
|
||||||
// TODO: Make this a trait
|
|
||||||
_locker: Arc<Option<AnvilLevelLocker>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
@ -167,16 +162,6 @@ impl Server {
|
|||||||
fs::copy(dat_path, backup_path).unwrap();
|
fs::copy(dat_path, backup_path).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let locker = match AnvilLevelLocker::lock(&world_path) {
|
|
||||||
Ok(l) => Some(l),
|
|
||||||
Err(err) => {
|
|
||||||
warn!(
|
|
||||||
"Could not lock the level file. Data corruption is possible if the world is accessed by multiple processes simultaneously. Error: {err}"
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let level_info = level_info.unwrap_or_else(|err| {
|
let level_info = level_info.unwrap_or_else(|err| {
|
||||||
warn!("Failed to get level_info, using default instead: {err}");
|
warn!("Failed to get level_info, using default instead: {err}");
|
||||||
let default_data = LevelData::default(basic_config.seed);
|
let default_data = LevelData::default(basic_config.seed);
|
||||||
@ -254,7 +239,6 @@ impl Server {
|
|||||||
mojang_public_keys: ArcSwap::from_pointee(Vec::new()),
|
mojang_public_keys: ArcSwap::from_pointee(Vec::new()),
|
||||||
world_info_writer: Arc::new(AnvilLevelInfo),
|
world_info_writer: Arc::new(AnvilLevelInfo),
|
||||||
level_info,
|
level_info,
|
||||||
_locker: Arc::new(locker),
|
|
||||||
};
|
};
|
||||||
let server = Arc::new(server);
|
let server = Arc::new(server);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user