From fae24008db3ac558b47638b9d8e31a7add557aa9 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 13 Aug 2025 17:11:56 +0200 Subject: [PATCH] pumpkin-world(structures): Add structure placement calculation --- Cargo.lock | 8 +- Cargo.toml | 2 +- assets/structure_set.json | 358 ++++++++++++++++++ pumpkin-util/src/random/mod.rs | 24 ++ pumpkin-world/src/block/entities/hopper.rs | 6 +- pumpkin-world/src/chunk/format/anvil.rs | 2 +- pumpkin-world/src/generation/generator/mod.rs | 2 +- pumpkin-world/src/generation/mod.rs | 1 + pumpkin-world/src/generation/proto_chunk.rs | 21 +- pumpkin-world/src/generation/structure/mod.rs | 16 + .../src/generation/structure/placement.rs | 167 ++++++++ pumpkin-world/src/item/categories.rs | 38 +- pumpkin-world/src/lib.rs | 2 +- pumpkin-world/src/world_info/mod.rs | 8 +- pumpkin/src/entity/player.rs | 2 +- 15 files changed, 613 insertions(+), 44 deletions(-) create mode 100644 assets/structure_set.json create mode 100644 pumpkin-world/src/generation/structure/placement.rs diff --git a/Cargo.lock b/Cargo.lock index e2ae5f5bb..17a0f58b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2264,9 +2264,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -2274,9 +2274,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", diff --git a/Cargo.toml b/Cargo.toml index cd0d7f117..b05d8dbca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ bytes = "1.10" # Concurrency/Parallelism and Synchronization futures = { version = "0.3", default-features = false, features = ["executor"] } -rayon = "1.10" +rayon = "1.11" crossbeam = "0.8" uuid = { version = "1.18", features = ["serde", "v3", "v4"] } diff --git a/assets/structure_set.json b/assets/structure_set.json new file mode 100644 index 000000000..e4509d74f --- /dev/null +++ b/assets/structure_set.json @@ -0,0 +1,358 @@ +{ + "ancient_cities": { + "structures": [ + { + "structure": "minecraft:ancient_city", + "weight": 1 + } + ], + "placement": { + "spacing": 24, + "separation": 8, + "salt": 20083232, + "type": "minecraft:random_spread" + } + }, + "buried_treasures": { + "structures": [ + { + "structure": "minecraft:buried_treasure", + "weight": 1 + } + ], + "placement": { + "spacing": 1, + "separation": 0, + "locate_offset": [ + 9, + 0, + 9 + ], + "frequency_reduction_method": "legacy_type_2", + "frequency": 0.01, + "salt": 0, + "type": "minecraft:random_spread" + } + }, + "desert_pyramids": { + "structures": [ + { + "structure": "minecraft:desert_pyramid", + "weight": 1 + } + ], + "placement": { + "spacing": 32, + "separation": 8, + "salt": 14357617, + "type": "minecraft:random_spread" + } + }, + "end_cities": { + "structures": [ + { + "structure": "minecraft:end_city", + "weight": 1 + } + ], + "placement": { + "spacing": 20, + "separation": 11, + "spread_type": "triangular", + "salt": 10387313, + "type": "minecraft:random_spread" + } + }, + "igloos": { + "structures": [ + { + "structure": "minecraft:igloo", + "weight": 1 + } + ], + "placement": { + "spacing": 32, + "separation": 8, + "salt": 14357618, + "type": "minecraft:random_spread" + } + }, + "jungle_temples": { + "structures": [ + { + "structure": "minecraft:jungle_pyramid", + "weight": 1 + } + ], + "placement": { + "spacing": 32, + "separation": 8, + "salt": 14357619, + "type": "minecraft:random_spread" + } + }, + "mineshafts": { + "structures": [ + { + "structure": "minecraft:mineshaft", + "weight": 1 + }, + { + "structure": "minecraft:mineshaft_mesa", + "weight": 1 + } + ], + "placement": { + "spacing": 1, + "separation": 0, + "frequency_reduction_method": "legacy_type_3", + "frequency": 0.004, + "salt": 0, + "type": "minecraft:random_spread" + } + }, + "nether_complexes": { + "structures": [ + { + "structure": "minecraft:fortress", + "weight": 2 + }, + { + "structure": "minecraft:bastion_remnant", + "weight": 3 + } + ], + "placement": { + "spacing": 27, + "separation": 4, + "salt": 30084232, + "type": "minecraft:random_spread" + } + }, + "nether_fossils": { + "structures": [ + { + "structure": "minecraft:nether_fossil", + "weight": 1 + } + ], + "placement": { + "spacing": 2, + "separation": 1, + "salt": 14357921, + "type": "minecraft:random_spread" + } + }, + "ocean_monuments": { + "structures": [ + { + "structure": "minecraft:monument", + "weight": 1 + } + ], + "placement": { + "spacing": 32, + "separation": 5, + "spread_type": "triangular", + "salt": 10387313, + "type": "minecraft:random_spread" + } + }, + "ocean_ruins": { + "structures": [ + { + "structure": "minecraft:ocean_ruin_cold", + "weight": 1 + }, + { + "structure": "minecraft:ocean_ruin_warm", + "weight": 1 + } + ], + "placement": { + "spacing": 20, + "separation": 8, + "salt": 14357621, + "type": "minecraft:random_spread" + } + }, + "pillager_outposts": { + "structures": [ + { + "structure": "minecraft:pillager_outpost", + "weight": 1 + } + ], + "placement": { + "exclusion_zone": { + "other_set": "minecraft:villages", + "chunk_count": 10 + }, + "spacing": 32, + "separation": 8, + "frequency_reduction_method": "legacy_type_1", + "frequency": 0.2, + "salt": 165745296, + "type": "minecraft:random_spread" + } + }, + "ruined_portals": { + "structures": [ + { + "structure": "minecraft:ruined_portal", + "weight": 1 + }, + { + "structure": "minecraft:ruined_portal_desert", + "weight": 1 + }, + { + "structure": "minecraft:ruined_portal_jungle", + "weight": 1 + }, + { + "structure": "minecraft:ruined_portal_swamp", + "weight": 1 + }, + { + "structure": "minecraft:ruined_portal_mountain", + "weight": 1 + }, + { + "structure": "minecraft:ruined_portal_ocean", + "weight": 1 + }, + { + "structure": "minecraft:ruined_portal_nether", + "weight": 1 + } + ], + "placement": { + "spacing": 40, + "separation": 15, + "salt": 34222645, + "type": "minecraft:random_spread" + } + }, + "shipwrecks": { + "structures": [ + { + "structure": "minecraft:shipwreck", + "weight": 1 + }, + { + "structure": "minecraft:shipwreck_beached", + "weight": 1 + } + ], + "placement": { + "spacing": 24, + "separation": 4, + "salt": 165745295, + "type": "minecraft:random_spread" + } + }, + "strongholds": { + "structures": [ + { + "structure": "minecraft:stronghold", + "weight": 1 + } + ], + "placement": { + "spread": 3, + "count": 128, + "preferred_biomes": "#minecraft:stronghold_biased_to", + "distance": 32, + "salt": 0, + "type": "minecraft:concentric_rings" + } + }, + "swamp_huts": { + "structures": [ + { + "structure": "minecraft:swamp_hut", + "weight": 1 + } + ], + "placement": { + "spacing": 32, + "separation": 8, + "salt": 14357620, + "type": "minecraft:random_spread" + } + }, + "trail_ruins": { + "structures": [ + { + "structure": "minecraft:trail_ruins", + "weight": 1 + } + ], + "placement": { + "spacing": 34, + "separation": 8, + "salt": 83469867, + "type": "minecraft:random_spread" + } + }, + "trial_chambers": { + "structures": [ + { + "structure": "minecraft:trial_chambers", + "weight": 1 + } + ], + "placement": { + "spacing": 34, + "separation": 12, + "salt": 94251327, + "type": "minecraft:random_spread" + } + }, + "villages": { + "structures": [ + { + "structure": "minecraft:village_plains", + "weight": 1 + }, + { + "structure": "minecraft:village_desert", + "weight": 1 + }, + { + "structure": "minecraft:village_savanna", + "weight": 1 + }, + { + "structure": "minecraft:village_snowy", + "weight": 1 + }, + { + "structure": "minecraft:village_taiga", + "weight": 1 + } + ], + "placement": { + "spacing": 34, + "separation": 8, + "salt": 10387312, + "type": "minecraft:random_spread" + } + }, + "woodland_mansions": { + "structures": [ + { + "structure": "minecraft:mansion", + "weight": 1 + } + ], + "placement": { + "spacing": 80, + "separation": 20, + "spread_type": "triangular", + "salt": 10387319, + "type": "minecraft:random_spread" + } + } +} \ No newline at end of file diff --git a/pumpkin-util/src/random/mod.rs b/pumpkin-util/src/random/mod.rs index b9c0da66e..f0393c31f 100644 --- a/pumpkin-util/src/random/mod.rs +++ b/pumpkin-util/src/random/mod.rs @@ -68,6 +68,30 @@ pub fn get_decorator_seed(population_seed: u64, index: usize, step: usize) -> u6 .wrapping_add(10_000u64.wrapping_mul(step as u64)) } +#[inline] +pub fn get_region_seed(world_seed: u64, region_x: i32, region_z: i32, salt: i32) -> u64 { + let region_x_long: u64 = region_x as u64; + let region_z_long: u64 = region_z as u64; + let salt_long: u64 = salt as u64; + region_x_long + .wrapping_mul(341873128712) + .wrapping_add(region_z_long.wrapping_mul(132897987541)) + .wrapping_add(world_seed) + .wrapping_add(salt_long) +} + +#[inline] +pub fn get_carver_seed( + random: &mut RandomGenerator, + world_seed: u64, + chunk_x: i32, + chunk_z: i32, +) -> u64 { + let x = random.next_i64(); + let z = random.next_i64(); + (chunk_x as u64).wrapping_mul(x as u64) ^ (chunk_z as u64).wrapping_mul(z as u64) ^ world_seed +} + #[enum_dispatch] pub trait RandomImpl { fn split(&mut self) -> Self; diff --git a/pumpkin-world/src/block/entities/hopper.rs b/pumpkin-world/src/block/entities/hopper.rs index 90ea30b00..b53f0b0d6 100644 --- a/pumpkin-world/src/block/entities/hopper.rs +++ b/pumpkin-world/src/block/entities/hopper.rs @@ -4,9 +4,9 @@ use crate::inventory::{Clearable, Inventory, split_stack}; use crate::item::ItemStack; use crate::world::SimpleWorld; use async_trait::async_trait; -use pumpkin_data::Block; use pumpkin_data::block_properties::{BlockProperties, HopperFacing, HopperLikeProperties}; use pumpkin_data::tag::Taggable; +use pumpkin_data::{Block, tag}; use pumpkin_nbt::tag::NbtTag; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; @@ -184,9 +184,7 @@ impl HopperBlockEntity { } let (block, state) = world.get_block_and_state(pos_up).await; if !(state.is_solid() - && block - .is_tagged_with("minecraft:does_not_block_hoppers") - .unwrap()) + && block.is_tagged_with_by_tag(&tag::Block::MINECRAFT_DOES_NOT_BLOCK_HOPPERS)) { // TODO getItemsAtAndAbove(level, hopper) return false; diff --git a/pumpkin-world/src/chunk/format/anvil.rs b/pumpkin-world/src/chunk/format/anvil.rs index 93dec915f..c1d20c62a 100644 --- a/pumpkin-world/src/chunk/format/anvil.rs +++ b/pumpkin-world/src/chunk/format/anvil.rs @@ -37,7 +37,7 @@ pub const CHUNK_COUNT: usize = REGION_SIZE * REGION_SIZE; /// The number of bytes in a sector (4 KiB) const SECTOR_BYTES: usize = 4096; -// 1.21.7 +// 1.21.8 pub const WORLD_DATA_VERSION: i32 = 4438; #[derive(Clone, Default)] diff --git a/pumpkin-world/src/generation/generator/mod.rs b/pumpkin-world/src/generation/generator/mod.rs index 26878cba5..7ae4c6c9c 100644 --- a/pumpkin-world/src/generation/generator/mod.rs +++ b/pumpkin-world/src/generation/generator/mod.rs @@ -102,7 +102,7 @@ impl WorldGenerator for VanillaGenerator { proto_chunk.populate_biomes(self.dimension); proto_chunk.populate_noise(); proto_chunk.build_surface(); - proto_chunk.generate_features(level, block_registry); + proto_chunk.generate_features_and_structure(level, block_registry); for y in 0..biome_coords::from_block(generation_settings.shape.height) { let relative_y = y as usize; diff --git a/pumpkin-world/src/generation/mod.rs b/pumpkin-world/src/generation/mod.rs index a3ec37877..fd6015708 100644 --- a/pumpkin-world/src/generation/mod.rs +++ b/pumpkin-world/src/generation/mod.rs @@ -19,6 +19,7 @@ pub mod rule; mod rule_test; mod seed; pub mod settings; +pub mod structure; mod surface; pub mod y_offset; diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index fb12d169b..b4872df1f 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -12,6 +12,8 @@ use pumpkin_util::{ }; use crate::generation::noise::perlin::DoublePerlinNoiseSampler; +use crate::generation::structure::STRUCTURE_SETS; +use crate::generation::structure::placement::StructurePlacementCalculator; use crate::{ BlockStateId, biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier, hash_seed}, @@ -702,7 +704,7 @@ impl<'a> ProtoChunk<'a> { } } - /// This generates "Features," also known as decorations, which include things like trees, grass, ores, and more. + /// This generates "Structure Pieces" and "Features" also known as decorations, which include things like trees, grass, ores, and more. /// Essentially, it encompasses everything above the surface or underground. It's crucial that this step is executed after biomes are generated, /// as the decoration directly depends on the biome. Similarly, running this after the surface is built is logical, as it often involves checking block types. /// For example, flowers are typically placed only on grass blocks. @@ -711,7 +713,11 @@ impl<'a> ProtoChunk<'a> { /// /// 1. First, we determine **whether** to generate a feature and **at which block positions** to place it. /// 2. Then, using the second file, we determine **how** to generate the feature. - pub fn generate_features(&mut self, level: &Arc, block_registry: &dyn BlockRegistryExt) { + pub fn generate_features_and_structure( + &mut self, + level: &Arc, + block_registry: &dyn BlockRegistryExt, + ) { let chunk_pos = self.chunk_pos; let min_y = self.noise_sampler.min_y(); let height = self.noise_sampler.height(); @@ -745,6 +751,17 @@ impl<'a> ProtoChunk<'a> { } } + pub fn set_structure_starts(&self) { + for (_name, set) in STRUCTURE_SETS.iter() { + let calculator = StructurePlacementCalculator { + seed: self.random_config.seed as i64, + }; + if !set.placement.should_generate(calculator, self.chunk_pos) { + continue; // ?? + } + } + } + fn start_cell_x(&self) -> i32 { self.start_block_x() / self.noise_sampler.horizontal_cell_block_count() as i32 } diff --git a/pumpkin-world/src/generation/structure/mod.rs b/pumpkin-world/src/generation/structure/mod.rs index e69de29bb..d85252d40 100644 --- a/pumpkin-world/src/generation/structure/mod.rs +++ b/pumpkin-world/src/generation/structure/mod.rs @@ -0,0 +1,16 @@ +use std::{collections::HashMap, sync::LazyLock}; + +use pumpkin_util::read_data_from_file; +use serde::Deserialize; + +use crate::generation::structure::placement::StructurePlacement; + +pub mod placement; + +#[derive(Deserialize)] +pub struct StructureSet { + pub placement: StructurePlacement, +} + +pub static STRUCTURE_SETS: LazyLock> = + LazyLock::new(|| read_data_from_file!("../../../assets/structure_set.json")); diff --git a/pumpkin-world/src/generation/structure/placement.rs b/pumpkin-world/src/generation/structure/placement.rs new file mode 100644 index 000000000..ae2311a1e --- /dev/null +++ b/pumpkin-world/src/generation/structure/placement.rs @@ -0,0 +1,167 @@ +use pumpkin_util::{ + math::{floor_div, vector2::Vector2}, + random::{ + RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, xoroshiro128::Xoroshiro, + }, +}; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct StructurePlacement { + frequency_reduction_method: Option, + frequency: Option, + salt: i32, + #[serde(flatten)] + r#type: StructurePlacementType, +} + +impl StructurePlacement { + pub fn should_generate( + &self, + calculator: StructurePlacementCalculator, + chunk_pos: Vector2, + ) -> bool { + self.r#type + .is_start_chunk(&calculator, chunk_pos, self.salt) + && self.apply_frequency_reduction(calculator.seed, chunk_pos) + // TODO: add exclusion_zone, only used for pillager_outposts + } + + fn apply_frequency_reduction(&self, seed: i64, chunk_pos: Vector2) -> bool { + let frequency = self.frequency.unwrap_or(1.0); + frequency >= 1.0 + || self + .frequency_reduction_method + .as_ref() + .unwrap_or(&FrequencyReductionMethod::Default) + .should_generate(seed, chunk_pos, self.salt, frequency) + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum FrequencyReductionMethod { + Default, + LegacyType1, + LegacyType2, + LegacyType3, +} + +impl FrequencyReductionMethod { + pub fn should_generate( + &self, + seed: i64, + chunk_pos: Vector2, + salt: i32, + frequency: f32, + ) -> bool { + match self { + FrequencyReductionMethod::Default => { + let region_seed = get_region_seed(seed as u64, chunk_pos.x, chunk_pos.y, salt); + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); + random.next_f32() < frequency + } + FrequencyReductionMethod::LegacyType1 => { + let x = chunk_pos.x >> 4; + let z = chunk_pos.y >> 4; + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed( + (x ^ z << 4) as u64 ^ seed as u64, + )); + random.next_i32(); // yeah mojang just does that and does not use the value + random.next_bounded_i32((1.0 / frequency) as i32) == 0 + } + FrequencyReductionMethod::LegacyType2 => { + let region_seed = get_region_seed(seed as u64, chunk_pos.x, chunk_pos.y, 10387320); + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); + random.next_f32() < frequency + } + FrequencyReductionMethod::LegacyType3 => { + let mut random: RandomGenerator = + RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64)); + let carver_seed = + get_carver_seed(&mut random, seed as u64, chunk_pos.x, chunk_pos.y); + let mut random: RandomGenerator = + RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed)); + + random.next_f64() < frequency as f64 + } + } + } +} + +#[derive(Deserialize)] +#[serde(tag = "type")] +pub enum StructurePlacementType { + #[serde(rename = "minecraft:random_spread")] + RandomSpread(RandomSpreadStructurePlacement), + #[serde(rename = "minecraft:concentric_rings")] + ConcentricRings, +} + +impl StructurePlacementType { + pub fn is_start_chunk( + &self, + calculator: &StructurePlacementCalculator, + chunk_pos: Vector2, + salt: i32, + ) -> bool { + match self { + StructurePlacementType::RandomSpread(placement) => { + placement.is_start_chunk(calculator, chunk_pos, salt) + } + StructurePlacementType::ConcentricRings => false, // TODO, This is needed for Stronghold, since it is placed in rings + } + } +} + +#[derive(Deserialize)] +pub struct RandomSpreadStructurePlacement { + spacing: i32, + separation: i32, + spread_type: SpreadType, +} + +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum SpreadType { + Linear, + Triangular, +} + +impl SpreadType { + pub fn get(&self, random: &mut RandomGenerator, bound: i32) -> i32 { + match self { + SpreadType::Linear => random.next_bounded_i32(bound), + SpreadType::Triangular => { + (random.next_bounded_i32(bound) + random.next_bounded_i32(bound)) / 2 + } + } + } +} + +impl RandomSpreadStructurePlacement { + fn get_start_chunk(&self, seed: i64, chunk_pos: Vector2, salt: i32) -> Vector2 { + let x = floor_div(chunk_pos.x, self.spacing); + let z = floor_div(chunk_pos.y, self.spacing); + let region_seed = get_region_seed(seed as u64, x, z, salt); + let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed)); + let bound = self.spacing - self.separation; + let rand_x = self.spread_type.get(&mut random, bound); + let rand_z = self.spread_type.get(&mut random, bound); + Vector2::new(x * self.spacing + rand_x, z * self.spacing + rand_z) + } + + pub fn is_start_chunk( + &self, + calculator: &StructurePlacementCalculator, + chunk_pos: Vector2, + salt: i32, + ) -> bool { + let pos = self.get_start_chunk(calculator.seed, chunk_pos, salt); + pos == chunk_pos + } +} + +pub struct StructurePlacementCalculator { + pub seed: i64, +} diff --git a/pumpkin-world/src/item/categories.rs b/pumpkin-world/src/item/categories.rs index 9ee75f33e..c8d8a004a 100644 --- a/pumpkin-world/src/item/categories.rs +++ b/pumpkin-world/src/item/categories.rs @@ -1,54 +1,42 @@ +use pumpkin_data::tag; use pumpkin_data::tag::Taggable; use crate::item::ItemStack; -const SWORDS_TAG: &str = "#minecraft:swords"; -const HEAD_ARMOR_TAG: &str = "#minecraft:head_armor"; -const SKULL_TAG: &str = "#minecraft:skulls"; -const CHEST_ARMOR_TAG: &str = "#minecraft:chest_armor"; -const LEG_ARMOR_TAG: &str = "#minecraft:leg_armor"; -const FOOT_ARMOR_TAG: &str = "#minecraft:foot_armor"; - impl ItemStack { #[inline] pub fn is_sword(&self) -> bool { - self.item.is_tagged_with(SWORDS_TAG).expect( - "This is a default minecraft tag that should have been gotten from the extractor", - ) + self.item + .is_tagged_with_by_tag(&tag::Item::MINECRAFT_SWORDS) } #[inline] pub fn is_helmet(&self) -> bool { - self.item.is_tagged_with(HEAD_ARMOR_TAG).expect( - "This is a default minecraft tag that should have been gotten from the extractor", - ) + self.item + .is_tagged_with_by_tag(&tag::Item::MINECRAFT_HEAD_ARMOR) } #[inline] pub fn is_skull(&self) -> bool { - self.item.is_tagged_with(SKULL_TAG).expect( - "This is a default minecraft tag that should have been gotten from the extractor", - ) + self.item + .is_tagged_with_by_tag(&tag::Item::MINECRAFT_SKULLS) } #[inline] pub fn is_chestplate(&self) -> bool { - self.item.is_tagged_with(CHEST_ARMOR_TAG).expect( - "This is a default minecraft tag that should have been gotten from the extractor", - ) + self.item + .is_tagged_with_by_tag(&tag::Item::MINECRAFT_CHEST_ARMOR) } #[inline] pub fn is_leggings(&self) -> bool { - self.item.is_tagged_with(LEG_ARMOR_TAG).expect( - "This is a default minecraft tag that should have been gotten from the extractor", - ) + self.item + .is_tagged_with_by_tag(&tag::Item::MINECRAFT_LEG_ARMOR) } #[inline] pub fn is_boots(&self) -> bool { - self.item.is_tagged_with(FOOT_ARMOR_TAG).expect( - "This is a default minecraft tag that should have been gotten from the extractor", - ) + self.item + .is_tagged_with_by_tag(&tag::Item::MINECRAFT_FOOT_ARMOR) } } diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 43ea948e8..c39b0e36c 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -22,7 +22,7 @@ pub mod world_info; pub type BlockId = u16; pub type BlockStateId = u16; -pub const CURRENT_MC_VERSION: &str = "1.21.7"; +pub const CURRENT_MC_VERSION: &str = "1.21.8"; pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.94"; #[macro_export] diff --git a/pumpkin-world/src/world_info/mod.rs b/pumpkin-world/src/world_info/mod.rs index 497a16613..5104473ac 100644 --- a/pumpkin-world/src/world_info/mod.rs +++ b/pumpkin-world/src/world_info/mod.rs @@ -12,11 +12,11 @@ use thiserror::Error; pub mod anvil; // Constraint: disk biome palette serialization changed in 1.21.5 -pub const MINIMUM_SUPPORTED_WORLD_DATA_VERSION: i32 = 4435; // 1.21.7 -pub const MAXIMUM_SUPPORTED_WORLD_DATA_VERSION: i32 = 4438; // 1.21.7 +pub const MINIMUM_SUPPORTED_WORLD_DATA_VERSION: i32 = 4435; // 1.21.8 +pub const MAXIMUM_SUPPORTED_WORLD_DATA_VERSION: i32 = 4438; // 1.21.8 -pub const MINIMUM_SUPPORTED_LEVEL_VERSION: i32 = 19132; // 1.21.7 -pub const MAXIMUM_SUPPORTED_LEVEL_VERSION: i32 = 19133; // 1.21.7 +pub const MINIMUM_SUPPORTED_LEVEL_VERSION: i32 = 19132; // 1.21.8 +pub const MAXIMUM_SUPPORTED_LEVEL_VERSION: i32 = 19133; // 1.21.8 pub trait WorldInfoReader { fn read_world_info(&self, level_folder: &Path) -> Result; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index bc531bf54..46d5bf1da 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -93,7 +93,7 @@ use pumpkin_data::potion::Effect; const MAX_CACHED_SIGNATURES: u8 = 128; // Vanilla: 128 const MAX_PREVIOUS_MESSAGES: u8 = 20; // Vanilla: 20 -pub const DATA_VERSION: i32 = 4438; // 1.21.7 +pub const DATA_VERSION: i32 = 4438; // 1.21.8 enum BatchState { Initial,