From aacc119a35fb975a58014504a8cd1d58eb5eb840 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 19 Apr 2026 22:46:34 +0200 Subject: [PATCH 1/9] Add BlockState.getMaterial() --- .../accepted-bukkit-public-api-changes.json | 10 ++++++++ .../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 ++++ .../worldedit/bukkit/BukkitBlockRegistry.java | 17 ++++++------- .../bukkit/adapter/BukkitImplAdapter.java | 11 +++++++++ .../coremc/internal/CoreMcBlockRegistry.java | 5 ++-- .../worldedit/world/block/BlockState.java | 8 +++++++ .../worldedit/world/block/BlockType.java | 2 +- .../world/registry/BlockRegistry.java | 24 ++++++++++++++++++- .../world/registry/BundledBlockRegistry.java | 4 ++-- .../worldedit/sponge/SpongeBlockRegistry.java | 21 +++++++--------- 16 files changed, 114 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 fd21c268d8..1c50324290 100644 --- a/verification/src/changes/accepted-bukkit-public-api-changes.json +++ b/verification/src/changes/accepted-bukkit-public-api-changes.json @@ -8,5 +8,15 @@ "ANNOTATION_REMOVED" ] } + ], + "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-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 816930823e..05eedcc971 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 @@ -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.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 fcf1579cea..f6df427ea9 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 @@ -567,6 +567,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 4d48915dee..2a9d773be9 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 @@ -565,6 +565,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 b7fb495d5f..cc665289eb 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 @@ -587,6 +587,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 65692f02f5..13bccc7fe9 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 @@ -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-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 60f922d154..a3dbb4aa8f 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 @@ -603,6 +603,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 70cd562329..9147712967 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 @@ -35,7 +35,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) { @@ -47,15 +47,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 041b4872a9..daed9fafb8 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 @@ -161,6 +161,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 6d6119d0a4..cacae6e1c6 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 @@ -52,10 +52,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 cb94239af9..b11f207ad0 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 @@ -19,9 +19,12 @@ package com.sk89q.worldedit.world.block; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.concurrency.LazyReference; +import com.sk89q.worldedit.world.registry.BlockMaterial; import org.enginehub.linbus.tree.LinCompoundTag; import java.util.HashSet; @@ -74,6 +77,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 66fa710d4c..d5a4dc80b3 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") @Deprecated private final LazyReference name = LazyReference.from(() -> WorldEdit.getInstance().getPlatformManager() 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 12f6b51ca1..92692f1de9 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,7 @@ package com.sk89q.worldedit.world.registry; +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; @@ -59,9 +60,30 @@ default String getName(BlockType blockType) { * * @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) { + 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 714fb50839..9e91177761 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 @@ -69,8 +69,8 @@ public String getName(BlockType blockType) { @Nullable @Override - public BlockMaterial getMaterial(BlockType blockType) { - return new PassthroughBlockMaterial(BundledBlockData.getInstance().getMaterialById(blockType.id())); + public BlockMaterial getMaterial(BlockType blockState) { + return new PassthroughBlockMaterial(BundledBlockData.getInstance().getMaterialById(blockState.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 5c0434d36a..f304d8ac53 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 @@ -50,19 +50,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 9f893f89e5ddb949077178f72f7acfab22503f85 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sun, 3 May 2026 18:34:30 +0200 Subject: [PATCH 2/9] Add ShapeType parameter to BlockMaterial.isFullCube --- .../accepted-core-public-api-changes.json | 9 ++++ .../accepted-sponge-public-api-changes.json | 7 +++ .../v1_21_11/PaperweightBlockMaterial.java | 19 +++++-- .../v1_21_4/PaperweightBlockMaterial.java | 19 +++++-- .../v1_21_5/PaperweightBlockMaterial.java | 19 +++++-- .../v1_21_6/PaperweightBlockMaterial.java | 19 +++++-- .../v1_21_9/PaperweightBlockMaterial.java | 19 +++++-- .../impl/v26_1/PaperweightBlockMaterial.java | 19 +++++-- .../coremc/internal/CoreMcBlockMaterial.java | 19 +++++-- .../com/sk89q/worldedit/blocks/ShapeType.java | 25 ++++++++++ .../worldedit/function/mask/FullCubeMask.java | 3 +- .../internal/block/AbstractBlockMaterial.java | 49 +++++++++++++++++++ .../world/registry/BlockMaterial.java | 14 +++++- .../registry/PassthroughBlockMaterial.java | 6 ++- .../world/registry/SimpleBlockMaterial.java | 4 +- .../worldedit/sponge/SpongeBlockMaterial.java | 19 +++++-- 16 files changed, 232 insertions(+), 37 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 4876c13d16..bf1117def7 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -23,5 +23,14 @@ "METHOD_REMOVED" ] } + ], + "Why on earth is this even an issue?": [ + { + "type": "com.sk89q.worldedit.world.registry.BlockMaterial", + "member": "Method com.sk89q.worldedit.world.registry.BlockMaterial.isFullCube()", + "changes": [ + "METHOD_ABSTRACT_NOW_DEFAULT" + ] + } ] } 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..1d25507427 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; @@ -42,8 +45,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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..a51a9ea50a 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; @@ -42,8 +45,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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..242b18fdca 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; @@ -42,8 +45,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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..ae4c7c8d82 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; @@ -42,8 +45,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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..3c209aa5ef 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; @@ -42,8 +45,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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..3bce6dd911 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; @@ -42,8 +45,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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 16c5f731b5..92db91f309 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; @@ -46,8 +49,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @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..726aada042 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ShapeType.java @@ -0,0 +1,25 @@ +/* + * 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 { + SHAPE, + 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..7a4fbbb55a --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/AbstractBlockMaterial.java @@ -0,0 +1,49 @@ +/* + * 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 (isShapeFullBlock(getShape(shapeType))) { + enumSet.add(shapeType); + } + } + + return enumSet; + }); + + @Override + public boolean isFullCube(ShapeType shapeType) { + return isFullCube.getValue().contains(shapeType); + } + + protected abstract VS getShape(ShapeType shapeType); + + protected abstract boolean isShapeFullBlock(VS shape); +} 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 d280f2cc0e..c038845a5a 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. */ @@ -36,7 +38,17 @@ public interface BlockMaterial { * * @return the value of the test */ - boolean isFullCube(); + 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(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 e868276a63..a601bd362d 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 de70a38619..d2cc6c92d1 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 4dd6622de4..177dc0d2c5 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; @@ -47,8 +50,16 @@ public boolean isAir() { } @Override - public boolean isFullCube() { - return Block.isShapeFullBlock(block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + protected 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 + protected boolean isShapeFullBlock(VoxelShape shape) { + return Block.isShapeFullBlock(shape); } @Override From af07d5c64bbdec8a9d26ad46a604f6e3475c4e1b Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Fri, 24 Apr 2026 23:49:57 +0200 Subject: [PATCH 3/9] 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 ++++ .../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 ++++ 20 files changed, 125 insertions(+), 15 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 05eedcc971..2b9efe661a 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 @@ -439,7 +439,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 1d25507427..be4093123d 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; @@ -57,6 +58,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 f6df427ea9..0f4e284390 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 @@ -427,7 +427,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 a51a9ea50a..a9f2a91f5c 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; @@ -57,6 +58,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 2a9d773be9..dbde1b24cc 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 @@ -427,7 +427,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 242b18fdca..69011c6a56 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; @@ -57,6 +58,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 cc665289eb..5f01b19927 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 @@ -440,7 +440,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 ae4c7c8d82..18f03a222f 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; @@ -57,6 +58,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 13bccc7fe9..33761d9f25 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 @@ -439,7 +439,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 3c209aa5ef..bccc5f3ea5 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; @@ -57,6 +58,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 a3dbb4aa8f..fd8a3f01c1 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 @@ -456,7 +456,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 3bce6dd911..4344ac59ac 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; @@ -57,6 +58,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 92db91f309..54ed09cb24 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; @@ -61,6 +62,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, 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 7a4fbbb55a..f199fc9a66 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,12 +40,38 @@ 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 (isFaceFull(getShape(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 VS getShape(ShapeType shapeType); protected abstract boolean isShapeFullBlock(VS shape); + + protected abstract boolean isFaceFull(VS shape, 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 c038845a5a..b9c02a79b8 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. @@ -50,6 +51,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 a601bd362d..b93c17ec53 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 177dc0d2c5..a88a8530d5 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; @@ -62,6 +63,11 @@ protected boolean isShapeFullBlock(VoxelShape shape) { return Block.isShapeFullBlock(shape); } + @Override + protected boolean isFaceFull(VoxelShape shape, Direction face) { + return Block.isFaceFull(shape, SpongeAdapter.adapt(face)); + } + @Override public boolean isOpaque() { return block.canOcclude(); From 2cb3233866b4608fb1eff401968c8ebe32d543e0 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 00:17:19 +0200 Subject: [PATCH 4/9] 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 0ec0725942..1bbd86abed 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().isMovementBlocker()) { + 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().isMovementBlocker()) { - 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 56ba5beba00ec9705b77ac90b10d85daa901cd91 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 12:14:18 +0200 Subject: [PATCH 5/9] 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 1bbd86abed..d3431e3400 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 2fa0dae2fcaaa504ce9a16ca1f87dcec637e775b Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 12:04:15 +0200 Subject: [PATCH 6/9] 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 d3431e3400..8fb70960a6 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().isMovementBlocker()); + + // 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().isMovementBlocker()) { 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 9c2338b1bd3628797bac39bba76c0bf47e52b021 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 02:33:26 +0200 Subject: [PATCH 7/9] //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 62a7a5e7ecd1566d1cc90679e50b90a4e9f129e6 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 13:06:04 +0200 Subject: [PATCH 8/9] 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 8fb70960a6..59503f3565 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 89846a75f89eea9a6cafb1b6b257f5914c4c1943 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 25 Apr 2026 00:23:27 +0200 Subject: [PATCH 9/9] 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 | 7 + .../java/com/sk89q/worldedit/EditSession.java | 212 +++++++++++++++--- .../worldedit/command/RegionCommands.java | 30 ++- 3 files changed, 215 insertions(+), 34 deletions(-) diff --git a/verification/src/changes/accepted-core-public-api-changes.json b/verification/src/changes/accepted-core-public-api-changes.json index bf1117def7..40c9cacc19 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -22,6 +22,13 @@ "changes": [ "METHOD_REMOVED" ] + }, + { + "type": "com.sk89q.worldedit.EditSession", + "member": "Method com.sk89q.worldedit.EditSession.makeCuboidFaces(com.sk89q.worldedit.regions.Region,int,com.sk89q.worldedit.function.pattern.Pattern)", + "changes": [ + "METHOD_NOW_FINAL" + ] } ], "Why on earth is this even an issue?": [ 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 59503f3565..6b4774f9db 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,12 +130,14 @@ 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; 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; @@ -2657,6 +2660,43 @@ public int deformRegion(final Region region, final Transform targetTransform, fi return affected; } + private static Direction[] getBlockedDirections(Extent extent, BlockVector3 position) { + BlockState blockState = extent.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). * @@ -2666,59 +2706,107 @@ 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. */ - public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { + @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, boolean openSides, Collection startingPositions, boolean useBlockGeometry) throws MaxChangedBlocksException { int affected = 0; final Set visible = new HashSet<>(); + if (startingPositions == null) { + // 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(); - // 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(); + 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)); + } + } - 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)); + for (int z = minZ; z <= maxZ; ++z) { + visible.add(BlockVector3.at(minX, y, z)); + visible.add(BlockVector3.at(maxX, y, z)); + } } - } - 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)); + for (int x = minX; x <= maxX; ++x) { + visible.add(BlockVector3.at(x, minY, z)); + visible.add(BlockVector3.at(x, maxY, 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)); + if (openSides) { + // Remove movement blockers from visible list + visible.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isMovementBlocker()); } + } else { + visible.addAll(startingPositions); } - // Remove movement blockers from visible list - visible.removeIf(blockVector3 -> getBlock(blockVector3).getBlockType().getMaterial().isMovementBlocker()); - // 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().isMovementBlocker()) { - continue; + Direction[] blockedDirections; + if (useBlockGeometry) { + // Get blocked directions for this block + blockedDirections = getBlockedDirections(this, current); + + // Short-cut if blocked in all directions + if (blockedDirections == CARDINAL_UPRIGHT_DIRECTIONS) { + continue; + } + } else { + final BlockState block = getBlock(current); + if (block.getBlockType().getMaterial().isMovementBlocker()) { + // In non-geometry mode, all movement blockers 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()); if (!region.contains(neighbor)) { continue; @@ -3171,6 +3259,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. */ @@ -3184,6 +3276,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..2aaeb0a408 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; @@ -548,15 +551,36 @@ public int deform(Actor actor, LocalSession session, EditSession editSession, ) @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") 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; }