1use steel_registry::vanilla_block_tags::BlockTag;
2
3use super::super::prelude::*;
4use super::super::runner::FeatureDecorationRunner;
5use crate::java_block_pos_set::JavaBlockPosSet;
6
7use rustc_hash::FxHashMap;
8use std::cell::{Cell, RefCell};
9
10use crate::block_entity::SharedBlockEntity;
11use crate::world::ScheduledTickAccess;
12
13mod decorators;
14mod fallen;
15mod foliage;
16mod leaves;
17mod root_system;
18mod roots;
19mod trunk;
20
21pub(crate) type NestedFeaturePlacer<L> =
23 fn(&mut L, &Registry, &mut WorldgenRandom, &ConfiguredFeatureKind, BlockPos, i64) -> bool;
24
25pub(crate) const fn no_nested_features<L>(
26 _level: &mut L,
27 _registry: &Registry,
28 _random: &mut WorldgenRandom,
29 _kind: &ConfiguredFeatureKind,
30 _origin: BlockPos,
31 _biome_zoom_seed: i64,
32) -> bool {
33 false
34}
35
36impl FeatureDecorationRunner {
37 pub(crate) fn place_tree_feature<L: LevelAccessor>(
38 region: &mut L,
39 registry: &Registry,
40 random: &mut WorldgenRandom,
41 config: &TreeConfiguration,
42 origin: BlockPos,
43 biome_zoom_seed: i64,
44 place_nested: NestedFeaturePlacer<L>,
45 ) -> bool {
46 if region.requires_live_write_preflight() {
47 let mut preflight_random = random.clone();
48 let mut preflight_region = TreeWritePreflight::new(region);
49 let mut preflight_placement = TreePlacement::default();
50 let preflight_placed = Self::do_place_tree(
51 &mut preflight_region,
52 registry,
53 &mut preflight_random,
54 config,
55 origin,
56 &mut preflight_placement,
57 );
58 if !preflight_placed || preflight_region.failed() {
59 return false;
60 }
61 }
62
63 let mut placement = TreePlacement::default();
64 let placed = Self::do_place_tree(region, registry, random, config, origin, &mut placement);
65 if !placed || (placement.trunks.is_empty() && placement.foliage.is_empty()) {
66 return false;
67 }
68
69 if !config.decorators.is_empty() {
70 Self::place_tree_decorators(
71 region,
72 registry,
73 random,
74 &config.decorators,
75 &mut placement,
76 biome_zoom_seed,
77 place_nested,
78 );
79 }
80
81 let Some(bounds) = TreeBounds::from_placement(&placement) else {
82 return false;
83 };
84 Self::update_tree_leaves(region, bounds, &placement);
85 true
86 }
87
88 fn do_place_tree(
89 region: &mut impl LevelAccessor,
90 registry: &Registry,
91 random: &mut WorldgenRandom,
92 config: &TreeConfiguration,
93 origin: BlockPos,
94 placement: &mut TreePlacement,
95 ) -> bool {
96 let tree_height = Self::tree_height(random, &config.trunk_placer);
97 let foliage_height = Self::tree_foliage_height(random, tree_height, config);
98 let trunk_height = tree_height - foliage_height;
99 let leaf_radius = Self::tree_foliage_radius(random, &config.foliage_placer, trunk_height);
100 let trunk_origin = Self::tree_root_origin(random, origin, config.root_placer.as_ref());
101 let min_y = origin.y().min(trunk_origin.y());
102 let max_y = origin.y().max(trunk_origin.y()) + tree_height + 1;
103
104 if min_y < region.min_y() + 1 || max_y > region.max_y_exclusive() {
105 return false;
106 }
107
108 let clipped_tree_height =
109 Self::max_free_tree_height(region, tree_height, trunk_origin, config);
110 let min_clipped_height = Self::tree_min_clipped_height(&config.minimum_size);
111 if clipped_tree_height < tree_height
112 && min_clipped_height.is_none_or(|height| clipped_tree_height < height)
113 {
114 return false;
115 }
116
117 if config.root_placer.is_some()
118 && !Self::place_tree_roots(
119 region,
120 registry,
121 random,
122 origin,
123 trunk_origin,
124 config,
125 placement,
126 )
127 {
128 return false;
129 }
130
131 let foliage_attachments = Self::place_tree_trunk(
132 region,
133 registry,
134 random,
135 clipped_tree_height,
136 trunk_origin,
137 config,
138 placement,
139 );
140 for foliage_attachment in foliage_attachments {
141 Self::create_tree_foliage(
142 region,
143 registry,
144 random,
145 config,
146 clipped_tree_height,
147 foliage_attachment,
148 foliage_height,
149 leaf_radius,
150 placement,
151 );
152 }
153
154 true
155 }
156
157 const fn tree_min_clipped_height(feature_size: &FeatureSize) -> Option<i32> {
158 match feature_size {
159 FeatureSize::TwoLayers(size) => size.min_clipped_height,
160 FeatureSize::ThreeLayers(size) => size.min_clipped_height,
161 }
162 }
163
164 const fn tree_size_at_height(feature_size: &FeatureSize, tree_height: i32, y: i32) -> i32 {
165 match feature_size {
166 FeatureSize::TwoLayers(size) => {
167 if y < size.limit {
168 size.lower_size
169 } else {
170 size.upper_size
171 }
172 }
173 FeatureSize::ThreeLayers(size) => {
174 if y < size.limit {
175 size.lower_size
176 } else if y >= tree_height - size.upper_limit {
177 size.upper_size
178 } else {
179 size.middle_size
180 }
181 }
182 }
183 }
184
185 fn max_free_tree_height(
186 region: &impl LevelReader,
187 max_tree_height: i32,
188 tree_pos: BlockPos,
189 config: &TreeConfiguration,
190 ) -> i32 {
191 for y in 0..=max_tree_height + 1 {
192 let radius = Self::tree_size_at_height(&config.minimum_size, max_tree_height, y);
193 for x in -radius..=radius {
194 for z in -radius..=radius {
195 let pos = tree_pos.offset(x, y, z);
196 if !Self::tree_trunk_placer_is_free(region, pos, &config.trunk_placer)
197 || (!config.ignore_vines && Self::tree_is_vine(region, pos))
198 {
199 return y - 2;
200 }
201 }
202 }
203 }
204
205 max_tree_height
206 }
207
208 fn tree_valid_pos(region: &impl LevelReader, pos: BlockPos) -> bool {
209 let state = region.get_block_state(pos);
210 state.is_air() || state.get_block().has_tag(&BlockTag::REPLACEABLE_BY_TREES)
211 }
212
213 fn tree_trunk_placer_is_free(
214 region: &impl LevelReader,
215 pos: BlockPos,
216 trunk_placer: &TrunkPlacer,
217 ) -> bool {
218 let state = region.get_block_state(pos);
219 Self::tree_valid_pos_for_trunk_placer(region, pos, trunk_placer)
220 || state.get_block().has_tag(&BlockTag::LOGS)
221 }
222
223 fn tree_valid_pos_for_trunk_placer(
224 region: &impl LevelReader,
225 pos: BlockPos,
226 trunk_placer: &TrunkPlacer,
227 ) -> bool {
228 match trunk_placer {
229 TrunkPlacer::UpwardsBranching(placer) => {
230 Self::tree_valid_pos_or_tag(region, pos, &placer.can_grow_through)
231 }
232 TrunkPlacer::Straight(_)
233 | TrunkPlacer::Forking(_)
234 | TrunkPlacer::Giant(_)
235 | TrunkPlacer::Fancy(_)
236 | TrunkPlacer::DarkOak(_)
237 | TrunkPlacer::MegaJungle(_)
238 | TrunkPlacer::Bending(_)
239 | TrunkPlacer::Cherry(_)
240 | TrunkPlacer::Poplar(_) => Self::tree_valid_pos(region, pos),
241 }
242 }
243
244 fn tree_valid_pos_or_tag(region: &impl LevelReader, pos: BlockPos, tag: &Identifier) -> bool {
245 let state = region.get_block_state(pos);
246 let block = state.get_block();
247 state.is_air() || block.has_tag(&BlockTag::REPLACEABLE_BY_TREES) || block.has_tag(tag)
248 }
249
250 fn tree_is_air_or_leaves(region: &impl LevelReader, pos: BlockPos) -> bool {
251 let state = region.get_block_state(pos);
252 state.is_air() || state.get_block().has_tag(&BlockTag::LEAVES)
253 }
254
255 fn tree_is_vine(region: &impl LevelReader, pos: BlockPos) -> bool {
256 region.get_block_state(pos).get_block() == &vanilla_blocks::VINE
257 }
258
259 fn set_tree_block(region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
260 let flags = UpdateFlags::UPDATE_NEIGHBORS
261 | UpdateFlags::UPDATE_CLIENTS
262 | UpdateFlags::UPDATE_KNOWN_SHAPE;
263 let _ = region.set_block_state(pos, state, flags);
264 }
265}
266
267struct TreeWritePreflight<'a, L: LevelAccessor + ?Sized> {
268 level: &'a mut L,
269 writes: RefCell<FxHashMap<BlockPos, BlockStateId>>,
270 failed: Cell<bool>,
271}
272
273impl<'a, L: LevelAccessor + ?Sized> TreeWritePreflight<'a, L> {
274 fn new(level: &'a mut L) -> Self {
275 Self {
276 level,
277 writes: RefCell::new(FxHashMap::default()),
278 failed: Cell::new(false),
279 }
280 }
281
282 const fn failed(&self) -> bool {
283 self.failed.get()
284 }
285}
286
287impl<L: LevelAccessor + ?Sized> LevelReader for TreeWritePreflight<'_, L> {
288 fn height_at(&self, heightmap_type: HeightmapType, x: i32, z: i32) -> i32 {
289 self.level.height_at(heightmap_type, x, z)
290 }
291
292 fn get_block_state(&self, pos: BlockPos) -> BlockStateId {
293 self.writes
294 .borrow()
295 .get(&pos)
296 .copied()
297 .unwrap_or_else(|| self.level.get_block_state(pos))
298 }
299
300 fn get_block_entity(&self, pos: BlockPos) -> Option<SharedBlockEntity> {
301 self.level.get_block_entity(pos)
302 }
303
304 fn is_face_sturdy_for(
305 &self,
306 state: BlockStateId,
307 pos: BlockPos,
308 direction: Direction,
309 support_type: shapes::SupportType,
310 ) -> bool {
311 self.level
312 .is_face_sturdy_for(state, pos, direction, support_type)
313 }
314
315 fn raw_brightness(&self, pos: BlockPos, sky_darkening: u8) -> u8 {
316 self.level.raw_brightness(pos, sky_darkening)
317 }
318
319 fn can_see_sky(&self, pos: BlockPos) -> bool {
320 self.level.can_see_sky(pos)
321 }
322
323 fn ambient_light(&self) -> f32 {
324 self.level.ambient_light()
325 }
326
327 fn min_y(&self) -> i32 {
328 self.level.min_y()
329 }
330
331 fn height(&self) -> i32 {
332 self.level.height()
333 }
334
335 fn sea_level(&self) -> i32 {
336 self.level.sea_level()
337 }
338}
339
340impl<L: LevelAccessor + ?Sized> ScheduledTickAccess for TreeWritePreflight<'_, L> {
341 fn fluid_tick_delay(&self, fluid: FluidRef) -> i32 {
342 self.level.fluid_tick_delay(fluid)
343 }
344
345 fn schedule_block_tick_default(&self, _pos: BlockPos, _block: BlockRef, _delay: i32) -> bool {
346 false
347 }
348
349 fn schedule_fluid_tick_default(&self, _pos: BlockPos, _fluid: FluidRef, _delay: i32) -> bool {
350 false
351 }
352}
353
354impl<L: LevelAccessor + ?Sized> LevelAccessor for TreeWritePreflight<'_, L> {
355 fn can_write_to_chunk(&self, chunk_x: i32, chunk_z: i32) -> bool {
356 self.level.can_write_to_chunk(chunk_x, chunk_z)
357 }
358
359 fn set_block_state(&self, pos: BlockPos, state: BlockStateId, _flags: UpdateFlags) -> bool {
360 let chunk_x = SectionPos::block_to_section_coord(pos.x());
361 let chunk_z = SectionPos::block_to_section_coord(pos.z());
362 if !self.level.can_write_to_chunk(chunk_x, chunk_z) {
363 self.failed.set(true);
364 return false;
365 }
366
367 self.writes.borrow_mut().insert(pos, state);
368 true
369 }
370
371 fn destroy_block(&self, pos: BlockPos, _drop_items: bool) -> bool {
372 if self.get_block_state(pos).is_air() {
373 return false;
374 }
375
376 self.set_block_state(
377 pos,
378 vanilla_blocks::AIR.default_state(),
379 UpdateFlags::UPDATE_ALL,
380 )
381 }
382}
383
384#[derive(Clone, Copy)]
385struct FoliageAttachment {
386 pos: BlockPos,
387 radius_offset: i32,
388 double_trunk: bool,
389}
390
391#[derive(Default)]
392struct TreePlacement {
393 roots: JavaBlockPosSet,
394 trunks: JavaBlockPosSet,
395 foliage: JavaBlockPosSet,
396 decorations: JavaBlockPosSet,
397}
398
399impl TreePlacement {
400 fn set_root(&mut self, region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
401 self.roots.insert(pos);
402 FeatureDecorationRunner::set_tree_block(region, pos, state);
403 }
404
405 fn set_trunk(&mut self, region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
406 self.trunks.insert(pos);
407 FeatureDecorationRunner::set_tree_block(region, pos, state);
408 }
409
410 fn set_foliage(&mut self, region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
411 self.foliage.insert(pos);
412 FeatureDecorationRunner::set_tree_block(region, pos, state);
413 }
414
415 fn set_decoration(
416 &mut self,
417 region: &mut impl LevelAccessor,
418 pos: BlockPos,
419 state: BlockStateId,
420 ) {
421 self.decorations.insert(pos);
422 FeatureDecorationRunner::set_tree_block(region, pos, state);
423 }
424}
425
426#[derive(Clone, Copy)]
427struct TreeBounds {
428 min_x: i32,
429 min_y: i32,
430 min_z: i32,
431 max_x: i32,
432 max_y: i32,
433 max_z: i32,
434}
435
436impl TreeBounds {
437 fn from_placement(placement: &TreePlacement) -> Option<Self> {
438 let mut bounds: Option<Self> = None;
439 for &pos in placement
440 .roots
441 .java_order()
442 .chain(placement.trunks.java_order())
443 .chain(placement.foliage.java_order())
444 .chain(placement.decorations.java_order())
445 {
446 match &mut bounds {
447 Some(bounds) => bounds.include(pos),
448 None => bounds = Some(Self::new(pos)),
449 }
450 }
451 bounds
452 }
453
454 const fn new(pos: BlockPos) -> Self {
455 Self {
456 min_x: pos.x(),
457 min_y: pos.y(),
458 min_z: pos.z(),
459 max_x: pos.x(),
460 max_y: pos.y(),
461 max_z: pos.z(),
462 }
463 }
464
465 fn include(&mut self, pos: BlockPos) {
466 self.min_x = self.min_x.min(pos.x());
467 self.min_y = self.min_y.min(pos.y());
468 self.min_z = self.min_z.min(pos.z());
469 self.max_x = self.max_x.max(pos.x());
470 self.max_y = self.max_y.max(pos.y());
471 self.max_z = self.max_z.max(pos.z());
472 }
473
474 const fn contains(self, pos: BlockPos) -> bool {
475 pos.x() >= self.min_x
476 && pos.x() <= self.max_x
477 && pos.y() >= self.min_y
478 && pos.y() <= self.max_y
479 && pos.z() >= self.min_z
480 && pos.z() <= self.max_z
481 }
482}
483
484const fn abs_i32(value: i32) -> i32 {
485 if value < 0 { -value } else { value }
486}
487
488#[cfg(test)]
489mod tests {
490 use super::*;
491 use steel_registry::{init_vanilla_registry, vanilla_blocks};
492 use steel_worldgen::density_functions::overworld::OverworldNoiseSettings;
493
494 struct WriteTestLevel {
495 can_write: bool,
496 }
497
498 impl LevelReader for WriteTestLevel {
499 fn get_block_state(&self, _pos: BlockPos) -> BlockStateId {
500 vanilla_blocks::AIR.default_state()
501 }
502
503 fn raw_brightness(&self, _pos: BlockPos, _sky_darkening: u8) -> u8 {
504 0
505 }
506
507 fn min_y(&self) -> i32 {
508 OverworldNoiseSettings::MIN_Y
509 }
510
511 fn height(&self) -> i32 {
512 OverworldNoiseSettings::HEIGHT
513 }
514
515 fn sea_level(&self) -> i32 {
516 OverworldNoiseSettings::SEA_LEVEL
517 }
518 }
519
520 impl ScheduledTickAccess for WriteTestLevel {
521 fn fluid_tick_delay(&self, _fluid: FluidRef) -> i32 {
522 0
523 }
524
525 fn schedule_block_tick_default(
526 &self,
527 _pos: BlockPos,
528 _block: BlockRef,
529 _delay: i32,
530 ) -> bool {
531 false
532 }
533
534 fn schedule_fluid_tick_default(
535 &self,
536 _pos: BlockPos,
537 _fluid: FluidRef,
538 _delay: i32,
539 ) -> bool {
540 false
541 }
542 }
543
544 impl LevelAccessor for WriteTestLevel {
545 fn can_write_to_chunk(&self, _chunk_x: i32, _chunk_z: i32) -> bool {
546 self.can_write
547 }
548
549 fn set_block_state(
550 &self,
551 _pos: BlockPos,
552 _state: BlockStateId,
553 _flags: UpdateFlags,
554 ) -> bool {
555 self.can_write
556 }
557
558 fn destroy_block(&self, _pos: BlockPos, _drop_items: bool) -> bool {
559 self.can_write
560 }
561 }
562
563 #[test]
564 fn tree_write_preflight_rejects_unwritable_chunks() {
565 init_vanilla_registry();
566 let mut level = WriteTestLevel { can_write: false };
567 let preflight_state = vanilla_blocks::OAK_LOG.default_state();
568 let preflight = TreeWritePreflight::new(&mut level);
569
570 assert!(!preflight.set_block_state(
571 BlockPos::ZERO,
572 preflight_state,
573 UpdateFlags::UPDATE_ALL,
574 ));
575 assert!(preflight.failed());
576 assert_eq!(
577 preflight.get_block_state(BlockPos::ZERO),
578 vanilla_blocks::AIR.default_state()
579 );
580 }
581
582 #[test]
583 fn tree_write_preflight_overlays_accepted_writes() {
584 init_vanilla_registry();
585 let mut level = WriteTestLevel { can_write: true };
586 let preflight_state = vanilla_blocks::OAK_LOG.default_state();
587 let preflight = TreeWritePreflight::new(&mut level);
588
589 assert!(preflight.set_block_state(
590 BlockPos::ZERO,
591 preflight_state,
592 UpdateFlags::UPDATE_ALL,
593 ));
594 assert!(!preflight.failed());
595 assert_eq!(preflight.get_block_state(BlockPos::ZERO), preflight_state);
596 }
597}