From 6b28b481a7d9acbefd9964413f768494f670e8f2 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 19 Apr 2026 22:46:34 +0200 Subject: [PATCH 01/14] Add BlockState.getMaterial() --- .../accepted-bukkit-public-api-changes.json | 10 +++++++ .../accepted-core-public-api-changes.json | 8 ++++++ .../accepted-sponge-public-api-changes.json | 9 +++++++ .../impl/v1_21_11/PaperweightAdapter.java | 5 ++++ .../impl/v1_21_4/PaperweightAdapter.java | 5 ++++ .../impl/v1_21_5/PaperweightAdapter.java | 5 ++++ .../impl/v1_21_6/PaperweightAdapter.java | 5 ++++ .../impl/v1_21_9/PaperweightAdapter.java | 5 ++++ .../impl/v26_1/PaperweightAdapter.java | 5 ++++ .../impl/v26_2/PaperweightAdapter.java | 5 ++++ .../impl/v26_3/PaperweightAdapter.java | 5 ++++ .../worldedit/bukkit/BukkitBlockRegistry.java | 17 ++++++------ .../bukkit/adapter/BukkitImplAdapter.java | 11 ++++++++ .../coremc/internal/CoreMcBlockRegistry.java | 5 ++-- .../worldedit/world/block/BlockState.java | 6 +++++ .../worldedit/world/block/BlockType.java | 2 +- .../world/registry/BlockRegistry.java | 26 ++++++++++++++++++- .../world/registry/BundledBlockRegistry.java | 4 +-- .../worldedit/sponge/SpongeBlockRegistry.java | 21 +++++++-------- 19 files changed, 132 insertions(+), 27 deletions(-) diff --git a/verification/src/changes/accepted-bukkit-public-api-changes.json b/verification/src/changes/accepted-bukkit-public-api-changes.json index 2c63c08510..b0c197718f 100644 --- a/verification/src/changes/accepted-bukkit-public-api-changes.json +++ b/verification/src/changes/accepted-bukkit-public-api-changes.json @@ -1,2 +1,12 @@ { + "Replaced by default method": [ + { + "type": "com.sk89q.worldedit.bukkit.BukkitBlockRegistry", + "member": "Method com.sk89q.worldedit.bukkit.BukkitBlockRegistry.getMaterial(com.sk89q.worldedit.world.block.BlockType)", + "changes": [ + "METHOD_REMOVED", + "ANNOTATION_REMOVED" + ] + } + ] } diff --git a/verification/src/changes/accepted-core-public-api-changes.json b/verification/src/changes/accepted-core-public-api-changes.json index 299d6c18aa..095ae12d91 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -100,6 +100,14 @@ "ANNOTATION_REMOVED" ] }, + { + "type": "com.sk89q.worldedit.world.registry.BlockRegistry", + "member": "Method com.sk89q.worldedit.world.registry.BlockRegistry.getMaterial(com.sk89q.worldedit.world.block.BlockType)", + "changes": [ + "METHOD_ABSTRACT_NOW_DEFAULT", + "ANNOTATION_DEPRECATED_ADDED" + ] + }, { "type": "com.sk89q.worldedit.world.registry.BlockRegistry", "member": "Method com.sk89q.worldedit.world.registry.BlockRegistry.getName(com.sk89q.worldedit.world.block.BlockType)", diff --git a/verification/src/changes/accepted-sponge-public-api-changes.json b/verification/src/changes/accepted-sponge-public-api-changes.json index 2c63c08510..1fec9a4c3b 100644 --- a/verification/src/changes/accepted-sponge-public-api-changes.json +++ b/verification/src/changes/accepted-sponge-public-api-changes.json @@ -1,2 +1,11 @@ { + "Replaced by default method": [ + { + "type": "com.sk89q.worldedit.sponge.SpongeBlockRegistry", + "member": "Method com.sk89q.worldedit.sponge.SpongeBlockRegistry.getMaterial(com.sk89q.worldedit.world.block.BlockType)", + "changes": [ + "METHOD_REMOVED" + ] + } + ] } diff --git a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java index a1e0d13cb7..b7f126ff87 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java @@ -607,6 +607,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java index a152c8c924..41289efb9b 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java @@ -588,6 +588,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java index 4012e7addf..f0ef8d9104 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java @@ -586,6 +586,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java index 7d6c0d2052..8044db88e9 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java @@ -608,6 +608,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java index 4bc0e6f733..fb1f9db555 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java @@ -607,6 +607,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java index 1c5a24e403..fb020c628a 100644 --- a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java @@ -625,6 +625,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java index 42a9d0f3ff..14c95ee043 100644 --- a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java @@ -630,6 +630,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java index 0c3c26ff62..b016a05bc4 100644 --- a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java @@ -640,6 +640,11 @@ public BlockMaterial getBlockMaterial(BlockType blockType) { return new PaperweightBlockMaterial(mcBlockState); } + @Override + public BlockMaterial getBlockMaterial(BlockState blockState) { + return new PaperweightBlockMaterial(adapt(blockState)); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java index fe81dbd032..da8b84698b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java @@ -37,7 +37,7 @@ import javax.annotation.Nullable; public class BukkitBlockRegistry implements BlockRegistry { - private final Map materialMap = new HashMap<>(); + private final Map materialMap = new HashMap<>(); @Override public Component getRichName(BlockType blockType) { @@ -49,15 +49,16 @@ public Component getRichName(BlockType blockType) { @Nullable @Override - public BlockMaterial getMaterial(BlockType blockType) { - Material mat = BukkitAdapter.adapt(blockType); - if (mat == null) { - return null; - } - return materialMap.computeIfAbsent(mat, material -> { + public BlockMaterial getMaterial(BlockState blockState) { + return materialMap.computeIfAbsent(blockState, _ -> { + Material material = BukkitAdapter.adapt(blockState.getBlockType()); + if (material == null) { + // return null means create no mapping + return null; + } BlockMaterial platformMaterial = null; if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) { - platformMaterial = WorldEditPlugin.getInstance().getBukkitImplAdapter().getBlockMaterial(blockType); + platformMaterial = WorldEditPlugin.getInstance().getBukkitImplAdapter().getBlockMaterial(blockState); } return new BukkitBlockMaterial(platformMaterial, material); }); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index caa8ad3a26..4ac3147a4e 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -164,6 +164,17 @@ default void tickWatchdog() { @Nullable BlockMaterial getBlockMaterial(BlockType blockType); + /** + * Gets the block material for the given block state. + * + * @param blockState the block state + * @return the material + */ + @Nullable + default BlockMaterial getBlockMaterial(BlockState blockState) { + return getBlockMaterial(blockState.getBlockType()); + } + /** * Get a map of {@code string -> property}. * diff --git a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockRegistry.java b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockRegistry.java index cc1399ea0e..b211a3afa3 100644 --- a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockRegistry.java +++ b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockRegistry.java @@ -56,10 +56,9 @@ public Component getRichName(BlockType blockType) { } @Override - public BlockMaterial getMaterial(BlockType blockType) { - Block block = platform.getAdapter().toNativeBlock(blockType); + public BlockMaterial getMaterial(BlockState blockState) { return materialMap.computeIfAbsent( - block.defaultBlockState(), + platform.getAdapter().toNativeBlockState(blockState), CoreMcBlockMaterial::new ); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index 4a2e9edfe1..102beac7ad 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.fluid.FluidState; +import com.sk89q.worldedit.world.registry.BlockMaterial; import org.enginehub.linbus.tree.LinCompoundTag; import java.util.HashSet; @@ -80,6 +81,11 @@ public BlockType getBlockType() { return this.blockType; } + public BlockMaterial getMaterial() { + return WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(this); + } + @Override public BlockState with(final Property property, final V value) { if (this.stateListIndex == -1) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java index 37f0da6494..e02eb50d41 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java @@ -72,7 +72,7 @@ public class BlockType implements Keyed { @SuppressWarnings("this-escape") private final LazyReference blockMaterial = LazyReference.from(() -> WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(this)); + .queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getMaterial(getDefaultState())); @SuppressWarnings("this-escape") private final LazyReference legacyId = LazyReference.from(() -> computeLegacy(0)); @SuppressWarnings("this-escape") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java index 47debab315..3178efcb49 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.world.registry; +import com.sk89q.worldedit.internal.util.DeprecationUtil; +import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.block.BlockState; @@ -48,9 +50,31 @@ public interface BlockRegistry { * * @param blockType the block * @return the material, or null if the material information is not known + * @deprecated Use {@link BlockRegistry#getMaterial(BlockState)} instead. */ + @Deprecated @Nullable - BlockMaterial getMaterial(BlockType blockType); + default BlockMaterial getMaterial(BlockType blockType) { + return getMaterial(blockType.getDefaultState()); + } + + /** + * Get the material for the given block state. + * + * @param blockState the block state + * @return the material, or null if the material information is not known + * @apiNote This must be overridden by new subclasses. See {@link NonAbstractForCompatibility} + * for details + */ + @NonAbstractForCompatibility( + delegateName = "getMaterial", + delegateParams = { BlockType.class } + ) + @Nullable + default BlockMaterial getMaterial(BlockState blockState) { + DeprecationUtil.checkDelegatingOverride(getClass()); + return getMaterial(blockState.getBlockType()); + } /** * Get an unmodifiable map of states for this block. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java index 799fe94185..46b4b290ca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java @@ -59,8 +59,8 @@ public Component getRichName(BlockType blockType) { @Nullable @Override - public BlockMaterial getMaterial(BlockType blockType) { - return new PassthroughBlockMaterial(BundledBlockData.getInstance().getMaterialById(blockType.id())); + public BlockMaterial getMaterial(BlockState blockState) { + return new PassthroughBlockMaterial(BundledBlockData.getInstance().getMaterialById(blockState.getBlockType().id())); } @Nullable diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java index 1220daa5ef..17ed83787d 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java @@ -54,19 +54,16 @@ public Component getRichName(BlockType blockType) { } @Override - public BlockMaterial getMaterial(BlockType blockType) { - org.spongepowered.api.block.BlockType spongeBlockType = - Sponge.game().registry(RegistryTypes.BLOCK_TYPE) - .value(ResourceKey.resolve(blockType.id())); + public BlockMaterial getMaterial(BlockState blockState) { return materialMap.computeIfAbsent( - spongeBlockType.defaultState(), - m -> { - net.minecraft.world.level.block.state.BlockState blockState = - (net.minecraft.world.level.block.state.BlockState) m; - return new SpongeBlockMaterial( - blockState - ); - } + SpongeAdapter.adapt(blockState), + m -> { + net.minecraft.world.level.block.state.BlockState mcBlockState = + (net.minecraft.world.level.block.state.BlockState) m; + return new SpongeBlockMaterial( + mcBlockState + ); + } ); } From 55415c2441749b1d92eb06a12b8b9769266f8ae6 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 3 May 2026 18:34:30 +0200 Subject: [PATCH 02/14] Add ShapeType parameter to BlockMaterial.isFullCube --- .../accepted-core-public-api-changes.json | 8 ++++ .../accepted-sponge-public-api-changes.json | 7 +++ .../v1_21_11/PaperweightBlockMaterial.java | 18 +++++-- .../v1_21_4/PaperweightBlockMaterial.java | 18 +++++-- .../v1_21_5/PaperweightBlockMaterial.java | 18 +++++-- .../v1_21_6/PaperweightBlockMaterial.java | 18 +++++-- .../v1_21_9/PaperweightBlockMaterial.java | 18 +++++-- .../impl/v26_1/PaperweightBlockMaterial.java | 18 +++++-- .../impl/v26_2/PaperweightBlockMaterial.java | 18 +++++-- .../impl/v26_3/PaperweightBlockMaterial.java | 18 +++++-- .../coremc/internal/CoreMcBlockMaterial.java | 18 +++++-- .../com/sk89q/worldedit/blocks/ShapeType.java | 32 +++++++++++++ .../worldedit/function/mask/FullCubeMask.java | 3 +- .../internal/block/AbstractBlockMaterial.java | 47 +++++++++++++++++++ .../world/registry/BlockMaterial.java | 16 ++++++- .../registry/PassthroughBlockMaterial.java | 6 ++- .../world/registry/SimpleBlockMaterial.java | 4 +- .../worldedit/sponge/SpongeBlockMaterial.java | 18 +++++-- 18 files changed, 258 insertions(+), 45 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ShapeType.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java diff --git a/verification/src/changes/accepted-core-public-api-changes.json b/verification/src/changes/accepted-core-public-api-changes.json index 095ae12d91..64db03f327 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -100,6 +100,14 @@ "ANNOTATION_REMOVED" ] }, + { + "type": "com.sk89q.worldedit.world.registry.BlockMaterial", + "member": "Method com.sk89q.worldedit.world.registry.BlockMaterial.isFullCube()", + "changes": [ + "METHOD_ABSTRACT_NOW_DEFAULT", + "ANNOTATION_DEPRECATED_ADDED" + ] + }, { "type": "com.sk89q.worldedit.world.registry.BlockRegistry", "member": "Method com.sk89q.worldedit.world.registry.BlockRegistry.getMaterial(com.sk89q.worldedit.world.block.BlockType)", diff --git a/verification/src/changes/accepted-sponge-public-api-changes.json b/verification/src/changes/accepted-sponge-public-api-changes.json index 1fec9a4c3b..3afedbaad5 100644 --- a/verification/src/changes/accepted-sponge-public-api-changes.json +++ b/verification/src/changes/accepted-sponge-public-api-changes.json @@ -6,6 +6,13 @@ "changes": [ "METHOD_REMOVED" ] + }, + { + "type": "com.sk89q.worldedit.sponge.SpongeBlockMaterial", + "member": "Method com.sk89q.worldedit.sponge.SpongeBlockMaterial.isFullCube()", + "changes": [ + "METHOD_REMOVED" + ] } ] } diff --git a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java index 2113b1ee1d..5bb65a1495 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_11; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java index 10c1f7d222..05b148498f 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_4; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java index bb44cd3fec..09c94b8283 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_5; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java index 8042660f50..3583e6edeb 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_6; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java index 1df238afb7..53e10e7be0 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v1_21_9; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java index 157d92e47f..3a41d2bdce 100644 --- a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v26_1; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java index cd738be493..f786b16b1c 100644 --- a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v26_2; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java index 858435ebc6..1bc32d45e3 100644 --- a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.v26_3; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,8 +28,10 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; -public class PaperweightBlockMaterial implements BlockMaterial { +public class PaperweightBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -41,9 +44,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java index a8d6a0b89d..1dfac8937f 100644 --- a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java +++ b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.coremc.internal; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,12 +28,14 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; /** * Minecraft block material implementation for platforms sharing native code. * Pulls as much info as possible from the Minecraft BlockState. */ -public final class CoreMcBlockMaterial implements BlockMaterial { +public final class CoreMcBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -45,9 +48,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ShapeType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ShapeType.java new file mode 100644 index 0000000000..56eb14e3fd --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ShapeType.java @@ -0,0 +1,32 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.blocks; + +public enum ShapeType { + /** + * Block shape that's used internally as a default/fallback for many of the others, mostly in less complex blocks. + */ + SHAPE, + + /** + * Block shape that most closely corresponds to what's visible or not. + */ + VISUAL_SHAPE, +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/FullCubeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/FullCubeMask.java index d7a8a8165b..6983c8cb05 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/FullCubeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/FullCubeMask.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.function.mask; +import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; @@ -33,7 +34,7 @@ public FullCubeMask(Extent extent) { public boolean test(BlockVector3 vector) { Extent extent = getExtent(); BlockState block = extent.getBlock(vector); - return block.getBlockType().getMaterial().isFullCube(); + return block.getBlockType().getMaterial().isFullCube(ShapeType.SHAPE); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java new file mode 100644 index 0000000000..fc113c4bbf --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java @@ -0,0 +1,47 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.block; + +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.util.concurrency.LazyReference; +import com.sk89q.worldedit.world.registry.BlockMaterial; + +import java.util.EnumSet; + +public abstract class AbstractBlockMaterial implements BlockMaterial { + @SuppressWarnings("this-escape") + public LazyReference> isFullCube = LazyReference.from(() -> { + EnumSet enumSet = EnumSet.noneOf(ShapeType.class); + for (ShapeType shapeType : ShapeType.values()) { + if (isFullCubeUncached(shapeType)) { + enumSet.add(shapeType); + } + } + + return enumSet; + }); + + @Override + public boolean isFullCube(ShapeType shapeType) { + return isFullCube.getValue().contains(shapeType); + } + + protected abstract boolean isFullCubeUncached(ShapeType shapeType); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java index 70682b18e7..47b6218c8d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.world.registry; +import com.sk89q.worldedit.blocks.ShapeType; + /** * Describes the material for a block. */ @@ -35,8 +37,20 @@ public interface BlockMaterial { * Get whether this block is a full sized cube. * * @return the value of the test + * @deprecated Use {@link BlockMaterial#isFullCube(ShapeType)} instead. + */ + @Deprecated + default boolean isFullCube() { + return isFullCube(ShapeType.SHAPE); + } + + /** + * Get whether this block is a full sized cube. + * + * @param shapeType which shape of the block to test + * @return the value of the test */ - boolean isFullCube(); + boolean isFullCube(ShapeType shapeType); /** * Get whether this block is opaque. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java index dc0a52fdab..4290a3144c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.world.registry; +import com.sk89q.worldedit.blocks.ShapeType; + import javax.annotation.Nullable; import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull; @@ -49,8 +51,8 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return blockMaterial.isFullCube(); + public boolean isFullCube(ShapeType shapeType) { + return blockMaterial.isFullCube(shapeType); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java index 36cab7e784..82d6420995 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.world.registry; +import com.sk89q.worldedit.blocks.ShapeType; + class SimpleBlockMaterial implements BlockMaterial { private boolean isAir; @@ -51,7 +53,7 @@ public void setIsAir(boolean isAir) { } @Override - public boolean isFullCube() { + public boolean isFullCube(ShapeType shapeType) { return fullCube; } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java index 1c6bdb9bbe..dd4bd27567 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.sponge; -import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -27,13 +28,15 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; /** * Sponge block material that pulls as much info as possible from the Minecraft * Material, and passes the rest to another implementation, typically the * bundled block info. */ -public class SpongeBlockMaterial implements BlockMaterial { +public class SpongeBlockMaterial extends AbstractBlockMaterial { private final BlockState block; @@ -46,9 +49,16 @@ public boolean isAir() { return block.isAir(); } + private VoxelShape getShape(ShapeType shapeType) { + return switch (shapeType) { + case SHAPE -> block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + case VISUAL_SHAPE -> block.getVisualShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty()); + }; + } + @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected boolean isFullCubeUncached(ShapeType shapeType) { + return Block.isShapeFullBlock(getShape(shapeType)); } @Override From 606780031df2d9016415cf48a36ec897fdb1be6f Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Fri, 24 Apr 2026 23:49:57 +0200 Subject: [PATCH 03/14] Add BlockMaterial.isFullFace --- .../impl/v1_21_11/PaperweightAdapter.java | 2 +- .../v1_21_11/PaperweightBlockMaterial.java | 6 ++++ .../impl/v1_21_4/PaperweightAdapter.java | 2 +- .../v1_21_4/PaperweightBlockMaterial.java | 6 ++++ .../impl/v1_21_5/PaperweightAdapter.java | 2 +- .../v1_21_5/PaperweightBlockMaterial.java | 6 ++++ .../impl/v1_21_6/PaperweightAdapter.java | 2 +- .../v1_21_6/PaperweightBlockMaterial.java | 6 ++++ .../impl/v1_21_9/PaperweightAdapter.java | 2 +- .../v1_21_9/PaperweightBlockMaterial.java | 6 ++++ .../impl/v26_1/PaperweightAdapter.java | 2 +- .../impl/v26_1/PaperweightBlockMaterial.java | 6 ++++ .../impl/v26_2/PaperweightAdapter.java | 2 +- .../impl/v26_2/PaperweightBlockMaterial.java | 6 ++++ .../impl/v26_3/PaperweightAdapter.java | 2 +- .../impl/v26_3/PaperweightBlockMaterial.java | 6 ++++ .../sk89q/worldedit/coremc/CoreMcAdapter.java | 10 ++----- .../coremc/internal/CoreMcBlockMaterial.java | 6 ++++ .../coremc/internal/CoreMcTransmogrifier.java | 13 ++++++++- .../internal/block/AbstractBlockMaterial.java | 28 +++++++++++++++++++ .../world/registry/BlockMaterial.java | 12 ++++++++ .../registry/PassthroughBlockMaterial.java | 6 ++++ .../sk89q/worldedit/sponge/SpongeAdapter.java | 11 ++++++++ .../worldedit/sponge/SpongeBlockMaterial.java | 6 ++++ 24 files changed, 139 insertions(+), 17 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java index b7f126ff87..40e8d4d2fc 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightAdapter.java @@ -460,7 +460,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java index 5bb65a1495..fd8cd69023 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_11/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java index 41289efb9b..9211818ff6 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightAdapter.java @@ -448,7 +448,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java index 05b148498f..65f7991cb3 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_4/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java index f0ef8d9104..f5dd9280de 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightAdapter.java @@ -448,7 +448,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java index 09c94b8283..765ead0ec6 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_5/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java index 8044db88e9..36d53b47a9 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightAdapter.java @@ -461,7 +461,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java index 3583e6edeb..2a87ddfe25 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_6/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java index fb1f9db555..832552c599 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightAdapter.java @@ -460,7 +460,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java index 53e10e7be0..a819a60ddf 100644 --- a/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1.21.9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_21_9/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java index fb020c628a..2f3f3b2784 100644 --- a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightAdapter.java @@ -478,7 +478,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java index 3a41d2bdce..d0a6e090fc 100644 --- a/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-26.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_1/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java index 14c95ee043..255c9616ee 100644 --- a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightAdapter.java @@ -479,7 +479,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java index f786b16b1c..3c58335ac8 100644 --- a/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_2/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java index b016a05bc4..c488eb8e3e 100644 --- a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightAdapter.java @@ -489,7 +489,7 @@ public void setBiome(Location location, BiomeType biome) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } - private static net.minecraft.core.Direction adapt(Direction face) { + public static net.minecraft.core.Direction adapt(Direction face) { return switch (face) { case NORTH -> net.minecraft.core.Direction.NORTH; case SOUTH -> net.minecraft.core.Direction.SOUTH; diff --git a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java index 1bc32d45e3..1380615898 100644 --- a/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-26.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v26_3/PaperweightBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -56,6 +57,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), PaperweightAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/CoreMcAdapter.java b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/CoreMcAdapter.java index 913701596d..e6785b576c 100644 --- a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/CoreMcAdapter.java +++ b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/CoreMcAdapter.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.coremc.internal.CoreMcCommandSender; import com.sk89q.worldedit.coremc.internal.CoreMcPlatform; import com.sk89q.worldedit.coremc.internal.CoreMcPlayer; +import com.sk89q.worldedit.coremc.internal.CoreMcTransmogrifier; import com.sk89q.worldedit.coremc.internal.CoreMcWorld; import com.sk89q.worldedit.coremc.internal.NBTConverter; import com.sk89q.worldedit.coremc.mixin.AccessorCommandSourceStack; @@ -121,14 +122,7 @@ public Vec3 toVec3(BlockVector3 vector) { } public net.minecraft.core.Direction adapt(Direction face) { - return switch (face) { - case NORTH -> net.minecraft.core.Direction.NORTH; - case SOUTH -> net.minecraft.core.Direction.SOUTH; - case WEST -> net.minecraft.core.Direction.WEST; - case EAST -> net.minecraft.core.Direction.EAST; - case DOWN -> net.minecraft.core.Direction.DOWN; - default -> net.minecraft.core.Direction.UP; - }; + return CoreMcTransmogrifier.transmogToMinecraft(face); } public Direction adaptEnumFacing(@Nullable net.minecraft.core.Direction face) { diff --git a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java index 1dfac8937f..7ccb6c82c4 100644 --- a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java +++ b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -60,6 +61,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), CoreMcTransmogrifier.transmogToMinecraft(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); diff --git a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcTransmogrifier.java b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcTransmogrifier.java index e1945d8b4a..2ea045e46e 100644 --- a/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcTransmogrifier.java +++ b/worldedit-core-mc/src/main/java/com/sk89q/worldedit/coremc/internal/CoreMcTransmogrifier.java @@ -71,6 +71,17 @@ public CoreMcTransmogrifier(CoreMcPlatform platform) { this.platform = platform; } + public static net.minecraft.core.Direction transmogToMinecraft(Direction face) { + return switch (face) { + case NORTH -> net.minecraft.core.Direction.NORTH; + case SOUTH -> net.minecraft.core.Direction.SOUTH; + case WEST -> net.minecraft.core.Direction.WEST; + case EAST -> net.minecraft.core.Direction.EAST; + case DOWN -> net.minecraft.core.Direction.DOWN; + default -> net.minecraft.core.Direction.UP; + }; + } + public Property transmogToWorldEditProperty(net.minecraft.world.level.block.state.properties.Property property) { return propertyCache.getUnchecked(property); } @@ -104,7 +115,7 @@ private net.minecraft.world.level.block.state.BlockState transmogToMinecraftProp if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { if (property.getValueClass() == net.minecraft.core.Direction.class) { Direction dir = (Direction) value; - value = platform.getAdapter().adapt(dir); + value = transmogToMinecraft(dir); } else { String enumName = (String) value; value = ((net.minecraft.world.level.block.state.properties.EnumProperty) property).getValue((String) value).orElseThrow(() -> diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java index fc113c4bbf..a888c32daf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java @@ -20,9 +20,11 @@ package com.sk89q.worldedit.internal.block; import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.registry.BlockMaterial; +import java.util.EnumMap; import java.util.EnumSet; public abstract class AbstractBlockMaterial implements BlockMaterial { @@ -38,10 +40,36 @@ public abstract class AbstractBlockMaterial implements BlockMaterial { return enumSet; }); + @SuppressWarnings("this-escape") + public LazyReference>> isFullFace = LazyReference.from(() -> { + EnumMap> enumMap = new EnumMap<>(Direction.class); + for (Direction face : Direction.values()) { + if (!face.isUpright() && !face.isCardinal()) { + continue; + } + + EnumSet enumSet = EnumSet.noneOf(ShapeType.class); + for (ShapeType shapeType : ShapeType.values()) { + if (isFullFaceUncached(shapeType, face)) { + enumSet.add(shapeType); + } + } + enumMap.put(face, enumSet); + } + return enumMap; + }); + @Override public boolean isFullCube(ShapeType shapeType) { return isFullCube.getValue().contains(shapeType); } + @Override + public boolean isFullFace(ShapeType shapeType, Direction face) { + return isFullFace.getValue().get(face).contains(shapeType); + } + protected abstract boolean isFullCubeUncached(ShapeType shapeType); + + protected abstract boolean isFullFaceUncached(ShapeType shapeType, Direction face); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java index 47b6218c8d..76c019b968 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.world.registry; import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.util.Direction; /** * Describes the material for a block. @@ -52,6 +53,17 @@ default boolean isFullCube() { */ boolean isFullCube(ShapeType shapeType); + /** + * Get whether this face is a full sized face. + * + * @param shapeType which shape of the block to test + * @param face the face to test + * @return the value of the test + */ + default boolean isFullFace(ShapeType shapeType, Direction face) { + return isFullCube(shapeType); + } + /** * Get whether this block is opaque. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java index 4290a3144c..bdc736e207 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.world.registry; import com.sk89q.worldedit.blocks.ShapeType; +import com.sk89q.worldedit.util.Direction; import javax.annotation.Nullable; @@ -55,6 +56,11 @@ public boolean isFullCube(ShapeType shapeType) { return blockMaterial.isFullCube(shapeType); } + @Override + public boolean isFullFace(ShapeType shapeType, Direction face) { + return blockMaterial.isFullFace(shapeType, face); + } + @Override public boolean isOpaque() { return blockMaterial.isOpaque(); diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java index 8339c5321f..a4200702ae 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java @@ -235,6 +235,17 @@ public static Direction adapt(org.spongepowered.api.util.Direction direction) { return Direction.valueOf(direction.name()); } + public static net.minecraft.core.Direction adapt(Direction face) { + return switch (face) { + case NORTH -> net.minecraft.core.Direction.NORTH; + case SOUTH -> net.minecraft.core.Direction.SOUTH; + case WEST -> net.minecraft.core.Direction.WEST; + case EAST -> net.minecraft.core.Direction.EAST; + case DOWN -> net.minecraft.core.Direction.DOWN; + default -> net.minecraft.core.Direction.UP; + }; + } + public static Vector3i adaptVector3i(BlockVector3 bv3) { return new Vector3i(bv3.x(), bv3.y(), bv3.z()); } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java index dd4bd27567..37c53170fd 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.internal.block.AbstractBlockMaterial; +import com.sk89q.worldedit.util.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.Clearable; import net.minecraft.world.level.EmptyBlockGetter; @@ -61,6 +62,11 @@ protected boolean isFullCubeUncached(ShapeType shapeType) { return Block.isShapeFullBlock(getShape(shapeType)); } + @Override + protected boolean isFullFaceUncached(ShapeType shapeType, Direction face) { + return Block.isFaceFull(getShape(shapeType), SpongeAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); From 30c8a4a9c8f771d2bc05b05622db55d94f266e06 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 00:17:19 +0200 Subject: [PATCH 04/14] Only iterate once, after queueing all blocks --- .../java/com/sk89q/worldedit/EditSession.java | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 7b9ba8adb3..682f7db772 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2668,6 +2668,8 @@ public int deformRegion(final Region region, final Transform targetTransform, fi public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { int affected = 0; + // Initialize BFS with selection bounding box + var queue = new ArrayDeque(); final Set outside = new HashSet<>(); final BlockVector3 min = region.getMinimumPoint(); @@ -2682,25 +2684,47 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { - recurseHollow(region, BlockVector3.at(x, y, minZ), outside); - recurseHollow(region, BlockVector3.at(x, y, maxZ), outside); + queue.addLast(BlockVector3.at(x, y, minZ)); + queue.addLast(BlockVector3.at(x, y, maxZ)); } } for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { - recurseHollow(region, BlockVector3.at(minX, y, z), outside); - recurseHollow(region, BlockVector3.at(maxX, y, z), outside); + queue.addLast(BlockVector3.at(minX, y, z)); + queue.addLast(BlockVector3.at(maxX, y, z)); } } for (int z = minZ; z <= maxZ; ++z) { for (int x = minX; x <= maxX; ++x) { - recurseHollow(region, BlockVector3.at(x, minY, z), outside); - recurseHollow(region, BlockVector3.at(x, maxY, z), outside); + queue.addLast(BlockVector3.at(x, minY, z)); + queue.addLast(BlockVector3.at(x, maxY, z)); + } + } + + // Do BFS to find visible blocks + while (!queue.isEmpty()) { + final BlockVector3 current = queue.poll(); + final BlockState block = getBlock(current); + if (block.getBlockType().getMaterial().isSolid()) { + continue; + } + + if (!outside.add(current)) { + continue; + } + + if (!region.contains(current)) { + continue; + } + + for (BlockVector3 recurseDirection : recurseDirections) { + queue.add(current.add(recurseDirection)); } } + // Expand by $thickness blocks final Set newOutside = new HashSet<>(); for (int i = 1; i < thickness; ++i) { outer: for (BlockVector3 position : region) { @@ -2718,6 +2742,7 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws newOutside.clear(); } + // Remove everything else in the selection outer: for (BlockVector3 position : region) { for (BlockVector3 recurseDirection : recurseDirections) { BlockVector3 neighbor = position.add(recurseDirection); @@ -2915,31 +2940,6 @@ private static Set getHollowed(Set vset) { return returnset; } - private void recurseHollow(Region region, BlockVector3 origin, Set outside) { - var queue = new ArrayDeque(); - queue.addLast(origin); - - while (!queue.isEmpty()) { - final BlockVector3 current = queue.removeFirst(); - final BlockState block = getBlock(current); - if (block.getBlockType().getMaterial().isSolid()) { - continue; - } - - if (!outside.add(current)) { - continue; - } - - if (!region.contains(current)) { - continue; - } - - for (BlockVector3 recurseDirection : recurseDirections) { - queue.addLast(current.add(recurseDirection)); - } - } - } - /** * Generate a biome shape for the given expression. * From 94daaf58e43d1fd865bf820f2a5fc0315d7f3d89 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 12:14:18 +0200 Subject: [PATCH 05/14] Use Queue interface instead of a concrete type --- .../java/com/sk89q/worldedit/EditSession.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 682f7db772..9d1290dfb1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -140,6 +140,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; @@ -2669,7 +2670,7 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws int affected = 0; // Initialize BFS with selection bounding box - var queue = new ArrayDeque(); + final Queue queue = new ArrayDeque<>(); final Set outside = new HashSet<>(); final BlockVector3 min = region.getMinimumPoint(); @@ -2684,22 +2685,22 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { - queue.addLast(BlockVector3.at(x, y, minZ)); - queue.addLast(BlockVector3.at(x, y, maxZ)); + queue.add(BlockVector3.at(x, y, minZ)); + queue.add(BlockVector3.at(x, y, maxZ)); } } for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { - queue.addLast(BlockVector3.at(minX, y, z)); - queue.addLast(BlockVector3.at(maxX, y, z)); + queue.add(BlockVector3.at(minX, y, z)); + queue.add(BlockVector3.at(maxX, y, z)); } } for (int z = minZ; z <= maxZ; ++z) { for (int x = minX; x <= maxX; ++x) { - queue.addLast(BlockVector3.at(x, minY, z)); - queue.addLast(BlockVector3.at(x, maxY, z)); + queue.add(BlockVector3.at(x, minY, z)); + queue.add(BlockVector3.at(x, maxY, z)); } } From 15be1efa55332aaf8b3a193b687c8a56570e4976 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 12:04:15 +0200 Subject: [PATCH 06/14] Determine "visible" instead of "outside" blocks Previously, the "Remove everything else in the selection" section had an extra recursion step to get from the "outside" blocks to the "visible" blocks. --- .../java/com/sk89q/worldedit/EditSession.java | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 9d1290dfb1..ae8b4709a2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2669,10 +2669,9 @@ public int deformRegion(final Region region, final Transform targetTransform, fi public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { int affected = 0; - // Initialize BFS with selection bounding box - final Queue queue = new ArrayDeque<>(); - final Set outside = new HashSet<>(); + final Set visible = new HashSet<>(); + // Initialize BFS with selection bounding box final BlockVector3 min = region.getMinimumPoint(); final BlockVector3 max = region.getMaximumPoint(); @@ -2685,72 +2684,75 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { - queue.add(BlockVector3.at(x, y, minZ)); - queue.add(BlockVector3.at(x, y, maxZ)); + visible.add(BlockVector3.at(x, y, minZ)); + visible.add(BlockVector3.at(x, y, maxZ)); } } for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { - queue.add(BlockVector3.at(minX, y, z)); - queue.add(BlockVector3.at(maxX, y, z)); + visible.add(BlockVector3.at(minX, y, z)); + visible.add(BlockVector3.at(maxX, y, z)); } } for (int z = minZ; z <= maxZ; ++z) { for (int x = minX; x <= maxX; ++x) { - queue.add(BlockVector3.at(x, minY, z)); - queue.add(BlockVector3.at(x, maxY, z)); + visible.add(BlockVector3.at(x, minY, z)); + visible.add(BlockVector3.at(x, maxY, z)); } } - // Do BFS to find visible blocks + // Remove movement blockers from visible list + visible.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isSolid()); + + // Do BFS to find more visible blocks + final Queue queue = new ArrayDeque<>(visible); while (!queue.isEmpty()) { final BlockVector3 current = queue.poll(); + final BlockState block = getBlock(current); if (block.getBlockType().getMaterial().isSolid()) { continue; } - if (!outside.add(current)) { - continue; - } + for (BlockVector3 recurseDirection : recurseDirections) { + final BlockVector3 neighbor = current.add(recurseDirection); - if (!region.contains(current)) { - continue; - } + if (!region.contains(neighbor)) { + continue; + } - for (BlockVector3 recurseDirection : recurseDirections) { - queue.add(current.add(recurseDirection)); + if (!visible.add(neighbor)) { + continue; + } + + queue.add(neighbor); } } // Expand by $thickness blocks - final Set newOutside = new HashSet<>(); + final Set newVisible = new HashSet<>(); for (int i = 1; i < thickness; ++i) { outer: for (BlockVector3 position : region) { for (BlockVector3 recurseDirection : recurseDirections) { BlockVector3 neighbor = position.add(recurseDirection); - if (outside.contains(neighbor)) { - newOutside.add(position); + if (visible.contains(neighbor)) { + newVisible.add(position); continue outer; } } } - outside.addAll(newOutside); - newOutside.clear(); + visible.addAll(newVisible); + newVisible.clear(); } // Remove everything else in the selection - outer: for (BlockVector3 position : region) { - for (BlockVector3 recurseDirection : recurseDirections) { - BlockVector3 neighbor = position.add(recurseDirection); - - if (outside.contains(neighbor)) { - continue outer; - } + for (BlockVector3 position : region) { + if (visible.contains(position)) { + continue; } if (setBlock(position, pattern.applyBlock(position))) { From 1c6637f25aebc92439c4a035c9b9481c580a2d81 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 02:33:26 +0200 Subject: [PATCH 07/14] //hollow: Limit thickness to >= 1 Thickness 0 behaved like thickness 1 --- .../main/java/com/sk89q/worldedit/command/RegionCommands.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index e258169d62..841f1b1032 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -550,11 +550,11 @@ public int deform(Actor actor, LocalSession session, EditSession editSession, @Logging(REGION) public int hollow(Actor actor, EditSession editSession, @Selection Region region, - @Arg(desc = "Thickness of the shell to leave", def = "0") + @Arg(desc = "Thickness of the shell to leave", def = "1") int thickness, @Arg(desc = "The pattern of blocks to replace the hollowed area with", def = "air") Pattern pattern) throws WorldEditException { - checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); + checkCommandArgument(thickness >= 1, "Thickness must be >= 1"); int affected = editSession.hollowOutRegion(region, thickness, pattern); actor.printInfo(TranslatableComponent.of("worldedit.hollow.changed", TextComponent.of(affected))); From 45182f1c08efe19f7e61fdec30fc2d432783e6bf Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 13:06:04 +0200 Subject: [PATCH 08/14] Rename recurseDirections to CARDINAL_UPRIGHT_OFFSETS and determine it algorithmically --- .../java/com/sk89q/worldedit/EditSession.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index ae8b4709a2..9a5aca229d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -134,6 +134,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -2716,7 +2717,7 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws continue; } - for (BlockVector3 recurseDirection : recurseDirections) { + for (BlockVector3 recurseDirection : CARDINAL_UPRIGHT_OFFSETS) { final BlockVector3 neighbor = current.add(recurseDirection); if (!region.contains(neighbor)) { @@ -2735,7 +2736,7 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws final Set newVisible = new HashSet<>(); for (int i = 1; i < thickness; ++i) { outer: for (BlockVector3 position : region) { - for (BlockVector3 recurseDirection : recurseDirections) { + for (BlockVector3 recurseDirection : CARDINAL_UPRIGHT_OFFSETS) { BlockVector3 neighbor = position.add(recurseDirection); if (visible.contains(neighbor)) { @@ -3074,7 +3075,7 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int totalFaces = 0; highestFreq = 0; highestState = blockState; - for (BlockVector3 vec3 : recurseDirections) { + for (BlockVector3 vec3 : CARDINAL_UPRIGHT_OFFSETS) { BlockState adj = currentBuffer[x + 1 + vec3.x()][y + 1 + vec3.y()][z + 1 + vec3.z()]; if (!adj.getBlockType().getMaterial().isLiquid() && !adj.getBlockType().getMaterial().isAir()) { @@ -3127,7 +3128,7 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int totalFaces = 0; highestFreq = 0; highestState = blockState; - for (BlockVector3 vec3 : recurseDirections) { + for (BlockVector3 vec3 : CARDINAL_UPRIGHT_OFFSETS) { BlockState adj = currentBuffer[x + 1 + vec3.x()][y + 1 + vec3.y()][z + 1 + vec3.z()]; if (adj.getBlockType().getMaterial().isLiquid() || adj.getBlockType().getMaterial().isAir()) { continue; @@ -3170,14 +3171,18 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int return changed; } - private static final BlockVector3[] recurseDirections = { - Direction.NORTH.toBlockVector(), - Direction.EAST.toBlockVector(), - Direction.SOUTH.toBlockVector(), - Direction.WEST.toBlockVector(), - Direction.UP.toBlockVector(), - Direction.DOWN.toBlockVector(), - }; + /** + * Contains all cardinal and upright directions. + */ + private static final Direction[] CARDINAL_UPRIGHT_DIRECTIONS = Arrays.stream(Direction.values()) + .filter(direction -> direction.isCardinal() || direction.isUpright()) + .toArray(Direction[]::new); + /** + * Contains the offset vectors for all cardinal and upright directions. + */ + private static final BlockVector3[] CARDINAL_UPRIGHT_OFFSETS = Arrays.stream(CARDINAL_UPRIGHT_DIRECTIONS) + .map(Direction::toBlockVector) + .toArray(BlockVector3[]::new); private static double lengthSq(double x, double y, double z) { return (x * x) + (y * y) + (z * z); From b224744a00a6f42c656ad62560bf70f2915c6a74 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 21 Jun 2026 11:20:10 +0200 Subject: [PATCH 09/14] Add comments --- .../src/main/java/com/sk89q/worldedit/EditSession.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 9a5aca229d..288f04b5dd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2668,8 +2668,10 @@ public int deformRegion(final Region region, final Transform targetTransform, fi * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { + // Number of affected blocks, based on what setBlock returns int affected = 0; + // The set of blocks that were seen final Set visible = new HashSet<>(); // Initialize BFS with selection bounding box @@ -2720,14 +2722,17 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws for (BlockVector3 recurseDirection : CARDINAL_UPRIGHT_OFFSETS) { final BlockVector3 neighbor = current.add(recurseDirection); + // Abort if we're outside the region if (!region.contains(neighbor)) { continue; } + // Mark the block as visible, abort if it already was if (!visible.add(neighbor)) { continue; } + // And queue it queue.add(neighbor); } } From 4092dc1dc7f88c5e346ee6cee2509eee9ac935ff Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 21 Jun 2026 11:30:47 +0200 Subject: [PATCH 10/14] Restructure EditSession.hollowOutRegion - Make a dedicated startingPositions Collection to initialize everything with - Move declarations closer to their usage --- .../java/com/sk89q/worldedit/EditSession.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 288f04b5dd..cd8ff384ad 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -135,6 +135,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -2668,11 +2669,8 @@ public int deformRegion(final Region region, final Transform targetTransform, fi * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { - // Number of affected blocks, based on what setBlock returns - int affected = 0; - - // The set of blocks that were seen - final Set visible = new HashSet<>(); + // Positions to consider as 'outside' + Collection startingPositions = new ArrayList<>(); // Initialize BFS with selection bounding box final BlockVector3 min = region.getMinimumPoint(); @@ -2687,30 +2685,33 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { - visible.add(BlockVector3.at(x, y, minZ)); - visible.add(BlockVector3.at(x, y, maxZ)); + startingPositions.add(BlockVector3.at(x, y, minZ)); + startingPositions.add(BlockVector3.at(x, y, maxZ)); } } for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { - visible.add(BlockVector3.at(minX, y, z)); - visible.add(BlockVector3.at(maxX, y, z)); + startingPositions.add(BlockVector3.at(minX, y, z)); + startingPositions.add(BlockVector3.at(maxX, y, z)); } } for (int z = minZ; z <= maxZ; ++z) { for (int x = minX; x <= maxX; ++x) { - visible.add(BlockVector3.at(x, minY, z)); - visible.add(BlockVector3.at(x, maxY, z)); + startingPositions.add(BlockVector3.at(x, minY, z)); + startingPositions.add(BlockVector3.at(x, maxY, z)); } } // Remove movement blockers from visible list - visible.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isSolid()); + startingPositions.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isSolid()); + + // The set of blocks that were seen + final Set visible = new HashSet<>(startingPositions); // Do BFS to find more visible blocks - final Queue queue = new ArrayDeque<>(visible); + final Queue queue = new ArrayDeque<>(startingPositions); while (!queue.isEmpty()) { final BlockVector3 current = queue.poll(); @@ -2755,6 +2756,9 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws newVisible.clear(); } + // Number of affected blocks, based on what setBlock returns + int affected = 0; + // Remove everything else in the selection for (BlockVector3 position : region) { if (visible.contains(position)) { From 3fc3c22e5bc7c3aabaf9399be14ed14c348a5b06 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Tue, 27 Oct 2026 00:10:27 +0100 Subject: [PATCH 11/14] Improve //hollow command - Add -p flag to iterate from placement position instead of the selection boundaries - Add -g flag to consider geometry in visibility calculations - By default, //hollow will no longer open up faces that are touching the selection bounding box. The old behaviour can be access with the -o flag. --- .../accepted-core-public-api-changes.json | 10 + .../java/com/sk89q/worldedit/EditSession.java | 215 +++++++++++++++--- .../worldedit/command/RegionCommands.java | 35 ++- 3 files changed, 222 insertions(+), 38 deletions(-) diff --git a/verification/src/changes/accepted-core-public-api-changes.json b/verification/src/changes/accepted-core-public-api-changes.json index 64db03f327..92aaf4f0df 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -148,5 +148,15 @@ "METHOD_REMOVED" ] } + ], + "//hollow overhaul": [ + { + "type": "com.sk89q.worldedit.EditSession", + "member": "Method com.sk89q.worldedit.EditSession.hollowOutRegion(com.sk89q.worldedit.regions.Region,int,com.sk89q.worldedit.function.pattern.Pattern)", + "changes": [ + "METHOD_NOW_FINAL", + "ANNOTATION_DEPRECATED_ADDED" + ] + } ] } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index cd8ff384ad..02260e5755 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.InlineMe; +import com.sk89q.worldedit.blocks.ShapeType; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.event.extent.EditSessionEvent; @@ -129,6 +130,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.generation.TreeType; +import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.LegacyMapper; import org.apache.logging.log4j.Logger; @@ -2658,6 +2660,46 @@ public int deformRegion(final Region region, final Transform targetTransform, fi return affected; } + private Direction[] getBlockedDirections(BlockVector3 position) { + BlockState blockState = getBlock(position); + BlockType blockType = blockState.getBlockType(); + BlockMaterial material = blockState.getMaterial(); + + if (material.isAir()) { + return NO_DIRECTIONS; + } + + if (material.isLiquid()) { + return NO_DIRECTIONS; + } + + Direction[] blockedDirections = BLOCKED_DIRECTIONS_OVERRIDE.get(blockType); + if (blockedDirections != null) { + return blockedDirections; + } + + if (material.isFullCube(ShapeType.VISUAL_SHAPE)) { + return CARDINAL_UPRIGHT_DIRECTIONS; + } + + Direction[] result = new Direction[CARDINAL_UPRIGHT_DIRECTIONS.length]; + int count = 0; + + for (Direction direction : CARDINAL_UPRIGHT_DIRECTIONS) { + if (material.isFullFace(ShapeType.VISUAL_SHAPE, direction)) { + result[count++] = direction; + } + } + + // Short-cut for blocks that are blocked in all directions but for some reason not isFullCube + // This enables == comparison later + if (count == 6) { + return CARDINAL_UPRIGHT_DIRECTIONS; + } + + return Arrays.copyOf(result, count); + } + /** * Hollows out the region (Semi-well-defined for non-cuboid selections). * @@ -2667,46 +2709,70 @@ public int deformRegion(final Region region, final Transform targetTransform, fi * * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @deprecated Use {@link EditSession#hollowOutRegion(Region, int, Pattern, boolean, Collection, boolean)} instead. + */ + @InlineMe(replacement = "this.hollowOutRegion(region, thickness, pattern, true, null, false)") + @Deprecated + public final int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { + return hollowOutRegion(region, thickness, pattern, true, null, false); + } + + /** + * Hollows out the region (bounding-box mode is semi-well-defined for non-cuboid selections). + * The startingPositions parameter takes precedence over usePlacementPosition. + * + * @param region the region to hollow out. + * @param thickness the thickness of the shell to leave (manhattan distance) + * @param pattern The block pattern to use + * @param openSides Open up faces touching the bounding box. This matches the legacy behaviour. + * @param startingPositions Positions to consider as 'outside'. If null, use the selection bounding box + * @param useBlockGeometry Consider block geometry for visibility calculation + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { - // Positions to consider as 'outside' - Collection startingPositions = new ArrayList<>(); + public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolean openSides, Collection startingPositions, boolean useBlockGeometry) throws MaxChangedBlocksException { + if (startingPositions == null) { + // If no startingPositions are specified, use the selection bounding box + startingPositions = new ArrayList<>(); - // Initialize BFS with selection bounding box - final BlockVector3 min = region.getMinimumPoint(); - final BlockVector3 max = region.getMaximumPoint(); + // Initialize BFS with selection bounding box + final BlockVector3 min = region.getMinimumPoint(); + final BlockVector3 max = region.getMaximumPoint(); - final int minX = min.x(); - final int minY = min.y(); - final int minZ = min.z(); - final int maxX = max.x(); - final int maxY = max.y(); - final int maxZ = max.z(); + final int minX = min.x(); + final int minY = min.y(); + final int minZ = min.z(); + final int maxX = max.x(); + final int maxY = max.y(); + final int maxZ = max.z(); + + for (int x = minX; x <= maxX; ++x) { + for (int y = minY; y <= maxY; ++y) { + startingPositions.add(BlockVector3.at(x, y, minZ)); + startingPositions.add(BlockVector3.at(x, y, maxZ)); + } + } - for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { - startingPositions.add(BlockVector3.at(x, y, minZ)); - startingPositions.add(BlockVector3.at(x, y, maxZ)); + for (int z = minZ; z <= maxZ; ++z) { + startingPositions.add(BlockVector3.at(minX, y, z)); + startingPositions.add(BlockVector3.at(maxX, y, z)); + } } - } - for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { - startingPositions.add(BlockVector3.at(minX, y, z)); - startingPositions.add(BlockVector3.at(maxX, y, z)); + for (int x = minX; x <= maxX; ++x) { + startingPositions.add(BlockVector3.at(x, minY, z)); + startingPositions.add(BlockVector3.at(x, maxY, z)); + } } - } - for (int z = minZ; z <= maxZ; ++z) { - for (int x = minX; x <= maxX; ++x) { - startingPositions.add(BlockVector3.at(x, minY, z)); - startingPositions.add(BlockVector3.at(x, maxY, z)); + if (openSides) { + // Remove movement blockers from visible list + startingPositions.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isSolid()); } } - // Remove movement blockers from visible list - startingPositions.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isSolid()); - // The set of blocks that were seen final Set visible = new HashSet<>(startingPositions); @@ -2715,13 +2781,36 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws while (!queue.isEmpty()) { final BlockVector3 current = queue.poll(); - final BlockState block = getBlock(current); - if (block.getBlockType().getMaterial().isSolid()) { - continue; + Direction[] blockedDirections; + if (useBlockGeometry) { + // Get blocked directions for this block + blockedDirections = getBlockedDirections(current); + + // Short-cut if blocked in all directions + if (blockedDirections == CARDINAL_UPRIGHT_DIRECTIONS) { + continue; + } + } else { + final BlockState block = getBlock(current); + if (block.getBlockType().getMaterial().isSolid()) { + // In non-geometry mode, all solid blocks are assumed to be blocked in all directions, so short-cut here + continue; + } + + // All other blocks are assumed to have no blocked directions + blockedDirections = NO_DIRECTIONS; } - for (BlockVector3 recurseDirection : CARDINAL_UPRIGHT_OFFSETS) { - final BlockVector3 neighbor = current.add(recurseDirection); + outer: + for (Direction direction : CARDINAL_UPRIGHT_DIRECTIONS) { + // Check if this direction is blocked + for (Direction blockedDirection : blockedDirections) { + if (blockedDirection == direction) { + continue outer; // skip this direction + } + } + + final BlockVector3 neighbor = current.add(direction.toBlockVector()); // Abort if we're outside the region if (!region.contains(neighbor)) { @@ -3180,6 +3269,10 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int return changed; } + /** + * Contains no directions. + */ + private static final Direction[] NO_DIRECTIONS = {}; /** * Contains all cardinal and upright directions. */ @@ -3193,6 +3286,64 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int .map(Direction::toBlockVector) .toArray(BlockVector3[]::new); + /** + * Some blocks need a special case for one reason or another. + */ + private static final Map BLOCKED_DIRECTIONS_OVERRIDE = new HashMap<>(); + + static { + Arrays.asList( + // fullcubes that you can see through on all 6 sides + BlockTypes.SPAWNER, + BlockTypes.BEACON, + BlockTypes.MANGROVE_ROOTS, + BlockTypes.ICE, + + // You can see through some of the doors/trapdoors, which is not reflected in their visual shape + // Commented lines are opaque and kept for reference + BlockTypes.OAK_DOOR, + BlockTypes.OAK_TRAPDOOR, + // BlockTypes.SPRUCE_DOOR, + // BlockTypes.SPRUCE_TRAPDOOR, + // BlockTypes.BIRCH_DOOR, + // BlockTypes.BIRCH_TRAPDOOR, + BlockTypes.JUNGLE_DOOR, + BlockTypes.JUNGLE_TRAPDOOR, + BlockTypes.ACACIA_DOOR, + BlockTypes.ACACIA_TRAPDOOR, + // BlockTypes.DARK_OAK_DOOR, + // BlockTypes.DARK_OAK_TRAPDOOR, + // BlockTypes.MANGROVE_DOOR, + BlockTypes.MANGROVE_TRAPDOOR, + BlockTypes.CHERRY_DOOR, + BlockTypes.CHERRY_TRAPDOOR, + // BlockTypes.PALE_OAK_DOOR, + // BlockTypes.PALE_OAK_TRAPDOOR, + BlockTypes.BAMBOO_DOOR, + BlockTypes.BAMBOO_TRAPDOOR, + BlockTypes.CRIMSON_DOOR, + BlockTypes.CRIMSON_TRAPDOOR, + BlockTypes.WARPED_DOOR, + BlockTypes.WARPED_TRAPDOOR, + BlockTypes.IRON_DOOR, + BlockTypes.IRON_TRAPDOOR + ).forEach(blockType -> { + BLOCKED_DIRECTIONS_OVERRIDE.put(blockType, NO_DIRECTIONS); + }); + + // The copper doors/trapdoors are too many to list individually + java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("^.*:(?:.*_)?(?:copper_door|copper_trapdoor)(?:_.*)?$"); + for (BlockType blockType : BlockType.REGISTRY) { + if (pattern.matcher(blockType.id()).matches()) { + BLOCKED_DIRECTIONS_OVERRIDE.put(blockType, NO_DIRECTIONS); + } + } + + // These are visual fullcubes, but they're mostly open: + BLOCKED_DIRECTIONS_OVERRIDE.put(BlockTypes.TRIAL_SPAWNER, new Direction[] { Direction.UP }); + BLOCKED_DIRECTIONS_OVERRIDE.put(BlockTypes.VAULT, new Direction[] { Direction.UP, Direction.DOWN }); + } + private static double lengthSq(double x, double y, double z) { return (x * x) + (y * y) + (z * z); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 841f1b1032..c1510291da 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -79,8 +79,11 @@ import org.enginehub.piston.annotation.param.Arg; import org.enginehub.piston.annotation.param.ArgFlag; import org.enginehub.piston.annotation.param.Switch; +import org.enginehub.piston.exception.CommandException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; @@ -543,20 +546,40 @@ public int deform(Actor actor, LocalSession session, EditSession editSession, @Command( name = "/hollow", - desc = "Hollows out the object contained in this selection", - descFooter = "Thickness is measured in manhattan distance." + desc = "Hollows out the object contained in this selection" ) @CommandPermissions("worldedit.region.hollow") @Logging(REGION) - public int hollow(Actor actor, EditSession editSession, + public int hollow(Actor actor, EditSession editSession, LocalSession session, @Selection Region region, - @Arg(desc = "Thickness of the shell to leave", def = "1") + @Arg(desc = "Thickness of the shell to leave, measured in manhattan distance", def = "1") int thickness, @Arg(desc = "The pattern of blocks to replace the hollowed area with", def = "air") - Pattern pattern) throws WorldEditException { + Pattern pattern, + @Switch(name = 'o', desc = "Open up faces touching the bounding box. This matches the legacy behaviour.") + boolean openSides, + @Switch(name = 'p', desc = "Consider placement position as 'outside' instead of the selection bounding box. Overrides -o.") + boolean usePlacementPosition, + @Switch(name = 'g', desc = "Consider block geometry for visibility calculation") + boolean useBlockGeometry) throws WorldEditException { checkCommandArgument(thickness >= 1, "Thickness must be >= 1"); - int affected = editSession.hollowOutRegion(region, thickness, pattern); + final Collection startingPositions; + if (usePlacementPosition) { + BlockVector3 placementPosition = session.getPlacementPosition(actor); + if (!region.contains(placementPosition)) { + throw new CommandException( + TextComponent.of("Placement position must be inside selection (") + .append(session.getPlacement().getInfo()) + .append(TextComponent.of(")")), + ImmutableList.of() + ); + } + startingPositions = Collections.singletonList(placementPosition); + } else { + startingPositions = null; + } + int affected = editSession.hollowOutRegion(region, thickness, pattern, openSides, startingPositions, useBlockGeometry); actor.printInfo(TranslatableComponent.of("worldedit.hollow.changed", TextComponent.of(affected))); return affected; } From c5ff2c9c4fd704e5e3f545ed17f2859c9efb1501 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 27 Jun 2026 16:55:45 +0200 Subject: [PATCH 12/14] Remove hardcoded overrides --- .../java/com/sk89q/worldedit/EditSession.java | 64 ------------------- 1 file changed, 64 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 02260e5755..a813afaac9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2662,7 +2662,6 @@ public int deformRegion(final Region region, final Transform targetTransform, fi private Direction[] getBlockedDirections(BlockVector3 position) { BlockState blockState = getBlock(position); - BlockType blockType = blockState.getBlockType(); BlockMaterial material = blockState.getMaterial(); if (material.isAir()) { @@ -2673,11 +2672,6 @@ private Direction[] getBlockedDirections(BlockVector3 position) { return NO_DIRECTIONS; } - Direction[] blockedDirections = BLOCKED_DIRECTIONS_OVERRIDE.get(blockType); - if (blockedDirections != null) { - return blockedDirections; - } - if (material.isFullCube(ShapeType.VISUAL_SHAPE)) { return CARDINAL_UPRIGHT_DIRECTIONS; } @@ -3286,64 +3280,6 @@ public int morph(BlockVector3 position, double brushSize, int minErodeFaces, int .map(Direction::toBlockVector) .toArray(BlockVector3[]::new); - /** - * Some blocks need a special case for one reason or another. - */ - private static final Map BLOCKED_DIRECTIONS_OVERRIDE = new HashMap<>(); - - static { - Arrays.asList( - // fullcubes that you can see through on all 6 sides - BlockTypes.SPAWNER, - BlockTypes.BEACON, - BlockTypes.MANGROVE_ROOTS, - BlockTypes.ICE, - - // You can see through some of the doors/trapdoors, which is not reflected in their visual shape - // Commented lines are opaque and kept for reference - BlockTypes.OAK_DOOR, - BlockTypes.OAK_TRAPDOOR, - // BlockTypes.SPRUCE_DOOR, - // BlockTypes.SPRUCE_TRAPDOOR, - // BlockTypes.BIRCH_DOOR, - // BlockTypes.BIRCH_TRAPDOOR, - BlockTypes.JUNGLE_DOOR, - BlockTypes.JUNGLE_TRAPDOOR, - BlockTypes.ACACIA_DOOR, - BlockTypes.ACACIA_TRAPDOOR, - // BlockTypes.DARK_OAK_DOOR, - // BlockTypes.DARK_OAK_TRAPDOOR, - // BlockTypes.MANGROVE_DOOR, - BlockTypes.MANGROVE_TRAPDOOR, - BlockTypes.CHERRY_DOOR, - BlockTypes.CHERRY_TRAPDOOR, - // BlockTypes.PALE_OAK_DOOR, - // BlockTypes.PALE_OAK_TRAPDOOR, - BlockTypes.BAMBOO_DOOR, - BlockTypes.BAMBOO_TRAPDOOR, - BlockTypes.CRIMSON_DOOR, - BlockTypes.CRIMSON_TRAPDOOR, - BlockTypes.WARPED_DOOR, - BlockTypes.WARPED_TRAPDOOR, - BlockTypes.IRON_DOOR, - BlockTypes.IRON_TRAPDOOR - ).forEach(blockType -> { - BLOCKED_DIRECTIONS_OVERRIDE.put(blockType, NO_DIRECTIONS); - }); - - // The copper doors/trapdoors are too many to list individually - java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("^.*:(?:.*_)?(?:copper_door|copper_trapdoor)(?:_.*)?$"); - for (BlockType blockType : BlockType.REGISTRY) { - if (pattern.matcher(blockType.id()).matches()) { - BLOCKED_DIRECTIONS_OVERRIDE.put(blockType, NO_DIRECTIONS); - } - } - - // These are visual fullcubes, but they're mostly open: - BLOCKED_DIRECTIONS_OVERRIDE.put(BlockTypes.TRIAL_SPAWNER, new Direction[] { Direction.UP }); - BLOCKED_DIRECTIONS_OVERRIDE.put(BlockTypes.VAULT, new Direction[] { Direction.UP, Direction.DOWN }); - } - private static double lengthSq(double x, double y, double z) { return (x * x) + (y * y) + (z * z); } From d0911430b84693b28fc6c1415b68b258ae4a5366 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 21 Jun 2026 10:52:57 +0200 Subject: [PATCH 13/14] Also consider the geometry of the entered block Previously, the algorithm could enter a top slab through the top side and leave through all other sides. --- .../java/com/sk89q/worldedit/EditSession.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index a813afaac9..665b61e560 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2770,6 +2770,9 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolea // The set of blocks that were seen final Set visible = new HashSet<>(startingPositions); + // The set of blocks that were not only seen, but entered + final Set entered = new HashSet<>(startingPositions); + // Do BFS to find more visible blocks final Queue queue = new ArrayDeque<>(startingPositions); while (!queue.isEmpty()) { @@ -2811,9 +2814,30 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolea continue; } - // Mark the block as visible, abort if it already was - if (!visible.add(neighbor)) { - continue; + if (useBlockGeometry) { + // Mark neighbor as visible + visible.add(neighbor); + + // If the neighbor was already entered, we can skip the expensive blocked calculation + if (entered.contains(neighbor)) { + continue; + } + + // Check if we can actually enter the block from here + Direction oppositeDirection = Direction.findClosest(direction.toVector().multiply(-1), Direction.Flag.ALL); + for (Direction blockedDirection : getBlockedDirections(neighbor)) { + if (blockedDirection == oppositeDirection) { + continue outer; // skip this direction + } + } + + // Mark neighbor as entered + entered.add(neighbor); + } else { + // Mark the block as visible, abort if it already was + if (!visible.add(neighbor)) { + continue; + } } // And queue it From 32ea1ef64d1d356b72dec87326e009575d8b1ff3 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 27 Jun 2026 15:06:16 +0200 Subject: [PATCH 14/14] Add flag to //hollow for blocks to consider for shell detection --- .../java/com/sk89q/worldedit/EditSession.java | 25 +++++++++++-------- .../worldedit/command/RegionCommands.java | 10 ++++++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 665b61e560..ea8bc6eb19 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2703,12 +2703,12 @@ private Direction[] getBlockedDirections(BlockVector3 position) { * * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed - * @deprecated Use {@link EditSession#hollowOutRegion(Region, int, Pattern, boolean, Collection, boolean)} instead. + * @deprecated Use {@link EditSession#hollowOutRegion(Region, int, Pattern, boolean, Collection, boolean, Mask)} instead. */ - @InlineMe(replacement = "this.hollowOutRegion(region, thickness, pattern, true, null, false)") + @InlineMe(replacement = "this.hollowOutRegion(region, thickness, pattern, true, null, false, Masks.alwaysTrue())", imports = "com.sk89q.worldedit.function.mask.Masks") @Deprecated public final int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { - return hollowOutRegion(region, thickness, pattern, true, null, false); + return hollowOutRegion(region, thickness, pattern, true, null, false, Masks.alwaysTrue()); } /** @@ -2721,10 +2721,11 @@ public final int hollowOutRegion(Region region, int thickness, Pattern pattern) * @param openSides Open up faces touching the bounding box. This matches the legacy behaviour. * @param startingPositions Positions to consider as 'outside'. If null, use the selection bounding box * @param useBlockGeometry Consider block geometry for visibility calculation + * @param includeMask Mask of blocks eligible for shell detection * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolean openSides, Collection startingPositions, boolean useBlockGeometry) throws MaxChangedBlocksException { + public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolean openSides, Collection startingPositions, boolean useBlockGeometry, Mask includeMask) throws MaxChangedBlocksException { if (startingPositions == null) { // If no startingPositions are specified, use the selection bounding box startingPositions = new ArrayList<>(); @@ -2779,7 +2780,9 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolea final BlockVector3 current = queue.poll(); Direction[] blockedDirections; - if (useBlockGeometry) { + if (!includeMask.test(current)) { + blockedDirections = NO_DIRECTIONS; + } else if (useBlockGeometry) { // Get blocked directions for this block blockedDirections = getBlockedDirections(current); @@ -2823,11 +2826,13 @@ public int hollowOutRegion(Region region, int thickness, Pattern pattern, boolea continue; } - // Check if we can actually enter the block from here - Direction oppositeDirection = Direction.findClosest(direction.toVector().multiply(-1), Direction.Flag.ALL); - for (Direction blockedDirection : getBlockedDirections(neighbor)) { - if (blockedDirection == oppositeDirection) { - continue outer; // skip this direction + if (includeMask.test(neighbor)) { + // Check if we can actually enter the block from here + Direction oppositeDirection = Direction.findClosest(direction.toVector().multiply(-1), Direction.Flag.ALL); + for (Direction blockedDirection : getBlockedDirections(neighbor)) { + if (blockedDirection == oppositeDirection) { + continue outer; // skip this direction + } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index c1510291da..f9a7bba718 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -43,6 +43,7 @@ import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.mask.NoiseFilter2D; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; @@ -561,7 +562,9 @@ public int hollow(Actor actor, EditSession editSession, LocalSession session, @Switch(name = 'p', desc = "Consider placement position as 'outside' instead of the selection bounding box. Overrides -o.") boolean usePlacementPosition, @Switch(name = 'g', desc = "Consider block geometry for visibility calculation") - boolean useBlockGeometry) throws WorldEditException { + boolean useBlockGeometry, + @ArgFlag(name = 'm', desc = "Set the mask of blocks eligible for shell detection") + Mask includeMask) throws WorldEditException { checkCommandArgument(thickness >= 1, "Thickness must be >= 1"); final Collection startingPositions; @@ -579,7 +582,10 @@ public int hollow(Actor actor, EditSession editSession, LocalSession session, } else { startingPositions = null; } - int affected = editSession.hollowOutRegion(region, thickness, pattern, openSides, startingPositions, useBlockGeometry); + if (includeMask == null) { + includeMask = Masks.alwaysTrue(); + } + int affected = editSession.hollowOutRegion(region, thickness, pattern, openSides, startingPositions, useBlockGeometry, includeMask); actor.printInfo(TranslatableComponent.of("worldedit.hollow.changed", TextComponent.of(affected))); return affected; }