chore(performance): flat compiled surface rules, no recursion at runtime
Decreased the surface function from taking 18% to 7%
This commit is contained in:
parent
cddb7e3f6a
commit
31a5687937
@ -1,459 +1,395 @@
|
|||||||
use heck::ToShoutySnakeCase;
|
use heck::ToShoutySnakeCase;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{ToTokens, format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{collections::BTreeMap, fs};
|
use std::{collections::BTreeMap, fs};
|
||||||
|
|
||||||
/// Deserialized block reference used in chunk generation settings (e.g., default block or fluid).
|
// ── Deserialized input types (unchanged from original) ────────────────────────
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct BlockStateCodecStruct {
|
pub struct BlockStateCodecStruct {
|
||||||
/// Block registry name including the `minecraft:` namespace prefix.
|
|
||||||
#[serde(rename = "Name")]
|
#[serde(rename = "Name")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Optional block state properties (e.g., `{"facing": "north"}`).
|
|
||||||
#[serde(rename = "Properties")]
|
#[serde(rename = "Properties")]
|
||||||
pub properties: Option<BTreeMap<String, String>>,
|
pub properties: Option<BTreeMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialized chunk generation settings for a dimension, sourced from `chunk_gen_settings.json`.
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GenerationSettingsStruct {
|
pub struct GenerationSettingsStruct {
|
||||||
/// Whether aquifer (underground water pocket) generation is enabled.
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub aquifers_enabled: bool,
|
pub aquifers_enabled: bool,
|
||||||
/// Whether ore-vein generation is enabled.
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub ore_veins_enabled: bool,
|
pub ore_veins_enabled: bool,
|
||||||
/// Whether to use the legacy random number source for this dimension.
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub legacy_random_source: bool,
|
pub legacy_random_source: bool,
|
||||||
/// Y-level treated as sea level for this dimension.
|
|
||||||
pub sea_level: i32,
|
pub sea_level: i32,
|
||||||
/// Default fluid block (usually water or lava) placed by the aquifer generator.
|
|
||||||
pub default_fluid: BlockStateCodecStruct,
|
pub default_fluid: BlockStateCodecStruct,
|
||||||
/// Default solid block used to fill the terrain.
|
|
||||||
pub default_block: BlockStateCodecStruct,
|
pub default_block: BlockStateCodecStruct,
|
||||||
/// Noise shape parameters controlling vertical and horizontal cell sizes.
|
|
||||||
#[serde(rename = "noise")]
|
#[serde(rename = "noise")]
|
||||||
pub shape: GenerationShapeConfigStruct,
|
pub shape: GenerationShapeConfigStruct,
|
||||||
/// Hierarchical surface material rule determining which block is placed at each surface point.
|
|
||||||
pub surface_rule: MaterialRuleStruct,
|
pub surface_rule: MaterialRuleStruct,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialized noise-shape configuration controlling terrain cell dimensions.
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GenerationShapeConfigStruct {
|
pub struct GenerationShapeConfigStruct {
|
||||||
/// Minimum Y level for terrain generation.
|
|
||||||
pub min_y: i8,
|
pub min_y: i8,
|
||||||
/// Total vertical span of the generation region in blocks.
|
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
/// Log₂ of the horizontal cell block count (cell width = `1 << size_horizontal`).
|
|
||||||
pub size_horizontal: u8,
|
pub size_horizontal: u8,
|
||||||
/// Log₂ of the vertical cell block count (cell height = `1 << size_vertical`).
|
|
||||||
pub size_vertical: u8,
|
pub size_vertical: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialized surface material rule that determines which block to place at a given surface point.
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum MaterialRuleStruct {
|
pub enum MaterialRuleStruct {
|
||||||
/// Place a specific block state unconditionally.
|
|
||||||
#[serde(rename = "minecraft:block")]
|
#[serde(rename = "minecraft:block")]
|
||||||
Block {
|
Block { result_state: BlockStateCodecStruct },
|
||||||
/// The block state to place.
|
|
||||||
result_state: BlockStateCodecStruct,
|
|
||||||
},
|
|
||||||
/// Evaluate each rule in order, stopping at the first match.
|
|
||||||
#[serde(rename = "minecraft:sequence")]
|
#[serde(rename = "minecraft:sequence")]
|
||||||
Sequence {
|
Sequence { sequence: Vec<Self> },
|
||||||
/// The ordered list of child rules.
|
|
||||||
sequence: Vec<Self>,
|
|
||||||
},
|
|
||||||
/// Apply `then_run` only when `if_true` evaluates to true.
|
|
||||||
#[serde(rename = "minecraft:condition")]
|
#[serde(rename = "minecraft:condition")]
|
||||||
Condition {
|
Condition {
|
||||||
/// The condition that must be satisfied.
|
|
||||||
if_true: MaterialConditionStruct,
|
if_true: MaterialConditionStruct,
|
||||||
/// The rule to apply when the condition is met.
|
|
||||||
then_run: Box<Self>,
|
then_run: Box<Self>,
|
||||||
},
|
},
|
||||||
/// Special Badlands terrain coloring rule.
|
|
||||||
#[serde(rename = "minecraft:bandlands")]
|
#[serde(rename = "minecraft:bandlands")]
|
||||||
Badlands,
|
Badlands,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialized surface material condition that gates a material rule.
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum MaterialConditionStruct {
|
pub enum MaterialConditionStruct {
|
||||||
/// True when the current position is in one of the listed biomes.
|
|
||||||
#[serde(rename = "minecraft:biome")]
|
#[serde(rename = "minecraft:biome")]
|
||||||
Biome {
|
Biome { biome_is: Vec<String> },
|
||||||
/// List of biome resource locations to match against.
|
|
||||||
biome_is: Vec<String>,
|
|
||||||
},
|
|
||||||
/// True when a named noise value is within the given range.
|
|
||||||
#[serde(rename = "minecraft:noise_threshold")]
|
#[serde(rename = "minecraft:noise_threshold")]
|
||||||
NoiseThreshold {
|
NoiseThreshold {
|
||||||
/// Resource location of the noise parameter.
|
|
||||||
noise: String,
|
noise: String,
|
||||||
/// Minimum threshold (inclusive).
|
|
||||||
min_threshold: f64,
|
min_threshold: f64,
|
||||||
/// Maximum threshold (inclusive).
|
|
||||||
max_threshold: f64,
|
max_threshold: f64,
|
||||||
},
|
},
|
||||||
/// True below a Y offset and false above another, with a linear gradient in between.
|
|
||||||
#[serde(rename = "minecraft:vertical_gradient")]
|
#[serde(rename = "minecraft:vertical_gradient")]
|
||||||
VerticalGradient {
|
VerticalGradient {
|
||||||
/// Name of the random source used for this gradient.
|
|
||||||
random_name: String,
|
random_name: String,
|
||||||
/// Y offset below which the condition is always true.
|
|
||||||
true_at_and_below: YOffsetStruct,
|
true_at_and_below: YOffsetStruct,
|
||||||
/// Y offset above which the condition is always false.
|
|
||||||
false_at_and_above: YOffsetStruct,
|
false_at_and_above: YOffsetStruct,
|
||||||
},
|
},
|
||||||
/// True when the position is above a given Y anchor.
|
|
||||||
#[serde(rename = "minecraft:y_above")]
|
#[serde(rename = "minecraft:y_above")]
|
||||||
YAbove {
|
YAbove {
|
||||||
/// The Y offset anchor to compare against.
|
|
||||||
anchor: YOffsetStruct,
|
anchor: YOffsetStruct,
|
||||||
/// Multiplier applied to surface depth when computing the threshold.
|
|
||||||
surface_depth_multiplier: i32,
|
surface_depth_multiplier: i32,
|
||||||
/// Whether to add stone depth to the comparison value.
|
|
||||||
add_stone_depth: bool,
|
add_stone_depth: bool,
|
||||||
},
|
},
|
||||||
/// True when the position is above a water surface within a certain offset.
|
|
||||||
#[serde(rename = "minecraft:water")]
|
#[serde(rename = "minecraft:water")]
|
||||||
Water {
|
Water {
|
||||||
/// Y offset relative to the water surface.
|
|
||||||
offset: i32,
|
offset: i32,
|
||||||
/// Multiplier applied to surface depth.
|
|
||||||
surface_depth_multiplier: i32,
|
surface_depth_multiplier: i32,
|
||||||
/// Whether to add stone depth to the comparison value.
|
|
||||||
add_stone_depth: bool,
|
add_stone_depth: bool,
|
||||||
},
|
},
|
||||||
/// True when the biome temperature is cold (below freezing).
|
|
||||||
#[serde(rename = "minecraft:temperature")]
|
#[serde(rename = "minecraft:temperature")]
|
||||||
Temperature,
|
Temperature,
|
||||||
/// True when the terrain is steep (high slope).
|
|
||||||
#[serde(rename = "minecraft:steep")]
|
#[serde(rename = "minecraft:steep")]
|
||||||
Steep,
|
Steep,
|
||||||
/// Inverts the inner condition.
|
|
||||||
#[serde(rename = "minecraft:not")]
|
#[serde(rename = "minecraft:not")]
|
||||||
Not {
|
Not { invert: Box<Self> },
|
||||||
/// The condition to invert.
|
|
||||||
invert: Box<Self>,
|
|
||||||
},
|
|
||||||
/// True when there is a hole (cave opening) at the position.
|
|
||||||
#[serde(rename = "minecraft:hole")]
|
#[serde(rename = "minecraft:hole")]
|
||||||
Hole,
|
Hole,
|
||||||
/// True when the position is above the preliminary surface estimate.
|
|
||||||
#[serde(rename = "minecraft:above_preliminary_surface")]
|
#[serde(rename = "minecraft:above_preliminary_surface")]
|
||||||
AbovePreliminarySurface,
|
AbovePreliminarySurface,
|
||||||
/// True based on the depth of stone/ceiling relative to the surface.
|
|
||||||
#[serde(rename = "minecraft:stone_depth")]
|
#[serde(rename = "minecraft:stone_depth")]
|
||||||
StoneDepth {
|
StoneDepth {
|
||||||
/// Y offset added to the depth value.
|
|
||||||
offset: i32,
|
offset: i32,
|
||||||
/// Whether to include surface depth in the calculation.
|
|
||||||
add_surface_depth: bool,
|
add_surface_depth: bool,
|
||||||
/// Additional secondary depth range.
|
|
||||||
secondary_depth_range: i32,
|
secondary_depth_range: i32,
|
||||||
/// Surface type to measure from: `"ceiling"` or `"floor"`.
|
|
||||||
surface_type: String,
|
surface_type: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialized Y offset that can be expressed relative to different reference points.
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum YOffsetStruct {
|
pub enum YOffsetStruct {
|
||||||
/// An absolute Y coordinate.
|
Absolute { absolute: i16 },
|
||||||
Absolute {
|
AboveBottom { above_bottom: i8 },
|
||||||
/// The absolute Y level.
|
BelowTop { below_top: i8 },
|
||||||
absolute: i16,
|
|
||||||
},
|
|
||||||
/// A Y level measured upward from the dimension's minimum Y.
|
|
||||||
AboveBottom {
|
|
||||||
/// Number of blocks above the bottom of the dimension.
|
|
||||||
above_bottom: i8,
|
|
||||||
},
|
|
||||||
/// A Y level measured downward from the dimension's maximum Y.
|
|
||||||
BelowTop {
|
|
||||||
/// Number of blocks below the top of the dimension.
|
|
||||||
below_top: i8,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- ToTokens Implementations ---
|
// ── Flat bytecode compiler ────────────────────────────────────────────────────
|
||||||
|
|
||||||
impl ToTokens for BlockStateCodecStruct {
|
/// Compiles a `MaterialRuleStruct` tree into a flat Vec of `TokenStream`s,
|
||||||
/// Emits a `BlockBlueprint` literal, stripping the `minecraft:` namespace prefix from the block name.
|
/// each representing one `SurfaceInstruction` literal.
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
///
|
||||||
let name = &self.name.strip_prefix("minecraft:").unwrap_or(&self.name);
|
/// Layout rules:
|
||||||
let name_stripped = name.strip_prefix("minecraft:").unwrap_or(name);
|
/// - `Sequence` → children in order, no instruction of its own
|
||||||
let block_ident =
|
/// - `Condition` → `Test*` instruction (with a `skip` field) followed by the
|
||||||
quote::format_ident!("{}", name_stripped.to_uppercase().replace([':', '-'], "_"));
|
/// body instructions; skip jumps past the body on failure
|
||||||
// TODO: use props
|
/// - `Block` → `PlaceBlock` terminal
|
||||||
tokens.extend(quote! {
|
/// - `Badlands` → `PlaceBadlands` terminal
|
||||||
crate::Block::#block_ident.default_state
|
struct Compiler {
|
||||||
});
|
out: Vec<TokenStream>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compiler {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { out: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends instructions for `rule` and returns the number of instructions emitted.
|
||||||
|
fn compile_rule(&mut self, rule: &MaterialRuleStruct) -> usize {
|
||||||
|
let before = self.out.len();
|
||||||
|
match rule {
|
||||||
|
MaterialRuleStruct::Block { result_state } => {
|
||||||
|
let ts = block_state_tokens(result_state);
|
||||||
|
self.out
|
||||||
|
.push(quote!(SurfaceInstruction::PlaceBlock { state: #ts }));
|
||||||
|
}
|
||||||
|
MaterialRuleStruct::Badlands => {
|
||||||
|
self.out.push(quote!(SurfaceInstruction::PlaceBadlands));
|
||||||
|
}
|
||||||
|
MaterialRuleStruct::Sequence { sequence } => {
|
||||||
|
// Sequences have no instruction of their own; children are inline
|
||||||
|
for child in sequence {
|
||||||
|
self.compile_rule(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaterialRuleStruct::Condition { if_true, then_run } => {
|
||||||
|
// Reserve a slot for the Test instruction; we need body size first
|
||||||
|
let test_slot = self.out.len();
|
||||||
|
self.out.push(TokenStream::new()); // placeholder
|
||||||
|
|
||||||
|
let body_len = self.compile_rule(then_run);
|
||||||
|
let skip = body_len as u16;
|
||||||
|
|
||||||
|
// Patch placeholder with the real instruction now that skip is known
|
||||||
|
self.out[test_slot] = compile_condition(if_true, skip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.out.len() - before
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for GenerationSettingsStruct {
|
/// Emits a `SurfaceInstruction::Test*` token stream for the given condition.
|
||||||
/// Emits a `GenerationSettings` struct literal with all fields populated from the deserialized data.
|
/// `skip` is the number of instructions to jump past on failure.
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn compile_condition(cond: &MaterialConditionStruct, skip: u16) -> TokenStream {
|
||||||
let aquifers = self.aquifers_enabled;
|
match cond {
|
||||||
let ores = self.ore_veins_enabled;
|
MaterialConditionStruct::Biome { biome_is } => {
|
||||||
let legacy = self.legacy_random_source;
|
let refs: Vec<TokenStream> = biome_is
|
||||||
let sea_level = self.sea_level;
|
.iter()
|
||||||
let fluid = &self.default_fluid;
|
.map(|b| {
|
||||||
let block = &self.default_block;
|
let ident = format_ident!(
|
||||||
let shape = &self.shape;
|
"{}",
|
||||||
let rule = &self.surface_rule;
|
b.strip_prefix("minecraft:").unwrap_or(b).to_uppercase()
|
||||||
|
);
|
||||||
|
quote!(&crate::biome::Biome::#ident)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
quote!(SurfaceInstruction::TestBiome {
|
||||||
|
biome_is: &[#(#refs),*],
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
tokens.extend(quote!(
|
MaterialConditionStruct::NoiseThreshold {
|
||||||
GenerationSettings {
|
noise,
|
||||||
aquifers_enabled: #aquifers,
|
min_threshold,
|
||||||
ore_veins_enabled: #ores,
|
max_threshold,
|
||||||
legacy_random_source: #legacy,
|
} => {
|
||||||
sea_level: #sea_level,
|
let noise_id = format_ident!(
|
||||||
default_fluid: #fluid,
|
"{}",
|
||||||
shape: #shape,
|
noise
|
||||||
surface_rule: #rule,
|
.strip_prefix("minecraft:")
|
||||||
default_block: #block,
|
.unwrap()
|
||||||
|
.to_shouty_snake_case()
|
||||||
|
);
|
||||||
|
// f64::MAX (or near it) means "no upper bound" — emit the cheaper variant
|
||||||
|
if *max_threshold >= f64::MAX / 2.0 {
|
||||||
|
quote!(SurfaceInstruction::TestNoiseAbove {
|
||||||
|
noise: DoublePerlinNoiseParameters::#noise_id,
|
||||||
|
min: #min_threshold,
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
quote!(SurfaceInstruction::TestNoiseRange {
|
||||||
|
noise: DoublePerlinNoiseParameters::#noise_id,
|
||||||
|
min: #min_threshold,
|
||||||
|
max: #max_threshold,
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
));
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for GenerationShapeConfigStruct {
|
MaterialConditionStruct::VerticalGradient {
|
||||||
/// Emits a `GenerationShapeConfig` struct literal with all noise-shape dimensions.
|
random_name,
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
true_at_and_below,
|
||||||
let min_y = self.min_y;
|
false_at_and_above,
|
||||||
let height = self.height;
|
} => {
|
||||||
let hor = self.size_horizontal;
|
let bytes = md5::compute(random_name.as_bytes());
|
||||||
let ver = self.size_vertical;
|
let lo = u64::from_be_bytes(bytes[0..8].try_into().expect("md5 slice"));
|
||||||
tokens.extend(quote!(
|
let hi = u64::from_be_bytes(bytes[8..16].try_into().expect("md5 slice"));
|
||||||
GenerationShapeConfig { min_y: #min_y, height: #height, size_horizontal: #hor, size_vertical: #ver }
|
let below = y_offset_tokens(true_at_and_below);
|
||||||
));
|
let above = y_offset_tokens(false_at_and_above);
|
||||||
}
|
quote!(SurfaceInstruction::TestVerticalGradient {
|
||||||
}
|
random_lo: #lo,
|
||||||
|
random_hi: #hi,
|
||||||
|
true_at_and_below: #below,
|
||||||
|
false_at_and_above: #above,
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl ToTokens for YOffsetStruct {
|
MaterialConditionStruct::YAbove {
|
||||||
/// Emits a `YOffset` enum variant literal corresponding to the deserialized offset kind.
|
anchor,
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
surface_depth_multiplier,
|
||||||
match self {
|
add_stone_depth,
|
||||||
Self::Absolute { absolute } => {
|
} => {
|
||||||
tokens.extend(quote!(YOffset::Absolute(pumpkin_util::y_offset::Absolute { absolute: #absolute })));
|
let anchor_ts = y_offset_tokens(anchor);
|
||||||
}
|
quote!(SurfaceInstruction::TestYAbove {
|
||||||
Self::AboveBottom { above_bottom } => {
|
anchor: #anchor_ts,
|
||||||
tokens.extend(quote!(YOffset::AboveBottom(pumpkin_util::y_offset::AboveBottom { above_bottom: #above_bottom })));
|
surface_depth_multiplier: #surface_depth_multiplier,
|
||||||
}
|
add_stone_depth: #add_stone_depth,
|
||||||
Self::BelowTop { below_top } => {
|
skip: #skip,
|
||||||
tokens.extend(quote!(YOffset::BelowTop(pumpkin_util::y_offset::BelowTop { below_top: #below_top })));
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaterialConditionStruct::Water {
|
||||||
|
offset,
|
||||||
|
surface_depth_multiplier,
|
||||||
|
add_stone_depth,
|
||||||
|
} => {
|
||||||
|
quote!(SurfaceInstruction::TestWater {
|
||||||
|
offset: #offset,
|
||||||
|
surface_depth_multiplier: #surface_depth_multiplier,
|
||||||
|
add_stone_depth: #add_stone_depth,
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialConditionStruct::StoneDepth {
|
||||||
|
offset,
|
||||||
|
add_surface_depth,
|
||||||
|
secondary_depth_range,
|
||||||
|
surface_type,
|
||||||
|
} => {
|
||||||
|
let st = match surface_type.as_str() {
|
||||||
|
"ceiling" => quote!(VerticalSurfaceType::Ceiling),
|
||||||
|
_ => quote!(VerticalSurfaceType::Floor),
|
||||||
|
};
|
||||||
|
quote!(SurfaceInstruction::TestStoneDepth {
|
||||||
|
offset: #offset,
|
||||||
|
add_surface_depth: #add_surface_depth,
|
||||||
|
secondary_depth_range: #secondary_depth_range,
|
||||||
|
surface_type: #st,
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialConditionStruct::Not { invert } => {
|
||||||
|
// Compile the inner condition with skip=0; the Not wrapper flips the result
|
||||||
|
let inner = compile_condition(invert, 0);
|
||||||
|
quote!(SurfaceInstruction::TestNot {
|
||||||
|
inner: &#inner,
|
||||||
|
skip: #skip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialConditionStruct::AbovePreliminarySurface => {
|
||||||
|
quote!(SurfaceInstruction::TestAbovePreliminarySurface { skip: #skip })
|
||||||
|
}
|
||||||
|
MaterialConditionStruct::Hole => {
|
||||||
|
quote!(SurfaceInstruction::TestHole { skip: #skip })
|
||||||
|
}
|
||||||
|
MaterialConditionStruct::Steep => {
|
||||||
|
quote!(SurfaceInstruction::TestSteep { skip: #skip })
|
||||||
|
}
|
||||||
|
MaterialConditionStruct::Temperature => {
|
||||||
|
quote!(SurfaceInstruction::TestTemperature { skip: #skip })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for MaterialConditionStruct {
|
// ── Small token helpers ───────────────────────────────────────────────────────
|
||||||
/// Emits a `MaterialCondition` enum variant literal for each surface condition kind.
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
match self {
|
|
||||||
Self::Biome { biome_is } => {
|
|
||||||
let biomes = biome_is
|
|
||||||
.iter()
|
|
||||||
.map(|b| b.strip_prefix("minecraft:").unwrap_or(b).to_uppercase());
|
|
||||||
let biome_refs: Vec<TokenStream> = biomes
|
|
||||||
.map(|b| {
|
|
||||||
let ident = format_ident!("{}", b);
|
|
||||||
quote!(&crate::biome::Biome::#ident)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
tokens.extend(quote!(
|
fn block_state_tokens(b: &BlockStateCodecStruct) -> TokenStream {
|
||||||
MaterialCondition::Biome(BiomeMaterialCondition {
|
let name = b.name.strip_prefix("minecraft:").unwrap_or(&b.name);
|
||||||
biome_is: &[#(#biome_refs),*],
|
let ident = format_ident!("{}", name.to_uppercase().replace([':', '-'], "_"));
|
||||||
})
|
quote!(crate::Block::#ident.default_state)
|
||||||
));
|
}
|
||||||
}
|
|
||||||
Self::NoiseThreshold {
|
|
||||||
noise,
|
|
||||||
min_threshold,
|
|
||||||
max_threshold,
|
|
||||||
} => {
|
|
||||||
let noise_id = quote::format_ident!(
|
|
||||||
"{}",
|
|
||||||
noise
|
|
||||||
.strip_prefix("minecraft:")
|
|
||||||
.unwrap()
|
|
||||||
.to_shouty_snake_case()
|
|
||||||
);
|
|
||||||
|
|
||||||
tokens.extend(quote!(
|
fn y_offset_tokens(y: &YOffsetStruct) -> TokenStream {
|
||||||
MaterialCondition::NoiseThreshold(NoiseThresholdMaterialCondition {
|
match y {
|
||||||
noise: DoublePerlinNoiseParameters::#noise_id,
|
YOffsetStruct::Absolute { absolute } => {
|
||||||
min_threshold: #min_threshold,
|
quote!(YOffset::Absolute(pumpkin_util::y_offset::Absolute { absolute: #absolute }))
|
||||||
max_threshold: #max_threshold,
|
}
|
||||||
})
|
YOffsetStruct::AboveBottom { above_bottom } => {
|
||||||
));
|
quote!(YOffset::AboveBottom(pumpkin_util::y_offset::AboveBottom { above_bottom: #above_bottom }))
|
||||||
}
|
}
|
||||||
Self::VerticalGradient {
|
YOffsetStruct::BelowTop { below_top } => {
|
||||||
random_name,
|
quote!(YOffset::BelowTop(pumpkin_util::y_offset::BelowTop { below_top: #below_top }))
|
||||||
true_at_and_below,
|
|
||||||
false_at_and_above,
|
|
||||||
} => {
|
|
||||||
// Pre calc for speed :D
|
|
||||||
let bytes = md5::compute(random_name.as_bytes());
|
|
||||||
let lo = u64::from_be_bytes(bytes[0..8].try_into().expect("incorrect length"));
|
|
||||||
let hi = u64::from_be_bytes(bytes[8..16].try_into().expect("incorrect length"));
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialCondition::VerticalGradient(VerticalGradientMaterialCondition {
|
|
||||||
random_lo: #lo,
|
|
||||||
random_hi: #hi,
|
|
||||||
true_at_and_below: #true_at_and_below,
|
|
||||||
false_at_and_above: #false_at_and_above,
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Self::YAbove {
|
|
||||||
anchor,
|
|
||||||
surface_depth_multiplier,
|
|
||||||
add_stone_depth,
|
|
||||||
} => {
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialCondition::YAbove(AboveYMaterialCondition {
|
|
||||||
anchor: #anchor,
|
|
||||||
surface_depth_multiplier: #surface_depth_multiplier,
|
|
||||||
add_stone_depth: #add_stone_depth,
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Self::Water {
|
|
||||||
offset,
|
|
||||||
surface_depth_multiplier,
|
|
||||||
add_stone_depth,
|
|
||||||
} => {
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialCondition::Water(WaterMaterialCondition {
|
|
||||||
offset: #offset,
|
|
||||||
surface_depth_multiplier: #surface_depth_multiplier,
|
|
||||||
add_stone_depth: #add_stone_depth,
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Self::Temperature => {
|
|
||||||
tokens.extend(quote!(MaterialCondition::Temperature));
|
|
||||||
}
|
|
||||||
Self::Steep => {
|
|
||||||
tokens.extend(quote!(MaterialCondition::Steep));
|
|
||||||
}
|
|
||||||
Self::Not { invert } => {
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialCondition::Not(NotMaterialCondition {
|
|
||||||
invert: &#invert,
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Self::Hole => {
|
|
||||||
tokens.extend(quote!(MaterialCondition::Hole(HoleMaterialCondition)));
|
|
||||||
}
|
|
||||||
Self::AbovePreliminarySurface => {
|
|
||||||
tokens.extend(quote!(MaterialCondition::AbovePreliminarySurface(
|
|
||||||
SurfaceMaterialCondition
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Self::StoneDepth {
|
|
||||||
offset,
|
|
||||||
add_surface_depth,
|
|
||||||
secondary_depth_range,
|
|
||||||
surface_type,
|
|
||||||
} => {
|
|
||||||
let surface_type_token = match surface_type.as_str() {
|
|
||||||
"ceiling" => quote!(
|
|
||||||
pumpkin_util::math::vertical_surface_type::VerticalSurfaceType::Ceiling
|
|
||||||
),
|
|
||||||
"floor" => quote!(
|
|
||||||
pumpkin_util::math::vertical_surface_type::VerticalSurfaceType::Floor
|
|
||||||
),
|
|
||||||
_ => quote!(panic!("Unknown surface type")),
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialCondition::StoneDepth(StoneDepthMaterialCondition {
|
|
||||||
offset: #offset,
|
|
||||||
add_surface_depth: #add_surface_depth,
|
|
||||||
secondary_depth_range: #secondary_depth_range,
|
|
||||||
surface_type: #surface_type_token,
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for MaterialRuleStruct {
|
fn shape_tokens(s: &GenerationShapeConfigStruct) -> TokenStream {
|
||||||
/// Emits a `MaterialRule` enum variant literal for each surface placement rule kind.
|
let min_y = s.min_y;
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
let height = s.height;
|
||||||
match self {
|
let hor = s.size_horizontal;
|
||||||
Self::Block { result_state } => {
|
let ver = s.size_vertical;
|
||||||
tokens.extend(quote!(
|
quote!(GenerationShapeConfig {
|
||||||
MaterialRule::Block(BlockMaterialRule {
|
min_y: #min_y,
|
||||||
result_state: #result_state
|
height: #height,
|
||||||
})
|
size_horizontal: #hor,
|
||||||
));
|
size_vertical: #ver,
|
||||||
}
|
})
|
||||||
Self::Sequence { sequence } => {
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialRule::Sequence(SequenceMaterialRule {
|
|
||||||
sequence: &[#(#sequence),*]
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Self::Condition { if_true, then_run } => {
|
|
||||||
tokens.extend(quote!(
|
|
||||||
MaterialRule::Condition(ConditionMaterialRule {
|
|
||||||
if_true: #if_true,
|
|
||||||
then_run: &#then_run
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Self::Badlands => {
|
|
||||||
tokens.extend(quote!(MaterialRule::Badlands(BadLandsMaterialRule)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads `chunk_gen_settings.json` and emits the complete chunk generation settings `TokenStream`.
|
fn settings_tokens(s: &GenerationSettingsStruct) -> TokenStream {
|
||||||
|
let aquifers = s.aquifers_enabled;
|
||||||
|
let ores = s.ore_veins_enabled;
|
||||||
|
let legacy = s.legacy_random_source;
|
||||||
|
let sea_level = s.sea_level;
|
||||||
|
let fluid = block_state_tokens(&s.default_fluid);
|
||||||
|
let block = block_state_tokens(&s.default_block);
|
||||||
|
let shape = shape_tokens(&s.shape);
|
||||||
|
|
||||||
|
let mut compiler = Compiler::new();
|
||||||
|
compiler.compile_rule(&s.surface_rule);
|
||||||
|
let instructions = &compiler.out;
|
||||||
|
|
||||||
|
quote!(GenerationSettings {
|
||||||
|
aquifers_enabled: #aquifers,
|
||||||
|
ore_veins_enabled: #ores,
|
||||||
|
legacy_random_source: #legacy,
|
||||||
|
sea_level: #sea_level,
|
||||||
|
default_fluid: #fluid,
|
||||||
|
default_block: #block,
|
||||||
|
shape: #shape,
|
||||||
|
surface_rule: CompiledSurfaceRule {
|
||||||
|
instructions: &[#(#instructions),*],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Entry point ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub fn build() -> TokenStream {
|
pub fn build() -> TokenStream {
|
||||||
let json: BTreeMap<String, GenerationSettingsStruct> =
|
let json: BTreeMap<String, GenerationSettingsStruct> =
|
||||||
serde_json::from_str(&fs::read_to_string("../assets/chunk_gen_settings.json").unwrap())
|
serde_json::from_str(&fs::read_to_string("../assets/chunk_gen_settings.json").unwrap())
|
||||||
.expect("Failed to parse settings.json");
|
.expect("Failed to parse chunk_gen_settings.json");
|
||||||
|
|
||||||
let mut const_defs = TokenStream::new();
|
let const_defs: TokenStream = json
|
||||||
|
.iter()
|
||||||
for (name, settings) in &json {
|
.map(|(name, settings)| {
|
||||||
let upper_name = name.to_uppercase();
|
let const_name = format_ident!("{}", name.to_uppercase());
|
||||||
let const_name = format_ident!("{}", upper_name);
|
let body = settings_tokens(settings);
|
||||||
|
quote!(pub const #const_name: GenerationSettings = #body;)
|
||||||
const_defs.extend(quote!(
|
})
|
||||||
pub const #const_name: GenerationSettings = #settings;
|
.collect();
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
use crate::dimension::Dimension;
|
use crate::dimension::Dimension;
|
||||||
use crate::chunk::DoublePerlinNoiseParameters;
|
use crate::chunk::DoublePerlinNoiseParameters;
|
||||||
use crate::BlockState;
|
use crate::BlockState;
|
||||||
|
|
||||||
use std::{cell::RefCell, num::NonZeroUsize};
|
|
||||||
use pumpkin_util::random::RandomDeriver;
|
|
||||||
use pumpkin_util::y_offset::YOffset;
|
use pumpkin_util::y_offset::YOffset;
|
||||||
|
use pumpkin_util::math::vertical_surface_type::VerticalSurfaceType;
|
||||||
use crate::biome::Biome;
|
use crate::biome::Biome;
|
||||||
use pumpkin_util::y_offset::Absolute;
|
|
||||||
|
// ── Core settings struct ──────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct GenerationSettings {
|
pub struct GenerationSettings {
|
||||||
pub aquifers_enabled: bool,
|
pub aquifers_enabled: bool,
|
||||||
@ -461,9 +397,10 @@ pub fn build() -> TokenStream {
|
|||||||
pub legacy_random_source: bool,
|
pub legacy_random_source: bool,
|
||||||
pub sea_level: i32,
|
pub sea_level: i32,
|
||||||
pub default_fluid: &'static BlockState,
|
pub default_fluid: &'static BlockState,
|
||||||
pub shape: GenerationShapeConfig,
|
|
||||||
pub surface_rule: MaterialRule,
|
|
||||||
pub default_block: &'static BlockState,
|
pub default_block: &'static BlockState,
|
||||||
|
pub shape: GenerationShapeConfig,
|
||||||
|
/// Flat compiled surface rule — no recursion at runtime.
|
||||||
|
pub surface_rule: CompiledSurfaceRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GenerationShapeConfig {
|
pub struct GenerationShapeConfig {
|
||||||
@ -474,12 +411,9 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GenerationShapeConfig {
|
impl GenerationShapeConfig {
|
||||||
#[inline]
|
#[inline] #[must_use]
|
||||||
#[must_use]
|
|
||||||
pub const fn vertical_cell_block_count(&self) -> u8 { self.size_vertical << 2 }
|
pub const fn vertical_cell_block_count(&self) -> u8 { self.size_vertical << 2 }
|
||||||
|
#[inline] #[must_use]
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub const fn horizontal_cell_block_count(&self) -> u8 { self.size_horizontal << 2 }
|
pub const fn horizontal_cell_block_count(&self) -> u8 { self.size_horizontal << 2 }
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -504,107 +438,148 @@ pub fn build() -> TokenStream {
|
|||||||
} else {
|
} else {
|
||||||
new_top + new_min.unsigned_abs() as u16
|
new_top + new_min.unsigned_abs() as u16
|
||||||
};
|
};
|
||||||
|
Self { min_y: new_min, height: new_height,
|
||||||
Self {
|
size_horizontal: self.size_horizontal, size_vertical: self.size_vertical }
|
||||||
min_y: new_min,
|
|
||||||
height: new_height,
|
|
||||||
size_horizontal: self.size_horizontal,
|
|
||||||
size_vertical: self.size_vertical,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockMaterialRule {
|
// ── Flat bytecode types ───────────────────────────────────────────────
|
||||||
pub result_state: &'static BlockState,
|
|
||||||
|
/// A compiled, flat representation of a surface rule tree.
|
||||||
|
/// Evaluated by a simple index-advancing loop — no recursion.
|
||||||
|
pub struct CompiledSurfaceRule {
|
||||||
|
pub instructions: &'static [SurfaceInstruction],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SequenceMaterialRule {
|
/// One instruction in the flat surface rule bytecode.
|
||||||
pub sequence: &'static [MaterialRule],
|
///
|
||||||
|
/// Test instructions carry a `skip: u16` field. On failure the evaluator
|
||||||
|
/// advances the program counter by `skip + 1` (past the body). On success
|
||||||
|
/// it advances by 1 (into the body). Terminals end evaluation immediately.
|
||||||
|
pub enum SurfaceInstruction {
|
||||||
|
// ── Terminals ────────────────────────────────────────────────────
|
||||||
|
PlaceBlock { state: &'static BlockState },
|
||||||
|
PlaceBadlands,
|
||||||
|
|
||||||
|
// ── Conditions ───────────────────────────────────────────────────
|
||||||
|
TestBiome {
|
||||||
|
biome_is: &'static [&'static Biome],
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
/// Noise >= min (the f64::MAX upper-bound fast path)
|
||||||
|
TestNoiseAbove {
|
||||||
|
noise: DoublePerlinNoiseParameters,
|
||||||
|
min: f64,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
/// min <= noise <= max
|
||||||
|
TestNoiseRange {
|
||||||
|
noise: DoublePerlinNoiseParameters,
|
||||||
|
min: f64,
|
||||||
|
max: f64,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
TestVerticalGradient {
|
||||||
|
random_lo: u64,
|
||||||
|
random_hi: u64,
|
||||||
|
true_at_and_below: YOffset,
|
||||||
|
false_at_and_above: YOffset,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
TestYAbove {
|
||||||
|
anchor: YOffset,
|
||||||
|
surface_depth_multiplier: i32,
|
||||||
|
add_stone_depth: bool,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
TestWater {
|
||||||
|
offset: i32,
|
||||||
|
surface_depth_multiplier: i32,
|
||||||
|
add_stone_depth: bool,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
TestStoneDepth {
|
||||||
|
offset: i32,
|
||||||
|
add_surface_depth: bool,
|
||||||
|
secondary_depth_range: i32,
|
||||||
|
surface_type: VerticalSurfaceType,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
|
TestAbovePreliminarySurface { skip: u16 },
|
||||||
|
TestHole { skip: u16 },
|
||||||
|
TestSteep { skip: u16 },
|
||||||
|
TestTemperature { skip: u16 },
|
||||||
|
/// Inverts a single inner condition.
|
||||||
|
/// The inner condition is stored inline — it must not itself contain
|
||||||
|
/// a body (i.e. it always has inner skip = 0).
|
||||||
|
TestNot {
|
||||||
|
inner: &'static SurfaceInstruction,
|
||||||
|
skip: u16,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConditionMaterialRule {
|
// ── Per-column noise cache ────────────────────────────────────────────
|
||||||
pub if_true: MaterialCondition,
|
|
||||||
pub then_run: &'static MaterialRule,
|
/// Caches noise samples for one (x, z) column.
|
||||||
|
/// Call `invalidate(x, z)` once per column; `get` then returns the cached
|
||||||
|
/// value on subsequent calls for the same noise parameter.
|
||||||
|
pub struct ColumnNoiseCache {
|
||||||
|
values: [f64; DoublePerlinNoiseParameters::COUNT],
|
||||||
|
valid: [bool; DoublePerlinNoiseParameters::COUNT],
|
||||||
|
col_x: i32,
|
||||||
|
col_z: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BadLandsMaterialRule;
|
impl ColumnNoiseCache {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
values: [0.0; DoublePerlinNoiseParameters::COUNT],
|
||||||
|
valid: [false; DoublePerlinNoiseParameters::COUNT],
|
||||||
|
col_x: i32::MIN,
|
||||||
|
col_z: i32::MIN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum MaterialRule {
|
/// Must be called at the start of each new (x, z) column.
|
||||||
Block(BlockMaterialRule),
|
#[inline]
|
||||||
Sequence(SequenceMaterialRule),
|
pub fn invalidate(&mut self, x: i32, z: i32) {
|
||||||
Condition(ConditionMaterialRule),
|
if self.col_x != x || self.col_z != z {
|
||||||
Badlands(BadLandsMaterialRule),
|
self.valid = [false; DoublePerlinNoiseParameters::COUNT];
|
||||||
|
self.col_x = x;
|
||||||
|
self.col_z = z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the cached noise value, sampling it on first access.
|
||||||
|
#[inline]
|
||||||
|
pub fn get(
|
||||||
|
&mut self,
|
||||||
|
noise: &DoublePerlinNoiseParameters,
|
||||||
|
sample: impl FnOnce() -> f64,
|
||||||
|
) -> f64 {
|
||||||
|
let idx = noise.id;
|
||||||
|
if !self.valid[idx] {
|
||||||
|
self.values[idx] = sample();
|
||||||
|
self.valid[idx] = true;
|
||||||
|
}
|
||||||
|
self.values[idx]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ColumnNoiseCache {
|
||||||
pub struct BiomeMaterialCondition {
|
fn default() -> Self { Self::new() }
|
||||||
pub biome_is: &'static [&'static Biome],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoiseThresholdMaterialCondition {
|
// ── Generated constants ───────────────────────────────────────────────
|
||||||
pub noise: DoublePerlinNoiseParameters,
|
|
||||||
pub min_threshold: f64,
|
|
||||||
pub max_threshold: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VerticalGradientMaterialCondition {
|
|
||||||
pub random_lo: u64,
|
|
||||||
pub random_hi: u64,
|
|
||||||
pub true_at_and_below: YOffset,
|
|
||||||
pub false_at_and_above: YOffset,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AboveYMaterialCondition {
|
|
||||||
pub anchor: YOffset,
|
|
||||||
pub surface_depth_multiplier: i32,
|
|
||||||
pub add_stone_depth: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WaterMaterialCondition {
|
|
||||||
pub offset: i32,
|
|
||||||
pub surface_depth_multiplier: i32,
|
|
||||||
pub add_stone_depth: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HoleMaterialCondition;
|
|
||||||
|
|
||||||
pub struct NotMaterialCondition {
|
|
||||||
pub invert: &'static MaterialCondition,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SurfaceMaterialCondition;
|
|
||||||
|
|
||||||
pub struct StoneDepthMaterialCondition {
|
|
||||||
pub offset: i32,
|
|
||||||
pub add_surface_depth: bool,
|
|
||||||
pub secondary_depth_range: i32,
|
|
||||||
pub surface_type: pumpkin_util::math::vertical_surface_type::VerticalSurfaceType,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MaterialCondition {
|
|
||||||
Biome(BiomeMaterialCondition),
|
|
||||||
NoiseThreshold(NoiseThresholdMaterialCondition),
|
|
||||||
VerticalGradient(VerticalGradientMaterialCondition),
|
|
||||||
YAbove(AboveYMaterialCondition),
|
|
||||||
Water(WaterMaterialCondition),
|
|
||||||
Temperature,
|
|
||||||
Steep,
|
|
||||||
Not(NotMaterialCondition),
|
|
||||||
Hole(HoleMaterialCondition),
|
|
||||||
AbovePreliminarySurface(SurfaceMaterialCondition),
|
|
||||||
StoneDepth(StoneDepthMaterialCondition),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenerationSettings {
|
impl GenerationSettings {
|
||||||
#const_defs
|
#const_defs
|
||||||
|
|
||||||
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
pub fn from_dimension(dimension: &Dimension) -> &'static Self {
|
||||||
if dimension == &Dimension::OVERWORLD {
|
match dimension {
|
||||||
&Self::OVERWORLD
|
d if d == &Dimension::OVERWORLD => &Self::OVERWORLD,
|
||||||
} else if dimension == &Dimension::THE_NETHER {
|
d if d == &Dimension::THE_NETHER => &Self::NETHER,
|
||||||
&Self::NETHER
|
_ => &Self::END,
|
||||||
} else {
|
|
||||||
&Self::END
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1019,8 +1019,9 @@ fn value_to_trunk_placer(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
"minecraft:upwards_branching_trunk_placer" => {
|
"minecraft:upwards_branching_trunk_placer" => {
|
||||||
let extra_branch_steps = value_to_int_provider(&v["extra_branch_steps"]);
|
let extra_branch_steps = value_to_int_provider(&v["extra_branch_steps"]);
|
||||||
let place_branch_per_log_probability =
|
let place_branch_per_log_probability = v["place_branch_per_log_probability"]
|
||||||
v["place_branch_per_log_probability"].as_f64().unwrap_or(0.0) as f32;
|
.as_f64()
|
||||||
|
.unwrap_or(0.0) as f32;
|
||||||
let extra_branch_length = value_to_int_provider(&v["extra_branch_length"]);
|
let extra_branch_length = value_to_int_provider(&v["extra_branch_length"]);
|
||||||
let can_grow_through = value_to_block_list(&v["can_grow_through"]);
|
let can_grow_through = value_to_block_list(&v["can_grow_through"]);
|
||||||
quote! {
|
quote! {
|
||||||
@ -1038,7 +1039,8 @@ fn value_to_trunk_placer(v: &Value) -> TokenStream {
|
|||||||
let branch_start_offset_v = &v["branch_start_offset_from_top"];
|
let branch_start_offset_v = &v["branch_start_offset_from_top"];
|
||||||
let min = branch_start_offset_v["min_inclusive"].as_i64().unwrap_or(0) as i32;
|
let min = branch_start_offset_v["min_inclusive"].as_i64().unwrap_or(0) as i32;
|
||||||
let max = branch_start_offset_v["max_inclusive"].as_i64().unwrap_or(0) as i32;
|
let max = branch_start_offset_v["max_inclusive"].as_i64().unwrap_or(0) as i32;
|
||||||
let branch_end_offset_from_top = value_to_int_provider(&v["branch_end_offset_from_top"]);
|
let branch_end_offset_from_top =
|
||||||
|
value_to_int_provider(&v["branch_end_offset_from_top"]);
|
||||||
quote! {
|
quote! {
|
||||||
TrunkType::Cherry(CherryTrunkPlacer {
|
TrunkType::Cherry(CherryTrunkPlacer {
|
||||||
branch_count: #branch_count,
|
branch_count: #branch_count,
|
||||||
|
|||||||
@ -12,41 +12,53 @@ pub fn build() -> TokenStream {
|
|||||||
let mut variants = TokenStream::new();
|
let mut variants = TokenStream::new();
|
||||||
let mut match_variants = TokenStream::new();
|
let mut match_variants = TokenStream::new();
|
||||||
|
|
||||||
for (raw_name, parameter) in &json {
|
for (i, (raw_name, parameter)) in json.iter().enumerate() {
|
||||||
let name = raw_name
|
let name = raw_name
|
||||||
.strip_prefix("minecraft:")
|
.strip_prefix("minecraft:")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.replace("/", "_");
|
.replace("/", "_");
|
||||||
let simple_id = name.clone();
|
let simple_id = name.clone();
|
||||||
let name = format_ident!("{}", name.to_uppercase());
|
let name_ident = format_ident!("{}", name.to_uppercase());
|
||||||
let first_octave = parameter.first_octave;
|
let first_octave = parameter.first_octave;
|
||||||
let amplitudes = ¶meter.amplitudes;
|
let amplitudes = ¶meter.amplitudes;
|
||||||
|
|
||||||
variants.extend([quote! {
|
variants.extend([quote! {
|
||||||
pub const #name: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(#first_octave, &[#(#amplitudes),*], #raw_name);
|
pub const #name_ident: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
#i,
|
||||||
|
#first_octave,
|
||||||
|
&[#(#amplitudes),*],
|
||||||
|
#raw_name
|
||||||
|
);
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
match_variants.extend([quote! {
|
match_variants.extend([quote! {
|
||||||
#simple_id => &Self::#name,
|
#simple_id => &Self::#name_ident,
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
let count = json.len();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
pub struct DoublePerlinNoiseParameters {
|
pub struct DoublePerlinNoiseParameters {
|
||||||
|
pub id: usize,
|
||||||
pub first_octave: i32,
|
pub first_octave: i32,
|
||||||
pub amplitudes: &'static [f64],
|
pub amplitudes: &'static [f64],
|
||||||
id: &'static str,
|
name: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DoublePerlinNoiseParameters {
|
impl DoublePerlinNoiseParameters {
|
||||||
pub const fn new(first_octave: i32, amplitudes: &'static [f64], id: &'static str) -> Self {
|
pub const COUNT: usize = #count;
|
||||||
|
|
||||||
|
pub const fn new(id: usize, first_octave: i32, amplitudes: &'static [f64], name: &'static str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id,
|
||||||
first_octave,
|
first_octave,
|
||||||
amplitudes,
|
amplitudes,
|
||||||
id
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn id(&self) -> &'static str {
|
pub const fn name(&self) -> &'static str {
|
||||||
self.id
|
self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id_to_parameters(id: &str) -> Option<&DoublePerlinNoiseParameters> {
|
pub fn id_to_parameters(id: &str) -> Option<&DoublePerlinNoiseParameters> {
|
||||||
@ -55,6 +67,7 @@ pub fn build() -> TokenStream {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#variants
|
#variants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,19 +1,27 @@
|
|||||||
/* This file is generated. Do not edit manually. */
|
/* This file is generated. Do not edit manually. */
|
||||||
pub struct DoublePerlinNoiseParameters {
|
pub struct DoublePerlinNoiseParameters {
|
||||||
|
pub id: usize,
|
||||||
pub first_octave: i32,
|
pub first_octave: i32,
|
||||||
pub amplitudes: &'static [f64],
|
pub amplitudes: &'static [f64],
|
||||||
id: &'static str,
|
name: &'static str,
|
||||||
}
|
}
|
||||||
impl DoublePerlinNoiseParameters {
|
impl DoublePerlinNoiseParameters {
|
||||||
pub const fn new(first_octave: i32, amplitudes: &'static [f64], id: &'static str) -> Self {
|
pub const COUNT: usize = 62usize;
|
||||||
|
pub const fn new(
|
||||||
|
id: usize,
|
||||||
|
first_octave: i32,
|
||||||
|
amplitudes: &'static [f64],
|
||||||
|
name: &'static str,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id,
|
||||||
first_octave,
|
first_octave,
|
||||||
amplitudes,
|
amplitudes,
|
||||||
id,
|
name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub const fn id(&self) -> &'static str {
|
pub const fn name(&self) -> &'static str {
|
||||||
self.id
|
self.name
|
||||||
}
|
}
|
||||||
pub fn id_to_parameters(id: &str) -> Option<&DoublePerlinNoiseParameters> {
|
pub fn id_to_parameters(id: &str) -> Option<&DoublePerlinNoiseParameters> {
|
||||||
Some(match id {
|
Some(match id {
|
||||||
@ -83,62 +91,91 @@ impl DoublePerlinNoiseParameters {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub const AQUIFER_BARRIER: DoublePerlinNoiseParameters =
|
pub const AQUIFER_BARRIER: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-3i32, &[1f64], "minecraft:aquifer_barrier");
|
DoublePerlinNoiseParameters::new(0usize, -3i32, &[1f64], "minecraft:aquifer_barrier");
|
||||||
pub const AQUIFER_FLUID_LEVEL_FLOODEDNESS: DoublePerlinNoiseParameters =
|
pub const AQUIFER_FLUID_LEVEL_FLOODEDNESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(
|
DoublePerlinNoiseParameters::new(
|
||||||
|
1usize,
|
||||||
-7i32,
|
-7i32,
|
||||||
&[1f64],
|
&[1f64],
|
||||||
"minecraft:aquifer_fluid_level_floodedness",
|
"minecraft:aquifer_fluid_level_floodedness",
|
||||||
);
|
);
|
||||||
pub const AQUIFER_FLUID_LEVEL_SPREAD: DoublePerlinNoiseParameters =
|
pub const AQUIFER_FLUID_LEVEL_SPREAD: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-5i32, &[1f64], "minecraft:aquifer_fluid_level_spread");
|
DoublePerlinNoiseParameters::new(
|
||||||
|
2usize,
|
||||||
|
-5i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:aquifer_fluid_level_spread",
|
||||||
|
);
|
||||||
pub const AQUIFER_LAVA: DoublePerlinNoiseParameters =
|
pub const AQUIFER_LAVA: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-1i32, &[1f64], "minecraft:aquifer_lava");
|
DoublePerlinNoiseParameters::new(3usize, -1i32, &[1f64], "minecraft:aquifer_lava");
|
||||||
pub const BADLANDS_PILLAR: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const BADLANDS_PILLAR: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
4usize,
|
||||||
-2i32,
|
-2i32,
|
||||||
&[1f64, 1f64, 1f64, 1f64],
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
"minecraft:badlands_pillar",
|
"minecraft:badlands_pillar",
|
||||||
);
|
);
|
||||||
pub const BADLANDS_PILLAR_ROOF: DoublePerlinNoiseParameters =
|
pub const BADLANDS_PILLAR_ROOF: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:badlands_pillar_roof");
|
DoublePerlinNoiseParameters::new(5usize, -8i32, &[1f64], "minecraft:badlands_pillar_roof");
|
||||||
pub const BADLANDS_SURFACE: DoublePerlinNoiseParameters =
|
pub const BADLANDS_SURFACE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-6i32, &[1f64, 1f64, 1f64], "minecraft:badlands_surface");
|
6usize,
|
||||||
pub const CALCITE: DoublePerlinNoiseParameters =
|
-6i32,
|
||||||
DoublePerlinNoiseParameters::new(-9i32, &[1f64, 1f64, 1f64, 1f64], "minecraft:calcite");
|
&[1f64, 1f64, 1f64],
|
||||||
|
"minecraft:badlands_surface",
|
||||||
|
);
|
||||||
|
pub const CALCITE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
7usize,
|
||||||
|
-9i32,
|
||||||
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
|
"minecraft:calcite",
|
||||||
|
);
|
||||||
pub const CAVE_CHEESE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const CAVE_CHEESE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
8usize,
|
||||||
-8i32,
|
-8i32,
|
||||||
&[0.5f64, 1f64, 2f64, 1f64, 2f64, 1f64, 0f64, 2f64, 0f64],
|
&[0.5f64, 1f64, 2f64, 1f64, 2f64, 1f64, 0f64, 2f64, 0f64],
|
||||||
"minecraft:cave_cheese",
|
"minecraft:cave_cheese",
|
||||||
);
|
);
|
||||||
pub const CAVE_ENTRANCE: DoublePerlinNoiseParameters =
|
pub const CAVE_ENTRANCE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[0.4f64, 0.5f64, 1f64], "minecraft:cave_entrance");
|
9usize,
|
||||||
|
-7i32,
|
||||||
|
&[0.4f64, 0.5f64, 1f64],
|
||||||
|
"minecraft:cave_entrance",
|
||||||
|
);
|
||||||
pub const CAVE_LAYER: DoublePerlinNoiseParameters =
|
pub const CAVE_LAYER: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:cave_layer");
|
DoublePerlinNoiseParameters::new(10usize, -8i32, &[1f64], "minecraft:cave_layer");
|
||||||
pub const CLAY_BANDS_OFFSET: DoublePerlinNoiseParameters =
|
pub const CLAY_BANDS_OFFSET: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:clay_bands_offset");
|
DoublePerlinNoiseParameters::new(11usize, -8i32, &[1f64], "minecraft:clay_bands_offset");
|
||||||
pub const CONTINENTALNESS: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const CONTINENTALNESS: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
12usize,
|
||||||
-9i32,
|
-9i32,
|
||||||
&[1f64, 1f64, 2f64, 2f64, 2f64, 1f64, 1f64, 1f64, 1f64],
|
&[1f64, 1f64, 2f64, 2f64, 2f64, 1f64, 1f64, 1f64, 1f64],
|
||||||
"minecraft:continentalness",
|
"minecraft:continentalness",
|
||||||
);
|
);
|
||||||
pub const CONTINENTALNESS_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const CONTINENTALNESS_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
13usize,
|
||||||
-11i32,
|
-11i32,
|
||||||
&[1f64, 1f64, 2f64, 2f64, 2f64, 1f64, 1f64, 1f64, 1f64],
|
&[1f64, 1f64, 2f64, 2f64, 2f64, 1f64, 1f64, 1f64, 1f64],
|
||||||
"minecraft:continentalness_large",
|
"minecraft:continentalness_large",
|
||||||
);
|
);
|
||||||
pub const EROSION: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const EROSION: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
14usize,
|
||||||
-9i32,
|
-9i32,
|
||||||
&[1f64, 1f64, 0f64, 1f64, 1f64],
|
&[1f64, 1f64, 0f64, 1f64, 1f64],
|
||||||
"minecraft:erosion",
|
"minecraft:erosion",
|
||||||
);
|
);
|
||||||
pub const EROSION_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const EROSION_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
15usize,
|
||||||
-11i32,
|
-11i32,
|
||||||
&[1f64, 1f64, 0f64, 1f64, 1f64],
|
&[1f64, 1f64, 0f64, 1f64, 1f64],
|
||||||
"minecraft:erosion_large",
|
"minecraft:erosion_large",
|
||||||
);
|
);
|
||||||
pub const GRAVEL: DoublePerlinNoiseParameters =
|
pub const GRAVEL: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64, 1f64, 1f64, 1f64], "minecraft:gravel");
|
16usize,
|
||||||
|
-8i32,
|
||||||
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
|
"minecraft:gravel",
|
||||||
|
);
|
||||||
pub const GRAVEL_LAYER: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const GRAVEL_LAYER: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
17usize,
|
||||||
-8i32,
|
-8i32,
|
||||||
&[
|
&[
|
||||||
1f64,
|
1f64,
|
||||||
@ -153,18 +190,28 @@ impl DoublePerlinNoiseParameters {
|
|||||||
],
|
],
|
||||||
"minecraft:gravel_layer",
|
"minecraft:gravel_layer",
|
||||||
);
|
);
|
||||||
pub const ICE: DoublePerlinNoiseParameters =
|
pub const ICE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-4i32, &[1f64, 1f64, 1f64, 1f64], "minecraft:ice");
|
18usize,
|
||||||
|
-4i32,
|
||||||
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
|
"minecraft:ice",
|
||||||
|
);
|
||||||
pub const ICEBERG_PILLAR: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const ICEBERG_PILLAR: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
19usize,
|
||||||
-6i32,
|
-6i32,
|
||||||
&[1f64, 1f64, 1f64, 1f64],
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
"minecraft:iceberg_pillar",
|
"minecraft:iceberg_pillar",
|
||||||
);
|
);
|
||||||
pub const ICEBERG_PILLAR_ROOF: DoublePerlinNoiseParameters =
|
pub const ICEBERG_PILLAR_ROOF: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-3i32, &[1f64], "minecraft:iceberg_pillar_roof");
|
DoublePerlinNoiseParameters::new(20usize, -3i32, &[1f64], "minecraft:iceberg_pillar_roof");
|
||||||
pub const ICEBERG_SURFACE: DoublePerlinNoiseParameters =
|
pub const ICEBERG_SURFACE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-6i32, &[1f64, 1f64, 1f64], "minecraft:iceberg_surface");
|
21usize,
|
||||||
|
-6i32,
|
||||||
|
&[1f64, 1f64, 1f64],
|
||||||
|
"minecraft:iceberg_surface",
|
||||||
|
);
|
||||||
pub const JAGGED: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const JAGGED: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
22usize,
|
||||||
-16i32,
|
-16i32,
|
||||||
&[
|
&[
|
||||||
1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64,
|
1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64, 1f64,
|
||||||
@ -172,61 +219,90 @@ impl DoublePerlinNoiseParameters {
|
|||||||
],
|
],
|
||||||
"minecraft:jagged",
|
"minecraft:jagged",
|
||||||
);
|
);
|
||||||
pub const NETHER_TEMPERATURE: DoublePerlinNoiseParameters =
|
pub const NETHER_TEMPERATURE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64, 1f64], "minecraft:nether/temperature");
|
23usize,
|
||||||
pub const NETHER_VEGETATION: DoublePerlinNoiseParameters =
|
-7i32,
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64, 1f64], "minecraft:nether/vegetation");
|
&[1f64, 1f64],
|
||||||
pub const NETHER_STATE_SELECTOR: DoublePerlinNoiseParameters =
|
"minecraft:nether/temperature",
|
||||||
DoublePerlinNoiseParameters::new(-4i32, &[1f64], "minecraft:nether_state_selector");
|
);
|
||||||
|
pub const NETHER_VEGETATION: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
24usize,
|
||||||
|
-7i32,
|
||||||
|
&[1f64, 1f64],
|
||||||
|
"minecraft:nether/vegetation",
|
||||||
|
);
|
||||||
|
pub const NETHER_STATE_SELECTOR: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
25usize,
|
||||||
|
-4i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:nether_state_selector",
|
||||||
|
);
|
||||||
pub const NETHER_WART: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const NETHER_WART: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
26usize,
|
||||||
-3i32,
|
-3i32,
|
||||||
&[1f64, 0f64, 0f64, 0.9f64],
|
&[1f64, 0f64, 0f64, 0.9f64],
|
||||||
"minecraft:nether_wart",
|
"minecraft:nether_wart",
|
||||||
);
|
);
|
||||||
pub const NETHERRACK: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const NETHERRACK: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
27usize,
|
||||||
-3i32,
|
-3i32,
|
||||||
&[1f64, 0f64, 0f64, 0.35f64],
|
&[1f64, 0f64, 0f64, 0.35f64],
|
||||||
"minecraft:netherrack",
|
"minecraft:netherrack",
|
||||||
);
|
);
|
||||||
pub const NOODLE: DoublePerlinNoiseParameters =
|
pub const NOODLE: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:noodle");
|
DoublePerlinNoiseParameters::new(28usize, -8i32, &[1f64], "minecraft:noodle");
|
||||||
pub const NOODLE_RIDGE_A: DoublePerlinNoiseParameters =
|
pub const NOODLE_RIDGE_A: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:noodle_ridge_a");
|
DoublePerlinNoiseParameters::new(29usize, -7i32, &[1f64], "minecraft:noodle_ridge_a");
|
||||||
pub const NOODLE_RIDGE_B: DoublePerlinNoiseParameters =
|
pub const NOODLE_RIDGE_B: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:noodle_ridge_b");
|
DoublePerlinNoiseParameters::new(30usize, -7i32, &[1f64], "minecraft:noodle_ridge_b");
|
||||||
pub const NOODLE_THICKNESS: DoublePerlinNoiseParameters =
|
pub const NOODLE_THICKNESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:noodle_thickness");
|
DoublePerlinNoiseParameters::new(31usize, -8i32, &[1f64], "minecraft:noodle_thickness");
|
||||||
pub const OFFSET: DoublePerlinNoiseParameters =
|
pub const OFFSET: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-3i32, &[1f64, 1f64, 1f64, 0f64], "minecraft:offset");
|
32usize,
|
||||||
|
-3i32,
|
||||||
|
&[1f64, 1f64, 1f64, 0f64],
|
||||||
|
"minecraft:offset",
|
||||||
|
);
|
||||||
pub const ORE_GAP: DoublePerlinNoiseParameters =
|
pub const ORE_GAP: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-5i32, &[1f64], "minecraft:ore_gap");
|
DoublePerlinNoiseParameters::new(33usize, -5i32, &[1f64], "minecraft:ore_gap");
|
||||||
pub const ORE_VEIN_A: DoublePerlinNoiseParameters =
|
pub const ORE_VEIN_A: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:ore_vein_a");
|
DoublePerlinNoiseParameters::new(34usize, -7i32, &[1f64], "minecraft:ore_vein_a");
|
||||||
pub const ORE_VEIN_B: DoublePerlinNoiseParameters =
|
pub const ORE_VEIN_B: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:ore_vein_b");
|
DoublePerlinNoiseParameters::new(35usize, -7i32, &[1f64], "minecraft:ore_vein_b");
|
||||||
pub const ORE_VEININESS: DoublePerlinNoiseParameters =
|
pub const ORE_VEININESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:ore_veininess");
|
DoublePerlinNoiseParameters::new(36usize, -8i32, &[1f64], "minecraft:ore_veininess");
|
||||||
pub const PACKED_ICE: DoublePerlinNoiseParameters =
|
pub const PACKED_ICE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64, 1f64, 1f64, 1f64], "minecraft:packed_ice");
|
37usize,
|
||||||
|
-7i32,
|
||||||
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
|
"minecraft:packed_ice",
|
||||||
|
);
|
||||||
pub const PATCH: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const PATCH: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
38usize,
|
||||||
-5i32,
|
-5i32,
|
||||||
&[1f64, 0f64, 0f64, 0f64, 0f64, 0.013333333333333334f64],
|
&[1f64, 0f64, 0f64, 0f64, 0f64, 0.013333333333333334f64],
|
||||||
"minecraft:patch",
|
"minecraft:patch",
|
||||||
);
|
);
|
||||||
pub const PILLAR: DoublePerlinNoiseParameters =
|
pub const PILLAR: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64, 1f64], "minecraft:pillar");
|
DoublePerlinNoiseParameters::new(39usize, -7i32, &[1f64, 1f64], "minecraft:pillar");
|
||||||
pub const PILLAR_RARENESS: DoublePerlinNoiseParameters =
|
pub const PILLAR_RARENESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:pillar_rareness");
|
DoublePerlinNoiseParameters::new(40usize, -8i32, &[1f64], "minecraft:pillar_rareness");
|
||||||
pub const PILLAR_THICKNESS: DoublePerlinNoiseParameters =
|
pub const PILLAR_THICKNESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:pillar_thickness");
|
DoublePerlinNoiseParameters::new(41usize, -8i32, &[1f64], "minecraft:pillar_thickness");
|
||||||
pub const POWDER_SNOW: DoublePerlinNoiseParameters =
|
pub const POWDER_SNOW: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
DoublePerlinNoiseParameters::new(-6i32, &[1f64, 1f64, 1f64, 1f64], "minecraft:powder_snow");
|
42usize,
|
||||||
|
-6i32,
|
||||||
|
&[1f64, 1f64, 1f64, 1f64],
|
||||||
|
"minecraft:powder_snow",
|
||||||
|
);
|
||||||
pub const RIDGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const RIDGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
43usize,
|
||||||
-7i32,
|
-7i32,
|
||||||
&[1f64, 2f64, 1f64, 0f64, 0f64, 0f64],
|
&[1f64, 2f64, 1f64, 0f64, 0f64, 0f64],
|
||||||
"minecraft:ridge",
|
"minecraft:ridge",
|
||||||
);
|
);
|
||||||
pub const SOUL_SAND_LAYER: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const SOUL_SAND_LAYER: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
44usize,
|
||||||
-8i32,
|
-8i32,
|
||||||
&[
|
&[
|
||||||
1f64,
|
1f64,
|
||||||
@ -242,50 +318,80 @@ impl DoublePerlinNoiseParameters {
|
|||||||
"minecraft:soul_sand_layer",
|
"minecraft:soul_sand_layer",
|
||||||
);
|
);
|
||||||
pub const SPAGHETTI_2D: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_2D: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:spaghetti_2d");
|
DoublePerlinNoiseParameters::new(45usize, -7i32, &[1f64], "minecraft:spaghetti_2d");
|
||||||
pub const SPAGHETTI_2D_ELEVATION: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_2D_ELEVATION: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:spaghetti_2d_elevation");
|
DoublePerlinNoiseParameters::new(
|
||||||
|
46usize,
|
||||||
|
-8i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:spaghetti_2d_elevation",
|
||||||
|
);
|
||||||
pub const SPAGHETTI_2D_MODULATOR: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_2D_MODULATOR: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-11i32, &[1f64], "minecraft:spaghetti_2d_modulator");
|
DoublePerlinNoiseParameters::new(
|
||||||
|
47usize,
|
||||||
|
-11i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:spaghetti_2d_modulator",
|
||||||
|
);
|
||||||
pub const SPAGHETTI_2D_THICKNESS: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_2D_THICKNESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-11i32, &[1f64], "minecraft:spaghetti_2d_thickness");
|
DoublePerlinNoiseParameters::new(
|
||||||
|
48usize,
|
||||||
|
-11i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:spaghetti_2d_thickness",
|
||||||
|
);
|
||||||
pub const SPAGHETTI_3D_1: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_3D_1: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:spaghetti_3d_1");
|
DoublePerlinNoiseParameters::new(49usize, -7i32, &[1f64], "minecraft:spaghetti_3d_1");
|
||||||
pub const SPAGHETTI_3D_2: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_3D_2: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-7i32, &[1f64], "minecraft:spaghetti_3d_2");
|
DoublePerlinNoiseParameters::new(50usize, -7i32, &[1f64], "minecraft:spaghetti_3d_2");
|
||||||
pub const SPAGHETTI_3D_RARITY: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_3D_RARITY: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-11i32, &[1f64], "minecraft:spaghetti_3d_rarity");
|
DoublePerlinNoiseParameters::new(51usize, -11i32, &[1f64], "minecraft:spaghetti_3d_rarity");
|
||||||
pub const SPAGHETTI_3D_THICKNESS: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_3D_THICKNESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:spaghetti_3d_thickness");
|
DoublePerlinNoiseParameters::new(
|
||||||
|
52usize,
|
||||||
|
-8i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:spaghetti_3d_thickness",
|
||||||
|
);
|
||||||
pub const SPAGHETTI_ROUGHNESS: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_ROUGHNESS: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-5i32, &[1f64], "minecraft:spaghetti_roughness");
|
DoublePerlinNoiseParameters::new(53usize, -5i32, &[1f64], "minecraft:spaghetti_roughness");
|
||||||
pub const SPAGHETTI_ROUGHNESS_MODULATOR: DoublePerlinNoiseParameters =
|
pub const SPAGHETTI_ROUGHNESS_MODULATOR: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-8i32, &[1f64], "minecraft:spaghetti_roughness_modulator");
|
DoublePerlinNoiseParameters::new(
|
||||||
|
54usize,
|
||||||
|
-8i32,
|
||||||
|
&[1f64],
|
||||||
|
"minecraft:spaghetti_roughness_modulator",
|
||||||
|
);
|
||||||
pub const SURFACE: DoublePerlinNoiseParameters =
|
pub const SURFACE: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-6i32, &[1f64, 1f64, 1f64], "minecraft:surface");
|
DoublePerlinNoiseParameters::new(55usize, -6i32, &[1f64, 1f64, 1f64], "minecraft:surface");
|
||||||
pub const SURFACE_SECONDARY: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const SURFACE_SECONDARY: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
56usize,
|
||||||
-6i32,
|
-6i32,
|
||||||
&[1f64, 1f64, 0f64, 1f64],
|
&[1f64, 1f64, 0f64, 1f64],
|
||||||
"minecraft:surface_secondary",
|
"minecraft:surface_secondary",
|
||||||
);
|
);
|
||||||
pub const SURFACE_SWAMP: DoublePerlinNoiseParameters =
|
pub const SURFACE_SWAMP: DoublePerlinNoiseParameters =
|
||||||
DoublePerlinNoiseParameters::new(-2i32, &[1f64], "minecraft:surface_swamp");
|
DoublePerlinNoiseParameters::new(57usize, -2i32, &[1f64], "minecraft:surface_swamp");
|
||||||
pub const TEMPERATURE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const TEMPERATURE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
58usize,
|
||||||
-10i32,
|
-10i32,
|
||||||
&[1.5f64, 0f64, 1f64, 0f64, 0f64, 0f64],
|
&[1.5f64, 0f64, 1f64, 0f64, 0f64, 0f64],
|
||||||
"minecraft:temperature",
|
"minecraft:temperature",
|
||||||
);
|
);
|
||||||
pub const TEMPERATURE_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const TEMPERATURE_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
59usize,
|
||||||
-12i32,
|
-12i32,
|
||||||
&[1.5f64, 0f64, 1f64, 0f64, 0f64, 0f64],
|
&[1.5f64, 0f64, 1f64, 0f64, 0f64, 0f64],
|
||||||
"minecraft:temperature_large",
|
"minecraft:temperature_large",
|
||||||
);
|
);
|
||||||
pub const VEGETATION: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const VEGETATION: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
60usize,
|
||||||
-8i32,
|
-8i32,
|
||||||
&[1f64, 1f64, 0f64, 0f64, 0f64, 0f64],
|
&[1f64, 1f64, 0f64, 0f64, 0f64, 0f64],
|
||||||
"minecraft:vegetation",
|
"minecraft:vegetation",
|
||||||
);
|
);
|
||||||
pub const VEGETATION_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
pub const VEGETATION_LARGE: DoublePerlinNoiseParameters = DoublePerlinNoiseParameters::new(
|
||||||
|
61usize,
|
||||||
-10i32,
|
-10i32,
|
||||||
&[1f64, 1f64, 0f64, 0f64, 0f64, 0f64],
|
&[1f64, 1f64, 0f64, 0f64, 0f64, 0f64],
|
||||||
"minecraft:vegetation_large",
|
"minecraft:vegetation_large",
|
||||||
|
|||||||
@ -596,7 +596,7 @@ fn is_vec(ty: &Type) -> bool {
|
|||||||
.path
|
.path
|
||||||
.segments
|
.segments
|
||||||
.iter()
|
.iter()
|
||||||
.last()
|
.next_back()
|
||||||
.is_some_and(|segment| segment.ident == "Vec")
|
.is_some_and(|segment| segment.ident == "Vec")
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|||||||
@ -80,7 +80,7 @@ mod double_perlin_noise_sampler_test {
|
|||||||
assert_eq!(rand.next_i32(), -1302745855);
|
assert_eq!(rand.next_i32(), -1302745855);
|
||||||
|
|
||||||
let mut rand_gen = RandomGenerator::Legacy(rand);
|
let mut rand_gen = RandomGenerator::Legacy(rand);
|
||||||
let params = DoublePerlinNoiseParameters::new(0, &[4f64], "");
|
let params = DoublePerlinNoiseParameters::new(0, 0, &[4f64], "");
|
||||||
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, ¶ms, true);
|
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, ¶ms, true);
|
||||||
|
|
||||||
let values = [
|
let values = [
|
||||||
@ -178,7 +178,7 @@ mod double_perlin_noise_sampler_test {
|
|||||||
|
|
||||||
let mut rand_gen = RandomGenerator::Xoroshiro(rand);
|
let mut rand_gen = RandomGenerator::Xoroshiro(rand);
|
||||||
|
|
||||||
let params = DoublePerlinNoiseParameters::new(1, &[2f64, 4f64], "");
|
let params = DoublePerlinNoiseParameters::new(0, 1, &[2f64, 4f64], "");
|
||||||
|
|
||||||
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, ¶ms, false);
|
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, ¶ms, false);
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ impl DoublePerlinNoiseBuilder {
|
|||||||
base_random_deriver: &XoroshiroSplitter,
|
base_random_deriver: &XoroshiroSplitter,
|
||||||
parameters: &DoublePerlinNoiseParameters,
|
parameters: &DoublePerlinNoiseParameters,
|
||||||
) -> DoublePerlinNoiseSampler {
|
) -> DoublePerlinNoiseSampler {
|
||||||
let mut random = base_random_deriver.split_string(parameters.id());
|
let mut random = base_random_deriver.split_string(parameters.name());
|
||||||
DoublePerlinNoiseSampler::from_params(&mut random, parameters, false)
|
DoublePerlinNoiseSampler::from_params(&mut random, parameters, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,7 +245,7 @@ impl ProtoNoiseRouters {
|
|||||||
// NETHER_TEMPERATURE and NETHER_VEGETATION always use NormalNoise.createLegacyNetherBiome with
|
// NETHER_TEMPERATURE and NETHER_VEGETATION always use NormalNoise.createLegacyNetherBiome with
|
||||||
// LegacyRandomSource(seed + 0) and LegacyRandomSource(seed + 1)
|
// LegacyRandomSource(seed + 0) and LegacyRandomSource(seed + 1)
|
||||||
// respectively, regardless of useLegacyRandomSource.
|
// respectively, regardless of useLegacyRandomSource.
|
||||||
let sampler = match data.noise_id.id() {
|
let sampler = match data.noise_id.name() {
|
||||||
"minecraft:nether/temperature" => {
|
"minecraft:nether/temperature" => {
|
||||||
let mut legacy_rand =
|
let mut legacy_rand =
|
||||||
LegacyRand::from_seed(random_config.seed.wrapping_add(0));
|
LegacyRand::from_seed(random_config.seed.wrapping_add(0));
|
||||||
|
|||||||
@ -53,7 +53,7 @@ use crate::generation::structure::structures::{
|
|||||||
StructureGeneratorContext, StructureInstance, create_chunk_random,
|
StructureGeneratorContext, StructureInstance, create_chunk_random,
|
||||||
};
|
};
|
||||||
use crate::generation::structure::try_generate_structure;
|
use crate::generation::structure::try_generate_structure;
|
||||||
use crate::generation::surface::rule::try_apply_material_rule;
|
use crate::generation::surface::evaluate_surface_rule;
|
||||||
use crate::{
|
use crate::{
|
||||||
BlockStateId, ProtoNoiseRouters,
|
BlockStateId, ProtoNoiseRouters,
|
||||||
block::RawBlockState,
|
block::RawBlockState,
|
||||||
@ -916,7 +916,7 @@ impl ProtoChunk {
|
|||||||
context.block_pos_y,
|
context.block_pos_y,
|
||||||
context.block_pos_z,
|
context.block_pos_z,
|
||||||
);
|
);
|
||||||
let new_state = try_apply_material_rule(
|
let new_state = evaluate_surface_rule(
|
||||||
&settings.surface_rule,
|
&settings.surface_rule,
|
||||||
self,
|
self,
|
||||||
&mut context,
|
&mut context,
|
||||||
|
|||||||
@ -410,8 +410,6 @@ impl NetherFortressPiece {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- opening helpers --------------------------------------------------
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn fill_forward_opening(
|
pub fn fill_forward_opening(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
chunk::Biome,
|
BlockState,
|
||||||
chunk_gen_settings::{
|
chunk::{Biome, DoublePerlinNoiseParameters},
|
||||||
AboveYMaterialCondition, MaterialCondition, NoiseThresholdMaterialCondition,
|
chunk_gen_settings::{ColumnNoiseCache, CompiledSurfaceRule, SurfaceInstruction},
|
||||||
NotMaterialCondition, StoneDepthMaterialCondition, VerticalGradientMaterialCondition,
|
|
||||||
WaterMaterialCondition,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{lerp2, vertical_surface_type::VerticalSurfaceType},
|
math::{lerp2, vertical_surface_type::VerticalSurfaceType},
|
||||||
random::{RandomImpl, xoroshiro128::XoroshiroSplitter},
|
random::{RandomImpl, xoroshiro128::XoroshiroSplitter},
|
||||||
|
y_offset::YOffset,
|
||||||
};
|
};
|
||||||
|
|
||||||
use terrain::SurfaceTerrainBuilder;
|
use terrain::SurfaceTerrainBuilder;
|
||||||
@ -26,7 +24,6 @@ use super::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod rule;
|
|
||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
|
|
||||||
pub struct MaterialRuleContext<'a> {
|
pub struct MaterialRuleContext<'a> {
|
||||||
@ -52,6 +49,7 @@ pub struct MaterialRuleContext<'a> {
|
|||||||
pub stone_depth_above: i32,
|
pub stone_depth_above: i32,
|
||||||
pub terrain_builder: &'a SurfaceTerrainBuilder,
|
pub terrain_builder: &'a SurfaceTerrainBuilder,
|
||||||
pub sea_level: i32,
|
pub sea_level: i32,
|
||||||
|
pub noise_cache: ColumnNoiseCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MaterialRuleContext<'a> {
|
impl<'a> MaterialRuleContext<'a> {
|
||||||
@ -88,6 +86,7 @@ impl<'a> MaterialRuleContext<'a> {
|
|||||||
stone_depth_below: 0,
|
stone_depth_below: 0,
|
||||||
stone_depth_above: 0,
|
stone_depth_above: 0,
|
||||||
sea_level,
|
sea_level,
|
||||||
|
noise_cache: ColumnNoiseCache::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,110 +134,233 @@ impl<'a> MaterialRuleContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::similar_names)]
|
pub fn evaluate_surface_rule(
|
||||||
pub fn test_condition(
|
rule: &CompiledSurfaceRule,
|
||||||
condition: &MaterialCondition,
|
|
||||||
chunk: &mut ProtoChunk,
|
chunk: &mut ProtoChunk,
|
||||||
context: &mut MaterialRuleContext,
|
ctx: &mut MaterialRuleContext,
|
||||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
sampler: &mut SurfaceHeightEstimateSampler,
|
||||||
) -> bool {
|
) -> Option<&'static BlockState> {
|
||||||
match condition {
|
let instrs = &rule.instructions;
|
||||||
MaterialCondition::Biome(biome) => BiomeMaterialCondition::test(biome.biome_is, context),
|
let mut pc = 0usize;
|
||||||
MaterialCondition::NoiseThreshold(noise_threshold) => {
|
let x = ctx.block_pos_x;
|
||||||
test_noise_threshold(noise_threshold, context)
|
let z = ctx.block_pos_z;
|
||||||
}
|
let deriver = ctx.random_deriver;
|
||||||
MaterialCondition::VerticalGradient(vertical_gradient) => {
|
while pc < instrs.len() {
|
||||||
test_vertical_gradient(vertical_gradient, context)
|
match &instrs[pc] {
|
||||||
}
|
SurfaceInstruction::PlaceBlock { state } => return Some(state),
|
||||||
MaterialCondition::YAbove(above_y) => test_above_y_material(above_y, context),
|
|
||||||
MaterialCondition::Water(water) => test_water_material(water, context),
|
|
||||||
MaterialCondition::Temperature => {
|
|
||||||
let temperature = context.biome.weather.compute_temperature(
|
|
||||||
context.block_pos_x as f64,
|
|
||||||
context.block_pos_y,
|
|
||||||
context.block_pos_z as f64,
|
|
||||||
context.sea_level,
|
|
||||||
);
|
|
||||||
temperature < 0.15f32
|
|
||||||
}
|
|
||||||
MaterialCondition::Steep => {
|
|
||||||
let local_x = context.block_pos_x & 15;
|
|
||||||
let local_z = context.block_pos_z & 15;
|
|
||||||
|
|
||||||
let local_z_sub = 0.max(local_z - 1);
|
SurfaceInstruction::PlaceBadlands => {
|
||||||
let local_z_add = 15.min(local_z + 1);
|
return Some(get_badlands_block(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
let sub_height = chunk.top_block_height_exclusive(local_x, local_z_sub);
|
SurfaceInstruction::TestBiome { biome_is, skip } => {
|
||||||
let add_height = chunk.top_block_height_exclusive(local_x, local_z_add);
|
if !biome_is.iter().any(|b| std::ptr::eq(*b, ctx.biome)) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if add_height >= sub_height + 4 {
|
SurfaceInstruction::TestNoiseAbove { noise, min, skip } => {
|
||||||
true
|
let v = ctx
|
||||||
} else {
|
.noise_cache
|
||||||
let local_x_sub = 0.max(local_x - 1);
|
.get(noise, || sample_noise(noise, deriver, x, z));
|
||||||
let local_x_add = 15.min(local_x + 1);
|
if v < *min {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let sub_height = chunk.top_block_height_exclusive(local_x_sub, local_z);
|
SurfaceInstruction::TestNoiseRange {
|
||||||
let add_height = chunk.top_block_height_exclusive(local_x_add, local_z);
|
noise,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
skip,
|
||||||
|
} => {
|
||||||
|
let v = ctx
|
||||||
|
.noise_cache
|
||||||
|
.get(noise, || sample_noise(noise, deriver, x, z));
|
||||||
|
if v < *min || v > *max {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub_height >= add_height + 4
|
SurfaceInstruction::TestVerticalGradient {
|
||||||
|
random_lo,
|
||||||
|
random_hi,
|
||||||
|
true_at_and_below,
|
||||||
|
false_at_and_above,
|
||||||
|
skip,
|
||||||
|
} => {
|
||||||
|
if !test_vertical_gradient(
|
||||||
|
ctx,
|
||||||
|
*random_lo,
|
||||||
|
*random_hi,
|
||||||
|
true_at_and_below,
|
||||||
|
false_at_and_above,
|
||||||
|
) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestYAbove {
|
||||||
|
anchor,
|
||||||
|
surface_depth_multiplier,
|
||||||
|
add_stone_depth,
|
||||||
|
skip,
|
||||||
|
} => {
|
||||||
|
if !test_y_above(ctx, anchor, *surface_depth_multiplier, *add_stone_depth) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestWater {
|
||||||
|
offset,
|
||||||
|
surface_depth_multiplier,
|
||||||
|
add_stone_depth,
|
||||||
|
skip,
|
||||||
|
} => {
|
||||||
|
if !test_water(ctx, *offset, *surface_depth_multiplier, *add_stone_depth) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestStoneDepth {
|
||||||
|
offset,
|
||||||
|
add_surface_depth,
|
||||||
|
secondary_depth_range,
|
||||||
|
surface_type,
|
||||||
|
skip,
|
||||||
|
} => {
|
||||||
|
if !test_stone_depth(
|
||||||
|
ctx,
|
||||||
|
*offset,
|
||||||
|
*add_surface_depth,
|
||||||
|
*secondary_depth_range,
|
||||||
|
surface_type,
|
||||||
|
) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestAbovePreliminarySurface { skip } => {
|
||||||
|
if !test_above_preliminary_surface(ctx, sampler) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestHole { skip } => {
|
||||||
|
if !test_hole(ctx) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestSteep { skip } => {
|
||||||
|
if !test_steep(ctx, chunk) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestTemperature { skip } => {
|
||||||
|
if !test_temperature(ctx) {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceInstruction::TestNot { inner, skip } => {
|
||||||
|
let passed = eval_single_instruction(inner, chunk, ctx, sampler);
|
||||||
|
if passed {
|
||||||
|
pc += *skip as usize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaterialCondition::Not(not) => {
|
pc += 1;
|
||||||
test_not_material(not, chunk, context, surface_height_estimate_sampler)
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn eval_single_instruction(
|
||||||
|
instr: &SurfaceInstruction,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
ctx: &mut MaterialRuleContext,
|
||||||
|
sampler: &mut SurfaceHeightEstimateSampler,
|
||||||
|
) -> bool {
|
||||||
|
let x = ctx.block_pos_x;
|
||||||
|
let z = ctx.block_pos_z;
|
||||||
|
let deriver = ctx.random_deriver;
|
||||||
|
match instr {
|
||||||
|
SurfaceInstruction::TestBiome { biome_is, .. } => {
|
||||||
|
biome_is.iter().any(|b| std::ptr::eq(*b, ctx.biome))
|
||||||
}
|
}
|
||||||
MaterialCondition::Hole(_hole) => HoleMaterialCondition::test(context),
|
SurfaceInstruction::TestNoiseAbove { noise, min, .. } => {
|
||||||
MaterialCondition::AbovePreliminarySurface(_above) => {
|
ctx.noise_cache
|
||||||
SurfaceMaterialCondition::test(context, surface_height_estimate_sampler)
|
.get(noise, || sample_noise(noise, deriver, x, z))
|
||||||
|
>= *min
|
||||||
}
|
}
|
||||||
MaterialCondition::StoneDepth(stone_depth) => test_stone_depth(stone_depth, context),
|
SurfaceInstruction::TestNoiseRange {
|
||||||
|
noise, min, max, ..
|
||||||
|
} => {
|
||||||
|
let v = ctx
|
||||||
|
.noise_cache
|
||||||
|
.get(noise, || sample_noise(noise, deriver, x, z));
|
||||||
|
v >= *min && v <= *max
|
||||||
|
}
|
||||||
|
SurfaceInstruction::TestHole { .. } => test_hole(ctx),
|
||||||
|
SurfaceInstruction::TestSteep { .. } => test_steep(ctx, chunk),
|
||||||
|
SurfaceInstruction::TestTemperature { .. } => test_temperature(ctx),
|
||||||
|
SurfaceInstruction::TestYAbove {
|
||||||
|
anchor,
|
||||||
|
surface_depth_multiplier,
|
||||||
|
add_stone_depth,
|
||||||
|
..
|
||||||
|
} => test_y_above(ctx, anchor, *surface_depth_multiplier, *add_stone_depth),
|
||||||
|
SurfaceInstruction::TestWater {
|
||||||
|
offset,
|
||||||
|
surface_depth_multiplier,
|
||||||
|
add_stone_depth,
|
||||||
|
..
|
||||||
|
} => test_water(ctx, *offset, *surface_depth_multiplier, *add_stone_depth),
|
||||||
|
SurfaceInstruction::TestAbovePreliminarySurface { .. } => {
|
||||||
|
test_above_preliminary_surface(ctx, sampler)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HoleMaterialCondition;
|
#[inline]
|
||||||
|
pub fn get_badlands_block(context: &MaterialRuleContext) -> &'static BlockState {
|
||||||
impl HoleMaterialCondition {
|
context.terrain_builder.get_terracotta_block(
|
||||||
pub const fn test(context: &MaterialRuleContext) -> bool {
|
context.block_pos_x,
|
||||||
context.run_depth <= 0
|
context.block_pos_y,
|
||||||
}
|
context.block_pos_z,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn test_above_y_material(
|
#[inline]
|
||||||
condition: &AboveYMaterialCondition,
|
pub const fn test_hole(context: &MaterialRuleContext) -> bool {
|
||||||
|
context.run_depth <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn test_y_above(
|
||||||
context: &MaterialRuleContext,
|
context: &MaterialRuleContext,
|
||||||
|
anchor: &YOffset,
|
||||||
|
surface_depth_multiplier: i32,
|
||||||
|
add_stone_depth: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
context.block_pos_y
|
context.block_pos_y
|
||||||
+ if condition.add_stone_depth {
|
+ if add_stone_depth {
|
||||||
context.stone_depth_above
|
context.stone_depth_above
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
>= condition.anchor.get_y(context.min_y as i16, context.height)
|
>= anchor.get_y(context.min_y as i16, context.height)
|
||||||
+ context.run_depth * condition.surface_depth_multiplier
|
+ context.run_depth * surface_depth_multiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_not_material(
|
#[inline]
|
||||||
condition: &NotMaterialCondition,
|
pub fn test_above_preliminary_surface(
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
context: &mut MaterialRuleContext,
|
context: &mut MaterialRuleContext,
|
||||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
!test_condition(
|
context.block_pos_y >= estimate_surface_height(context, surface_height_estimate_sampler)
|
||||||
condition.invert,
|
|
||||||
chunk,
|
|
||||||
context,
|
|
||||||
surface_height_estimate_sampler,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SurfaceMaterialCondition;
|
|
||||||
|
|
||||||
impl SurfaceMaterialCondition {
|
|
||||||
pub fn test(
|
|
||||||
context: &mut MaterialRuleContext,
|
|
||||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
|
||||||
) -> bool {
|
|
||||||
context.block_pos_y >= estimate_surface_height(context, surface_height_estimate_sampler)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn estimate_surface_height(
|
pub fn estimate_surface_height(
|
||||||
@ -283,41 +405,33 @@ pub fn estimate_surface_height(
|
|||||||
context.surface_min_y
|
context.surface_min_y
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BiomeMaterialCondition;
|
pub fn sample_noise(
|
||||||
|
parameters: &DoublePerlinNoiseParameters,
|
||||||
impl BiomeMaterialCondition {
|
random_deriver: &XoroshiroSplitter,
|
||||||
pub fn test(biome_is: &[&'static Biome], context: &MaterialRuleContext) -> bool {
|
x: i32,
|
||||||
biome_is.contains(&context.biome)
|
z: i32,
|
||||||
}
|
) -> f64 {
|
||||||
}
|
let sampler = DoublePerlinNoiseBuilder::get_noise_sampler_for_id(random_deriver, parameters);
|
||||||
|
sampler.sample(x as f64, 0.0, z as f64)
|
||||||
pub fn test_noise_threshold(
|
|
||||||
condition: &NoiseThresholdMaterialCondition,
|
|
||||||
context: &mut MaterialRuleContext,
|
|
||||||
) -> bool {
|
|
||||||
// TODO: we want to cache these
|
|
||||||
let sampler = DoublePerlinNoiseBuilder::get_noise_sampler_for_id(
|
|
||||||
context.random_deriver,
|
|
||||||
&condition.noise,
|
|
||||||
);
|
|
||||||
let value = sampler.sample(context.block_pos_x as f64, 0.0, context.block_pos_z as f64);
|
|
||||||
value >= condition.min_threshold && value <= condition.max_threshold
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_stone_depth(
|
pub fn test_stone_depth(
|
||||||
condition: &StoneDepthMaterialCondition,
|
|
||||||
context: &mut MaterialRuleContext,
|
context: &mut MaterialRuleContext,
|
||||||
|
offset: i32,
|
||||||
|
add_surface_depth: bool,
|
||||||
|
secondary_depth_range: i32,
|
||||||
|
surface_type: &VerticalSurfaceType,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let stone_depth = match &condition.surface_type {
|
let stone_depth = match surface_type {
|
||||||
VerticalSurfaceType::Ceiling => context.stone_depth_below,
|
VerticalSurfaceType::Ceiling => context.stone_depth_below,
|
||||||
VerticalSurfaceType::Floor => context.stone_depth_above,
|
VerticalSurfaceType::Floor => context.stone_depth_above,
|
||||||
};
|
};
|
||||||
let depth = if condition.add_surface_depth {
|
let depth = if add_surface_depth {
|
||||||
context.run_depth
|
context.run_depth
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
let depth_range = if condition.secondary_depth_range == 0 {
|
let depth_range = if secondary_depth_range == 0 {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
pumpkin_util::math::map(
|
pumpkin_util::math::map(
|
||||||
@ -325,40 +439,37 @@ pub fn test_stone_depth(
|
|||||||
-1.0,
|
-1.0,
|
||||||
1.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
condition.secondary_depth_range as f64,
|
secondary_depth_range as f64,
|
||||||
) as i32
|
) as i32
|
||||||
};
|
};
|
||||||
stone_depth <= 1 + condition.offset + depth + depth_range
|
stone_depth <= 1 + offset + depth + depth_range
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn test_water_material(
|
pub const fn test_water(
|
||||||
condition: &WaterMaterialCondition,
|
|
||||||
context: &MaterialRuleContext,
|
context: &MaterialRuleContext,
|
||||||
|
offset: i32,
|
||||||
|
surface_depth_multiplier: i32,
|
||||||
|
add_stone_depth: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
context.fluid_height == i32::MIN
|
context.fluid_height == i32::MIN
|
||||||
|| context.block_pos_y
|
|| context.block_pos_y
|
||||||
+ (if condition.add_stone_depth {
|
+ (if add_stone_depth {
|
||||||
context.stone_depth_above
|
context.stone_depth_above
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
})
|
})
|
||||||
>= context.fluid_height
|
>= context.fluid_height + offset + context.run_depth * surface_depth_multiplier
|
||||||
+ condition.offset
|
|
||||||
+ context.run_depth * condition.surface_depth_multiplier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// random_deriver: ThreadLocal<RefCell<LruCache<usize, RandomDeriver>>>,
|
|
||||||
|
|
||||||
pub fn test_vertical_gradient(
|
pub fn test_vertical_gradient(
|
||||||
condition: &VerticalGradientMaterialCondition,
|
|
||||||
context: &MaterialRuleContext,
|
context: &MaterialRuleContext,
|
||||||
|
random_lo: u64,
|
||||||
|
random_hi: u64,
|
||||||
|
true_at_and_below: &YOffset,
|
||||||
|
false_at_and_above: &YOffset,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let true_at = condition
|
let true_at = true_at_and_below.get_y(context.min_y as i16, context.height);
|
||||||
.true_at_and_below
|
let false_at = false_at_and_above.get_y(context.min_y as i16, context.height);
|
||||||
.get_y(context.min_y as i16, context.height);
|
|
||||||
let false_at = condition
|
|
||||||
.false_at_and_above
|
|
||||||
.get_y(context.min_y as i16, context.height);
|
|
||||||
|
|
||||||
let block_y = context.block_pos_y;
|
let block_y = context.block_pos_y;
|
||||||
if block_y <= true_at {
|
if block_y <= true_at {
|
||||||
@ -369,9 +480,42 @@ pub fn test_vertical_gradient(
|
|||||||
}
|
}
|
||||||
let splitter = context
|
let splitter = context
|
||||||
.random_deriver
|
.random_deriver
|
||||||
.from_lo_and_hi(condition.random_lo, condition.random_hi)
|
.from_lo_and_hi(random_lo, random_hi)
|
||||||
.next_splitter();
|
.next_splitter();
|
||||||
let mapped = pumpkin_util::math::map(block_y as f32, true_at as f32, false_at as f32, 1.0, 0.0);
|
let mapped = pumpkin_util::math::map(block_y as f32, true_at as f32, false_at as f32, 1.0, 0.0);
|
||||||
let mut random = splitter.split_pos(context.block_pos_x, block_y, context.block_pos_z);
|
let mut random = splitter.split_pos(context.block_pos_x, block_y, context.block_pos_z);
|
||||||
random.next_f32() < mapped
|
random.next_f32() < mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn test_steep(context: &MaterialRuleContext, chunk: &ProtoChunk) -> bool {
|
||||||
|
let local_x = context.block_pos_x & 15;
|
||||||
|
let local_z = context.block_pos_z & 15;
|
||||||
|
|
||||||
|
let local_z_sub = 0.max(local_z - 1);
|
||||||
|
let local_z_add = 15.min(local_z + 1);
|
||||||
|
|
||||||
|
let sub_height = chunk.top_block_height_exclusive(local_x, local_z_sub);
|
||||||
|
let add_height = chunk.top_block_height_exclusive(local_x, local_z_add);
|
||||||
|
|
||||||
|
if add_height >= sub_height + 4 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let local_x_sub = 0.max(local_x - 1);
|
||||||
|
let local_x_add = 15.min(local_x + 1);
|
||||||
|
|
||||||
|
let sub_height = chunk.top_block_height_exclusive(local_x_sub, local_z);
|
||||||
|
let add_height = chunk.top_block_height_exclusive(local_x_add, local_z);
|
||||||
|
|
||||||
|
sub_height >= add_height + 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_temperature(context: &MaterialRuleContext) -> bool {
|
||||||
|
let temperature = context.biome.weather.compute_temperature(
|
||||||
|
context.block_pos_x as f64,
|
||||||
|
context.block_pos_y,
|
||||||
|
context.block_pos_z as f64,
|
||||||
|
context.sea_level,
|
||||||
|
);
|
||||||
|
temperature < 0.15f32
|
||||||
|
}
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
use pumpkin_data::{
|
|
||||||
BlockState,
|
|
||||||
chunk_gen_settings::{ConditionMaterialRule, MaterialRule, SequenceMaterialRule},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::MaterialRuleContext;
|
|
||||||
use crate::{
|
|
||||||
ProtoChunk,
|
|
||||||
generation::{
|
|
||||||
noise::router::surface_height_sampler::SurfaceHeightEstimateSampler,
|
|
||||||
surface::test_condition,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn try_apply_material_rule(
|
|
||||||
rule: &MaterialRule,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
context: &mut MaterialRuleContext,
|
|
||||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
|
||||||
) -> Option<&'static BlockState> {
|
|
||||||
match rule {
|
|
||||||
MaterialRule::Badlands(_badlands) => Some(BadLandsMaterialRule::try_apply(context)),
|
|
||||||
MaterialRule::Block(block) => Some(BlockMaterialRule::try_apply(block.result_state)),
|
|
||||||
MaterialRule::Sequence(sequence) => {
|
|
||||||
try_apply_sequence(sequence, chunk, context, surface_height_estimate_sampler)
|
|
||||||
}
|
|
||||||
MaterialRule::Condition(condition) => {
|
|
||||||
try_apply_condition(condition, chunk, context, surface_height_estimate_sampler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BadLandsMaterialRule;
|
|
||||||
|
|
||||||
impl BadLandsMaterialRule {
|
|
||||||
pub fn try_apply(context: &mut MaterialRuleContext) -> &'static BlockState {
|
|
||||||
context.terrain_builder.get_terracotta_block(
|
|
||||||
context.block_pos_x,
|
|
||||||
context.block_pos_y,
|
|
||||||
context.block_pos_z,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BlockMaterialRule;
|
|
||||||
|
|
||||||
impl BlockMaterialRule {
|
|
||||||
pub const fn try_apply(state: &'static BlockState) -> &'static BlockState {
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_apply_sequence(
|
|
||||||
rule: &SequenceMaterialRule,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
context: &mut MaterialRuleContext,
|
|
||||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
|
||||||
) -> Option<&'static BlockState> {
|
|
||||||
for seq in rule.sequence {
|
|
||||||
if let Some(state) =
|
|
||||||
try_apply_material_rule(seq, chunk, context, surface_height_estimate_sampler)
|
|
||||||
{
|
|
||||||
return Some(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_apply_condition(
|
|
||||||
rule: &ConditionMaterialRule,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
context: &mut MaterialRuleContext,
|
|
||||||
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
|
|
||||||
) -> Option<&'static BlockState> {
|
|
||||||
if test_condition(
|
|
||||||
&rule.if_true,
|
|
||||||
chunk,
|
|
||||||
context,
|
|
||||||
surface_height_estimate_sampler,
|
|
||||||
) {
|
|
||||||
return try_apply_material_rule(
|
|
||||||
rule.then_run,
|
|
||||||
chunk,
|
|
||||||
context,
|
|
||||||
surface_height_estimate_sampler,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@ -107,10 +107,6 @@ const MAX_PREVIOUS_MESSAGES: u8 = 20; // Vanilla: 20
|
|||||||
|
|
||||||
pub const DATA_VERSION: i32 = 4790; // 26.1.2
|
pub const DATA_VERSION: i32 = 4790; // 26.1.2
|
||||||
|
|
||||||
enum BatchState {
|
|
||||||
Count(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct HeapNode(i32, Vector2<i32>, SyncChunk);
|
struct HeapNode(i32, Vector2<i32>, SyncChunk);
|
||||||
|
|
||||||
impl Eq for HeapNode {}
|
impl Eq for HeapNode {}
|
||||||
@ -141,7 +137,7 @@ pub struct ChunkManager {
|
|||||||
chunk_sent: HashMap<Vector2<i32>, Weak<ChunkData>>,
|
chunk_sent: HashMap<Vector2<i32>, Weak<ChunkData>>,
|
||||||
chunk_queue: BinaryHeap<HeapNode>,
|
chunk_queue: BinaryHeap<HeapNode>,
|
||||||
entity_chunk_queue: VecDeque<(Vector2<i32>, SyncEntityChunk)>,
|
entity_chunk_queue: VecDeque<(Vector2<i32>, SyncEntityChunk)>,
|
||||||
batches_sent_since_ack: BatchState,
|
batches_sent_since_ack: u8,
|
||||||
last_chunk_batch_sent_at: Instant,
|
last_chunk_batch_sent_at: Instant,
|
||||||
/// The current world for chunk loading. Updated on dimension change.
|
/// The current world for chunk loading. Updated on dimension change.
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
@ -165,7 +161,7 @@ impl ChunkManager {
|
|||||||
chunk_sent: HashMap::new(),
|
chunk_sent: HashMap::new(),
|
||||||
chunk_queue: BinaryHeap::new(),
|
chunk_queue: BinaryHeap::new(),
|
||||||
entity_chunk_queue: VecDeque::new(),
|
entity_chunk_queue: VecDeque::new(),
|
||||||
batches_sent_since_ack: BatchState::Count(0),
|
batches_sent_since_ack: 0,
|
||||||
last_chunk_batch_sent_at: Instant::now(),
|
last_chunk_batch_sent_at: Instant::now(),
|
||||||
world,
|
world,
|
||||||
}
|
}
|
||||||
@ -186,9 +182,7 @@ impl ChunkManager {
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
const fn ack_window_open(&self) -> bool {
|
const fn ack_window_open(&self) -> bool {
|
||||||
match self.batches_sent_since_ack {
|
self.batches_sent_since_ack < Self::NOTCHIAN_BATCHES_WITHOUT_ACK_UNTIL_PAUSE
|
||||||
BatchState::Count(count) => count < Self::NOTCHIAN_BATCHES_WITHOUT_ACK_UNTIL_PAUSE,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -198,28 +192,19 @@ impl ChunkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn pull_new_chunks(&mut self) {
|
pub fn pull_new_chunks(&mut self) {
|
||||||
let mut new_nodes = Vec::new();
|
|
||||||
while let Ok((pos, chunk)) = self.chunk_listener.try_recv() {
|
while let Ok((pos, chunk)) = self.chunk_listener.try_recv() {
|
||||||
let dst = (pos.x - self.center.x)
|
let dst = Self::chebyshev(pos, self.center);
|
||||||
.abs()
|
|
||||||
.max((pos.y - self.center.y).abs());
|
|
||||||
if dst > i32::from(self.view_distance) {
|
if dst > i32::from(self.view_distance) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.should_enqueue_chunk(pos, &chunk) {
|
if self.should_enqueue_chunk(pos, &chunk) {
|
||||||
new_nodes.push(HeapNode(dst, pos, chunk));
|
self.chunk_queue.push(HeapNode(dst, pos, chunk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !new_nodes.is_empty() {
|
fn chebyshev(a: Vector2<i32>, b: Vector2<i32>) -> i32 {
|
||||||
if self.chunk_queue.is_empty() {
|
(a.x - b.x).abs().max((a.y - b.y).abs())
|
||||||
self.chunk_queue = BinaryHeap::from(new_nodes);
|
|
||||||
} else {
|
|
||||||
for node in new_nodes {
|
|
||||||
self.chunk_queue.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_center_and_view_distance(
|
pub fn update_center_and_view_distance(
|
||||||
@ -259,14 +244,15 @@ impl ChunkManager {
|
|||||||
&& !unloading_chunks.contains(pos)
|
&& !unloading_chunks.contains(pos)
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut tasks: Vec<_> = self.chunk_queue.drain().collect();
|
let mut tasks: Vec<_> = self
|
||||||
tasks.retain(|node| {
|
.chunk_queue
|
||||||
let dst = (node.1.x - center.x).abs().max((node.1.y - center.y).abs());
|
.drain()
|
||||||
dst <= view_distance_i32 && !unloading_chunks.contains(&node.1)
|
.filter_map(|node| {
|
||||||
});
|
let dst = Self::chebyshev(node.1, center);
|
||||||
for node in &mut tasks {
|
(dst <= view_distance_i32 && !unloading_chunks.contains(&node.1))
|
||||||
node.0 = (node.1.x - center.x).abs().max((node.1.y - center.y).abs());
|
.then(|| HeapNode(dst, node.1, node.2))
|
||||||
}
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
for pos in loading_chunks {
|
for pos in loading_chunks {
|
||||||
if !self.chunk_sent.contains_key(pos)
|
if !self.chunk_sent.contains_key(pos)
|
||||||
@ -297,7 +283,7 @@ impl ChunkManager {
|
|||||||
self.chunk_sent.clear();
|
self.chunk_sent.clear();
|
||||||
self.chunk_queue.clear();
|
self.chunk_queue.clear();
|
||||||
self.entity_chunk_queue.clear();
|
self.entity_chunk_queue.clear();
|
||||||
self.batches_sent_since_ack = BatchState::Count(0);
|
self.batches_sent_since_ack = 0;
|
||||||
self.last_chunk_batch_sent_at = Instant::now();
|
self.last_chunk_batch_sent_at = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,12 +300,12 @@ impl ChunkManager {
|
|||||||
self.chunk_queue.clear();
|
self.chunk_queue.clear();
|
||||||
self.world = new_world;
|
self.world = new_world;
|
||||||
// Reset batch state so chunks can be sent immediately in the new dimension
|
// Reset batch state so chunks can be sent immediately in the new dimension
|
||||||
self.batches_sent_since_ack = BatchState::Count(0);
|
self.batches_sent_since_ack = 0;
|
||||||
self.last_chunk_batch_sent_at = Instant::now();
|
self.last_chunk_batch_sent_at = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn handle_acknowledge(&mut self, chunks_per_tick: f32) {
|
pub const fn handle_acknowledge(&mut self, chunks_per_tick: f32) {
|
||||||
self.batches_sent_since_ack = BatchState::Count(0);
|
self.batches_sent_since_ack = 0;
|
||||||
self.chunks_per_tick = chunks_per_tick.ceil() as usize;
|
self.chunks_per_tick = chunks_per_tick.ceil() as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,17 +330,12 @@ impl ChunkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_chunk(&mut self) -> Box<[SyncChunk]> {
|
pub fn next_chunk(&mut self) -> Box<[SyncChunk]> {
|
||||||
let mut chunk_size = self.chunk_queue.len().min(self.chunks_per_tick.max(1));
|
let take = self.chunk_queue.len().min(self.chunks_per_tick.max(1));
|
||||||
let mut chunks = Vec::<Arc<ChunkData>>::with_capacity(chunk_size);
|
let mut chunks = Vec::with_capacity(take);
|
||||||
while chunk_size > 0 {
|
for _ in 0..take {
|
||||||
chunks.push(self.chunk_queue.pop().unwrap().2);
|
chunks.push(self.chunk_queue.pop().unwrap().2);
|
||||||
chunk_size -= 1;
|
|
||||||
}
|
|
||||||
match &mut self.batches_sent_since_ack {
|
|
||||||
BatchState::Count(count) => {
|
|
||||||
*count = count.saturating_add(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.batches_sent_since_ack = self.batches_sent_since_ack.saturating_add(1);
|
||||||
self.last_chunk_batch_sent_at = Instant::now();
|
self.last_chunk_batch_sent_at = Instant::now();
|
||||||
|
|
||||||
chunks.into_boxed_slice()
|
chunks.into_boxed_slice()
|
||||||
@ -372,11 +353,7 @@ impl ChunkManager {
|
|||||||
.map(|(_, chunk)| chunk)
|
.map(|(_, chunk)| chunk)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match &mut self.batches_sent_since_ack {
|
self.batches_sent_since_ack = self.batches_sent_since_ack.saturating_add(1);
|
||||||
BatchState::Count(count) => {
|
|
||||||
*count = count.saturating_add(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.last_chunk_batch_sent_at = Instant::now();
|
self.last_chunk_batch_sent_at = Instant::now();
|
||||||
|
|
||||||
chunks
|
chunks
|
||||||
|
|||||||
@ -386,13 +386,6 @@ impl World {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_difficulty(&self, difficulty: Difficulty) {
|
|
||||||
let current_info = self.level_info.load();
|
|
||||||
let mut new_info = (**current_info).clone();
|
|
||||||
new_info.difficulty = difficulty;
|
|
||||||
self.level_info.store(Arc::new(new_info));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_difficulty(&self, difficulty: Difficulty) {
|
pub fn set_difficulty(&self, difficulty: Difficulty) {
|
||||||
let current_info = self.level_info.load();
|
let current_info = self.level_info.load();
|
||||||
let mut new_info = (**current_info).clone();
|
let mut new_info = (**current_info).clone();
|
||||||
@ -821,7 +814,11 @@ impl World {
|
|||||||
Vector3<i32>,
|
Vector3<i32>,
|
||||||
Vec<(BlockPos, BlockStateId)>,
|
Vec<(BlockPos, BlockStateId)>,
|
||||||
> = HashMap::new();
|
> = HashMap::new();
|
||||||
for (position, block_state_id) in self.unsent_block_changes.lock().await.drain() {
|
let changes = {
|
||||||
|
let mut guard = self.unsent_block_changes.lock().await;
|
||||||
|
std::mem::take(&mut *guard)
|
||||||
|
};
|
||||||
|
for (position, block_state_id) in changes {
|
||||||
let chunk_section = chunk_section_from_pos(&position);
|
let chunk_section = chunk_section_from_pos(&position);
|
||||||
block_state_updates_by_chunk_section
|
block_state_updates_by_chunk_section
|
||||||
.entry(chunk_section)
|
.entry(chunk_section)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user