Files
Pumpkin/pumpkin-codegen/src/structures.rs
Jamie Little 75994f3302 feat: /place command (#2473)
* Add /place template command with BlockPlacer trait and tab-completion support

- Add BlockPlacer trait to abstract block placement over ProtoChunk (worldgen)
  and WorldBlockPlacer (live command), used by place_template()
- Implement BlockPlacer for ProtoChunk (pumpkin-world) and WorldBlockPlacer
  (pumpkin crate)
- Add World::queue_block_updates() to insert into unsent_block_changes
  without triggering full set_block_state callbacks
- Generate _generated_all_template_names() at build time from structure
  assets for use in tab-completion suggestions
- Add TemplateNameArgumentType for the new command system with
  list_suggestions() using all_template_names()
- Implement /place template <template> [pos] in the new command system
  (CommandDispatcher, CommandExecutor, ArgumentBuilder)
- Fix borrow-across-await: clone template name to owned String early

* feat: add /place structure and /place jigsaw commands

Adds /place structure <id> [pos] for all structure types with two placement paths:
- Jigsaw structures (ancient_city, bastion_remnant, etc.): fast path using
  JigsawPlacement::add_pieces directly at the target position
- Non-jigsaw structures (desert_pyramid, end_city, etc.): generate pieces
  via dispatch, place into synthetic ProtoChunks with pre-seeded heightmaps
  and a stone floor so pieces can detect terrain, then delta-apply only
  changed blocks to the world via WorldBlockPlacer

Also adds /place jigsaw <pool> <target> <depth> [pos] for manual jigsaw
template placement.

Architecture:
- generate_structure_position() extracts the shared generator dispatch
  from try_generate_structure / lazily_generate_structure (both now
  delegate to it, eliminating ~150 lines of duplicated match arms)
- place_pool_element_templates() extracted from PoolElementStructurePiece
  for reuse by the command with WorldBlockPlacer
- StructureKeys gains from_name()/to_name()/all_names() via codegen
- StructureNameArgumentType and PoolNameArgumentType for tab-completion
- flat_ocean_floor_height_map made pub in ProtoChunk for heightmap seeding

* feat: add /place feature command

Adds /place feature <feature> [pos] for placing configured features
at a specific position. Resolves the PlacedFeature name via from_name(),
resolves the inner ConfiguredFeature from PLACED_FEATURES /
CONFIGURED_FEATURES, then calls ConfiguredFeature::generate() directly
at the target position -- skipping placement modifiers so the feature
appears exactly where specified.

Also adds /place structure improvements:
- Synthetic chunk terrain fill: stone below surface + grass on top, so
  pieces that carve through solid terrain (stronghold corridors, etc.)
  find material to work with. Snapshot/delta ensures only structure
  blocks reach the world.
- Shared snapshot_blocks() and apply_delta() helpers eliminate ~30 lines
  of duplicated diff logic between structure and feature paths.
- ground_y() and chunk_population_seed() helpers replace magic numbers.
- Structure success message now reports the structure name instead of
  the piece count.

Infrastructure:
- PlacedFeature::all_names() generated via pumpkin-codegen for
  tab-completion suggestions in PlacedFeatureNameArgumentType.
- GenerationCache trait implemented for ProtoChunk (single-chunk
  delegation) so ConfiguredFeature::generate() works without the full
  chunk-generation cache system.
- flat_ocean_floor_height_map made pub in ProtoChunk for heightmap
  seeding.
- configured_features and feature modules made pub for access from
  the command crate.

* fix(command): use world settings for place

* docs(place): clarify synthetic terrain delta behavior
2026-07-31 11:45:23 +02:00

731 lines
25 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote};
use serde::Deserialize;
use std::{collections::BTreeMap, fs};
/// Deserialized structure set containing placement rules and weighted structure entries.
#[derive(Deserialize)]
pub struct StructureSetStruct {
/// Placement algorithm and parameters for distributing this structure set.
pub placement: StructurePlacementStruct,
/// Weighted list of structures that belong to this set.
pub structures: Vec<WeightedEntryStruct>,
}
/// Deserialized weighted structure entry within a structure set.
#[derive(Deserialize, Clone)]
pub struct WeightedEntryStruct {
/// Registry key of the structure (e.g., `"minecraft:village_plains"`).
pub structure: String,
/// Relative weight controlling how often this structure is chosen.
pub weight: u32,
}
/// Deserialized placement configuration for a structure set.
#[derive(Deserialize)]
pub struct StructurePlacementStruct {
/// Optional frequency-reduction method name applied before placement.
pub frequency_reduction_method: Option<String>,
/// Optional probability (01) that a candidate chunk actually spawns the structure.
pub frequency: Option<f32>,
/// Per-structure salt mixed into the placement RNG seed.
pub salt: u32,
/// Optional exclusion zone to prevent this structure from generating near others.
pub exclusion_zone: Option<ExclusionZoneStruct>,
/// The specific placement algorithm and its parameters.
#[serde(flatten)]
pub r#type: StructurePlacementTypeStruct,
}
/// Deserialized exclusion zone configuration.
#[derive(Deserialize)]
pub struct ExclusionZoneStruct {
pub other_set: String,
pub chunk_count: i32,
}
/// Deserialized placement algorithm for a structure set.
#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum StructurePlacementTypeStruct {
/// Places structures at pseudo-random positions within regularly spaced grid regions.
#[serde(rename = "minecraft:random_spread")]
RandomSpread {
/// Region size (spacing between potential structure positions).
spacing: i32,
/// Minimum separation between two structures in the same region.
separation: i32,
/// Optional distribution type (`"linear"` or `"triangular"`).
spread_type: Option<String>,
},
/// Places structures in concentric rings around the world origin.
#[serde(rename = "minecraft:concentric_rings")]
ConcentricRings {
/// Angular spread between ring placements.
spread: i32,
/// Distance increment between successive rings.
distance: i32,
/// Total number of structures placed across all rings.
count: i32,
/// Biome tag that ring placements must be located within.
preferred_biomes: String,
},
}
/// Deserialized structure entry specifying its valid biomes and generation step.
#[derive(Deserialize, Clone)]
pub struct StructureStruct {
/// Biome tag that this structure can generate in.
pub biomes: String,
/// Generation step during which this structure is placed.
pub step: String,
/// Optional jigsaw start pool.
pub start_pool: Option<String>,
/// Optional named jigsaw in the start pool that must be placed at the start position.
pub start_jigsaw_name: Option<String>,
/// Optional jigsaw size (depth).
pub size: Option<i32>,
/// Optional terrain adaptation (bearding).
pub terrain_adaptation: Option<String>,
/// Optional jigsaw start height.
pub start_height: Option<serde_json::Value>,
/// Optional heightmap to project the start to.
pub project_start_to_heightmap: Option<String>,
/// Optional max distance from center.
pub max_distance_from_center: Option<i32>,
/// Optional liquid settings.
pub liquid_settings: Option<String>,
/// Optional dimension padding.
pub dimension_padding: Option<i32>,
/// Critical for villages to appropriately truncate long village streets.
pub use_expansion_hack: Option<bool>,
/// Defines the generation behavior (e.g. "minecraft:jigsaw").
#[serde(rename = "type")]
pub structure_type: String,
}
impl ToTokens for StructureSetStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
let placement = &self.placement;
let structures = &self.structures;
tokens.extend(quote!(
StructureSet {
placement: #placement,
structures: &[#(#structures),*],
}
));
}
}
impl ToTokens for WeightedEntryStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
let structure = structure_key_to_token(&self.structure);
let weight = self.weight;
tokens.extend(quote!(
WeightedEntry {
structure: #structure,
weight: #weight,
}
));
}
}
impl ToTokens for StructurePlacementStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
let frequency_reduction = if let Some(method) = &self.frequency_reduction_method {
let method_token = frequency_reduction_to_token(method);
quote!(Some(#method_token))
} else {
quote!(None)
};
let frequency = if let Some(f) = self.frequency {
quote!(Some(#f))
} else {
quote!(None)
};
let exclusion_zone = if let Some(ez) = &self.exclusion_zone {
let other_set = &ez.other_set;
let chunk_count = ez.chunk_count;
quote!(Some(ExclusionZone {
other_set: #other_set,
chunk_count: #chunk_count,
}))
} else {
quote!(None)
};
let salt = self.salt;
let placement_type = &self.r#type;
tokens.extend(quote!(
StructurePlacement {
frequency_reduction_method: #frequency_reduction,
frequency: #frequency,
salt: #salt,
exclusion_zone: #exclusion_zone,
placement_type: #placement_type,
}
));
}
}
impl ToTokens for StructurePlacementTypeStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::RandomSpread {
spacing,
separation,
spread_type,
} => {
let spread_type_token = if let Some(st) = spread_type {
let st_token = spread_type_to_token(st);
quote!(Some(#st_token))
} else {
quote!(None)
};
tokens.extend(quote!(
StructurePlacementType::RandomSpread(RandomSpreadStructurePlacement {
spacing: #spacing,
separation: #separation,
spread_type: #spread_type_token,
})
));
}
Self::ConcentricRings {
spread,
distance,
count,
preferred_biomes,
} => {
tokens.extend(quote!(
StructurePlacementType::ConcentricRings(ConcentricRingsStructurePlacement {
spread: #spread,
distance: #distance,
count: #count,
preferred_biomes: #preferred_biomes,
})
));
}
}
}
}
impl ToTokens for StructureStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
let biomes = &self.biomes;
let step = generation_step_to_token(&self.step);
let start_pool = if let Some(pool) = &self.start_pool {
quote!(Some(#pool))
} else {
quote!(None)
};
let start_jigsaw_name = if let Some(name) = &self.start_jigsaw_name {
quote!(Some(#name))
} else {
quote!(None)
};
let size = if let Some(s) = self.size {
quote!(Some(#s))
} else {
quote!(None)
};
let terrain_adaptation = if let Some(ta) = &self.terrain_adaptation {
let ta_token = terrain_adaptation_to_token(ta);
quote!(#ta_token)
} else {
quote!(TerrainAdaptation::None)
};
let project_start_to_heightmap = if let Some(ps) = &self.project_start_to_heightmap {
quote!(Some(#ps))
} else {
quote!(None)
};
let max_distance_from_center = if let Some(m) = self.max_distance_from_center {
quote!(Some(#m))
} else {
quote!(None)
};
let liquid_settings = if let Some(ls) = &self.liquid_settings {
quote!(Some(#ls))
} else {
quote!(None)
};
let dimension_padding = if let Some(dp) = self.dimension_padding {
quote!(Some(#dp))
} else {
quote!(None)
};
let use_expansion_hack = if let Some(hack) = self.use_expansion_hack {
quote!(Some(#hack))
} else {
quote!(None)
};
let structure_type = structure_type_to_token(&self.structure_type);
let start_height = if let Some(sh) = &self.start_height {
if let Some(abs) = sh.get("absolute") {
let abs_val = abs.as_i64().unwrap() as i16;
quote!(Some(#abs_val))
} else if let Some(min_inc) = sh.get("min_inclusive") {
if let Some(abs) = min_inc.get("absolute") {
let abs_val = abs.as_i64().unwrap() as i16;
quote!(Some(#abs_val))
} else {
quote!(None)
}
} else {
quote!(None)
}
} else {
quote!(None)
};
tokens.extend(quote!(
Structure {
biomes: #biomes,
step: #step,
start_pool: #start_pool,
start_jigsaw_name: #start_jigsaw_name,
size: #size,
terrain_adaptation: #terrain_adaptation,
start_height: #start_height,
project_start_to_heightmap: #project_start_to_heightmap,
max_distance_from_center: #max_distance_from_center,
liquid_settings: #liquid_settings,
dimension_padding: #dimension_padding,
use_expansion_hack: #use_expansion_hack,
structure_type: #structure_type,
}
));
}
}
// Helper functions
fn structure_type_to_token(st: &str) -> TokenStream {
match st {
"minecraft:jigsaw" => quote!(StructureType::Jigsaw),
"minecraft:buried_treasure" => quote!(StructureType::BuriedTreasure),
"minecraft:desert_pyramid" => quote!(StructureType::DesertPyramid),
"minecraft:end_city" => quote!(StructureType::EndCity),
"minecraft:fortress" => quote!(StructureType::Fortress),
"minecraft:igloo" => quote!(StructureType::Igloo),
"minecraft:jungle_temple" => quote!(StructureType::JungleTemple),
"minecraft:woodland_mansion" => quote!(StructureType::WoodlandMansion),
"minecraft:mineshaft" => quote!(StructureType::Mineshaft),
"minecraft:ocean_monument" => quote!(StructureType::OceanMonument),
"minecraft:nether_fossil" => quote!(StructureType::NetherFossil),
"minecraft:ocean_ruin" => quote!(StructureType::OceanRuin),
"minecraft:ruined_portal" => quote!(StructureType::RuinedPortal),
"minecraft:shipwreck" => quote!(StructureType::Shipwreck),
"minecraft:stronghold" => quote!(StructureType::Stronghold),
"minecraft:swamp_hut" => quote!(StructureType::SwampHut),
_ => quote!(StructureType::Unknown),
}
}
fn terrain_adaptation_to_token(ta: &str) -> TokenStream {
match ta {
"beard_thin" => quote!(TerrainAdaptation::BeardThin),
"beard_box" => quote!(TerrainAdaptation::BeardBox),
"bury" => quote!(TerrainAdaptation::Bury),
"encapsulate" => quote!(TerrainAdaptation::Encapsulate),
_ => quote!(TerrainAdaptation::None),
}
}
fn structure_key_to_token(key: &str) -> TokenStream {
let stripped = key.strip_prefix("minecraft:").unwrap_or(key);
match stripped {
"pillager_outpost" => quote!(StructureKeys::PillagerOutpost),
"mineshaft" => quote!(StructureKeys::Mineshaft),
"mineshaft_mesa" => quote!(StructureKeys::MineshaftMesa),
"mansion" => quote!(StructureKeys::Mansion),
"jungle_pyramid" => quote!(StructureKeys::JunglePyramid),
"desert_pyramid" => quote!(StructureKeys::DesertPyramid),
"igloo" => quote!(StructureKeys::Igloo),
"shipwreck" => quote!(StructureKeys::Shipwreck),
"shipwreck_beached" => quote!(StructureKeys::ShipwreckBeached),
"swamp_hut" => quote!(StructureKeys::SwampHut),
"stronghold" => quote!(StructureKeys::Stronghold),
"monument" => quote!(StructureKeys::Monument),
"ocean_ruin_cold" => quote!(StructureKeys::OceanRuinCold),
"ocean_ruin_warm" => quote!(StructureKeys::OceanRuinWarm),
"fortress" => quote!(StructureKeys::Fortress),
"nether_fossil" => quote!(StructureKeys::NetherFossil),
"end_city" => quote!(StructureKeys::EndCity),
"buried_treasure" => quote!(StructureKeys::BuriedTreasure),
"bastion_remnant" => quote!(StructureKeys::BastionRemnant),
"village_plains" => quote!(StructureKeys::VillagePlains),
"village_desert" => quote!(StructureKeys::VillageDesert),
"village_savanna" => quote!(StructureKeys::VillageSavanna),
"village_snowy" => quote!(StructureKeys::VillageSnowy),
"village_taiga" => quote!(StructureKeys::VillageTaiga),
"ruined_portal" => quote!(StructureKeys::RuinedPortal),
"ruined_portal_desert" => quote!(StructureKeys::RuinedPortalDesert),
"ruined_portal_jungle" => quote!(StructureKeys::RuinedPortalJungle),
"ruined_portal_swamp" => quote!(StructureKeys::RuinedPortalSwamp),
"ruined_portal_mountain" => quote!(StructureKeys::RuinedPortalMountain),
"ruined_portal_ocean" => quote!(StructureKeys::RuinedPortalOcean),
"ruined_portal_nether" => quote!(StructureKeys::RuinedPortalNether),
"ancient_city" => quote!(StructureKeys::AncientCity),
"trail_ruins" => quote!(StructureKeys::TrailRuins),
"trial_chambers" => quote!(StructureKeys::TrialChambers),
_ => panic!("Unknown structure key: {stripped}"),
}
}
fn frequency_reduction_to_token(method: &str) -> TokenStream {
match method {
"default" => quote!(FrequencyReductionMethod::Default),
"legacy_type_1" => quote!(FrequencyReductionMethod::LegacyType1),
"legacy_type_2" => quote!(FrequencyReductionMethod::LegacyType2),
"legacy_type_3" => quote!(FrequencyReductionMethod::LegacyType3),
_ => quote!(FrequencyReductionMethod::Default),
}
}
fn spread_type_to_token(spread: &str) -> TokenStream {
match spread {
"linear" => quote!(SpreadType::Linear),
"triangular" => quote!(SpreadType::Triangular),
_ => quote!(SpreadType::Linear),
}
}
fn generation_step_to_token(step: &str) -> TokenStream {
match step {
"raw_generation" => quote!(GenerationStep::RawGeneration),
"lakes" => quote!(GenerationStep::Lakes),
"local_modifications" => quote!(GenerationStep::LocalModifications),
"underground_structures" => quote!(GenerationStep::UndergroundStructures),
"surface_structures" => quote!(GenerationStep::SurfaceStructures),
"strongholds" => quote!(GenerationStep::Strongholds),
"underground_ores" => quote!(GenerationStep::UndergroundOres),
"underground_decoration" => quote!(GenerationStep::UndergroundDecoration),
"fluid_springs" => quote!(GenerationStep::FluidSprings),
"vegetal_decoration" => quote!(GenerationStep::VegetalDecoration),
"top_layer_modification" => quote!(GenerationStep::TopLayerModification),
_ => panic!("Unknown generation step: {step}"),
}
}
/// Reads `structures.json` and `structure_set.json` and emits the complete structures `TokenStream`.
pub fn build() -> TokenStream {
let structures_json: BTreeMap<String, StructureStruct> =
serde_json::from_str(&fs::read_to_string("../assets/structures.json").unwrap())
.expect("Failed to parse structures.json");
let structure_sets_json: BTreeMap<String, StructureSetStruct> =
serde_json::from_str(&fs::read_to_string("../assets/structure_set.json").unwrap())
.expect("Failed to parse structure_set.json");
let mut structure_const_defs = TokenStream::new();
let mut structure_lookup_arms = TokenStream::new();
let mut structure_from_name_arms = Vec::new();
let mut structure_to_name_arms = Vec::new();
let mut structure_all_names: Vec<String> = Vec::new();
for (name, structure) in &structures_json {
let stripped_name = name.strip_prefix("minecraft:").unwrap_or(name);
let upper_name = stripped_name.to_uppercase();
let const_name = format_ident!("{}", upper_name);
let variant_ident = format_ident!("{}", stripped_name.to_pascal_case());
let key_variant = structure_key_to_token(name);
structure_const_defs.extend(quote!(
pub const #const_name: Self = #structure;
));
structure_lookup_arms.extend(quote!(
#key_variant => &Self::#const_name,
));
structure_from_name_arms.push(quote! {
#stripped_name => Some(Self::#variant_ident),
});
structure_to_name_arms.push(quote! {
Self::#variant_ident => #name,
});
structure_all_names.push(stripped_name.to_string());
}
let mut structure_set_const_defs = TokenStream::new();
let mut structure_set_lookup_arms = TokenStream::new();
let mut all_structure_set_idents = Vec::new();
for (name, structure_set) in &structure_sets_json {
let stripped_name = name.strip_prefix("minecraft:").unwrap_or(name);
let upper_name = stripped_name.to_uppercase().replace('/', "_");
let const_name = format_ident!("{}", upper_name);
structure_set_const_defs.extend(quote!(
pub const #const_name: Self = #structure_set;
));
structure_set_lookup_arms.extend(quote!(
#stripped_name => Some(&Self::#const_name),
));
all_structure_set_idents.push(const_name);
}
let structure_all_names_tokens: Vec<TokenStream> = structure_all_names
.iter()
.map(|name| {
let full = format!("minecraft:{name}");
quote! { #full }
})
.collect();
quote!(
use pumpkin_util::math::floor_div;
use pumpkin_util::random::{
RandomGenerator, RandomImpl, get_carver_seed, get_region_seed,
legacy_rand::LegacyRand, xoroshiro128::Xoroshiro,
};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum StructureKeys {
PillagerOutpost,
Mineshaft,
MineshaftMesa,
Mansion,
JunglePyramid,
DesertPyramid,
Igloo,
Shipwreck,
ShipwreckBeached,
SwampHut,
Stronghold,
Monument,
OceanRuinCold,
OceanRuinWarm,
Fortress,
NetherFossil,
EndCity,
BuriedTreasure,
BastionRemnant,
VillagePlains,
VillageDesert,
VillageSavanna,
VillageSnowy,
VillageTaiga,
RuinedPortal,
RuinedPortalDesert,
RuinedPortalJungle,
RuinedPortalSwamp,
RuinedPortalMountain,
RuinedPortalOcean,
RuinedPortalNether,
AncientCity,
TrailRuins,
TrialChambers,
}
impl StructureKeys {
pub fn from_name(name: &str) -> Option<Self> {
let name = name.strip_prefix("minecraft:").unwrap_or(name);
match name {
#(#structure_from_name_arms)*
_ => None,
}
}
#[must_use]
pub const fn to_name(&self) -> &'static str {
match self {
#(#structure_to_name_arms)*
}
}
#[must_use]
pub const fn all_names() -> &'static [&'static str] {
&[#(#structure_all_names_tokens),*]
}
}
pub struct StructureSet {
pub placement: StructurePlacement,
pub structures: &'static [WeightedEntry],
}
#[derive(Clone)]
pub struct WeightedEntry {
pub structure: StructureKeys,
pub weight: u32,
}
pub struct ExclusionZone {
pub other_set: &'static str,
pub chunk_count: i32,
}
pub struct StructurePlacement {
pub frequency_reduction_method: Option<FrequencyReductionMethod>,
pub frequency: Option<f32>,
pub salt: u32,
pub exclusion_zone: Option<ExclusionZone>,
pub placement_type: StructurePlacementType,
}
#[derive(Clone, Copy)]
pub enum FrequencyReductionMethod {
Default,
LegacyType1,
LegacyType2,
LegacyType3,
}
pub enum StructurePlacementType {
RandomSpread(RandomSpreadStructurePlacement),
ConcentricRings(ConcentricRingsStructurePlacement),
}
pub struct RandomSpreadStructurePlacement {
pub spacing: i32,
pub separation: i32,
pub spread_type: Option<SpreadType>,
}
pub struct ConcentricRingsStructurePlacement {
pub spread: i32,
pub distance: i32,
pub count: i32,
pub preferred_biomes: &'static str,
}
#[derive(Clone, Copy)]
pub enum SpreadType {
Linear,
Triangular,
}
impl SpreadType {
pub fn get(&self, random: &mut RandomGenerator, bound: i32) -> i32 {
match self {
Self::Linear => random.next_bounded_i32(bound),
Self::Triangular => i32::midpoint(
random.next_bounded_i32(bound),
random.next_bounded_i32(bound),
),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TerrainAdaptation {
None,
BeardThin,
BeardBox,
Bury,
Encapsulate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StructureType {
Jigsaw,
BuriedTreasure,
DesertPyramid,
EndCity,
Fortress,
Igloo,
JungleTemple,
WoodlandMansion,
Mineshaft,
OceanMonument,
NetherFossil,
OceanRuin,
RuinedPortal,
Shipwreck,
Stronghold,
SwampHut,
Unknown,
}
pub struct Structure {
pub biomes: &'static str,
pub step: GenerationStep,
pub start_pool: Option<&'static str>,
pub start_jigsaw_name: Option<&'static str>,
pub size: Option<i32>,
pub terrain_adaptation: TerrainAdaptation,
pub start_height: Option<i16>,
pub project_start_to_heightmap: Option<&'static str>,
pub max_distance_from_center: Option<i32>,
pub liquid_settings: Option<&'static str>,
pub dimension_padding: Option<i32>,
pub use_expansion_hack: Option<bool>,
pub structure_type: StructureType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GenerationStep {
RawGeneration,
Lakes,
LocalModifications,
UndergroundStructures,
SurfaceStructures,
Strongholds,
UndergroundOres,
UndergroundDecoration,
FluidSprings,
VegetalDecoration,
TopLayerModification,
}
impl GenerationStep {
#[must_use]
pub const fn ordinal(&self) -> usize {
*self as usize
}
}
pub struct StructurePlacementCalculator {
pub seed: i64,
}
impl StructurePlacementCalculator {
#[must_use]
pub const fn new(seed: i64) -> Self {
Self { seed }
}
}
impl Structure {
#structure_const_defs
#[must_use]
pub const fn get(key: &StructureKeys) -> &'static Self {
match *key {
#structure_lookup_arms
}
}
}
impl StructureSet {
#structure_set_const_defs
pub const ALL: &'static [StructureSet] = &[
#(Self::#all_structure_set_idents),*
];
#[must_use]
pub fn get(name: &str) -> Option<&'static Self> {
match name {
#structure_set_lookup_arms
_ => None,
}
}
}
)
}