1use steel_registry::vanilla_block_tags::BlockTag;
2
3use super::super::prelude::*;
4use super::super::runner::FeatureDecorationRunner;
5use super::super::vanilla_collections::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(_) => Self::tree_valid_pos(region, pos),
240 }
241 }
242
243 fn tree_valid_pos_or_tag(region: &impl LevelReader, pos: BlockPos, tag: &Identifier) -> bool {
244 let state = region.get_block_state(pos);
245 let block = state.get_block();
246 state.is_air() || block.has_tag(&BlockTag::REPLACEABLE_BY_TREES) || block.has_tag(tag)
247 }
248
249 fn tree_is_air_or_leaves(region: &impl LevelReader, pos: BlockPos) -> bool {
250 let state = region.get_block_state(pos);
251 state.is_air() || state.get_block().has_tag(&BlockTag::LEAVES)
252 }
253
254 fn tree_is_vine(region: &impl LevelReader, pos: BlockPos) -> bool {
255 region.get_block_state(pos).get_block() == &vanilla_blocks::VINE
256 }
257
258 fn set_tree_block(region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
259 let flags = UpdateFlags::UPDATE_NEIGHBORS
260 | UpdateFlags::UPDATE_CLIENTS
261 | UpdateFlags::UPDATE_KNOWN_SHAPE;
262 let _ = region.set_block_state(pos, state, flags);
263 }
264}
265
266struct TreeWritePreflight<'a, L: LevelAccessor + ?Sized> {
267 level: &'a mut L,
268 writes: RefCell<FxHashMap<BlockPos, BlockStateId>>,
269 failed: Cell<bool>,
270}
271
272impl<'a, L: LevelAccessor + ?Sized> TreeWritePreflight<'a, L> {
273 fn new(level: &'a mut L) -> Self {
274 Self {
275 level,
276 writes: RefCell::new(FxHashMap::default()),
277 failed: Cell::new(false),
278 }
279 }
280
281 const fn failed(&self) -> bool {
282 self.failed.get()
283 }
284}
285
286impl<L: LevelAccessor + ?Sized> LevelReader for TreeWritePreflight<'_, L> {
287 fn height_at(&self, heightmap_type: HeightmapType, x: i32, z: i32) -> i32 {
288 self.level.height_at(heightmap_type, x, z)
289 }
290
291 fn get_block_state(&self, pos: BlockPos) -> BlockStateId {
292 self.writes
293 .borrow()
294 .get(&pos)
295 .copied()
296 .unwrap_or_else(|| self.level.get_block_state(pos))
297 }
298
299 fn get_block_entity(&self, pos: BlockPos) -> Option<SharedBlockEntity> {
300 self.level.get_block_entity(pos)
301 }
302
303 fn is_face_sturdy_for(
304 &self,
305 state: BlockStateId,
306 pos: BlockPos,
307 direction: Direction,
308 support_type: shapes::SupportType,
309 ) -> bool {
310 self.level
311 .is_face_sturdy_for(state, pos, direction, support_type)
312 }
313
314 fn raw_brightness(&self, pos: BlockPos, sky_darkening: u8) -> u8 {
315 self.level.raw_brightness(pos, sky_darkening)
316 }
317
318 fn can_see_sky(&self, pos: BlockPos) -> bool {
319 self.level.can_see_sky(pos)
320 }
321
322 fn ambient_light(&self) -> f32 {
323 self.level.ambient_light()
324 }
325
326 fn min_y(&self) -> i32 {
327 self.level.min_y()
328 }
329
330 fn height(&self) -> i32 {
331 self.level.height()
332 }
333}
334
335impl<L: LevelAccessor + ?Sized> ScheduledTickAccess for TreeWritePreflight<'_, L> {
336 fn fluid_tick_delay(&self, fluid: FluidRef) -> i32 {
337 self.level.fluid_tick_delay(fluid)
338 }
339
340 fn schedule_block_tick_default(&self, _pos: BlockPos, _block: BlockRef, _delay: i32) -> bool {
341 false
342 }
343
344 fn schedule_fluid_tick_default(&self, _pos: BlockPos, _fluid: FluidRef, _delay: i32) -> bool {
345 false
346 }
347}
348
349impl<L: LevelAccessor + ?Sized> LevelAccessor for TreeWritePreflight<'_, L> {
350 fn can_write_to_chunk(&self, chunk_x: i32, chunk_z: i32) -> bool {
351 self.level.can_write_to_chunk(chunk_x, chunk_z)
352 }
353
354 fn set_block_state(&self, pos: BlockPos, state: BlockStateId, _flags: UpdateFlags) -> bool {
355 let chunk_x = SectionPos::block_to_section_coord(pos.x());
356 let chunk_z = SectionPos::block_to_section_coord(pos.z());
357 if !self.level.can_write_to_chunk(chunk_x, chunk_z) {
358 self.failed.set(true);
359 return false;
360 }
361
362 self.writes.borrow_mut().insert(pos, state);
363 true
364 }
365
366 fn destroy_block(&self, pos: BlockPos, _drop_items: bool) -> bool {
367 if self.get_block_state(pos).is_air() {
368 return false;
369 }
370
371 self.set_block_state(
372 pos,
373 vanilla_blocks::AIR.default_state(),
374 UpdateFlags::UPDATE_ALL,
375 )
376 }
377}
378
379#[derive(Clone, Copy)]
380struct FoliageAttachment {
381 pos: BlockPos,
382 radius_offset: i32,
383 double_trunk: bool,
384}
385
386#[derive(Default)]
387struct TreePlacement {
388 roots: JavaBlockPosSet,
389 trunks: JavaBlockPosSet,
390 foliage: JavaBlockPosSet,
391 decorations: JavaBlockPosSet,
392}
393
394impl TreePlacement {
395 fn set_root(&mut self, region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
396 self.roots.insert(pos);
397 FeatureDecorationRunner::set_tree_block(region, pos, state);
398 }
399
400 fn set_trunk(&mut self, region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
401 self.trunks.insert(pos);
402 FeatureDecorationRunner::set_tree_block(region, pos, state);
403 }
404
405 fn set_foliage(&mut self, region: &mut impl LevelAccessor, pos: BlockPos, state: BlockStateId) {
406 self.foliage.insert(pos);
407 FeatureDecorationRunner::set_tree_block(region, pos, state);
408 }
409
410 fn set_decoration(
411 &mut self,
412 region: &mut impl LevelAccessor,
413 pos: BlockPos,
414 state: BlockStateId,
415 ) {
416 self.decorations.insert(pos);
417 FeatureDecorationRunner::set_tree_block(region, pos, state);
418 }
419}
420
421#[derive(Clone, Copy)]
422struct TreeBounds {
423 min_x: i32,
424 min_y: i32,
425 min_z: i32,
426 max_x: i32,
427 max_y: i32,
428 max_z: i32,
429}
430
431impl TreeBounds {
432 fn from_placement(placement: &TreePlacement) -> Option<Self> {
433 let mut bounds: Option<Self> = None;
434 for &pos in placement
435 .roots
436 .insertion_order()
437 .chain(placement.trunks.insertion_order())
438 .chain(placement.foliage.insertion_order())
439 .chain(placement.decorations.insertion_order())
440 {
441 match &mut bounds {
442 Some(bounds) => bounds.include(pos),
443 None => bounds = Some(Self::new(pos)),
444 }
445 }
446 bounds
447 }
448
449 const fn new(pos: BlockPos) -> Self {
450 Self {
451 min_x: pos.x(),
452 min_y: pos.y(),
453 min_z: pos.z(),
454 max_x: pos.x(),
455 max_y: pos.y(),
456 max_z: pos.z(),
457 }
458 }
459
460 fn include(&mut self, pos: BlockPos) {
461 self.min_x = self.min_x.min(pos.x());
462 self.min_y = self.min_y.min(pos.y());
463 self.min_z = self.min_z.min(pos.z());
464 self.max_x = self.max_x.max(pos.x());
465 self.max_y = self.max_y.max(pos.y());
466 self.max_z = self.max_z.max(pos.z());
467 }
468
469 const fn contains(self, pos: BlockPos) -> bool {
470 pos.x() >= self.min_x
471 && pos.x() <= self.max_x
472 && pos.y() >= self.min_y
473 && pos.y() <= self.max_y
474 && pos.z() >= self.min_z
475 && pos.z() <= self.max_z
476 }
477}
478
479const fn abs_i32(value: i32) -> i32 {
480 if value < 0 { -value } else { value }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486 use steel_registry::{init_vanilla_registry, vanilla_blocks};
487
488 struct WriteTestLevel {
489 can_write: bool,
490 }
491
492 impl LevelReader for WriteTestLevel {
493 fn get_block_state(&self, _pos: BlockPos) -> BlockStateId {
494 vanilla_blocks::AIR.default_state()
495 }
496
497 fn raw_brightness(&self, _pos: BlockPos, _sky_darkening: u8) -> u8 {
498 0
499 }
500
501 fn min_y(&self) -> i32 {
502 -64
503 }
504
505 fn height(&self) -> i32 {
506 384
507 }
508 }
509
510 impl ScheduledTickAccess for WriteTestLevel {
511 fn fluid_tick_delay(&self, _fluid: FluidRef) -> i32 {
512 0
513 }
514
515 fn schedule_block_tick_default(
516 &self,
517 _pos: BlockPos,
518 _block: BlockRef,
519 _delay: i32,
520 ) -> bool {
521 false
522 }
523
524 fn schedule_fluid_tick_default(
525 &self,
526 _pos: BlockPos,
527 _fluid: FluidRef,
528 _delay: i32,
529 ) -> bool {
530 false
531 }
532 }
533
534 impl LevelAccessor for WriteTestLevel {
535 fn can_write_to_chunk(&self, _chunk_x: i32, _chunk_z: i32) -> bool {
536 self.can_write
537 }
538
539 fn set_block_state(
540 &self,
541 _pos: BlockPos,
542 _state: BlockStateId,
543 _flags: UpdateFlags,
544 ) -> bool {
545 self.can_write
546 }
547
548 fn destroy_block(&self, _pos: BlockPos, _drop_items: bool) -> bool {
549 self.can_write
550 }
551 }
552
553 #[test]
554 fn tree_write_preflight_rejects_unwritable_chunks() {
555 init_vanilla_registry();
556 let mut level = WriteTestLevel { can_write: false };
557 let preflight_state = vanilla_blocks::OAK_LOG.default_state();
558 let preflight = TreeWritePreflight::new(&mut level);
559
560 assert!(!preflight.set_block_state(
561 BlockPos::ZERO,
562 preflight_state,
563 UpdateFlags::UPDATE_ALL,
564 ));
565 assert!(preflight.failed());
566 assert_eq!(
567 preflight.get_block_state(BlockPos::ZERO),
568 vanilla_blocks::AIR.default_state()
569 );
570 }
571
572 #[test]
573 fn tree_write_preflight_overlays_accepted_writes() {
574 init_vanilla_registry();
575 let mut level = WriteTestLevel { can_write: true };
576 let preflight_state = vanilla_blocks::OAK_LOG.default_state();
577 let preflight = TreeWritePreflight::new(&mut level);
578
579 assert!(preflight.set_block_state(
580 BlockPos::ZERO,
581 preflight_state,
582 UpdateFlags::UPDATE_ALL,
583 ));
584 assert!(!preflight.failed());
585 assert_eq!(preflight.get_block_state(BlockPos::ZERO), preflight_state);
586 }
587}