mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
add Chunk features (#685)
* inital chunk features * add more things great title i know * add some features still no result, but we are getting there... * make it parse * add simple BlockPredicate * add simple random selector * new things * change stuff * base tree impl * change things * add more heightmaps * fix master merge * steps towards proper random * fix upper level block placement logic * Make it compile * fix block dir serde name * Added Tree Foliage * more Tree work * Implement would_survive check I don't say its good, but it works :D * Fixed biome placement modifier (#836) * Implement all tree foliages * Fix grass, flowers, seagrass, seapickles... * Add Ores * add Bamboo, Vine, Spring feature Also more fixes * Corals * Add Tree decoration & more Many many fixes * fix vines * fixed bamboo * fix some clippy warns * fix trees outside of chunk * make it compile again upsi * fix clippy warns * fix InsideWorldBoundsBlockPredicate * set dirt under tress trunk dirt, not grass * more trees work --------- Co-authored-by: kralverde <github@email.kralverde.dev> Co-authored-by: Laurent Stéphenne <laurent@guibi.dev>
This commit is contained in:
committed by
GitHub
parent
00df0d7ee7
commit
abccd3f700
493
Cargo.lock
generated
493
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,7 @@ futures = "0.3"
|
||||
rayon = "1.10"
|
||||
crossbeam = "0.8"
|
||||
|
||||
uuid = { version = "1.17", features = ["serde", "v3", "v4"] }
|
||||
uuid = { version = "1.16", features = ["serde", "v3", "v4"] }
|
||||
derive_more = { version = "2.0", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
@@ -275,15 +275,20 @@ impl ToTokens for BlockPropertyStruct {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handles_block_id(block_id: u16) -> bool where Self: Sized {
|
||||
[#(#block_ids),*].contains(&block_id)
|
||||
}
|
||||
|
||||
fn to_state_id(&self, block: &Block) -> u16 {
|
||||
if ![#(#block_ids),*].contains(&block.id) {
|
||||
if !Self::handles_block_id(block.id) {
|
||||
panic!("{} is not a valid block for {}", &block.name, #struct_name);
|
||||
}
|
||||
block.states[0].id + self.to_index()
|
||||
}
|
||||
|
||||
fn from_state_id(state_id: u16, block: &Block) -> Self {
|
||||
if ![#(#block_ids),*].contains(&block.id) {
|
||||
if !Self::handles_block_id(block.id) {
|
||||
panic!("{} is not a valid block for {}", &block.name, #struct_name);
|
||||
}
|
||||
if state_id >= block.states[0].id && state_id <= block.states.last().unwrap().id {
|
||||
@@ -295,7 +300,7 @@ impl ToTokens for BlockPropertyStruct {
|
||||
}
|
||||
|
||||
fn default(block: &Block) -> Self {
|
||||
if ![#(#block_ids),*].contains(&block.id) {
|
||||
if !Self::handles_block_id(block.id) {
|
||||
panic!("{} is not a valid block for {}", &block.name, #struct_name);
|
||||
}
|
||||
Self::from_state_id(block.default_state_id, block)
|
||||
@@ -307,7 +312,6 @@ impl ToTokens for BlockPropertyStruct {
|
||||
#(#to_props_values)*
|
||||
props
|
||||
}
|
||||
|
||||
fn from_props(props: Vec<(&str, &str)>, block: &Block) -> Self {
|
||||
if ![#(#block_ids),*].contains(&block.id) {
|
||||
panic!("{} is not a valid block for {}", &block.name, #struct_name);
|
||||
@@ -1316,6 +1320,9 @@ pub(crate) fn build() -> TokenStream {
|
||||
// Convert an index back to properties.
|
||||
fn from_index(index: u16) -> Self where Self: Sized;
|
||||
|
||||
// Check if a block uses this property
|
||||
fn handles_block_id(block_id: u16) -> bool where Self: Sized;
|
||||
|
||||
// Convert properties to a state id.
|
||||
fn to_state_id(&self, block: &Block) -> u16;
|
||||
// Convert a state id back to properties.
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use pumpkin_util::DoublePerlinNoiseParametersCodec;
|
||||
use quote::{format_ident, quote};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DoublePerlinNoiseParameters {
|
||||
#[serde(rename = "firstOctave")]
|
||||
first_octave: i32,
|
||||
amplitudes: Vec<f64>,
|
||||
}
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/noise_parameters.json");
|
||||
|
||||
let json: HashMap<String, DoublePerlinNoiseParameters> =
|
||||
let json: HashMap<String, DoublePerlinNoiseParametersCodec> =
|
||||
serde_json::from_str(include_str!("../../assets/noise_parameters.json"))
|
||||
.expect("Failed to parse noise_parameters.json");
|
||||
let mut variants = TokenStream::new();
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
use crate::block_properties::{Axis, Facing, HorizontalFacing};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::{
|
||||
math::vector3::Vector3,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq)]
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum BlockDirection {
|
||||
Down = 0,
|
||||
Up,
|
||||
@@ -54,6 +59,10 @@ impl BlockDirection {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random(random: &mut RandomGenerator) -> Self {
|
||||
Self::all()[random.next_bounded_i32(Self::all().len() as i32 - 1) as usize]
|
||||
}
|
||||
|
||||
pub fn by_index(index: usize) -> Option<Self> {
|
||||
Self::all().get(index % Self::all().len()).cloned()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use std::io::Write;
|
||||
|
||||
use crate::{
|
||||
ClientPacket,
|
||||
codec::identifier::Identifier,
|
||||
ser::{NetworkWriteExt, WritingError},
|
||||
};
|
||||
|
||||
use pumpkin_data::{
|
||||
block_properties::get_block,
|
||||
fluid::Fluid,
|
||||
@@ -8,12 +14,6 @@ use pumpkin_data::{
|
||||
};
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{
|
||||
ClientPacket,
|
||||
codec::identifier::Identifier,
|
||||
ser::{NetworkWriteExt, WritingError},
|
||||
};
|
||||
|
||||
#[packet(CONFIG_UPDATE_TAGS)]
|
||||
pub struct CUpdateTags<'a> {
|
||||
tags: &'a [pumpkin_data::tag::RegistryKey],
|
||||
|
||||
@@ -107,6 +107,13 @@ impl<T> Index<usize> for MutableSplitSlice<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct DoublePerlinNoiseParametersCodec {
|
||||
#[serde(rename = "firstOctave")]
|
||||
pub first_octave: i32,
|
||||
pub amplitudes: Vec<f64>,
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for MutableSplitSlice<'_, T> {
|
||||
#[allow(clippy::comparison_chain)]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
|
||||
@@ -3,12 +3,23 @@ use quote::{ToTokens, quote};
|
||||
use serde::Deserialize;
|
||||
use syn::LitInt;
|
||||
|
||||
use crate::random::{RandomGenerator, RandomImpl};
|
||||
|
||||
use super::pool::{Pool, Weighted};
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NormalIntProvider {
|
||||
#[serde(rename = "minecraft:uniform")]
|
||||
Uniform(UniformIntProvider),
|
||||
// TODO: Add more...
|
||||
#[serde(rename = "minecraft:weighted_list")]
|
||||
WeightedList(WeightedListIntProvider),
|
||||
#[serde(rename = "minecraft:clamped")]
|
||||
Clamped(ClampedIntProvider),
|
||||
#[serde(rename = "minecraft:clamped_normal")]
|
||||
ClampedNormal(ClampedNormalIntProvider),
|
||||
#[serde(rename = "minecraft:biased_to_bottom")]
|
||||
BiasedToBottom(BiasedToBottomIntProvider), // TODO: Add more...
|
||||
}
|
||||
|
||||
impl ToTokens for NormalIntProvider {
|
||||
@@ -19,6 +30,10 @@ impl ToTokens for NormalIntProvider {
|
||||
NormalIntProvider::Uniform(#uniform)
|
||||
});
|
||||
}
|
||||
NormalIntProvider::WeightedList(_) => todo!(),
|
||||
NormalIntProvider::Clamped(_) => todo!(),
|
||||
NormalIntProvider::BiasedToBottom(_biased_to_bottom_int_provider) => todo!(),
|
||||
NormalIntProvider::ClampedNormal(_clamped_int_provider) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,15 +65,23 @@ impl IntProvider {
|
||||
match self {
|
||||
IntProvider::Object(int_provider) => match int_provider {
|
||||
NormalIntProvider::Uniform(uniform) => uniform.get_min(),
|
||||
NormalIntProvider::WeightedList(provider) => provider.get_min(),
|
||||
NormalIntProvider::Clamped(provider) => provider.get_min(),
|
||||
NormalIntProvider::BiasedToBottom(provider) => provider.get_min(),
|
||||
NormalIntProvider::ClampedNormal(provider) => provider.get_min(),
|
||||
},
|
||||
IntProvider::Constant(i) => *i,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> i32 {
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> i32 {
|
||||
match self {
|
||||
IntProvider::Object(int_provider) => match int_provider {
|
||||
NormalIntProvider::Uniform(uniform) => uniform.get(),
|
||||
NormalIntProvider::Uniform(uniform) => uniform.get(random),
|
||||
NormalIntProvider::WeightedList(provider) => provider.get(random),
|
||||
NormalIntProvider::Clamped(provider) => provider.get(random),
|
||||
NormalIntProvider::BiasedToBottom(provider) => provider.get(random),
|
||||
NormalIntProvider::ClampedNormal(provider) => provider.get(random),
|
||||
},
|
||||
IntProvider::Constant(i) => *i,
|
||||
}
|
||||
@@ -68,12 +91,108 @@ impl IntProvider {
|
||||
match self {
|
||||
IntProvider::Object(int_provider) => match int_provider {
|
||||
NormalIntProvider::Uniform(uniform) => uniform.get_max(),
|
||||
NormalIntProvider::WeightedList(provider) => provider.get_max(),
|
||||
NormalIntProvider::Clamped(provider) => provider.get_max(),
|
||||
NormalIntProvider::BiasedToBottom(provider) => provider.get_max(),
|
||||
NormalIntProvider::ClampedNormal(provider) => provider.get_max(),
|
||||
},
|
||||
IntProvider::Constant(i) => *i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct ClampedNormalIntProvider {
|
||||
mean: f32,
|
||||
deviation: f32,
|
||||
min_inclusive: i32,
|
||||
max_inclusive: i32,
|
||||
}
|
||||
|
||||
impl ClampedNormalIntProvider {
|
||||
pub fn get_min(&self) -> i32 {
|
||||
self.min_inclusive
|
||||
}
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> i32 {
|
||||
(self.mean + random.next_gaussian() as f32 * self.deviation)
|
||||
.clamp(self.min_inclusive as f32, self.max_inclusive as f32) as i32
|
||||
}
|
||||
pub fn get_max(&self) -> i32 {
|
||||
self.max_inclusive
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct BiasedToBottomIntProvider {
|
||||
min_inclusive: i32,
|
||||
max_inclusive: i32,
|
||||
}
|
||||
|
||||
impl BiasedToBottomIntProvider {
|
||||
pub fn get_min(&self) -> i32 {
|
||||
self.min_inclusive
|
||||
}
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> i32 {
|
||||
// TODO: not sure if this is called first this matches vanilla
|
||||
let first_gen = random.next_bounded_i32(self.max_inclusive - self.min_inclusive + 1) + 1;
|
||||
self.min_inclusive + random.next_bounded_i32(first_gen)
|
||||
}
|
||||
pub fn get_max(&self) -> i32 {
|
||||
self.max_inclusive
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct ClampedIntProvider {
|
||||
source: Box<IntProvider>,
|
||||
min_inclusive: i32,
|
||||
max_inclusive: i32,
|
||||
}
|
||||
|
||||
impl ClampedIntProvider {
|
||||
pub fn get_min(&self) -> i32 {
|
||||
self.min_inclusive.max(self.source.get_min())
|
||||
}
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> i32 {
|
||||
self.source
|
||||
.get(random)
|
||||
.clamp(self.min_inclusive, self.max_inclusive)
|
||||
}
|
||||
pub fn get_max(&self) -> i32 {
|
||||
self.max_inclusive.min(self.source.get_max())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct WeightedListIntProvider {
|
||||
distribution: Vec<Weighted<IntProvider>>,
|
||||
}
|
||||
|
||||
impl WeightedListIntProvider {
|
||||
pub fn get_min(&self) -> i32 {
|
||||
let mut min = i32::MAX;
|
||||
for dist in &self.distribution {
|
||||
let dmin = dist.data.get_min();
|
||||
min = min.min(dmin);
|
||||
}
|
||||
min
|
||||
}
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> i32 {
|
||||
if let Some(int) = Pool.get(&self.distribution, random) {
|
||||
return int.get(random);
|
||||
}
|
||||
0
|
||||
}
|
||||
pub fn get_max(&self) -> i32 {
|
||||
let mut max = i32::MIN;
|
||||
for dist in &self.distribution {
|
||||
let dmax = dist.data.get_max();
|
||||
max = max.max(dmax);
|
||||
}
|
||||
max
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct UniformIntProvider {
|
||||
pub min_inclusive: i32,
|
||||
@@ -95,8 +214,8 @@ impl UniformIntProvider {
|
||||
pub fn get_min(&self) -> i32 {
|
||||
self.min_inclusive
|
||||
}
|
||||
pub fn get(&self) -> i32 {
|
||||
rand::random_range(self.min_inclusive..self.max_inclusive)
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> i32 {
|
||||
random.next_inbetween_i32(self.min_inclusive, self.max_inclusive)
|
||||
}
|
||||
pub fn get_max(&self) -> i32 {
|
||||
self.max_inclusive
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod boundingbox;
|
||||
pub mod experience;
|
||||
pub mod float_provider;
|
||||
pub mod int_provider;
|
||||
pub mod pool;
|
||||
pub mod position;
|
||||
pub mod vector2;
|
||||
pub mod vector3;
|
||||
|
||||
55
pumpkin-util/src/math/pool.rs
Normal file
55
pumpkin-util/src/math/pool.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::random::{RandomGenerator, RandomImpl};
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct Pool;
|
||||
|
||||
impl Pool {
|
||||
pub fn get<E: Clone>(
|
||||
&self,
|
||||
distribution: &[Weighted<E>],
|
||||
random: &mut RandomGenerator,
|
||||
) -> Option<E> {
|
||||
let mut total_weight = 0;
|
||||
for dist in distribution {
|
||||
total_weight += dist.weight;
|
||||
}
|
||||
let index = random.next_bounded_i32(total_weight);
|
||||
if total_weight < 64 {
|
||||
return Some(FlattenedContent::get(index, distribution, total_weight));
|
||||
} else {
|
||||
// WrappedContent
|
||||
for dist in distribution {
|
||||
if index - dist.weight >= 0 {
|
||||
continue;
|
||||
}
|
||||
return Some(dist.data.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct Weighted<E> {
|
||||
pub data: E,
|
||||
pub weight: i32,
|
||||
}
|
||||
|
||||
struct FlattenedContent;
|
||||
|
||||
impl FlattenedContent {
|
||||
pub fn get<E: Clone>(index: i32, entries: &[Weighted<E>], total_weight: i32) -> E {
|
||||
let mut final_entries = Vec::with_capacity(total_weight as usize);
|
||||
let mut cur_index = 0;
|
||||
for entry in entries {
|
||||
let weight = entry.weight;
|
||||
for i in cur_index..cur_index + weight {
|
||||
final_entries.insert(i as usize, entry.data.clone());
|
||||
}
|
||||
cur_index += weight;
|
||||
}
|
||||
final_entries[index as usize].clone()
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,10 @@ impl BlockPos {
|
||||
BlockPos(self.0 + offset)
|
||||
}
|
||||
|
||||
pub fn add(&self, x: i32, y: i32, z: i32) -> Self {
|
||||
BlockPos::new(self.0.x + x, self.0.y + y, self.0.z + z)
|
||||
}
|
||||
|
||||
pub fn offset_dir(&self, offset: Vector3<i32>, direction: i32) -> Self {
|
||||
BlockPos(Vector3::new(
|
||||
self.0.x + offset.x * direction,
|
||||
@@ -168,9 +172,17 @@ impl BlockPos {
|
||||
self.offset(Vector3::new(0, 1, 0))
|
||||
}
|
||||
|
||||
pub fn up_height(&self, height: i32) -> Self {
|
||||
self.offset(Vector3::new(0, height, 0))
|
||||
}
|
||||
|
||||
pub fn down(&self) -> Self {
|
||||
self.offset(Vector3::new(0, -1, 0))
|
||||
}
|
||||
|
||||
pub fn down_height(&self, height: i32) -> Self {
|
||||
self.offset(Vector3::new(0, -height, 0))
|
||||
}
|
||||
}
|
||||
impl Serialize for BlockPos {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
||||
@@ -239,6 +239,39 @@ impl Math for i32 {}
|
||||
impl Math for i64 {}
|
||||
impl Math for u8 {}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Vector3<i32> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct Vector3Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Vector3Visitor {
|
||||
type Value = Vector3<i32>;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a valid Vector<i32>")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
if let Some(x) = seq.next_element::<i32>()? {
|
||||
if let Some(y) = seq.next_element::<i32>()? {
|
||||
if let Some(z) = seq.next_element::<i32>()? {
|
||||
return Ok(Vector3::new(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(serde::de::Error::custom("Failed to read Vector<i32>"))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_seq(Vector3Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Vector3<f32> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
||||
@@ -3,7 +3,7 @@ use super::{
|
||||
hash_block_pos,
|
||||
};
|
||||
|
||||
use crate::math::java_string_hash;
|
||||
use crate::{math::java_string_hash, population_seed_fn};
|
||||
|
||||
pub struct LegacyRand {
|
||||
seed: u64,
|
||||
@@ -11,6 +11,8 @@ pub struct LegacyRand {
|
||||
}
|
||||
|
||||
impl LegacyRand {
|
||||
population_seed_fn!();
|
||||
|
||||
pub fn from_seed(seed: u64) -> Self {
|
||||
LegacyRand {
|
||||
seed: (seed ^ 0x5DEECE66D) & 0xFFFFFFFFFFFF,
|
||||
|
||||
@@ -44,6 +44,30 @@ pub enum RandomDeriver {
|
||||
Legacy(LegacySplitter),
|
||||
}
|
||||
|
||||
// TODO: Write unit test for this
|
||||
#[macro_export]
|
||||
macro_rules! population_seed_fn {
|
||||
() => {
|
||||
pub fn get_population_seed(world_seed: u64, block_x: i32, block_z: i32) -> u64 {
|
||||
let mut rand = Self::from_seed(world_seed);
|
||||
let l = rand.next_i64() | 1;
|
||||
let m = rand.next_i64() | 1;
|
||||
let base = (block_x as i64)
|
||||
.wrapping_mul(l)
|
||||
.wrapping_add((block_z as i64).wrapping_mul(m));
|
||||
(base as u64) ^ world_seed
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Write unit test for this
|
||||
#[inline]
|
||||
pub fn get_decorator_seed(population_seed: u64, index: usize, step: usize) -> u64 {
|
||||
population_seed
|
||||
.wrapping_add(index as u64)
|
||||
.wrapping_add(10_000u64.wrapping_mul(step as u64))
|
||||
}
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait RandomImpl {
|
||||
fn split(&mut self) -> Self;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::population_seed_fn;
|
||||
|
||||
use super::{
|
||||
RandomDeriver, RandomDeriverImpl, RandomGenerator, RandomImpl, gaussian::GaussianGenerator,
|
||||
hash_block_pos,
|
||||
@@ -10,6 +12,8 @@ pub struct Xoroshiro {
|
||||
}
|
||||
|
||||
impl Xoroshiro {
|
||||
population_seed_fn!();
|
||||
|
||||
pub fn from_seed(seed: u64) -> Self {
|
||||
let (lo, hi) = Self::mix_u64(seed);
|
||||
let lo = mix_stafford_13(lo);
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
use std::{fs, path::PathBuf, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_world::{chunk::ChunkData, dimension::Dimension, global_path, level::Level};
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
|
||||
use pumpkin_world::{
|
||||
chunk::ChunkData,
|
||||
dimension::Dimension,
|
||||
global_path,
|
||||
level::Level,
|
||||
world::{BlockAccessor, BlockRegistryExt},
|
||||
};
|
||||
use tokio::{runtime::Runtime, sync::RwLock};
|
||||
|
||||
async fn test_reads(level: &Arc<Level>, positions: Vec<Vector2<i32>>) {
|
||||
@@ -69,6 +77,21 @@ const MAX_CHUNK: i32 = 16;
|
||||
// How many chunks to use on parallel tests
|
||||
//const CHUNKS_ON_PARALLEL: usize = 32;
|
||||
|
||||
struct BlockRegistry;
|
||||
|
||||
#[async_trait]
|
||||
impl BlockRegistryExt for BlockRegistry {
|
||||
async fn can_place_at(
|
||||
&self,
|
||||
_block: &pumpkin_data::Block,
|
||||
_block_accessor: &dyn BlockAccessor,
|
||||
_block_pos: &BlockPos,
|
||||
_face: BlockDirection,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_level(
|
||||
async_handler: &Runtime,
|
||||
root_dir: PathBuf,
|
||||
@@ -78,10 +101,12 @@ fn initialize_level(
|
||||
let mut chunks = Vec::new();
|
||||
async_handler.block_on(async {
|
||||
let (send, mut recv) = tokio::sync::mpsc::unbounded_channel();
|
||||
let block_registry = Arc::new(BlockRegistry);
|
||||
|
||||
// Our data dir is empty, so we're generating new chunks here
|
||||
let level_to_save = Arc::new(Level::from_root_folder(
|
||||
root_dir.clone(),
|
||||
block_registry,
|
||||
123,
|
||||
Dimension::Overworld,
|
||||
));
|
||||
@@ -193,6 +218,8 @@ fn bench_chunk_io(c: &mut Criterion) {
|
||||
n_chunks,
|
||||
chunks.len()
|
||||
);
|
||||
let block_registry = Arc::new(BlockRegistry);
|
||||
|
||||
write_group.bench_with_input(
|
||||
BenchmarkId::new("Single", n_chunks),
|
||||
&chunks,
|
||||
@@ -200,6 +227,7 @@ fn bench_chunk_io(c: &mut Criterion) {
|
||||
b.to_async(&async_handler).iter(async || {
|
||||
let level = Arc::new(Level::from_root_folder(
|
||||
root_dir.clone(),
|
||||
block_registry.clone(),
|
||||
123,
|
||||
Dimension::Overworld,
|
||||
));
|
||||
@@ -222,6 +250,7 @@ fn bench_chunk_io(c: &mut Criterion) {
|
||||
n_chunks,
|
||||
positions.len()
|
||||
);
|
||||
let block_registry = Arc::new(BlockRegistry);
|
||||
|
||||
read_group.bench_with_input(
|
||||
BenchmarkId::new("Single", n_chunks),
|
||||
@@ -230,6 +259,7 @@ fn bench_chunk_io(c: &mut Criterion) {
|
||||
b.to_async(&async_handler).iter(async || {
|
||||
let level = Arc::new(Level::from_root_folder(
|
||||
root_dir.clone(),
|
||||
block_registry.clone(),
|
||||
123,
|
||||
Dimension::Overworld,
|
||||
));
|
||||
|
||||
@@ -1,14 +1,46 @@
|
||||
pub mod entities;
|
||||
pub mod state;
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pumpkin_data::{
|
||||
BlockState,
|
||||
block_properties::{get_block, get_state_by_state_id},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use state::RawBlockState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct BlockStateCodec {
|
||||
/// Block name
|
||||
pub name: String,
|
||||
// TODO: properties...
|
||||
/// Key-value pairs of properties
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub properties: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl BlockStateCodec {
|
||||
pub fn get_state(&self) -> Option<BlockState> {
|
||||
let block = get_block(self.name.as_str());
|
||||
|
||||
if let Some(block) = block {
|
||||
let mut state_id = block.default_state_id;
|
||||
|
||||
if let Some(properties) = &self.properties {
|
||||
let mut properties_vec: Vec<(&str, &str)> = Vec::with_capacity(properties.len());
|
||||
for (key, value) in properties {
|
||||
properties_vec.push((key, value));
|
||||
}
|
||||
let block_properties = block.from_properties(properties_vec).unwrap();
|
||||
state_id = block_properties.to_state_id(&block);
|
||||
}
|
||||
|
||||
return get_state_by_state_id(state_id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use pumpkin_data::block_properties::{get_block, get_block_by_state_id, get_state_by_state_id};
|
||||
|
||||
use crate::{BlockStateId, chunk::format::PaletteBlockEntry};
|
||||
use crate::BlockStateId;
|
||||
|
||||
/// Instead of using a memory heavy normal BlockState This is used for internal representation in chunks to save memory
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -19,27 +19,6 @@ impl RawBlockState {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_palette(palette: &PaletteBlockEntry) -> Option<Self> {
|
||||
let block = get_block(palette.name.as_str());
|
||||
|
||||
if let Some(block) = block {
|
||||
let mut state_id = block.default_state_id;
|
||||
|
||||
if let Some(properties) = &palette.properties {
|
||||
let mut properties_vec: Vec<(&str, &str)> = Vec::with_capacity(properties.len());
|
||||
for (key, value) in properties {
|
||||
properties_vec.push((key, value));
|
||||
}
|
||||
let block_properties = block.from_properties(properties_vec).unwrap();
|
||||
state_id = block_properties.to_state_id(&block);
|
||||
}
|
||||
|
||||
return Some(Self { state_id });
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn to_state(&self) -> pumpkin_data::BlockState {
|
||||
get_state_by_state_id(self.state_id).unwrap()
|
||||
}
|
||||
|
||||
@@ -901,7 +901,10 @@ pub async fn chunk_to_bytes(chunk_data: &ChunkData) -> Result<Vec<u8>, ChunkSeri
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_config::{AdvancedConfiguration, advanced_config, override_config_for_testing};
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
@@ -914,7 +917,23 @@ mod tests {
|
||||
use crate::chunk::io::{ChunkIO, LoadedData};
|
||||
use crate::dimension::Dimension;
|
||||
use crate::generation::{Seed, get_world_gen};
|
||||
use crate::level::{LevelFolder, SyncChunk};
|
||||
use crate::level::{Level, LevelFolder, SyncChunk};
|
||||
use crate::world::{BlockAccessor, BlockRegistryExt};
|
||||
|
||||
struct BlockRegistry;
|
||||
|
||||
#[async_trait]
|
||||
impl BlockRegistryExt for BlockRegistry {
|
||||
async fn can_place_at(
|
||||
&self,
|
||||
_block: &pumpkin_data::Block,
|
||||
_block_accessor: &dyn BlockAccessor,
|
||||
_block_pos: &BlockPos,
|
||||
_face: BlockDirection,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_chunks(
|
||||
saver: &ChunkFileManager<AnvilChunkFile>,
|
||||
@@ -992,13 +1011,22 @@ mod tests {
|
||||
};
|
||||
fs::create_dir(&level_folder.region_folder).expect("couldn't create region folder");
|
||||
let chunk_saver = ChunkFileManager::<AnvilChunkFile>::default();
|
||||
let block_registry = Arc::new(BlockRegistry);
|
||||
|
||||
// Generate chunks
|
||||
let mut chunks = vec![];
|
||||
let level = Arc::new(Level::from_root_folder(
|
||||
temp_dir.path().to_path_buf(),
|
||||
block_registry.clone(),
|
||||
0,
|
||||
Dimension::Overworld,
|
||||
));
|
||||
for x in -5..5 {
|
||||
for y in -5..5 {
|
||||
let position = Vector2::new(x, y);
|
||||
let chunk = generator.generate_chunk(&position);
|
||||
let chunk = generator
|
||||
.generate_chunk(&level, block_registry.as_ref(), &position)
|
||||
.await;
|
||||
chunks.push((position, Arc::new(RwLock::new(chunk))));
|
||||
}
|
||||
}
|
||||
@@ -1260,13 +1288,22 @@ mod tests {
|
||||
};
|
||||
fs::create_dir(&level_folder.region_folder).expect("couldn't create region folder");
|
||||
let chunk_saver = ChunkFileManager::<AnvilChunkFile>::default();
|
||||
let block_registry = Arc::new(BlockRegistry);
|
||||
|
||||
// Generate chunks
|
||||
let mut chunks = vec![];
|
||||
let level = Arc::new(Level::from_root_folder(
|
||||
temp_dir.path().to_path_buf(),
|
||||
block_registry.clone(),
|
||||
0,
|
||||
Dimension::Overworld,
|
||||
));
|
||||
for x in -5..5 {
|
||||
for y in -5..5 {
|
||||
let position = Vector2::new(x, y);
|
||||
let chunk = generator.generate_chunk(&position);
|
||||
let chunk = generator
|
||||
.generate_chunk(&level, block_registry.as_ref(), &position)
|
||||
.await;
|
||||
chunks.push((position, Arc::new(RwLock::new(chunk))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,10 @@ impl ChunkSerializer for LinearFile {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_trait::async_trait;
|
||||
use core::panic;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
@@ -378,7 +381,23 @@ mod tests {
|
||||
use crate::chunk::io::{ChunkIO, LoadedData};
|
||||
use crate::dimension::Dimension;
|
||||
use crate::generation::{Seed, get_world_gen};
|
||||
use crate::level::LevelFolder;
|
||||
use crate::level::{Level, LevelFolder};
|
||||
use crate::world::{BlockAccessor, BlockRegistryExt};
|
||||
|
||||
struct BlockRegistry;
|
||||
|
||||
#[async_trait]
|
||||
impl BlockRegistryExt for BlockRegistry {
|
||||
async fn can_place_at(
|
||||
&self,
|
||||
_block: &pumpkin_data::Block,
|
||||
_block_accessor: &dyn BlockAccessor,
|
||||
_block_pos: &BlockPos,
|
||||
_face: BlockDirection,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn not_existing() {
|
||||
@@ -419,13 +438,21 @@ mod tests {
|
||||
};
|
||||
fs::create_dir(&level_folder.region_folder).expect("couldn't create region folder");
|
||||
let chunk_saver = ChunkFileManager::<LinearFile>::default();
|
||||
|
||||
let block_registry = Arc::new(BlockRegistry);
|
||||
// Generate chunks
|
||||
let mut chunks = vec![];
|
||||
let level = Arc::new(Level::from_root_folder(
|
||||
temp_dir.path().to_path_buf(),
|
||||
block_registry.clone(),
|
||||
0,
|
||||
Dimension::Overworld,
|
||||
));
|
||||
for x in -5..5 {
|
||||
for y in -5..5 {
|
||||
let position = Vector2::new(x, y);
|
||||
let chunk = generator.generate_chunk(&position);
|
||||
let chunk = generator
|
||||
.generate_chunk(&level, block_registry.as_ref(), &position)
|
||||
.await;
|
||||
chunks.push((position, Arc::new(RwLock::new(chunk))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ use crate::{block::entities::block_entity_from_nbt, generation::section_coords};
|
||||
use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::block::BlockStateCodec;
|
||||
|
||||
use super::{
|
||||
ChunkData, ChunkHeightmaps, ChunkLight, ChunkParsingError, ChunkSections, ScheduledTick,
|
||||
SubChunk, TickPriority,
|
||||
@@ -216,7 +218,7 @@ pub struct ChunkSectionBlockStates {
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub(crate) data: Option<Box<[i64]>>,
|
||||
pub(crate) palette: Vec<PaletteBlockEntry>,
|
||||
pub(crate) palette: Vec<BlockStateCodec>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -293,16 +295,6 @@ impl Default for LightContainer {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct PaletteBlockEntry {
|
||||
/// Block name
|
||||
pub name: String,
|
||||
/// Key-value pairs of properties
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub properties: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct SerializedScheduledTick {
|
||||
#[serde(rename = "x")]
|
||||
|
||||
@@ -224,7 +224,7 @@ impl ChunkSections {
|
||||
block_state: BlockStateId,
|
||||
) {
|
||||
let y = y - self.min_y;
|
||||
debug_assert!(y >= 0);
|
||||
debug_assert!(y > 0);
|
||||
let relative_y = y as usize;
|
||||
|
||||
self.set_relative_block(relative_x, relative_y, relative_z, block_state);
|
||||
|
||||
@@ -7,11 +7,9 @@ use std::{
|
||||
use pumpkin_data::{Block, block_properties::get_state_by_state_id, chunk::Biome};
|
||||
use pumpkin_util::encompassing_bits;
|
||||
|
||||
use crate::block::RawBlockState;
|
||||
use crate::block::BlockStateCodec;
|
||||
|
||||
use super::format::{
|
||||
ChunkSectionBiomes, ChunkSectionBlockStates, PaletteBiomeEntry, PaletteBlockEntry,
|
||||
};
|
||||
use super::format::{ChunkSectionBiomes, ChunkSectionBlockStates, PaletteBiomeEntry};
|
||||
|
||||
/// 3d array indexed by y,z,x
|
||||
type AbstractCube<T, const DIM: usize> = [[[T; DIM]; DIM]; DIM];
|
||||
@@ -401,8 +399,8 @@ impl BlockPalette {
|
||||
.palette
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
if let Some(block_state) = RawBlockState::from_palette(&entry) {
|
||||
block_state.state_id
|
||||
if let Some(block_state) = entry.get_state() {
|
||||
block_state.id
|
||||
} else {
|
||||
log::warn!(
|
||||
"Could not find valid block state for {}. Defaulting...",
|
||||
@@ -436,10 +434,10 @@ impl BlockPalette {
|
||||
}
|
||||
}
|
||||
|
||||
fn block_state_id_to_palette_entry(registry_id: u16) -> PaletteBlockEntry {
|
||||
fn block_state_id_to_palette_entry(registry_id: u16) -> BlockStateCodec {
|
||||
let block = Block::from_state_id(registry_id).unwrap();
|
||||
|
||||
PaletteBlockEntry {
|
||||
BlockStateCodec {
|
||||
name: block.name.into(),
|
||||
properties: {
|
||||
if let Some(properties) = block.properties(registry_id) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::level::Level;
|
||||
use crate::{level::Level, world::BlockRegistryExt};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Deserialize, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -13,12 +13,17 @@ pub enum Dimension {
|
||||
}
|
||||
|
||||
impl Dimension {
|
||||
pub fn into_level(&self, mut base_directory: PathBuf, seed: i64) -> Level {
|
||||
pub fn into_level(
|
||||
&self,
|
||||
mut base_directory: PathBuf,
|
||||
block_registry: Arc<dyn BlockRegistryExt>,
|
||||
seed: i64,
|
||||
) -> Level {
|
||||
match self {
|
||||
Dimension::Overworld => {}
|
||||
Dimension::Nether => base_directory.push("DIM-1"),
|
||||
Dimension::End => base_directory.push("DIM1"),
|
||||
}
|
||||
Level::from_root_folder(base_directory, seed, *self)
|
||||
Level::from_root_folder(base_directory, block_registry, seed, *self)
|
||||
}
|
||||
}
|
||||
|
||||
277
pumpkin-world/src/generation/block_predicate.rs
Normal file
277
pumpkin-world/src/generation/block_predicate.rs
Normal file
@@ -0,0 +1,277 @@
|
||||
use itertools::Itertools;
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockState, block_properties::get_block_by_state_id, tag::Tagable,
|
||||
};
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk, block::BlockStateCodec, generation::height_limit::HeightLimitView,
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EmptyTODOStruct {}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum BlockPredicate {
|
||||
#[serde(rename = "minecraft:matching_blocks")]
|
||||
MatchingBlocks(MatchingBlocksBlockPredicate),
|
||||
#[serde(rename = "minecraft:matching_block_tag")]
|
||||
MatchingBlockTag(MatchingBlockTagPredicate),
|
||||
#[serde(rename = "minecraft:matching_fluids")]
|
||||
MatchingFluids(EmptyTODOStruct),
|
||||
#[serde(rename = "minecraft:has_sturdy_face")]
|
||||
HasSturdyFace(HasSturdyFacePredicate),
|
||||
#[serde(rename = "minecraft:solid")]
|
||||
Solid(SolidBlockPredicate),
|
||||
#[serde(rename = "minecraft:replaceable")]
|
||||
Replaceable(ReplaceableBlockPredicate),
|
||||
#[serde(rename = "minecraft:would_survive")]
|
||||
WouldSurvive(WouldSurviveBlockPredicate),
|
||||
#[serde(rename = "minecraft:inside_world_bounds")]
|
||||
InsideWorldBounds(InsideWorldBoundsBlockPredicate),
|
||||
#[serde(rename = "minecraft:any_of")]
|
||||
AnyOf(AnyOfBlockPredicate),
|
||||
#[serde(rename = "minecraft:all_of")]
|
||||
AllOf(AllOfBlockPredicate),
|
||||
#[serde(rename = "minecraft:not")]
|
||||
Not(NotBlockPredicate),
|
||||
#[serde(rename = "minecraft:true")]
|
||||
AlwaysTrue,
|
||||
/// Not used
|
||||
#[serde(rename = "minecraft:unobstructed")]
|
||||
Unobstructed(EmptyTODOStruct),
|
||||
}
|
||||
|
||||
impl BlockPredicate {
|
||||
pub async fn test(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &ProtoChunk<'_>,
|
||||
pos: &BlockPos,
|
||||
) -> bool {
|
||||
match self {
|
||||
BlockPredicate::MatchingBlocks(predicate) => predicate.test(chunk, pos),
|
||||
BlockPredicate::MatchingBlockTag(predicate) => predicate.test(chunk, pos),
|
||||
BlockPredicate::MatchingFluids(_predicate) => false,
|
||||
BlockPredicate::HasSturdyFace(predicate) => predicate.test(chunk, pos),
|
||||
BlockPredicate::Solid(predicate) => predicate.test(chunk, pos),
|
||||
BlockPredicate::Replaceable(predicate) => predicate.test(chunk, pos),
|
||||
BlockPredicate::WouldSurvive(predicate) => {
|
||||
predicate.test(block_registry, chunk, pos).await
|
||||
}
|
||||
BlockPredicate::InsideWorldBounds(predicate) => predicate.test(chunk, pos),
|
||||
BlockPredicate::AnyOf(predicate) => predicate.test(block_registry, chunk, pos).await,
|
||||
BlockPredicate::AllOf(predicate) => predicate.test(block_registry, chunk, pos).await,
|
||||
BlockPredicate::Not(predicate) => predicate.test(block_registry, chunk, pos).await,
|
||||
BlockPredicate::AlwaysTrue => true,
|
||||
BlockPredicate::Unobstructed(_predicate) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MatchingBlocksBlockPredicate {
|
||||
#[serde(flatten)]
|
||||
offset: OffsetBlocksBlockPredicate,
|
||||
blocks: MatchingBlocksWrapper,
|
||||
}
|
||||
|
||||
impl MatchingBlocksBlockPredicate {
|
||||
pub fn test(&self, chunk: &ProtoChunk, pos: &BlockPos) -> bool {
|
||||
let block = self.offset.get_block(chunk, pos);
|
||||
match &self.blocks {
|
||||
MatchingBlocksWrapper::Single(single_block) => {
|
||||
single_block.replace("minecraft:", "") == block.name
|
||||
}
|
||||
MatchingBlocksWrapper::Multiple(blocks) => blocks
|
||||
.iter()
|
||||
.map(|s| s.replace("minecraft:", ""))
|
||||
.contains(block.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct InsideWorldBoundsBlockPredicate {
|
||||
offset: Vector3<i32>,
|
||||
}
|
||||
|
||||
impl InsideWorldBoundsBlockPredicate {
|
||||
pub fn test(&self, chunk: &ProtoChunk, pos: &BlockPos) -> bool {
|
||||
let pos = pos.offset(self.offset);
|
||||
!chunk.out_of_height(pos.0.y as i16)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MatchingBlockTagPredicate {
|
||||
#[serde(flatten)]
|
||||
offset: OffsetBlocksBlockPredicate,
|
||||
tag: String,
|
||||
}
|
||||
|
||||
impl MatchingBlockTagPredicate {
|
||||
pub fn test(&self, chunk: &ProtoChunk, pos: &BlockPos) -> bool {
|
||||
let block = self.offset.get_block(chunk, pos);
|
||||
block.is_tagged_with(&self.tag).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HasSturdyFacePredicate {
|
||||
#[serde(flatten)]
|
||||
offset: OffsetBlocksBlockPredicate,
|
||||
direction: BlockDirection,
|
||||
}
|
||||
|
||||
impl HasSturdyFacePredicate {
|
||||
pub fn test(&self, chunk: &ProtoChunk, pos: &BlockPos) -> bool {
|
||||
let state = self.offset.get_state(chunk, pos);
|
||||
state.is_side_solid(self.direction)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AnyOfBlockPredicate {
|
||||
predicates: Vec<BlockPredicate>,
|
||||
}
|
||||
|
||||
impl AnyOfBlockPredicate {
|
||||
pub async fn test(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &ProtoChunk<'_>,
|
||||
pos: &BlockPos,
|
||||
) -> bool {
|
||||
for predicate in &self.predicates {
|
||||
if !Box::pin(predicate.test(block_registry, chunk, pos)).await {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AllOfBlockPredicate {
|
||||
predicates: Vec<BlockPredicate>,
|
||||
}
|
||||
|
||||
impl AllOfBlockPredicate {
|
||||
pub async fn test(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &ProtoChunk<'_>,
|
||||
pos: &BlockPos,
|
||||
) -> bool {
|
||||
for predicate in &self.predicates {
|
||||
if Box::pin(predicate.test(block_registry, chunk, pos)).await {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NotBlockPredicate {
|
||||
predicate: Box<BlockPredicate>,
|
||||
}
|
||||
|
||||
impl NotBlockPredicate {
|
||||
pub async fn test(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &ProtoChunk<'_>,
|
||||
pos: &BlockPos,
|
||||
) -> bool {
|
||||
!Box::pin(self.predicate.test(block_registry, chunk, pos)).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SolidBlockPredicate {
|
||||
#[serde(flatten)]
|
||||
offset: OffsetBlocksBlockPredicate,
|
||||
}
|
||||
|
||||
impl SolidBlockPredicate {
|
||||
pub fn test(&self, chunk: &ProtoChunk, pos: &BlockPos) -> bool {
|
||||
let state = self.offset.get_state(chunk, pos);
|
||||
state.is_solid()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WouldSurviveBlockPredicate {
|
||||
#[serde(flatten)]
|
||||
offset: OffsetBlocksBlockPredicate,
|
||||
state: BlockStateCodec,
|
||||
}
|
||||
|
||||
impl WouldSurviveBlockPredicate {
|
||||
pub async fn test(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &ProtoChunk<'_>,
|
||||
pos: &BlockPos,
|
||||
) -> bool {
|
||||
let state = self.state.get_state().unwrap();
|
||||
let pos = self.offset.get(pos);
|
||||
return block_registry
|
||||
.can_place_at(
|
||||
&get_block_by_state_id(state.id).unwrap(),
|
||||
chunk,
|
||||
&pos,
|
||||
BlockDirection::Up,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ReplaceableBlockPredicate {
|
||||
#[serde(flatten)]
|
||||
offset: OffsetBlocksBlockPredicate,
|
||||
}
|
||||
|
||||
impl ReplaceableBlockPredicate {
|
||||
pub fn test(&self, chunk: &ProtoChunk, pos: &BlockPos) -> bool {
|
||||
let state = self.offset.get_state(chunk, pos);
|
||||
state.replaceable()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OffsetBlocksBlockPredicate {
|
||||
offset: Option<Vector3<i32>>,
|
||||
}
|
||||
|
||||
impl OffsetBlocksBlockPredicate {
|
||||
pub fn get(&self, pos: &BlockPos) -> BlockPos {
|
||||
if let Some(offset) = self.offset {
|
||||
return pos.offset(offset);
|
||||
}
|
||||
*pos
|
||||
}
|
||||
pub fn get_block(&self, chunk: &ProtoChunk, pos: &BlockPos) -> Block {
|
||||
let pos = self.get(pos);
|
||||
chunk.get_block_state(&pos.0).to_block()
|
||||
}
|
||||
pub fn get_state(&self, chunk: &ProtoChunk, pos: &BlockPos) -> BlockState {
|
||||
let pos = self.get(pos);
|
||||
chunk.get_block_state(&pos.0).to_state()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum MatchingBlocksWrapper {
|
||||
Single(String),
|
||||
Multiple(Vec<String>),
|
||||
}
|
||||
228
pumpkin-world/src/generation/block_state_provider.rs
Normal file
228
pumpkin-world/src/generation/block_state_provider.rs
Normal file
@@ -0,0 +1,228 @@
|
||||
use pumpkin_data::{BlockState, chunk::DoublePerlinNoiseParameters};
|
||||
use pumpkin_util::{
|
||||
DoublePerlinNoiseParametersCodec,
|
||||
math::{
|
||||
clamped_map,
|
||||
int_provider::IntProvider,
|
||||
pool::{Pool, Weighted},
|
||||
position::BlockPos,
|
||||
vector3::Vector3,
|
||||
},
|
||||
random::{RandomGenerator, RandomImpl, legacy_rand::LegacyRand},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::block::BlockStateCodec;
|
||||
|
||||
use super::noise::perlin::DoublePerlinNoiseSampler;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum BlockStateProvider {
|
||||
#[serde(rename = "minecraft:simple_state_provider")]
|
||||
Simple(SimpleStateProvider),
|
||||
#[serde(rename = "minecraft:weighted_state_provider")]
|
||||
Weighted(WeightedBlockStateProvider),
|
||||
#[serde(rename = "minecraft:noise_threshold_provider")]
|
||||
NoiseThreshold(NoiseThresholdBlockStateProvider),
|
||||
#[serde(rename = "minecraft:noise_provider")]
|
||||
NoiseProvider(NoiseBlockStateProvider),
|
||||
#[serde(rename = "minecraft:dual_noise_provider")]
|
||||
DualNoise(DualNoiseBlockStateProvider),
|
||||
#[serde(rename = "minecraft:rotated_block_provider")]
|
||||
Pillar(PillarBlockStateProvider),
|
||||
#[serde(rename = "minecraft:randomized_int_state_provider")]
|
||||
RandomizedInt(RandomizedIntBlockStateProvider),
|
||||
}
|
||||
|
||||
impl BlockStateProvider {
|
||||
pub fn get(&self, random: &mut RandomGenerator, pos: BlockPos) -> BlockState {
|
||||
match self {
|
||||
BlockStateProvider::NoiseThreshold(provider) => provider.get(random, pos),
|
||||
BlockStateProvider::NoiseProvider(provider) => provider.get(pos),
|
||||
BlockStateProvider::Simple(provider) => provider.get(pos),
|
||||
BlockStateProvider::Weighted(provider) => provider.get(random),
|
||||
BlockStateProvider::DualNoise(provider) => provider.get(pos),
|
||||
BlockStateProvider::Pillar(provider) => provider.get(pos),
|
||||
BlockStateProvider::RandomizedInt(provider) => provider.get(random, pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RandomizedIntBlockStateProvider {
|
||||
source: Box<BlockStateProvider>,
|
||||
property: String,
|
||||
values: IntProvider,
|
||||
}
|
||||
|
||||
impl RandomizedIntBlockStateProvider {
|
||||
pub fn get(&self, random: &mut RandomGenerator, pos: BlockPos) -> BlockState {
|
||||
// TODO
|
||||
self.source.get(random, pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PillarBlockStateProvider {
|
||||
state: BlockStateCodec,
|
||||
}
|
||||
|
||||
impl PillarBlockStateProvider {
|
||||
pub fn get(&self, _pos: BlockPos) -> BlockState {
|
||||
// TODO: random axis
|
||||
self.state.get_state().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DualNoiseBlockStateProvider {
|
||||
#[serde(flatten)]
|
||||
base: NoiseBlockStateProvider,
|
||||
variety: [u32; 2],
|
||||
slow_noise: DoublePerlinNoiseParametersCodec,
|
||||
slow_scale: f32,
|
||||
}
|
||||
|
||||
impl DualNoiseBlockStateProvider {
|
||||
pub fn get(&self, pos: BlockPos) -> BlockState {
|
||||
let noise = perlin_codec_to_static(self.slow_noise.clone());
|
||||
let sampler = DoublePerlinNoiseSampler::new(
|
||||
&mut RandomGenerator::Legacy(LegacyRand::from_seed(self.base.base.seed as u64)),
|
||||
&noise,
|
||||
false,
|
||||
);
|
||||
let slow_noise = self.get_slow_noise(&pos, &sampler);
|
||||
let mapped = clamped_map(
|
||||
slow_noise,
|
||||
-1.0,
|
||||
1.0,
|
||||
self.variety[0] as f64,
|
||||
self.variety[1] as f64 + 1.0,
|
||||
) as i32;
|
||||
let mut list = Vec::with_capacity(mapped as usize);
|
||||
for i in 0..mapped {
|
||||
let value = self.get_slow_noise(
|
||||
&BlockPos(pos.0.add(&Vector3::new(i * 54545, 0, i * 34234))),
|
||||
&sampler,
|
||||
);
|
||||
list.push(self.base.get_state_by_value(&self.base.states, value));
|
||||
}
|
||||
let value = self.base.base.get_noise(pos);
|
||||
self.base
|
||||
.get_state_by_value(&list, value)
|
||||
.get_state()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn get_slow_noise(&self, pos: &BlockPos, sampler: &DoublePerlinNoiseSampler) -> f64 {
|
||||
sampler.sample(
|
||||
pos.0.x as f64 * self.slow_scale as f64,
|
||||
pos.0.y as f64 * self.slow_scale as f64,
|
||||
pos.0.z as f64 * self.slow_scale as f64,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WeightedBlockStateProvider {
|
||||
entries: Vec<Weighted<BlockStateCodec>>,
|
||||
}
|
||||
|
||||
impl WeightedBlockStateProvider {
|
||||
pub fn get(&self, random: &mut RandomGenerator) -> BlockState {
|
||||
Pool.get(&self.entries, random)
|
||||
.unwrap()
|
||||
.get_state()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SimpleStateProvider {
|
||||
state: BlockStateCodec,
|
||||
}
|
||||
|
||||
impl SimpleStateProvider {
|
||||
pub fn get(&self, _pos: BlockPos) -> BlockState {
|
||||
self.state.get_state().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NoiseBlockStateProviderBase {
|
||||
seed: i64,
|
||||
noise: DoublePerlinNoiseParametersCodec,
|
||||
scale: f32,
|
||||
}
|
||||
|
||||
fn perlin_codec_to_static(noise: DoublePerlinNoiseParametersCodec) -> DoublePerlinNoiseParameters {
|
||||
let amplitudes_static: &'static [f64] = noise.amplitudes.leak();
|
||||
DoublePerlinNoiseParameters::new(noise.first_octave, amplitudes_static, "none")
|
||||
}
|
||||
|
||||
impl NoiseBlockStateProviderBase {
|
||||
pub fn get_noise(&self, pos: BlockPos) -> f64 {
|
||||
let noise = perlin_codec_to_static(self.noise.clone());
|
||||
let sampler = DoublePerlinNoiseSampler::new(
|
||||
&mut RandomGenerator::Legacy(LegacyRand::from_seed(self.seed as u64)),
|
||||
&noise,
|
||||
false,
|
||||
);
|
||||
sampler.sample(
|
||||
pos.0.x as f64 * self.scale as f64,
|
||||
pos.0.y as f64 * self.scale as f64,
|
||||
pos.0.z as f64 * self.scale as f64,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NoiseBlockStateProvider {
|
||||
#[serde(flatten)]
|
||||
base: NoiseBlockStateProviderBase,
|
||||
states: Vec<BlockStateCodec>,
|
||||
}
|
||||
|
||||
impl NoiseBlockStateProvider {
|
||||
pub fn get(&self, pos: BlockPos) -> BlockState {
|
||||
let value = self.base.get_noise(pos);
|
||||
self.get_state_by_value(&self.states, value)
|
||||
.get_state()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn get_state_by_value(&self, states: &[BlockStateCodec], value: f64) -> BlockStateCodec {
|
||||
let val = ((1.0 + value) / 2.0).clamp(0.0, 0.9999);
|
||||
states[(val * states.len() as f64) as usize].clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NoiseThresholdBlockStateProvider {
|
||||
#[serde(flatten)]
|
||||
base: NoiseBlockStateProviderBase,
|
||||
threshold: f32,
|
||||
high_chance: f32,
|
||||
default_state: BlockStateCodec,
|
||||
low_states: Vec<BlockStateCodec>,
|
||||
high_states: Vec<BlockStateCodec>,
|
||||
}
|
||||
|
||||
impl NoiseThresholdBlockStateProvider {
|
||||
pub fn get(&self, random: &mut RandomGenerator, pos: BlockPos) -> BlockState {
|
||||
let value = self.base.get_noise(pos);
|
||||
if value < self.threshold as f64 {
|
||||
return self.low_states[random.next_bounded_i32(self.low_states.len() as i32) as usize]
|
||||
.get_state()
|
||||
.unwrap();
|
||||
}
|
||||
if random.next_f32() < self.high_chance {
|
||||
return self.high_states
|
||||
[random.next_bounded_i32(self.high_states.len() as i32) as usize]
|
||||
.get_state()
|
||||
.unwrap();
|
||||
}
|
||||
self.default_state.get_state().unwrap()
|
||||
}
|
||||
}
|
||||
385
pumpkin-world/src/generation/feature/configured_features.rs
Normal file
385
pumpkin-world/src/generation/feature/configured_features.rs
Normal file
@@ -0,0 +1,385 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
|
||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, level::Level, world::BlockRegistryExt};
|
||||
|
||||
use super::features::{
|
||||
bamboo::BambooFeature,
|
||||
basalt_columns::BasaltColumnsFeature,
|
||||
basalt_pillar::BasaltPillarFeature,
|
||||
block_column::BlockColumnFeature,
|
||||
block_pile::BlockPileFeature,
|
||||
blue_ice::BlueIceFeature,
|
||||
bonus_chest::BonusChestFeature,
|
||||
chorus_plant::ChorusPlantFeature,
|
||||
coral::{
|
||||
coral_claw::CoralClawFeature, coral_mushroom::CoralMushroomFeature,
|
||||
coral_tree::CoralTreeFeature,
|
||||
},
|
||||
delta_feature::DeltaFeatureFeature,
|
||||
desert_well::DesertWellFeature,
|
||||
disk::DiskFeature,
|
||||
drip_stone::{
|
||||
cluster::DripstoneClusterFeature, large::LargeDripstoneFeature,
|
||||
small::SmallDripstoneFeature,
|
||||
},
|
||||
end_gateway::EndGatewayFeature,
|
||||
end_island::EndIslandFeature,
|
||||
end_platform::EndPlatformFeature,
|
||||
end_spike::EndSpikeFeature,
|
||||
fallen_tree::FallenTreeFeature,
|
||||
fill_layer::FillLayerFeature,
|
||||
forest_rock::ForestRockFeature,
|
||||
fossil::FossilFeature,
|
||||
freeze_top_layer::FreezeTopLayerFeature,
|
||||
geode::GeodeFeature,
|
||||
glowstone_blob::GlowstoneBlobFeature,
|
||||
huge_brown_mushroom::HugeBrownMushroomFeature,
|
||||
huge_fungus::HugeFungusFeature,
|
||||
huge_red_mushroom::HugeRedMushroomFeature,
|
||||
ice_spike::IceSpikeFeature,
|
||||
iceberg::IcebergFeature,
|
||||
kelp::KelpFeature,
|
||||
lake::LakeFeature,
|
||||
monster_room::DungeonFeature,
|
||||
multiface_growth::MultifaceGrowthFeature,
|
||||
nether_forest_vegetation::NetherForestVegetationFeature,
|
||||
netherrack_replace_blobs::ReplaceBlobsFeature,
|
||||
ore::OreFeature,
|
||||
random_boolean_selector::RandomBooleanFeature,
|
||||
random_patch::RandomPatchFeature,
|
||||
random_selector::RandomFeature,
|
||||
replace_single_block::ReplaceSingleBlockFeature,
|
||||
root_system::RootSystemFeature,
|
||||
scattered_ore::ScatteredOreFeature,
|
||||
sculk_patch::SculkPatchFeature,
|
||||
sea_pickle::SeaPickleFeature,
|
||||
seagrass::SeagrassFeature,
|
||||
simple_block::SimpleBlockFeature,
|
||||
simple_random_selector::SimpleRandomFeature,
|
||||
spring_feature::SpringFeatureFeature,
|
||||
tree::TreeFeature,
|
||||
twisting_vines::TwistingVinesFeature,
|
||||
underwater_magma::UnderwaterMagmaFeature,
|
||||
vegetation_patch::VegetationPatchFeature,
|
||||
vines::VinesFeature,
|
||||
void_start_platform::VoidStartPlatformFeature,
|
||||
waterlogged_vegetation_patch::WaterloggedVegetationPatchFeature,
|
||||
weeping_vines::WeepingVinesFeature,
|
||||
};
|
||||
|
||||
pub static CONFIGURED_FEATURES: LazyLock<HashMap<String, ConfiguredFeature>> =
|
||||
LazyLock::new(|| {
|
||||
serde_json::from_str(include_str!("../../../../assets/configured_features.json"))
|
||||
.expect("Could not parse configured_features.json registry.")
|
||||
});
|
||||
|
||||
// Yes this may look ugly and you wonder why this is hard coded, but its makes sense to hardcode since we have to add logic for these in code
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type", content = "config")]
|
||||
pub enum ConfiguredFeature {
|
||||
#[serde(rename = "minecraft:no_op")]
|
||||
NoOp,
|
||||
#[serde(rename = "minecraft:tree")]
|
||||
Tree(Box<TreeFeature>),
|
||||
#[serde(rename = "minecraft:fallen_tree")]
|
||||
FallenTree(FallenTreeFeature),
|
||||
#[serde(rename = "minecraft:flower")]
|
||||
Flower(RandomPatchFeature),
|
||||
#[serde(rename = "minecraft:no_bonemeal_flower")]
|
||||
NoBonemealFlower(RandomPatchFeature),
|
||||
#[serde(rename = "minecraft:random_patch")]
|
||||
RandomPatch(RandomPatchFeature),
|
||||
#[serde(rename = "minecraft:block_pile")]
|
||||
BlockPile(BlockPileFeature),
|
||||
#[serde(rename = "minecraft:spring_feature")]
|
||||
SpringFeature(SpringFeatureFeature),
|
||||
#[serde(rename = "minecraft:chorus_plant")]
|
||||
ChorusPlant(ChorusPlantFeature),
|
||||
#[serde(rename = "minecraft:replace_single_block")]
|
||||
ReplaceSingleBlock(ReplaceSingleBlockFeature),
|
||||
#[serde(rename = "minecraft:void_start_platform")]
|
||||
VoidStartPlatform(VoidStartPlatformFeature),
|
||||
#[serde(rename = "minecraft:desert_well")]
|
||||
DesertWell(DesertWellFeature),
|
||||
#[serde(rename = "minecraft:fossil")]
|
||||
Fossil(FossilFeature),
|
||||
#[serde(rename = "minecraft:huge_red_mushroom")]
|
||||
HugeRedMushroom(HugeRedMushroomFeature),
|
||||
#[serde(rename = "minecraft:huge_brown_mushroom")]
|
||||
HugeBrownMushroom(HugeBrownMushroomFeature),
|
||||
#[serde(rename = "minecraft:ice_spike")]
|
||||
IceSpike(IceSpikeFeature),
|
||||
#[serde(rename = "minecraft:glowstone_blob")]
|
||||
GlowstoneBlob(GlowstoneBlobFeature),
|
||||
#[serde(rename = "minecraft:freeze_top_layer")]
|
||||
FreezeTopLayer(FreezeTopLayerFeature),
|
||||
#[serde(rename = "minecraft:vines")]
|
||||
Vines(VinesFeature),
|
||||
#[serde(rename = "minecraft:block_column")]
|
||||
BlockColumn(BlockColumnFeature),
|
||||
#[serde(rename = "minecraft:vegetation_patch")]
|
||||
VegetationPatch(VegetationPatchFeature),
|
||||
#[serde(rename = "minecraft:waterlogged_vegetation_patch")]
|
||||
WaterloggedVegetationPatch(WaterloggedVegetationPatchFeature),
|
||||
#[serde(rename = "minecraft:root_system")]
|
||||
RootSystem(RootSystemFeature),
|
||||
#[serde(rename = "minecraft:multiface_growth")]
|
||||
MultifaceGrowth(MultifaceGrowthFeature),
|
||||
#[serde(rename = "minecraft:underwater_magma")]
|
||||
UnderwaterMagma(UnderwaterMagmaFeature),
|
||||
#[serde(rename = "minecraft:monster_room")]
|
||||
MonsterRoom(DungeonFeature),
|
||||
#[serde(rename = "minecraft:blue_ice")]
|
||||
BlueIce(BlueIceFeature),
|
||||
#[serde(rename = "minecraft:iceberg")]
|
||||
Iceberg(IcebergFeature),
|
||||
#[serde(rename = "minecraft:forest_rock")]
|
||||
ForestRock(ForestRockFeature),
|
||||
#[serde(rename = "minecraft:disk")]
|
||||
Disk(DiskFeature),
|
||||
#[serde(rename = "minecraft:lake")]
|
||||
Lake(LakeFeature),
|
||||
#[serde(rename = "minecraft:ore")]
|
||||
Ore(OreFeature),
|
||||
#[serde(rename = "minecraft:end_platform")]
|
||||
EndPlatform(EndPlatformFeature),
|
||||
#[serde(rename = "minecraft:end_spike")]
|
||||
EndSpike(EndSpikeFeature),
|
||||
#[serde(rename = "minecraft:end_island")]
|
||||
EndIsland(EndIslandFeature),
|
||||
#[serde(rename = "minecraft:end_gateway")]
|
||||
EndGateway(EndGatewayFeature),
|
||||
#[serde(rename = "minecraft:seagrass")]
|
||||
Seagrass(SeagrassFeature),
|
||||
#[serde(rename = "minecraft:kelp")]
|
||||
Kelp(KelpFeature),
|
||||
#[serde(rename = "minecraft:coral_tree")]
|
||||
CoralTree(CoralTreeFeature),
|
||||
#[serde(rename = "minecraft:coral_mushroom")]
|
||||
CoralMushroom(CoralMushroomFeature),
|
||||
#[serde(rename = "minecraft:coral_claw")]
|
||||
CoralClaw(CoralClawFeature),
|
||||
#[serde(rename = "minecraft:sea_pickle")]
|
||||
SeaPickle(SeaPickleFeature),
|
||||
#[serde(rename = "minecraft:simple_block")]
|
||||
SimpleBlock(SimpleBlockFeature),
|
||||
#[serde(rename = "minecraft:bamboo")]
|
||||
Bamboo(BambooFeature),
|
||||
#[serde(rename = "minecraft:huge_fungus")]
|
||||
HugeFungus(HugeFungusFeature),
|
||||
#[serde(rename = "minecraft:nether_forest_vegetation")]
|
||||
NetherForestVegetation(NetherForestVegetationFeature),
|
||||
#[serde(rename = "minecraft:weeping_vines")]
|
||||
WeepingVines(WeepingVinesFeature),
|
||||
#[serde(rename = "minecraft:twisting_vines")]
|
||||
TwistingVines(TwistingVinesFeature),
|
||||
#[serde(rename = "minecraft:basalt_columns")]
|
||||
BasaltColumns(BasaltColumnsFeature),
|
||||
#[serde(rename = "minecraft:delta_feature")]
|
||||
DeltaFeature(DeltaFeatureFeature),
|
||||
#[serde(rename = "minecraft:netherrack_replace_blobs")]
|
||||
NetherrackReplaceBlobs(ReplaceBlobsFeature),
|
||||
#[serde(rename = "minecraft:fill_layer")]
|
||||
FillLayer(FillLayerFeature),
|
||||
#[serde(rename = "minecraft:bonus_chest")]
|
||||
BonusChest(BonusChestFeature),
|
||||
#[serde(rename = "minecraft:basalt_pillar")]
|
||||
BasaltPillar(BasaltPillarFeature),
|
||||
#[serde(rename = "minecraft:scattered_ore")]
|
||||
ScatteredOre(ScatteredOreFeature),
|
||||
#[serde(rename = "minecraft:random_selector")]
|
||||
RandomSelector(RandomFeature),
|
||||
#[serde(rename = "minecraft:simple_random_selector")]
|
||||
SimpleRandomSelector(SimpleRandomFeature),
|
||||
#[serde(rename = "minecraft:random_boolean_selector")]
|
||||
RandomBooleanSelector(RandomBooleanFeature),
|
||||
#[serde(rename = "minecraft:geode")]
|
||||
Geode(GeodeFeature),
|
||||
#[serde(rename = "minecraft:dripstone_cluster")]
|
||||
DripstoneCluster(DripstoneClusterFeature),
|
||||
#[serde(rename = "minecraft:large_dripstone")]
|
||||
LargeDripstone(LargeDripstoneFeature),
|
||||
#[serde(rename = "minecraft:pointed_dripstone")]
|
||||
PointedDripstone(SmallDripstoneFeature),
|
||||
#[serde(rename = "minecraft:sculk_patch")]
|
||||
SculkPatch(SculkPatchFeature),
|
||||
}
|
||||
|
||||
impl ConfiguredFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
feature_name: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
match self {
|
||||
Self::PointedDripstone(feature) => feature.generate(chunk, random, pos),
|
||||
Self::CoralMushroom(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::CoralTree(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::CoralClaw(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::SpringFeature(feature) => feature.generate(block_registry, chunk, random, pos),
|
||||
Self::SimpleBlock(feature) => feature.generate(block_registry, chunk, random, pos),
|
||||
Self::Flower(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::NoBonemealFlower(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::DesertWell(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::Bamboo(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::BlockColumn(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::RandomPatch(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::RandomBooleanSelector(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::Tree(feature) => {
|
||||
feature
|
||||
.generate(chunk, level, min_y, height, feature_name, random, pos)
|
||||
.await
|
||||
}
|
||||
Self::RandomSelector(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::SimpleRandomSelector(feature) => {
|
||||
feature
|
||||
.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::Vines(feature) => feature.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
),
|
||||
Self::Seagrass(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::SeaPickle(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::Ore(feature) => feature.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
),
|
||||
_ => false, // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
98
pumpkin-world/src/generation/feature/features/bamboo.rs
Normal file
98
pumpkin-world/src/generation/feature/features/bamboo.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection,
|
||||
block_properties::{
|
||||
BambooLeaves, BambooLikeProperties, BlockProperties, Integer0To1, get_state_by_state_id,
|
||||
},
|
||||
tag::Tagable,
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::{position::BlockPos, vector2::Vector2},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, world::BlockRegistryExt};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BambooFeature {
|
||||
probability: f32,
|
||||
}
|
||||
|
||||
impl BambooFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let mut i = 0;
|
||||
if chunk.is_air(&pos.0) {
|
||||
if block_registry
|
||||
.can_place_at(&Block::BAMBOO, chunk, &pos, BlockDirection::Up)
|
||||
.await
|
||||
{
|
||||
let height = random.next_bounded_i32(12) + 5;
|
||||
if random.next_f32() < self.probability {
|
||||
let rnd = random.next_bounded_i32(4) + 1;
|
||||
for x in pos.0.x - rnd..pos.0.x + rnd {
|
||||
for z in pos.0.z - rnd..pos.0.z + rnd {
|
||||
let block_below = BlockPos::new(
|
||||
x,
|
||||
chunk.top_block_height_exclusive(&Vector2::new(x, z)) as i32 - 1,
|
||||
z,
|
||||
);
|
||||
let block = chunk.get_block_state(&block_below.0);
|
||||
if !block.to_block().is_tagged_with("minecraft:dirt").unwrap() {
|
||||
continue;
|
||||
}
|
||||
chunk.set_block_state(
|
||||
&block_below.0,
|
||||
&get_state_by_state_id(Block::PODZOL.id).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut bpos = pos;
|
||||
let bamboo = get_state_by_state_id(Block::BAMBOO.default_state_id).unwrap();
|
||||
for _ in 0..height {
|
||||
if chunk.is_air(&bpos.0) {
|
||||
chunk.set_block_state(&bpos.0, &bamboo);
|
||||
bpos = bpos.up();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Top block
|
||||
if bpos.0.y - pos.0.y >= 3 {
|
||||
let mut props = BambooLikeProperties::default(&Block::BAMBOO);
|
||||
props.leaves = BambooLeaves::Large;
|
||||
props.stage = Integer0To1::L1;
|
||||
|
||||
chunk.set_block_state(
|
||||
&bpos.0,
|
||||
&get_state_by_state_id(props.to_state_id(&Block::BAMBOO)).unwrap(),
|
||||
);
|
||||
props.stage = Integer0To1::L0;
|
||||
|
||||
chunk.set_block_state(
|
||||
&bpos.down().0,
|
||||
&get_state_by_state_id(props.to_state_id(&Block::BAMBOO)).unwrap(),
|
||||
);
|
||||
props.leaves = BambooLeaves::Small;
|
||||
|
||||
chunk.set_block_state(
|
||||
&bpos.down().down().0,
|
||||
&get_state_by_state_id(props.to_state_id(&Block::BAMBOO)).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
i > 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BasaltColumnsFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BasaltPillarFeature {
|
||||
// TODO
|
||||
}
|
||||
114
pumpkin-world/src/generation/feature/features/block_column.rs
Normal file
114
pumpkin-world/src/generation/feature/features/block_column.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::{
|
||||
math::{int_provider::IntProvider, position::BlockPos},
|
||||
random::RandomGenerator,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
generation::{block_predicate::BlockPredicate, block_state_provider::BlockStateProvider},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BlockColumnFeature {
|
||||
layers: Vec<Layer>,
|
||||
direction: BlockDirection,
|
||||
allowed_placement: BlockPredicate,
|
||||
prioritize_tip: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Layer {
|
||||
height: IntProvider,
|
||||
provider: BlockStateProvider,
|
||||
}
|
||||
|
||||
impl BlockColumnFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let i = self.layers.len();
|
||||
let mut is = vec![0; i];
|
||||
let mut j = 0;
|
||||
|
||||
for (k, item) in is.iter_mut().enumerate().take(i) {
|
||||
*item = (self.layers[k].height).get(random);
|
||||
j += *item;
|
||||
}
|
||||
|
||||
if j == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut mutable = pos;
|
||||
let mut mutable2 = mutable.offset(self.direction.to_offset());
|
||||
|
||||
let mut l = 0;
|
||||
while l < j {
|
||||
if !Box::pin(
|
||||
self.allowed_placement
|
||||
.test(block_registry, chunk, &mutable2),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Self::adjust_layer_heights(&mut is, j, l, self.prioritize_tip);
|
||||
break;
|
||||
}
|
||||
mutable2 = mutable2.offset(self.direction.to_offset());
|
||||
l += 1;
|
||||
}
|
||||
|
||||
for (l, m) in is.iter().enumerate().take(i) {
|
||||
if *m == 0 {
|
||||
continue;
|
||||
}
|
||||
let layer = &self.layers[l];
|
||||
for _n in 0..*m {
|
||||
let state = layer.provider.get(random, mutable);
|
||||
chunk.set_block_state(&mutable.0, &state);
|
||||
mutable = mutable.offset(self.direction.to_offset());
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn adjust_layer_heights(
|
||||
layer_heights: &mut [i32],
|
||||
expected_height: i32,
|
||||
actual_height: i32,
|
||||
prioritize_tip: bool,
|
||||
) {
|
||||
let mut i = expected_height - actual_height;
|
||||
let j = if prioritize_tip { 1 } else { -1 };
|
||||
let k = if prioritize_tip {
|
||||
0
|
||||
} else {
|
||||
layer_heights.len() as i32 - 1
|
||||
};
|
||||
let l = if prioritize_tip {
|
||||
layer_heights.len() as i32
|
||||
} else {
|
||||
-1
|
||||
};
|
||||
|
||||
let mut m = k;
|
||||
while m != l && i > 0 {
|
||||
let n = layer_heights[m as usize];
|
||||
let o = std::cmp::min(n, i);
|
||||
layer_heights[m as usize] -= o;
|
||||
i -= o;
|
||||
m += j;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BlockPileFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BlueIceFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BonusChestFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ChorusPlantFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
use super::CoralFeature;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CoralClawFeature;
|
||||
|
||||
impl CoralClawFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
// First lets get a random coral
|
||||
let block = CoralFeature::get_random_tag_entry("minecraft:coral_blocks", random);
|
||||
if !CoralFeature::generate_coral_piece(chunk, random, &block, pos) {
|
||||
return false;
|
||||
}
|
||||
let i = random.next_bounded_i32(2) + 2;
|
||||
let direction = BlockDirection::horizontal()
|
||||
[random.next_bounded_i32(BlockDirection::horizontal().len() as i32 - 1) as usize];
|
||||
// TODO: Shuffle
|
||||
let directions: Vec<_> = BlockDirection::horizontal()
|
||||
.into_iter()
|
||||
.take(i as usize)
|
||||
.collect();
|
||||
'block0: for direction2 in directions {
|
||||
let mut pos = pos;
|
||||
let j = random.next_bounded_i32(2) + 1;
|
||||
pos = pos.offset(direction2.to_offset());
|
||||
|
||||
let direction3;
|
||||
let k;
|
||||
|
||||
if direction2 == direction {
|
||||
direction3 = direction;
|
||||
k = random.next_bounded_i32(3) + 2;
|
||||
} else {
|
||||
pos = pos.up();
|
||||
let _directions = [direction2, BlockDirection::Up];
|
||||
direction3 = direction2; // TODO: make this random
|
||||
k = random.next_bounded_i32(3) + 5;
|
||||
}
|
||||
|
||||
for _ in 0..j {
|
||||
if !CoralFeature::generate_coral_piece(chunk, random, &block, pos) {
|
||||
break;
|
||||
}
|
||||
pos = pos.offset(direction3.to_offset());
|
||||
}
|
||||
|
||||
pos = pos.offset(direction3.to_offset());
|
||||
pos = pos.up();
|
||||
|
||||
for _l in 0..k {
|
||||
pos = pos.offset(direction.opposite().to_offset());
|
||||
if !CoralFeature::generate_coral_piece(chunk, random, &block, pos) {
|
||||
continue 'block0;
|
||||
}
|
||||
if random.next_f32() < 0.25 {
|
||||
pos = pos.up();
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use pumpkin_util::{
|
||||
math::{position::BlockPos, vector3::Vector3},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
use super::CoralFeature;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CoralMushroomFeature;
|
||||
|
||||
impl CoralMushroomFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
// First lets get a random coral
|
||||
let block = CoralFeature::get_random_tag_entry("minecraft:coral_blocks", random);
|
||||
|
||||
let i = random.next_bounded_i32(3) + 3;
|
||||
let j = random.next_bounded_i32(3) + 3;
|
||||
let k = random.next_bounded_i32(3) + 3;
|
||||
let l = random.next_bounded_i32(3) + 1;
|
||||
|
||||
for m in 0..=j {
|
||||
for n in 0..=i {
|
||||
for o in 0..=k {
|
||||
let mut pos = pos;
|
||||
pos = pos.offset(Vector3::new(pos.0.x + m, pos.0.y + n, pos.0.z + o));
|
||||
pos = pos.down_height(l);
|
||||
|
||||
let condition_a = (m != 0 && m != j) || (n != 0 && n != i);
|
||||
let condition_b = (o != 0 && o != k) || (n != 0 && n != i);
|
||||
let condition_c = (m != 0 && m != j) || (o != 0 && o != k);
|
||||
let condition_d = m == 0 || m == j || n == 0 || n == i || o == 0 || o == k;
|
||||
let random_check = random.next_f32() < 0.1f32;
|
||||
|
||||
if !((condition_a && condition_b && condition_c && condition_d)
|
||||
&& !random_check
|
||||
&& CoralFeature::generate_coral_piece(chunk, random, &block, pos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
use super::CoralFeature;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CoralTreeFeature;
|
||||
|
||||
impl CoralTreeFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
// First lets get a random coral
|
||||
let block = CoralFeature::get_random_tag_entry("minecraft:coral_blocks", random);
|
||||
let mut pos = pos;
|
||||
let i = random.next_bounded_i32(3) + 1;
|
||||
for _ in 0..i {
|
||||
if !CoralFeature::generate_coral_piece(chunk, random, &block, pos) {
|
||||
return true;
|
||||
}
|
||||
pos = pos.up();
|
||||
}
|
||||
let i = random.next_bounded_i32(3) + 2;
|
||||
|
||||
// TODO: Shuffle
|
||||
let directions: Vec<_> = BlockDirection::horizontal()
|
||||
.into_iter()
|
||||
.take(i as usize)
|
||||
.collect();
|
||||
for dir in directions {
|
||||
pos = pos.offset(dir.to_offset());
|
||||
let times = random.next_bounded_i32(5) + 2;
|
||||
let mut m = 0;
|
||||
for n in 0..times {
|
||||
if !CoralFeature::generate_coral_piece(chunk, random, &block, pos) {
|
||||
break;
|
||||
}
|
||||
pos = pos.up();
|
||||
m += 1;
|
||||
if n != 0 && (m < 2 || random.next_f32() >= 0.25) {
|
||||
continue;
|
||||
}
|
||||
pos = pos.offset(dir.to_offset());
|
||||
m = 0;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
100
pumpkin-world/src/generation/feature/features/coral/mod.rs
Normal file
100
pumpkin-world/src/generation/feature/features/coral/mod.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockState,
|
||||
block_properties::{
|
||||
BlockProperties, EnumVariants, Integer1To4, SeaPickleLikeProperties, get_block,
|
||||
get_state_by_state_id,
|
||||
},
|
||||
tag::{RegistryKey, Tagable, get_tag_values},
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
pub mod coral_claw;
|
||||
pub mod coral_mushroom;
|
||||
pub mod coral_tree;
|
||||
|
||||
pub struct CoralFeature;
|
||||
|
||||
impl CoralFeature {
|
||||
pub fn generate_coral_piece(
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
state: &BlockState,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let block = chunk.get_block_state(&pos.0).to_block();
|
||||
let above_block = chunk.get_block_state(&pos.up().0).to_block();
|
||||
|
||||
if block != Block::WATER && !block.is_tagged_with("minecraft:corals").unwrap()
|
||||
|| above_block != Block::WATER
|
||||
{
|
||||
return false;
|
||||
}
|
||||
chunk.set_block_state(&pos.0, state);
|
||||
if random.next_f32() < 0.25 {
|
||||
chunk.set_block_state(
|
||||
&pos.0,
|
||||
&Self::get_random_tag_entry("minecraft:corals", random),
|
||||
);
|
||||
} else if random.next_f32() < 0.05 {
|
||||
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
|
||||
chunk.set_block_state(
|
||||
&pos.0,
|
||||
&get_state_by_state_id(props.to_state_id(&Block::SEA_PICKLE)).unwrap(),
|
||||
);
|
||||
}
|
||||
for dir in BlockDirection::horizontal() {
|
||||
let dir_pos = pos.offset(dir.to_offset());
|
||||
if random.next_f32() >= 0.2
|
||||
|| chunk.get_block_state(&dir_pos.0).to_block() != Block::WATER
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let wall_coral = Self::get_random_tag_entry_block("minecraft:wall_corals", random);
|
||||
let original_props = &wall_coral
|
||||
.properties(wall_coral.default_state_id)
|
||||
.unwrap()
|
||||
.to_props();
|
||||
let facing = dir.to_facing();
|
||||
// Set the right Axis
|
||||
let props_vec: Vec<(&str, &str)> = original_props
|
||||
.iter()
|
||||
.map(|(key, value)| {
|
||||
if key == "facing" {
|
||||
(key.as_str(), facing.to_value())
|
||||
} else {
|
||||
(key.as_str(), value.as_str())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
chunk.set_block_state(
|
||||
&dir_pos.0,
|
||||
&get_state_by_state_id(
|
||||
wall_coral
|
||||
.from_properties(props_vec)
|
||||
.unwrap()
|
||||
.to_state_id(&wall_coral),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get_random_tag_entry(tag: &str, random: &mut RandomGenerator) -> BlockState {
|
||||
let block = Self::get_random_tag_entry_block(tag, random);
|
||||
get_state_by_state_id(block.default_state_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_random_tag_entry_block(tag: &str, random: &mut RandomGenerator) -> Block {
|
||||
let values = get_tag_values(RegistryKey::Block, tag).unwrap();
|
||||
let value = values[random.next_bounded_i32(values.len() as i32) as usize];
|
||||
get_block(value).unwrap()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DeltaFeatureFeature {
|
||||
// TODO
|
||||
}
|
||||
152
pumpkin-world/src/generation/feature/features/desert_well.rs
Normal file
152
pumpkin-world/src/generation/feature/features/desert_well.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_macros::default_block_state;
|
||||
use pumpkin_util::{
|
||||
math::{position::BlockPos, vector3::Vector3},
|
||||
random::RandomGenerator,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
block::RawBlockState,
|
||||
generation::{chunk_noise::WATER_BLOCK, height_limit::HeightLimitView},
|
||||
};
|
||||
|
||||
// TODO: remove .to_state()
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DesertWellFeature;
|
||||
|
||||
impl DesertWellFeature {
|
||||
const CAN_GENERATE: RawBlockState = default_block_state!("sand");
|
||||
const SAND: RawBlockState = default_block_state!("sand");
|
||||
const SLAB: RawBlockState = default_block_state!("sandstone_slab");
|
||||
const WALL: RawBlockState = default_block_state!("sandstone");
|
||||
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
_random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let mut block_pos = pos.up();
|
||||
while chunk.is_air(&block_pos.0) && block_pos.0.y > chunk.bottom_y() as i32 + 2 {
|
||||
block_pos = block_pos.down();
|
||||
}
|
||||
let block = chunk.get_block_state(&pos.0).to_block();
|
||||
const CAN_GENERATE: RawBlockState = default_block_state!("sand");
|
||||
if CAN_GENERATE.to_block().id != block.id {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in -2..=2 {
|
||||
for j2 in -2..=2 {
|
||||
if !chunk.is_air(&block_pos.0.add(&Vector3::new(i, -1, j2)))
|
||||
|| !chunk.is_air(&block_pos.0.add(&Vector3::new(i, -2, j2)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for i in -2..=0 {
|
||||
for j2 in -2..=2 {
|
||||
for k in -2..=2 {
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(j2, i, k)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chunk.set_block_state(&block_pos.0, &WATER_BLOCK.to_state());
|
||||
|
||||
for direction in BlockDirection::horizontal().iter() {
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&direction.to_offset()),
|
||||
&WATER_BLOCK.to_state(),
|
||||
);
|
||||
}
|
||||
|
||||
let block_pos2 = &block_pos.0.add(&Vector3::new(0, -1, 0));
|
||||
chunk.set_block_state(block_pos2, &Self::SAND.to_state());
|
||||
|
||||
for direction2 in BlockDirection::horizontal().iter() {
|
||||
chunk.set_block_state(
|
||||
&block_pos2.add(&direction2.to_offset()),
|
||||
&Self::SAND.to_state(),
|
||||
);
|
||||
}
|
||||
|
||||
for j in -2..=2 {
|
||||
for k in -2..=2 {
|
||||
if j != -2 && j != 2 && k != -2 && k != 2 {
|
||||
continue;
|
||||
}
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(j, 1, k)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(2, 1, 0)),
|
||||
&Self::SLAB.to_state(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(-2, 1, 0)),
|
||||
&Self::SLAB.to_state(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(0, 1, 2)),
|
||||
&Self::SLAB.to_state(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(0, 1, -2)),
|
||||
&Self::SLAB.to_state(),
|
||||
);
|
||||
|
||||
for j in -1..=1 {
|
||||
for k in -1..=1 {
|
||||
if j == 0 && k == 0 {
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(j, 4, k)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(j, 4, k)),
|
||||
&Self::SLAB.to_state(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for j in 1..=3 {
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(-1, j, -1)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(-1, j, 1)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(1, j, -1)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&block_pos.0.add(&Vector3::new(1, j, 1)),
|
||||
&Self::WALL.to_state(),
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
6
pumpkin-world/src/generation/feature/features/disk.rs
Normal file
6
pumpkin-world/src/generation/feature/features/disk.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DiskFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DripstoneClusterFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LargeDripstoneFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use pumpkin_data::{Block, block_properties::get_state_by_state_id, tag::Tagable};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
pub mod cluster;
|
||||
pub mod large;
|
||||
pub mod small;
|
||||
|
||||
pub(super) fn can_replace(block: &Block) -> bool {
|
||||
block == &Block::DRIPSTONE_BLOCK
|
||||
|| block
|
||||
.is_tagged_with("minecraft:dripstone_replaceable_blocks")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn gen_dripstone(chunk: &mut ProtoChunk, pos: BlockPos) -> bool {
|
||||
let block = chunk.get_block_state(&pos.0).to_block();
|
||||
if block
|
||||
.is_tagged_with("minecraft:dripstone_replaceable_blocks")
|
||||
.unwrap()
|
||||
{
|
||||
chunk.set_block_state(
|
||||
&pos.0,
|
||||
&get_state_by_state_id(Block::DRIPSTONE_BLOCK.default_state_id).unwrap(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SmallDripstoneFeature {
|
||||
chance_of_taller_dripstone: f32,
|
||||
chance_of_directional_spread: f32,
|
||||
chance_of_spread_radius2: f32,
|
||||
chance_of_spread_radius3: f32,
|
||||
}
|
||||
|
||||
impl SmallDripstoneFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
if let Some(dir) = Self::get_direction(chunk, pos, random) {
|
||||
let pos = pos.offset(dir.opposite().to_offset());
|
||||
self.gen_dripstone_blocks(chunk, pos, random);
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_direction(
|
||||
chunk: &mut ProtoChunk,
|
||||
pos: BlockPos,
|
||||
random: &mut RandomGenerator,
|
||||
) -> Option<BlockDirection> {
|
||||
let up = super::can_replace(&chunk.get_block_state(&pos.up().0).to_block());
|
||||
let down: bool = super::can_replace(&chunk.get_block_state(&pos.down().0).to_block());
|
||||
if up && down {
|
||||
return if random.next_bool() {
|
||||
Some(BlockDirection::Down)
|
||||
} else {
|
||||
Some(BlockDirection::Up)
|
||||
};
|
||||
}
|
||||
if up {
|
||||
return Some(BlockDirection::Down);
|
||||
}
|
||||
if down {
|
||||
return Some(BlockDirection::Up);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn gen_dripstone_blocks(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
pos: BlockPos,
|
||||
random: &mut RandomGenerator,
|
||||
) {
|
||||
super::gen_dripstone(chunk, pos);
|
||||
for dir in BlockDirection::horizontal() {
|
||||
if random.next_f32() > self.chance_of_directional_spread {
|
||||
continue;
|
||||
}
|
||||
let pos = pos.offset(dir.to_offset());
|
||||
super::gen_dripstone(chunk, pos);
|
||||
if random.next_f32() > self.chance_of_spread_radius2 {
|
||||
continue;
|
||||
}
|
||||
let pos = pos.offset(BlockDirection::random(random).to_offset());
|
||||
super::gen_dripstone(chunk, pos);
|
||||
if random.next_f32() > self.chance_of_spread_radius3 {
|
||||
continue;
|
||||
}
|
||||
let pos = pos.offset(BlockDirection::random(random).to_offset());
|
||||
super::gen_dripstone(chunk, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EndGatewayFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EndIslandFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EndPlatformFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EndSpikeFeature {
|
||||
// TODO
|
||||
}
|
||||
27
pumpkin-world/src/generation/feature/features/fallen_tree.rs
Normal file
27
pumpkin-world/src/generation/feature/features/fallen_tree.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::block_state_provider::BlockStateProvider};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FallenTreeFeature {
|
||||
trunk_provider: BlockStateProvider,
|
||||
}
|
||||
|
||||
impl FallenTreeFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
_chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
_random: &mut RandomGenerator,
|
||||
_pos: BlockPos,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn gen_stump(&self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, pos: BlockPos) {
|
||||
chunk.set_block_state(&pos.0, &self.trunk_provider.get(random, pos));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FillLayerFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ForestRockFeature {
|
||||
// TODO
|
||||
}
|
||||
6
pumpkin-world/src/generation/feature/features/fossil.rs
Normal file
6
pumpkin-world/src/generation/feature/features/fossil.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FossilFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FreezeTopLayerFeature {
|
||||
// TODO
|
||||
}
|
||||
6
pumpkin-world/src/generation/feature/features/geode.rs
Normal file
6
pumpkin-world/src/generation/feature/features/geode.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GeodeFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GlowstoneBlobFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HugeBrownMushroomFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HugeFungusFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HugeRedMushroomFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IceSpikeFeature {
|
||||
// TODO
|
||||
}
|
||||
6
pumpkin-world/src/generation/feature/features/iceberg.rs
Normal file
6
pumpkin-world/src/generation/feature/features/iceberg.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IcebergFeature {
|
||||
// TODO
|
||||
}
|
||||
6
pumpkin-world/src/generation/feature/features/kelp.rs
Normal file
6
pumpkin-world/src/generation/feature/features/kelp.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct KelpFeature {
|
||||
// TODO
|
||||
}
|
||||
6
pumpkin-world/src/generation/feature/features/lake.rs
Normal file
6
pumpkin-world/src/generation/feature/features/lake.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LakeFeature {
|
||||
// TODO
|
||||
}
|
||||
56
pumpkin-world/src/generation/feature/features/mod.rs
Normal file
56
pumpkin-world/src/generation/feature/features/mod.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
pub mod bamboo;
|
||||
pub mod basalt_columns;
|
||||
pub mod basalt_pillar;
|
||||
pub mod block_column;
|
||||
pub mod block_pile;
|
||||
pub mod blue_ice;
|
||||
pub mod bonus_chest;
|
||||
pub mod chorus_plant;
|
||||
pub mod coral;
|
||||
pub mod delta_feature;
|
||||
pub mod desert_well;
|
||||
pub mod disk;
|
||||
pub mod drip_stone;
|
||||
pub mod end_gateway;
|
||||
pub mod end_island;
|
||||
pub mod end_platform;
|
||||
pub mod end_spike;
|
||||
pub mod fallen_tree;
|
||||
pub mod fill_layer;
|
||||
pub mod forest_rock;
|
||||
pub mod fossil;
|
||||
pub mod freeze_top_layer;
|
||||
pub mod geode;
|
||||
pub mod glowstone_blob;
|
||||
pub mod huge_brown_mushroom;
|
||||
pub mod huge_fungus;
|
||||
pub mod huge_red_mushroom;
|
||||
pub mod ice_spike;
|
||||
pub mod iceberg;
|
||||
pub mod kelp;
|
||||
pub mod lake;
|
||||
pub mod monster_room;
|
||||
pub mod multiface_growth;
|
||||
pub mod nether_forest_vegetation;
|
||||
pub mod netherrack_replace_blobs;
|
||||
pub mod ore;
|
||||
pub mod random_boolean_selector;
|
||||
pub mod random_patch;
|
||||
pub mod random_selector;
|
||||
pub mod replace_single_block;
|
||||
pub mod root_system;
|
||||
pub mod scattered_ore;
|
||||
pub mod sculk_patch;
|
||||
pub mod sea_pickle;
|
||||
pub mod seagrass;
|
||||
pub mod simple_block;
|
||||
pub mod simple_random_selector;
|
||||
pub mod spring_feature;
|
||||
pub mod tree;
|
||||
pub mod twisting_vines;
|
||||
pub mod underwater_magma;
|
||||
pub mod vegetation_patch;
|
||||
pub mod vines;
|
||||
pub mod void_start_platform;
|
||||
pub mod waterlogged_vegetation_patch;
|
||||
pub mod weeping_vines;
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DungeonFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MultifaceGrowthFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NetherForestVegetationFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ReplaceBlobsFeature {
|
||||
// TODO
|
||||
}
|
||||
252
pumpkin-world/src/generation/feature/features/ore.rs
Normal file
252
pumpkin-world/src/generation/feature/features/ore.rs
Normal file
@@ -0,0 +1,252 @@
|
||||
use core::f32;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use pumpkin_data::{BlockDirection, BlockState};
|
||||
use pumpkin_util::{
|
||||
math::{lerp, position::BlockPos, vector3::Vector3},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
block::BlockStateCodec,
|
||||
generation::{height_limit::HeightLimitView, rule::RuleTest},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OreFeature {
|
||||
size: i32,
|
||||
discard_chance_on_air_exposure: f32,
|
||||
targets: Vec<OreTarget>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct OreTarget {
|
||||
pub target: RuleTest,
|
||||
pub state: BlockStateCodec,
|
||||
}
|
||||
|
||||
impl OreFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_block_registry: &dyn BlockRegistryExt,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let f = random.next_f32() * f32::consts::PI;
|
||||
let g = self.size as f32 / 8.0f32;
|
||||
let i = ((self.size as f32 / 16.0f32 * 2.0 + 1.0) / 2.0).ceil() as i32;
|
||||
|
||||
let d = pos.0.x as f64 + f.sin() as f64 * g as f64;
|
||||
let e = pos.0.x as f64 - f.sin() as f64 * g as f64;
|
||||
let h = pos.0.z as f64 + f.cos() as f64 * g as f64; // Use f.cos() for Math.cos(f)
|
||||
let j = pos.0.z as f64 - f.cos() as f64 * g as f64;
|
||||
|
||||
let _k = 2; // This variable 'k' from Java seems unused
|
||||
let l = pos.0.y as f64 + random.next_bounded_i32(3) as f64 - 2.0;
|
||||
let m = pos.0.y as f64 + random.next_bounded_i32(3) as f64 - 2.0;
|
||||
|
||||
let n = pos.0.x - g.ceil() as i32 - i;
|
||||
let o = pos.0.y - 2 - i;
|
||||
let p = pos.0.z - g.ceil() as i32 - i;
|
||||
let q = 2 * (g.ceil() as i32 + i);
|
||||
let r = 2 * (2 + i);
|
||||
|
||||
for _ in n..=(n + q) {
|
||||
for _ in p..=(p + q) {
|
||||
if o > chunk.ocean_floor_height_exclusive(&pos.0.to_vec2_i32()) as i32 {
|
||||
continue;
|
||||
}
|
||||
return self.generate_vein_part(chunk, random, d, e, h, j, l, m, n, o, p, q, r);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn generate_vein_part(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
start_x: f64,
|
||||
end_x: f64,
|
||||
start_z: f64,
|
||||
end_z: f64,
|
||||
start_y: f64,
|
||||
end_y: f64,
|
||||
x_bound: i32,
|
||||
y_bound: i32,
|
||||
z_bound: i32,
|
||||
horizontal_size: i32,
|
||||
vertical_size: i32,
|
||||
) -> bool {
|
||||
let mut placed_blocks_count = 0;
|
||||
let mut bit_set = HashSet::new();
|
||||
let mut mutable_pos = BlockPos::new(0, 0, 0);
|
||||
let j = self.size;
|
||||
let mut ds = vec![0.0; (j * 4) as usize];
|
||||
for k in 0..j {
|
||||
let f = k as f32 / j as f32;
|
||||
let d = lerp(f as f64, start_x, end_x);
|
||||
let e = lerp(f as f64, start_y, end_y);
|
||||
let g = lerp(f as f64, start_z, end_z);
|
||||
let h = random.next_f64() * j as f64 / 16.0;
|
||||
let l = (((f32::consts::PI * f).sin() + 1.0) * h as f32 + 1.0) / 2.0;
|
||||
|
||||
ds[k as usize * 4] = d;
|
||||
ds[k as usize * 4 + 1] = e;
|
||||
ds[k as usize * 4 + 2] = g;
|
||||
ds[k as usize * 4 + 3] = l as f64;
|
||||
}
|
||||
|
||||
for k in 0..(j - 1) {
|
||||
if ds[k as usize * 4 + 3] <= 0.0 {
|
||||
continue;
|
||||
}
|
||||
for m in (k + 1)..j {
|
||||
if ds[m as usize * 4 + 3] <= 0.0 {
|
||||
continue;
|
||||
}
|
||||
let h_val = ds[k as usize * 4 + 3] - ds[m as usize * 4 + 3];
|
||||
let d_val = ds[k as usize * 4] - ds[m as usize * 4];
|
||||
let e_val = ds[k as usize * 4 + 1] - ds[m as usize * 4 + 1];
|
||||
let g_val = ds[k as usize * 4 + 2] - ds[m as usize * 4 + 2];
|
||||
|
||||
if h_val * h_val > d_val * d_val + e_val * e_val + g_val * g_val {
|
||||
if h_val > 0.0 {
|
||||
ds[m as usize * 4 + 3] = -1.0;
|
||||
continue;
|
||||
}
|
||||
ds[k as usize * 4 + 3] = -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for m_idx in 0..j {
|
||||
let d_val = ds[m_idx as usize * 4 + 3];
|
||||
if d_val < 0.0 {
|
||||
continue;
|
||||
}
|
||||
let e_val = ds[m_idx as usize * 4];
|
||||
let g_val = ds[m_idx as usize * 4 + 1];
|
||||
let h_val = ds[m_idx as usize * 4 + 2];
|
||||
|
||||
let n_bound = ((e_val - d_val).floor() as i32).max(x_bound);
|
||||
let o_bound = ((g_val - d_val).floor() as i32).max(y_bound);
|
||||
let p_bound = ((h_val - d_val).floor() as i32).max(z_bound);
|
||||
let q_bound = ((e_val + d_val).floor() as i32).max(n_bound);
|
||||
let r_bound = ((g_val + d_val).floor() as i32).max(o_bound);
|
||||
let s_bound = ((h_val + d_val).floor() as i32).max(p_bound);
|
||||
|
||||
for t_val in n_bound..=q_bound {
|
||||
let u_val = (t_val as f64 + 0.5 - e_val) / d_val;
|
||||
if u_val * u_val >= 1.0 {
|
||||
continue;
|
||||
}
|
||||
for v_val in o_bound..=r_bound {
|
||||
let w_val = (v_val as f64 + 0.5 - g_val) / d_val;
|
||||
if u_val * u_val + w_val * w_val >= 1.0 {
|
||||
continue;
|
||||
}
|
||||
for aa_val in p_bound..=s_bound {
|
||||
let ab_val = (aa_val as f64 + 0.5 - h_val) / d_val;
|
||||
if u_val * u_val + w_val * w_val + ab_val * ab_val >= 1.0 {
|
||||
continue;
|
||||
}
|
||||
if chunk.out_of_height(v_val as i16) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ac = (t_val - x_bound)
|
||||
+ (v_val - y_bound) * horizontal_size
|
||||
+ (aa_val - z_bound) * horizontal_size * vertical_size;
|
||||
|
||||
if bit_set.contains(&ac) {
|
||||
continue;
|
||||
}
|
||||
bit_set.insert(ac);
|
||||
|
||||
mutable_pos.0.x = t_val;
|
||||
mutable_pos.0.y = v_val;
|
||||
mutable_pos.0.z = aa_val;
|
||||
|
||||
// if !world.is_valid_for_set_block(&mutable_pos) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
let ad = t_val;
|
||||
let ae = v_val;
|
||||
let af = aa_val;
|
||||
|
||||
let block_state = chunk.get_block_state(&Vector3::new(ad, ae, af));
|
||||
|
||||
for target in &self.targets {
|
||||
if self.should_place(
|
||||
chunk,
|
||||
block_state.to_state(),
|
||||
random,
|
||||
target,
|
||||
&mut mutable_pos,
|
||||
) {
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(ad, ae, af),
|
||||
&target.state.get_state().unwrap(),
|
||||
);
|
||||
placed_blocks_count += 1;
|
||||
break; // Equivalent to 'continue block11;'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
placed_blocks_count > 0
|
||||
}
|
||||
|
||||
fn should_place(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
state: BlockState,
|
||||
random: &mut RandomGenerator,
|
||||
target: &OreTarget,
|
||||
pos: &mut BlockPos,
|
||||
) -> bool {
|
||||
if !target.target.test(&state, random) {
|
||||
return false;
|
||||
}
|
||||
if Self::should_not_discard(random, self.discard_chance_on_air_exposure) {
|
||||
return true;
|
||||
}
|
||||
!Self::is_exposed_to_air(chunk, pos)
|
||||
}
|
||||
|
||||
fn should_not_discard(random: &mut RandomGenerator, chance: f32) -> bool {
|
||||
if chance <= 0.0f32 {
|
||||
return true;
|
||||
}
|
||||
if chance >= 1.0f32 {
|
||||
return false;
|
||||
}
|
||||
random.next_f32() >= chance
|
||||
}
|
||||
|
||||
fn is_exposed_to_air(chunk: &mut ProtoChunk, pos: &BlockPos) -> bool {
|
||||
for dir in BlockDirection::all() {
|
||||
if chunk
|
||||
.get_block_state(&pos.offset(dir.to_offset()).0)
|
||||
.to_state()
|
||||
.is_air()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk, generation::feature::placed_features::PlacedFeatureWrapper, level::Level,
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RandomBooleanFeature {
|
||||
feature_true: Box<PlacedFeatureWrapper>,
|
||||
feature_false: Box<PlacedFeatureWrapper>,
|
||||
}
|
||||
|
||||
impl RandomBooleanFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
feature_name: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let val = random.next_bool();
|
||||
let feature = if val {
|
||||
&self.feature_true
|
||||
} else {
|
||||
&self.feature_false
|
||||
};
|
||||
Box::pin(feature.get().generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
))
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_util::{
|
||||
math::{position::BlockPos, vector3::Vector3},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk, generation::feature::placed_features::PlacedFeature, level::Level,
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RandomPatchFeature {
|
||||
tries: u8,
|
||||
xz_spread: u8,
|
||||
y_spread: u8,
|
||||
feature: Box<PlacedFeature>,
|
||||
}
|
||||
|
||||
impl RandomPatchFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
feature: &str,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let mut i = 0;
|
||||
let xz = self.xz_spread as i32 + 1;
|
||||
let y = self.y_spread as i32 + 1;
|
||||
for _ in 0..self.tries {
|
||||
let pos = Vector3::new(
|
||||
pos.0.x + random.next_bounded_i32(xz) - random.next_bounded_i32(xz),
|
||||
pos.0.y + random.next_bounded_i32(y) - random.next_bounded_i32(y),
|
||||
pos.0.z + random.next_bounded_i32(xz) - random.next_bounded_i32(xz),
|
||||
);
|
||||
if !Box::pin(self.feature.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature,
|
||||
random,
|
||||
BlockPos(pos),
|
||||
))
|
||||
.await
|
||||
{
|
||||
continue;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
i > 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk, generation::feature::placed_features::PlacedFeatureWrapper, level::Level,
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RandomFeature {
|
||||
features: Vec<RandomFeatureEntry>,
|
||||
default: Box<PlacedFeatureWrapper>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RandomFeatureEntry {
|
||||
feature: PlacedFeatureWrapper,
|
||||
chance: f32,
|
||||
}
|
||||
|
||||
impl RandomFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
feature_name: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
for feature in &self.features {
|
||||
if random.next_f32() >= feature.chance {
|
||||
continue;
|
||||
}
|
||||
return Box::pin(feature.feature.get().generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Box::pin(self.default.get().generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
))
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ReplaceSingleBlockFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RootSystemFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ScatteredOreFeature {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SculkPatchFeature {
|
||||
// TODO
|
||||
}
|
||||
51
pumpkin-world/src/generation/feature/features/sea_pickle.rs
Normal file
51
pumpkin-world/src/generation/feature/features/sea_pickle.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use pumpkin_data::{
|
||||
Block,
|
||||
block_properties::{
|
||||
BlockProperties, EnumVariants, Integer1To4, SeaPickleLikeProperties, get_state_by_state_id,
|
||||
},
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::{int_provider::IntProvider, position::BlockPos, vector2::Vector2},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SeaPickleFeature {
|
||||
count: IntProvider,
|
||||
}
|
||||
|
||||
impl SeaPickleFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let mut times = 0;
|
||||
let count = self.count.get(random);
|
||||
for _ in 0..count {
|
||||
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||
let y =
|
||||
chunk.ocean_floor_height_exclusive(&Vector2::new(pos.0.x + x, pos.0.z + z)) as i32;
|
||||
if chunk.get_block_state(&pos.0).to_block() != Block::WATER {
|
||||
continue;
|
||||
}
|
||||
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
|
||||
let pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
||||
chunk.set_block_state(
|
||||
&pos.0,
|
||||
&get_state_by_state_id(props.to_state_id(&Block::SEA_PICKLE)).unwrap(),
|
||||
);
|
||||
times += 1;
|
||||
}
|
||||
times > 0
|
||||
}
|
||||
}
|
||||
60
pumpkin-world/src/generation/feature/features/seagrass.rs
Normal file
60
pumpkin-world/src/generation/feature/features/seagrass.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use pumpkin_data::{
|
||||
Block,
|
||||
block_properties::{
|
||||
BlockProperties, DoubleBlockHalf, TallSeagrassLikeProperties, get_state_by_state_id,
|
||||
},
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::{position::BlockPos, vector2::Vector2},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SeagrassFeature {
|
||||
probability: f32,
|
||||
}
|
||||
|
||||
impl SeagrassFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
_feature: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||
let y = chunk.ocean_floor_height_exclusive(&Vector2::new(pos.0.x + x, pos.0.z + z)) as i32;
|
||||
let top_pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
||||
if chunk.get_block_state(&top_pos.0).to_block() == Block::WATER {
|
||||
let tall = random.next_f64() < self.probability as f64;
|
||||
if tall {
|
||||
let tall_pos = top_pos.up();
|
||||
if chunk.get_block_state(&tall_pos.0).to_block() == Block::WATER {
|
||||
let mut props = TallSeagrassLikeProperties::default(&Block::TALL_SEAGRASS);
|
||||
props.half = DoubleBlockHalf::Upper;
|
||||
chunk.set_block_state(
|
||||
&top_pos.0,
|
||||
&get_state_by_state_id(Block::TALL_SEAGRASS.default_state_id).unwrap(),
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&tall_pos.0,
|
||||
&get_state_by_state_id(props.to_state_id(&Block::TALL_SEAGRASS)).unwrap(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
chunk.set_block_state(
|
||||
&top_pos.0,
|
||||
&get_state_by_state_id(Block::SEAGRASS.default_state_id).unwrap(),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use pumpkin_data::{BlockDirection, block_properties::get_block_by_state_id};
|
||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
generation::block_state_provider::BlockStateProvider,
|
||||
world::{BlockAccessor, BlockRegistryExt},
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SimpleBlockFeature {
|
||||
to_place: BlockStateProvider,
|
||||
schedule_tick: Option<bool>,
|
||||
}
|
||||
|
||||
impl SimpleBlockFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let state = self.to_place.get(random, pos);
|
||||
let block = get_block_by_state_id(state.id).unwrap();
|
||||
let block_accessor: &dyn BlockAccessor = chunk;
|
||||
if !futures::executor::block_on(async move {
|
||||
block_registry
|
||||
.can_place_at(&block, block_accessor, &pos, BlockDirection::Up)
|
||||
.await
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: check things..
|
||||
chunk.set_block_state(&pos.0, &state);
|
||||
// TODO: schedule tick when needed
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk, generation::feature::placed_features::PlacedFeature, level::Level,
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SimpleRandomFeature {
|
||||
features: Vec<PlacedFeature>,
|
||||
}
|
||||
|
||||
impl SimpleRandomFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
feature_name: &str, // This placed feature
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
let i = random.next_bounded_i32(self.features.len() as i32);
|
||||
let feature = &self.features[i as usize];
|
||||
Box::pin(feature.generate(
|
||||
chunk,
|
||||
level,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
))
|
||||
.await
|
||||
}
|
||||
}
|
||||
130
pumpkin-world/src/generation/feature/features/spring_feature.rs
Normal file
130
pumpkin-world/src/generation/feature/features/spring_feature.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, block::BlockStateCodec, world::BlockRegistryExt};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SpringFeatureFeature {
|
||||
state: BlockStateCodec,
|
||||
requires_block_below: bool,
|
||||
rock_count: i32,
|
||||
hole_count: i32,
|
||||
valid_blocks: BlockWrapper,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
enum BlockWrapper {
|
||||
Single(String),
|
||||
Multi(Vec<String>),
|
||||
}
|
||||
|
||||
impl SpringFeatureFeature {
|
||||
pub fn generate(
|
||||
&self,
|
||||
_block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &mut ProtoChunk,
|
||||
_random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
// i don't think this is the most efficient way, but it works
|
||||
let valid_blocks = match self.valid_blocks.clone() {
|
||||
BlockWrapper::Single(item) => vec![item],
|
||||
BlockWrapper::Multi(items) => items,
|
||||
};
|
||||
if !valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.up().0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if self.requires_block_below
|
||||
&& !valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.offset(BlockDirection::Down.to_offset()).0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let state = chunk.get_block_state(&pos.0);
|
||||
if !state.to_state().is_air() && !valid_blocks.contains(&state.to_block().name.to_string())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut valid = 0;
|
||||
if valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.offset(BlockDirection::West.to_offset()).0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
) {
|
||||
valid += 1;
|
||||
}
|
||||
if valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.offset(BlockDirection::East.to_offset()).0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
) {
|
||||
valid += 1;
|
||||
}
|
||||
if valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.offset(BlockDirection::North.to_offset()).0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
) {
|
||||
valid += 1;
|
||||
}
|
||||
if valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.offset(BlockDirection::South.to_offset()).0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
) {
|
||||
valid += 1;
|
||||
}
|
||||
if valid_blocks.contains(
|
||||
&chunk
|
||||
.get_block_state(&pos.offset(BlockDirection::Down.to_offset()).0)
|
||||
.to_block()
|
||||
.name
|
||||
.to_string(),
|
||||
) {
|
||||
valid += 1;
|
||||
}
|
||||
let mut air = 0;
|
||||
if chunk.is_air(&pos.offset(BlockDirection::West.to_offset()).0) {
|
||||
air += 1;
|
||||
}
|
||||
if chunk.is_air(&pos.offset(BlockDirection::East.to_offset()).0) {
|
||||
air += 1;
|
||||
}
|
||||
if chunk.is_air(&pos.offset(BlockDirection::North.to_offset()).0) {
|
||||
air += 1;
|
||||
}
|
||||
if chunk.is_air(&pos.offset(BlockDirection::South.to_offset()).0) {
|
||||
air += 1;
|
||||
}
|
||||
if chunk.is_air(&pos.offset(BlockDirection::Down.to_offset()).0) {
|
||||
air += 1;
|
||||
}
|
||||
if valid == self.rock_count && air == self.hole_count {
|
||||
chunk.set_block_state(&pos.0, &self.state.get_state().unwrap());
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AlterGroundTreeDecorator {}
|
||||
@@ -0,0 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AttachedToLeavesTreeDecorator {}
|
||||
@@ -0,0 +1,37 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::block_state_provider::BlockStateProvider};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AttachedToLogsTreeDecorator {
|
||||
probability: f32,
|
||||
block_provider: BlockStateProvider,
|
||||
directions: Vec<BlockDirection>,
|
||||
}
|
||||
|
||||
impl AttachedToLogsTreeDecorator {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
_root_positions: Vec<BlockPos>,
|
||||
log_positions: Vec<BlockPos>,
|
||||
) {
|
||||
// TODO: shuffle
|
||||
for pos in log_positions {
|
||||
// TODO: random
|
||||
let pos = pos.offset(self.directions[0].to_offset());
|
||||
if random.next_f32() > self.probability
|
||||
|| !chunk.get_block_state(&pos.0).to_state().is_air()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
chunk.set_block_state(&pos.0, &self.block_provider.get(random, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BeehiveTreeDecorator {
|
||||
probability: f32,
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CocoaTreeDecorator {}
|
||||
@@ -0,0 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreakingHeartTreeDecorator {}
|
||||
@@ -0,0 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LeavesVineTreeDecorator {}
|
||||
@@ -0,0 +1,96 @@
|
||||
use alter_ground::AlterGroundTreeDecorator;
|
||||
use attached_to_leaves::AttachedToLeavesTreeDecorator;
|
||||
use attached_to_logs::AttachedToLogsTreeDecorator;
|
||||
use beehive::BeehiveTreeDecorator;
|
||||
use cocoa::CocoaTreeDecorator;
|
||||
use creaking_heart::CreakingHeartTreeDecorator;
|
||||
use leave_vine::LeavesVineTreeDecorator;
|
||||
use pale_moss::PaleMossTreeDecorator;
|
||||
use place_on_ground::PlaceOnGroundTreeDecorator;
|
||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||
use serde::Deserialize;
|
||||
use trunk_vine::TrunkVineTreeDecorator;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
mod alter_ground;
|
||||
mod attached_to_leaves;
|
||||
mod attached_to_logs;
|
||||
mod beehive;
|
||||
mod cocoa;
|
||||
mod creaking_heart;
|
||||
mod leave_vine;
|
||||
mod pale_moss;
|
||||
mod place_on_ground;
|
||||
mod trunk_vine;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum TreeDecorator {
|
||||
#[serde(rename = "minecraft:trunk_vine")]
|
||||
TrunkVine(TrunkVineTreeDecorator),
|
||||
#[serde(rename = "minecraft:leave_vine")]
|
||||
LeaveVine(LeavesVineTreeDecorator),
|
||||
#[serde(rename = "minecraft:pale_moss")]
|
||||
PaleMoss(PaleMossTreeDecorator),
|
||||
#[serde(rename = "minecraft:creaking_heart")]
|
||||
CreakingHeart(CreakingHeartTreeDecorator),
|
||||
#[serde(rename = "minecraft:cocoa")]
|
||||
Cocoa(CocoaTreeDecorator),
|
||||
#[serde(rename = "minecraft:beehive")]
|
||||
Beehive(BeehiveTreeDecorator),
|
||||
#[serde(rename = "minecraft:alter_ground")]
|
||||
AlterGround(AlterGroundTreeDecorator),
|
||||
#[serde(rename = "minecraft:attached_to_leaves")]
|
||||
AttachedToLeaves(AttachedToLeavesTreeDecorator),
|
||||
#[serde(rename = "minecraft:place_on_ground")]
|
||||
PlaceOnGround(PlaceOnGroundTreeDecorator),
|
||||
#[serde(rename = "minecraft:attached_to_logs")]
|
||||
AttachedToLogs(AttachedToLogsTreeDecorator),
|
||||
}
|
||||
|
||||
impl TreeDecorator {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
root_positions: Vec<BlockPos>,
|
||||
log_positions: Vec<BlockPos>,
|
||||
) {
|
||||
match self {
|
||||
TreeDecorator::TrunkVine(decorator) => decorator.generate(chunk, random, log_positions),
|
||||
TreeDecorator::LeaveVine(_decorator) => {}
|
||||
TreeDecorator::PaleMoss(_decorator) => {}
|
||||
TreeDecorator::CreakingHeart(_decorator) => {}
|
||||
TreeDecorator::Cocoa(_decorator) => {}
|
||||
TreeDecorator::Beehive(_decorator) => {}
|
||||
TreeDecorator::AlterGround(_decorator) => {}
|
||||
TreeDecorator::PlaceOnGround(decorator) => {
|
||||
decorator.generate(chunk, random, root_positions, log_positions)
|
||||
}
|
||||
TreeDecorator::AttachedToLeaves(_decorator) => {}
|
||||
TreeDecorator::AttachedToLogs(decorator) => {
|
||||
decorator.generate(chunk, random, root_positions, log_positions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_leaf_litter_positions(
|
||||
root_positions: Vec<BlockPos>,
|
||||
log_positions: Vec<BlockPos>,
|
||||
) -> Vec<BlockPos> {
|
||||
let mut list = Vec::new();
|
||||
if root_positions.is_empty() {
|
||||
list.extend_from_slice(&log_positions);
|
||||
} else if !log_positions.is_empty()
|
||||
&& root_positions.first().unwrap().0.y == log_positions.first().unwrap().0.y
|
||||
{
|
||||
list.extend_from_slice(&log_positions);
|
||||
list.extend_from_slice(&root_positions);
|
||||
} else {
|
||||
list.extend_from_slice(&root_positions);
|
||||
}
|
||||
|
||||
list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PaleMossTreeDecorator {}
|
||||
@@ -0,0 +1,83 @@
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_util::{
|
||||
math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::block_state_provider::BlockStateProvider};
|
||||
|
||||
use super::TreeDecorator;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PlaceOnGroundTreeDecorator {
|
||||
tries: i32,
|
||||
radius: i32,
|
||||
height: i32,
|
||||
block_state_provider: BlockStateProvider,
|
||||
}
|
||||
|
||||
impl PlaceOnGroundTreeDecorator {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
root_positions: Vec<BlockPos>,
|
||||
log_positions: Vec<BlockPos>,
|
||||
) {
|
||||
let list = TreeDecorator::get_leaf_litter_positions(root_positions, log_positions);
|
||||
|
||||
if list.is_empty() {
|
||||
return;
|
||||
}
|
||||
let pos = list.first().unwrap();
|
||||
let i = pos.0.y;
|
||||
let mut j = pos.0.x;
|
||||
let mut k = pos.0.x;
|
||||
let mut l = pos.0.z;
|
||||
let mut m = pos.0.z;
|
||||
|
||||
for block_pos_2 in list {
|
||||
if block_pos_2.0.y != i {
|
||||
continue;
|
||||
}
|
||||
j = j.min(block_pos_2.0.x);
|
||||
k = k.max(block_pos_2.0.x);
|
||||
l = l.min(block_pos_2.0.z);
|
||||
m = m.max(block_pos_2.0.z);
|
||||
}
|
||||
|
||||
let block_box = BoundingBox::new(
|
||||
Vector3::new(j as f64, i as f64, l as f64),
|
||||
Vector3::new(k as f64, i as f64, m as f64),
|
||||
)
|
||||
.expand(self.radius as f64, self.height as f64, self.radius as f64);
|
||||
|
||||
for _n in 0..self.tries {
|
||||
let pos = BlockPos::new(
|
||||
random.next_inbetween_i32(block_box.min.x as i32, block_box.max.x as i32),
|
||||
random.next_inbetween_i32(block_box.min.y as i32, block_box.max.y as i32),
|
||||
random.next_inbetween_i32(block_box.min.z as i32, block_box.max.z as i32),
|
||||
);
|
||||
self.generate_decoration(chunk, pos, random);
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_decoration(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
pos: BlockPos,
|
||||
random: &mut RandomGenerator,
|
||||
) {
|
||||
let state = chunk.get_block_state(&pos.0);
|
||||
let pos = pos.up();
|
||||
let up_state = chunk.get_block_state(&pos.0);
|
||||
|
||||
// TODO
|
||||
if (up_state.to_state().is_air() || up_state.to_block() == Block::VINE)
|
||||
&& state.to_state().is_full_cube()
|
||||
{
|
||||
chunk.set_block_state(&pos.0, &self.block_state_provider.get(random, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection,
|
||||
block_properties::{BlockProperties, VineLikeProperties, get_state_by_state_id},
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ProtoChunk;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct TrunkVineTreeDecorator;
|
||||
|
||||
impl TrunkVineTreeDecorator {
|
||||
pub fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk,
|
||||
random: &mut RandomGenerator,
|
||||
log_positions: Vec<BlockPos>,
|
||||
) {
|
||||
for pos in log_positions {
|
||||
if random.next_bounded_i32(3) > 0
|
||||
&& chunk.is_air(&pos.offset(BlockDirection::West.to_offset()).0)
|
||||
{
|
||||
let mut vine = VineLikeProperties::default(&Block::VINE);
|
||||
vine.east = true;
|
||||
chunk.set_block_state(
|
||||
&pos.offset(BlockDirection::West.to_offset()).0,
|
||||
&get_state_by_state_id(vine.to_state_id(&Block::VINE)).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
if random.next_bounded_i32(3) > 0
|
||||
&& chunk.is_air(&pos.offset(BlockDirection::East.to_offset()).0)
|
||||
{
|
||||
let mut vine = VineLikeProperties::default(&Block::VINE);
|
||||
vine.west = true;
|
||||
chunk.set_block_state(
|
||||
&pos.offset(BlockDirection::West.to_offset()).0,
|
||||
&get_state_by_state_id(vine.to_state_id(&Block::VINE)).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
if random.next_bounded_i32(3) > 0
|
||||
&& chunk.is_air(&pos.offset(BlockDirection::North.to_offset()).0)
|
||||
{
|
||||
let mut vine = VineLikeProperties::default(&Block::VINE);
|
||||
vine.south = true;
|
||||
chunk.set_block_state(
|
||||
&pos.offset(BlockDirection::West.to_offset()).0,
|
||||
&get_state_by_state_id(vine.to_state_id(&Block::VINE)).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
if random.next_bounded_i32(3) > 0
|
||||
&& chunk.is_air(&pos.offset(BlockDirection::South.to_offset()).0)
|
||||
{
|
||||
let mut vine = VineLikeProperties::default(&Block::VINE);
|
||||
vine.north = true;
|
||||
chunk.set_block_state(
|
||||
&pos.offset(BlockDirection::West.to_offset()).0,
|
||||
&get_state_by_state_id(vine.to_state_id(&Block::VINE)).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::random::RandomGenerator;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::feature::features::tree::TreeNode, level::Level};
|
||||
|
||||
use super::{FoliagePlacer, LeaveValidator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AcaciaFoliagePlacer;
|
||||
|
||||
impl AcaciaFoliagePlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
random: &mut RandomGenerator,
|
||||
node: &TreeNode,
|
||||
foliage_height: i32,
|
||||
radius: i32,
|
||||
_offset: i32,
|
||||
foliage_provider: &BlockState,
|
||||
) {
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
node.center,
|
||||
radius + node.foliage_radius,
|
||||
-1,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
node.center,
|
||||
radius - 1,
|
||||
-foliage_height,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
node.center,
|
||||
radius + node.foliage_radius - 1,
|
||||
0,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveValidator for AcaciaFoliagePlacer {
|
||||
fn is_invalid_for_leaves(
|
||||
&self,
|
||||
_random: &mut pumpkin_util::random::RandomGenerator,
|
||||
dx: i32,
|
||||
y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
_giant_trunk: bool,
|
||||
) -> bool {
|
||||
if y == 0 {
|
||||
return (dx > 1 || dz > 1) && dx != 0 && dz != 0;
|
||||
}
|
||||
dx == radius && dz == radius && radius > 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::feature::features::tree::TreeNode, level::Level};
|
||||
|
||||
use super::{FoliagePlacer, LeaveValidator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BlobFoliagePlacer {
|
||||
height: i32,
|
||||
}
|
||||
|
||||
impl BlobFoliagePlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
random: &mut RandomGenerator,
|
||||
node: &TreeNode,
|
||||
foliage_height: i32,
|
||||
radius: i32,
|
||||
offset: i32,
|
||||
foliage_provider: &BlockState,
|
||||
) {
|
||||
for y in (offset - foliage_height..=offset).rev() {
|
||||
let radius = (radius + node.foliage_radius - 1 - y / 2).max(0);
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
node.center,
|
||||
radius,
|
||||
y,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveValidator for BlobFoliagePlacer {
|
||||
fn is_invalid_for_leaves(
|
||||
&self,
|
||||
random: &mut pumpkin_util::random::RandomGenerator,
|
||||
dx: i32,
|
||||
y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
_giant_trunk: bool,
|
||||
) -> bool {
|
||||
dx == radius && dz == radius && (random.next_bounded_i32(2) == 0 || y == 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::feature::features::tree::TreeNode, level::Level};
|
||||
|
||||
use super::{FoliagePlacer, LeaveValidator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BushFoliagePlacer {
|
||||
height: i32,
|
||||
}
|
||||
|
||||
impl BushFoliagePlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
random: &mut RandomGenerator,
|
||||
node: &TreeNode,
|
||||
foliage_height: i32,
|
||||
radius: i32,
|
||||
offset: i32,
|
||||
foliage_provider: &BlockState,
|
||||
) {
|
||||
for y in (offset - foliage_height..=offset).rev() {
|
||||
let radius = radius + node.foliage_radius - 1 - y;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
node.center,
|
||||
radius,
|
||||
y,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveValidator for BushFoliagePlacer {
|
||||
fn is_invalid_for_leaves(
|
||||
&self,
|
||||
random: &mut pumpkin_util::random::RandomGenerator,
|
||||
dx: i32,
|
||||
_y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
_giant_trunk: bool,
|
||||
) -> bool {
|
||||
dx == radius && dz == radius && random.next_bounded_i32(2) == 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::{
|
||||
math::int_provider::IntProvider,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::feature::features::tree::TreeNode, level::Level};
|
||||
|
||||
use super::{FoliagePlacer, LeaveValidator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CherryFoliagePlacer {
|
||||
height: IntProvider,
|
||||
wide_bottom_layer_hole_chance: f32,
|
||||
corner_hole_chance: f32,
|
||||
hanging_leaves_chance: f32,
|
||||
hanging_leaves_extension_chance: f32,
|
||||
}
|
||||
|
||||
impl CherryFoliagePlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
random: &mut RandomGenerator,
|
||||
node: &TreeNode,
|
||||
foliage_height: i32,
|
||||
radius: i32,
|
||||
offset: i32,
|
||||
foliage_provider: &BlockState,
|
||||
) {
|
||||
let pos = node.center.up_height(offset);
|
||||
let radius = radius + node.foliage_radius - 1;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius - 2,
|
||||
foliage_height - 3,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius - 1,
|
||||
foliage_height - 4,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
for y in foliage_height - 5..0 {
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius,
|
||||
y,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
// TODO: generateSquareWithHangingLeaves
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius,
|
||||
-1,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
// TODO: generateSquareWithHangingLeaves
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius - 1,
|
||||
-2,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
pub fn get_random_height(&self, random: &mut RandomGenerator) -> i32 {
|
||||
self.height.get(random)
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveValidator for CherryFoliagePlacer {
|
||||
fn is_invalid_for_leaves(
|
||||
&self,
|
||||
random: &mut pumpkin_util::random::RandomGenerator,
|
||||
dx: i32,
|
||||
y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
_giant_trunk: bool,
|
||||
) -> bool {
|
||||
if y == -1
|
||||
&& (dx == radius || dz == radius)
|
||||
&& random.next_f32() < self.wide_bottom_layer_hole_chance
|
||||
{
|
||||
return true;
|
||||
}
|
||||
let in_radius = dx == radius && dz == radius;
|
||||
if radius > 2 {
|
||||
return in_radius
|
||||
|| dx + dz > radius * 2 - 2 && random.next_f32() < self.corner_hole_chance;
|
||||
}
|
||||
in_radius && random.next_f32() < self.corner_hole_chance
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::feature::features::tree::TreeNode, level::Level};
|
||||
|
||||
use super::{FoliagePlacer, LeaveValidator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DarkOakFoliagePlacer;
|
||||
|
||||
impl DarkOakFoliagePlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
random: &mut RandomGenerator,
|
||||
node: &TreeNode,
|
||||
_foliage_height: i32,
|
||||
radius: i32,
|
||||
offset: i32,
|
||||
foliage_provider: &BlockState,
|
||||
) {
|
||||
let pos = node.center.up_height(offset);
|
||||
let is_giant = node.giant_trunk;
|
||||
if is_giant {
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius + 2,
|
||||
-1,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius + 3,
|
||||
0,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius + 2,
|
||||
1,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
if random.next_bool() {
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius,
|
||||
2,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius + 2,
|
||||
-1,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
pos,
|
||||
radius + 1,
|
||||
0,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveValidator for DarkOakFoliagePlacer {
|
||||
fn is_position_invalid(
|
||||
&self,
|
||||
random: &mut RandomGenerator,
|
||||
dx: i32,
|
||||
y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
giant_trunk: bool,
|
||||
) -> bool {
|
||||
dbg!("aa");
|
||||
if !(y != 0 || !giant_trunk || dx != -radius && dx < radius || dz != -radius && dz < radius)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// This is default
|
||||
let x = if giant_trunk {
|
||||
dx.abs().min((dx - 1).abs())
|
||||
} else {
|
||||
dx.abs()
|
||||
};
|
||||
let z = if giant_trunk {
|
||||
dz.abs().min((dz - 1).abs())
|
||||
} else {
|
||||
dz.abs()
|
||||
};
|
||||
self.is_invalid_for_leaves(random, x, y, z, radius, giant_trunk)
|
||||
}
|
||||
|
||||
fn is_invalid_for_leaves(
|
||||
&self,
|
||||
_random: &mut pumpkin_util::random::RandomGenerator,
|
||||
dx: i32,
|
||||
y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
giant_trunk: bool,
|
||||
) -> bool {
|
||||
if y == -1 && !giant_trunk {
|
||||
return dx == radius && dz == radius;
|
||||
}
|
||||
if y == 1 {
|
||||
return dx + dz > radius * 2 - 2;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::{math::square, random::RandomGenerator};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{ProtoChunk, generation::feature::features::tree::TreeNode, level::Level};
|
||||
|
||||
use super::{FoliagePlacer, LeaveValidator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LargeOakFoliagePlacer {
|
||||
height: i32,
|
||||
}
|
||||
|
||||
impl LargeOakFoliagePlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub async fn generate(
|
||||
&self,
|
||||
chunk: &mut ProtoChunk<'_>,
|
||||
level: &Arc<Level>,
|
||||
random: &mut RandomGenerator,
|
||||
node: &TreeNode,
|
||||
foliage_height: i32,
|
||||
radius: i32,
|
||||
offset: i32,
|
||||
foliage_provider: &BlockState,
|
||||
) {
|
||||
for y in (offset - foliage_height..=offset).rev() {
|
||||
let radius = radius
|
||||
+ if y == offset || y == offset - foliage_height {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
FoliagePlacer::generate_square(
|
||||
self,
|
||||
chunk,
|
||||
level,
|
||||
random,
|
||||
node.center,
|
||||
radius,
|
||||
y,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveValidator for LargeOakFoliagePlacer {
|
||||
fn is_invalid_for_leaves(
|
||||
&self,
|
||||
_random: &mut pumpkin_util::random::RandomGenerator,
|
||||
dx: i32,
|
||||
_y: i32,
|
||||
dz: i32,
|
||||
radius: i32,
|
||||
_giant_trunk: bool,
|
||||
) -> bool {
|
||||
square(dx as f32 + 0.5) + square(dz as f32 + 0.5) > (radius * radius) as f32
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user