mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
* 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
731 lines
25 KiB
Rust
731 lines
25 KiB
Rust
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 (0–1) 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,
|
||
}
|
||
}
|
||
}
|
||
)
|
||
}
|