124124import com .sk89q .worldedit .world .World ;
125125import com .sk89q .worldedit .world .biome .BiomeType ;
126126import com .sk89q .worldedit .world .block .BaseBlock ;
127+ import com .sk89q .worldedit .world .block .BlockCategory ;
127128import com .sk89q .worldedit .world .block .BlockState ;
128129import com .sk89q .worldedit .world .block .BlockStateHolder ;
129130import com .sk89q .worldedit .world .block .BlockType ;
130131import com .sk89q .worldedit .world .block .BlockTypes ;
131132import com .sk89q .worldedit .world .generation .TreeType ;
133+ import com .sk89q .worldedit .world .registry .BlockMaterial ;
132134import com .sk89q .worldedit .world .registry .LegacyMapper ;
133135import org .apache .logging .log4j .Logger ;
134136
135137import java .util .ArrayDeque ;
136138import java .util .ArrayList ;
137139import java .util .Arrays ;
140+ import java .util .Collection ;
138141import java .util .Collections ;
139142import java .util .HashMap ;
140143import java .util .HashSet ;
@@ -2657,6 +2660,43 @@ public int deformRegion(final Region region, final Transform targetTransform, fi
26572660 return affected ;
26582661 }
26592662
2663+ private static Direction [] getBlockedDirections (Extent extent , BlockVector3 position ) {
2664+ BlockState blockState = extent .getBlock (position );
2665+ BlockType blockType = blockState .getBlockType ();
2666+ BlockMaterial material = blockState .getMaterial ();
2667+
2668+ if (material .isAir ()) {
2669+ return NO_DIRECTIONS ;
2670+ }
2671+ if (material .isLiquid ()) {
2672+ return NO_DIRECTIONS ;
2673+ }
2674+ Direction [] blockedDirections = BLOCKED_DIRECTIONS_OVERRIDE .get (blockType );
2675+ if (blockedDirections != null ) {
2676+ return blockedDirections ;
2677+ }
2678+ if (material .isFullCube ()) {
2679+ return CARDINAL_UPRIGHT_DIRECTIONS ;
2680+ }
2681+
2682+ Direction [] result = new Direction [CARDINAL_UPRIGHT_DIRECTIONS .length ];
2683+ int count = 0 ;
2684+
2685+ for (Direction direction : CARDINAL_UPRIGHT_DIRECTIONS ) {
2686+ if (material .isFullFace (direction )) {
2687+ result [count ++] = direction ;
2688+ }
2689+ }
2690+
2691+ // Short-cut for blocks that are blocked in all directions but for some reason not isFullCube
2692+ // This enables == comparison later
2693+ if (count == 6 ) {
2694+ return CARDINAL_UPRIGHT_DIRECTIONS ;
2695+ }
2696+
2697+ return Arrays .copyOf (result , count );
2698+ }
2699+
26602700 /**
26612701 * Hollows out the region (Semi-well-defined for non-cuboid selections).
26622702 *
@@ -2666,59 +2706,107 @@ public int deformRegion(final Region region, final Transform targetTransform, fi
26662706 *
26672707 * @return number of blocks affected
26682708 * @throws MaxChangedBlocksException thrown if too many blocks are changed
2709+ * @deprecated Use {@link EditSession#hollowOutRegion(Region, int, Pattern, boolean, Collection, boolean)} instead.
26692710 */
2670- public int hollowOutRegion (Region region , int thickness , Pattern pattern ) throws MaxChangedBlocksException {
2711+ @ InlineMe (replacement = "this.hollowOutRegion(region, thickness, pattern, true, null, false)" )
2712+ @ Deprecated
2713+ public final int hollowOutRegion (Region region , int thickness , Pattern pattern ) throws MaxChangedBlocksException {
2714+ return hollowOutRegion (region , thickness , pattern , true , null , false );
2715+ }
2716+
2717+ /**
2718+ * Hollows out the region (bounding-box mode is semi-well-defined for non-cuboid selections).
2719+ * The startingPositions parameter takes precedence over usePlacementPosition.
2720+ *
2721+ * @param region the region to hollow out.
2722+ * @param thickness the thickness of the shell to leave (manhattan distance)
2723+ * @param pattern The block pattern to use
2724+ * @param openSides Open up faces touching the bounding box. This matches the legacy behaviour.
2725+ * @param startingPositions Positions to consider as 'outside'. If null, use the selection bounding box
2726+ * @param useBlockGeometry Consider block geometry for visibility calculation
2727+ * @return number of blocks affected
2728+ * @throws MaxChangedBlocksException thrown if too many blocks are changed
2729+ */
2730+ public int hollowOutRegion (Region region , int thickness , Pattern pattern , boolean openSides , Collection <BlockVector3 > startingPositions , boolean useBlockGeometry ) throws MaxChangedBlocksException {
26712731 int affected = 0 ;
26722732
26732733 final Set <BlockVector3 > visible = new HashSet <>();
2734+ if (startingPositions == null ) {
2735+ // Initialize BFS with selection bounding box
2736+ final BlockVector3 min = region .getMinimumPoint ();
2737+ final BlockVector3 max = region .getMaximumPoint ();
2738+
2739+ final int minX = min .x ();
2740+ final int minY = min .y ();
2741+ final int minZ = min .z ();
2742+ final int maxX = max .x ();
2743+ final int maxY = max .y ();
2744+ final int maxZ = max .z ();
26742745
2675- // Initialize BFS with selection bounding box
2676- final BlockVector3 min = region .getMinimumPoint ();
2677- final BlockVector3 max = region .getMaximumPoint ();
2678-
2679- final int minX = min .x ();
2680- final int minY = min .y ();
2681- final int minZ = min .z ();
2682- final int maxX = max .x ();
2683- final int maxY = max .y ();
2684- final int maxZ = max .z ();
2746+ for (int x = minX ; x <= maxX ; ++x ) {
2747+ for (int y = minY ; y <= maxY ; ++y ) {
2748+ visible .add (BlockVector3 .at (x , y , minZ ));
2749+ visible .add (BlockVector3 .at (x , y , maxZ ));
2750+ }
2751+ }
26852752
2686- for (int x = minX ; x <= maxX ; ++x ) {
26872753 for (int y = minY ; y <= maxY ; ++y ) {
2688- visible .add (BlockVector3 .at (x , y , minZ ));
2689- visible .add (BlockVector3 .at (x , y , maxZ ));
2754+ for (int z = minZ ; z <= maxZ ; ++z ) {
2755+ visible .add (BlockVector3 .at (minX , y , z ));
2756+ visible .add (BlockVector3 .at (maxX , y , z ));
2757+ }
26902758 }
2691- }
26922759
2693- for (int y = minY ; y <= maxY ; ++y ) {
26942760 for (int z = minZ ; z <= maxZ ; ++z ) {
2695- visible .add (BlockVector3 .at (minX , y , z ));
2696- visible .add (BlockVector3 .at (maxX , y , z ));
2761+ for (int x = minX ; x <= maxX ; ++x ) {
2762+ visible .add (BlockVector3 .at (x , minY , z ));
2763+ visible .add (BlockVector3 .at (x , maxY , z ));
2764+ }
26972765 }
2698- }
26992766
2700- for (int z = minZ ; z <= maxZ ; ++z ) {
2701- for (int x = minX ; x <= maxX ; ++x ) {
2702- visible .add (BlockVector3 .at (x , minY , z ));
2703- visible .add (BlockVector3 .at (x , maxY , z ));
2767+ if (openSides ) {
2768+ // Remove movement blockers from visible list
2769+ visible .removeIf (blockVector3 -> getBlock (blockVector3 ).getBlockType ().getMaterial ().isMovementBlocker ());
27042770 }
2771+ } else {
2772+ visible .addAll (startingPositions );
27052773 }
27062774
2707- // Remove movement blockers from visible list
2708- visible .removeIf (blockVector3 -> getBlock (blockVector3 ).getBlockType ().getMaterial ().isMovementBlocker ());
2709-
27102775 // Do BFS to find more visible blocks
27112776 final Queue <BlockVector3 > queue = new ArrayDeque <>(visible );
27122777 while (!queue .isEmpty ()) {
27132778 final BlockVector3 current = queue .poll ();
27142779
2715- final BlockState block = getBlock (current );
2716- if (block .getBlockType ().getMaterial ().isMovementBlocker ()) {
2717- continue ;
2780+ Direction [] blockedDirections ;
2781+ if (useBlockGeometry ) {
2782+ // Get blocked directions for this block
2783+ blockedDirections = getBlockedDirections (this , current );
2784+
2785+ // Short-cut if blocked in all directions
2786+ if (blockedDirections == CARDINAL_UPRIGHT_DIRECTIONS ) {
2787+ continue ;
2788+ }
2789+ } else {
2790+ final BlockState block = getBlock (current );
2791+ if (block .getBlockType ().getMaterial ().isMovementBlocker ()) {
2792+ // In non-geometry mode, all movement blockers are assumed to be blocked in all directions, so short-cut here
2793+ continue ;
2794+ }
2795+
2796+ // All other blocks are assumed to have no blocked directions
2797+ blockedDirections = NO_DIRECTIONS ;
27182798 }
27192799
2720- for (BlockVector3 recurseDirection : CARDINAL_UPRIGHT_OFFSETS ) {
2721- final BlockVector3 neighbor = current .add (recurseDirection );
2800+ outer :
2801+ for (Direction direction : CARDINAL_UPRIGHT_DIRECTIONS ) {
2802+ // Check if this direction is blocked
2803+ for (Direction blockedDirection : blockedDirections ) {
2804+ if (blockedDirection == direction ) {
2805+ continue outer ; // skip this direction
2806+ }
2807+ }
2808+
2809+ final BlockVector3 neighbor = current .add (direction .toBlockVector ());
27222810
27232811 if (!region .contains (neighbor )) {
27242812 continue ;
@@ -3171,6 +3259,10 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int
31713259 return changed ;
31723260 }
31733261
3262+ /**
3263+ * Contains no directions.
3264+ */
3265+ private static final Direction [] NO_DIRECTIONS = {};
31743266 /**
31753267 * Contains all cardinal and upright directions.
31763268 */
@@ -3184,6 +3276,77 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int
31843276 .map (Direction ::toBlockVector )
31853277 .toArray (BlockVector3 []::new );
31863278
3279+ /**
3280+ * Some blocks need a special case for one reason or another.
3281+ */
3282+ private static final Map <BlockType , Direction []> BLOCKED_DIRECTIONS_OVERRIDE = new HashMap <>();
3283+
3284+ static {
3285+ Arrays .asList (
3286+ // fullcubes that you can see through on all 6 sides
3287+ BlockTypes .SPAWNER ,
3288+ BlockTypes .CHEST ,
3289+ BlockTypes .BEACON ,
3290+ BlockTypes .KELP ,
3291+ BlockTypes .KELP_PLANT ,
3292+ BlockTypes .MANGROVE_ROOTS ,
3293+ BlockTypes .ICE ,
3294+
3295+ BlockTypes .OAK_DOOR ,
3296+ BlockTypes .OAK_TRAPDOOR ,
3297+ // BlockTypes.SPRUCE_DOOR,
3298+ // BlockTypes.SPRUCE_TRAPDOOR,
3299+ // BlockTypes.BIRCH_DOOR,
3300+ // BlockTypes.BIRCH_TRAPDOOR,
3301+ BlockTypes .JUNGLE_DOOR ,
3302+ BlockTypes .JUNGLE_TRAPDOOR ,
3303+ BlockTypes .ACACIA_DOOR ,
3304+ BlockTypes .ACACIA_TRAPDOOR ,
3305+ // BlockTypes.DARK_OAK_DOOR,
3306+ // BlockTypes.DARK_OAK_TRAPDOOR,
3307+ // BlockTypes.MANGROVE_DOOR,
3308+ BlockTypes .MANGROVE_TRAPDOOR ,
3309+ BlockTypes .CHERRY_DOOR ,
3310+ BlockTypes .CHERRY_TRAPDOOR ,
3311+ // BlockTypes.PALE_OAK_DOOR,
3312+ // BlockTypes.PALE_OAK_TRAPDOOR,
3313+ BlockTypes .BAMBOO_DOOR ,
3314+ BlockTypes .BAMBOO_TRAPDOOR ,
3315+ BlockTypes .CRIMSON_DOOR ,
3316+ BlockTypes .CRIMSON_TRAPDOOR ,
3317+ BlockTypes .WARPED_DOOR ,
3318+ BlockTypes .WARPED_TRAPDOOR ,
3319+ BlockTypes .IRON_DOOR ,
3320+ BlockTypes .IRON_TRAPDOOR
3321+ ).forEach (blockType -> {
3322+ BLOCKED_DIRECTIONS_OVERRIDE .put (blockType , NO_DIRECTIONS );
3323+ });
3324+ BLOCKED_DIRECTIONS_OVERRIDE .put (BlockTypes .TRIAL_SPAWNER , new Direction [] { Direction .UP });
3325+ BLOCKED_DIRECTIONS_OVERRIDE .put (BlockTypes .VAULT , new Direction [] { Direction .UP , Direction .DOWN });
3326+ addSeeThroughBlockCategory ("minecraft:fall_damage_resetting" );
3327+ addSeeThroughBlockCategory ("minecraft:replaceable_by_trees" );
3328+
3329+ java .util .regex .Pattern pattern = java .util .regex .Pattern .compile ("^.*:(?:.*_)?(?:glass|grate|copper_door|copper_trapdoor)(?:_.*)?$" );
3330+ for (BlockType blockType : BlockType .REGISTRY ) {
3331+ if (pattern .matcher (blockType .id ()).matches ()) {
3332+ BLOCKED_DIRECTIONS_OVERRIDE .put (blockType , NO_DIRECTIONS );
3333+ }
3334+ }
3335+ }
3336+
3337+ private static void addSeeThroughBlockCategory (String blockCategoryKey ) {
3338+ BlockCategory blockCategory = BlockCategory .REGISTRY .get (blockCategoryKey );
3339+ if (blockCategory == null ) {
3340+ WorldEdit .logger .warn ("BlockCategory {} not found during initialization" , blockCategoryKey );
3341+ return ;
3342+ }
3343+
3344+ for (BlockType blockType : blockCategory .getAll ()) {
3345+ BLOCKED_DIRECTIONS_OVERRIDE .put (blockType , NO_DIRECTIONS );
3346+ }
3347+
3348+ }
3349+
31873350 private static double lengthSq (double x , double y , double z ) {
31883351 return (x * x ) + (y * y ) + (z * z );
31893352 }
0 commit comments