diff --git a/README.md b/README.md
index c151d55..21985c7 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,10 @@
### Configuration File:
[config.yml](https://github.com/TWME-TW/DebugStickPro/blob/main/src/main/resources/config.yml)
+`BlockDataFilter.AllowUnsafeBisectedData` is disabled by default to prevent invalid
+double-block structures and duplicate drops. Enable it only when multi-block
+`Bisected` properties are intentionally needed.
+
### Language File:
[lang](https://github.com/TWME-TW/DebugStickPro/tree/main/src/main/resources/lang)
### Permissions:
diff --git a/pom.xml b/pom.xml
index 57a5f76..6b94a40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,6 +126,12 @@
3.3.3+119e9e1-SNAPSHOT
compile
+
+ junit
+ junit
+ 4.13.2
+ test
+
diff --git a/src/main/java/dev/twme/debugstickpro/DebugStickPro.java b/src/main/java/dev/twme/debugstickpro/DebugStickPro.java
index c102a01..2200e98 100644
--- a/src/main/java/dev/twme/debugstickpro/DebugStickPro.java
+++ b/src/main/java/dev/twme/debugstickpro/DebugStickPro.java
@@ -63,7 +63,7 @@ public final class DebugStickPro extends JavaPlugin {
/**
* This is the version of the plugin
*/
- public static final int CONFIG_VERSION = 7;
+ public static final int CONFIG_VERSION = 8;
/**
* This is the version of the language file
diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java
index 5dee3e1..2a06af7 100644
--- a/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java
+++ b/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java
@@ -222,7 +222,7 @@ private static ArrayList separateRaw(BlockData blockData) {
blockDataList.add(bigDripleafData);
}
- if (blockData instanceof Bisected) {
+ if (isBisectedHalfSafeToModify(blockData)) {
SubBlockData bisectedData = new BisectedData(blockData);
blockDataList.add(bisectedData);
}
@@ -881,4 +881,16 @@ private static ArrayList separateRaw(BlockData blockData) {
return cloneForBlockData(blockDataList, blockData);
}
+
+ static boolean isBisectedHalfSafeToModify(BlockData blockData) {
+ if (ConfigFile.BlockDataFilter.AllowUnsafeBisectedData) {
+ return blockData instanceof Bisected;
+ }
+
+ // Stairs and trapdoors use half as a single-block placement property. Other
+ // Bisected blocks depend on a matching block above or below; exposing half
+ // lets one side become a second bottom half and duplicate its drops.
+ return blockData instanceof Bisected
+ && (blockData instanceof Stairs || blockData instanceof TrapDoor);
+ }
}
diff --git a/src/main/java/dev/twme/debugstickpro/config/ConfigFile.java b/src/main/java/dev/twme/debugstickpro/config/ConfigFile.java
index b55571c..d863a57 100644
--- a/src/main/java/dev/twme/debugstickpro/config/ConfigFile.java
+++ b/src/main/java/dev/twme/debugstickpro/config/ConfigFile.java
@@ -63,6 +63,8 @@ public static class Blacklist {
}
public static class BlockDataFilter {
+ public static boolean AllowUnsafeBisectedData;
+
public static class Whitelist {
public static boolean Enabled;
public static HashSet Whitelist;
diff --git a/src/main/java/dev/twme/debugstickpro/config/ConfigLoader.java b/src/main/java/dev/twme/debugstickpro/config/ConfigLoader.java
index 9192734..d62b4ba 100644
--- a/src/main/java/dev/twme/debugstickpro/config/ConfigLoader.java
+++ b/src/main/java/dev/twme/debugstickpro/config/ConfigLoader.java
@@ -107,6 +107,7 @@ private void loadValues() {
ConfigFile.BlockDataFilter.Whitelist.Whitelist = new HashSet<>(config.getStringList("BlockDataFilter.Whitelist.Whitelist"));
ConfigFile.BlockDataFilter.Blacklist.Enabled = config.getBoolean("BlockDataFilter.Blacklist.Enabled");
ConfigFile.BlockDataFilter.Blacklist.Blacklist = new HashSet<>(config.getStringList("BlockDataFilter.Blacklist.Blacklist"));
+ ConfigFile.BlockDataFilter.AllowUnsafeBisectedData = config.getBoolean("BlockDataFilter.AllowUnsafeBisectedData");
ConfigFile.ModeSetting.ClassicMode.ClearSelectedDataTypeWhenModeChange = config.getBoolean("ModeSetting.ClassicMode.ClearSelectedDataTypeWhenModeChange");
ConfigFile.ModeSetting.CopyMode.ClearStoredDataWhenModeChange = config.getBoolean("ModeSetting.CopyMode.ClearStoredDataWhenModeChange");
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 892a0a6..e05b848 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,5 +1,5 @@
# Don't touch this value
-ConfigVersion: 7
+ConfigVersion: 8
Language:
# Language files are located in the "lang" folder.
@@ -67,6 +67,10 @@ AutoRegionProtection:
Enabled: true
BlockDataFilter:
+ # If enabled, exposes the half property of multi-block structures such as doors
+ # and double plants. This can create invalid block pairs and duplicate drops.
+ # Default: false
+ AllowUnsafeBisectedData: false
Whitelist:
# If enabled, only the specified block data will be displayed.
Enabled: false
diff --git a/src/test/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparaterTest.java b/src/test/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparaterTest.java
new file mode 100644
index 0000000..f587c01
--- /dev/null
+++ b/src/test/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparaterTest.java
@@ -0,0 +1,60 @@
+package dev.twme.debugstickpro.blockdatautil;
+
+import org.bukkit.block.data.Bisected;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.type.Door;
+import org.bukkit.block.data.type.PitcherCrop;
+import org.bukkit.block.data.type.SmallDripleaf;
+import org.bukkit.block.data.type.Stairs;
+import org.bukkit.block.data.type.TrapDoor;
+import org.junit.Test;
+
+import dev.twme.debugstickpro.config.ConfigFile;
+
+import java.lang.reflect.Proxy;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class BlockDataSeparaterTest {
+
+ @Test
+ public void allowsSingleBlockBisectedProperties() {
+ assertTrue(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(Stairs.class)));
+ assertTrue(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(TrapDoor.class)));
+ }
+
+ @Test
+ public void rejectsMultiBlockBisectedProperties() {
+ assertFalse(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(Bisected.class)));
+ assertFalse(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(Door.class)));
+ assertFalse(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(SmallDripleaf.class)));
+ assertFalse(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(PitcherCrop.class)));
+ }
+
+ @Test
+ public void allowsUnsafeBisectedPropertiesWhenConfigured() {
+ ConfigFile.BlockDataFilter.AllowUnsafeBisectedData = true;
+ try {
+ assertTrue(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(Bisected.class)));
+ assertTrue(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(Door.class)));
+ } finally {
+ ConfigFile.BlockDataFilter.AllowUnsafeBisectedData = false;
+ }
+ }
+
+ @Test
+ public void rejectsNonBisectedProperties() {
+ assertFalse(BlockDataSeparater.isBisectedHalfSafeToModify(blockData(BlockData.class)));
+ }
+
+ private static BlockData blockData(Class extends BlockData> type) {
+ return (BlockData) Proxy.newProxyInstance(
+ type.getClassLoader(),
+ new Class>[]{type},
+ (proxy, method, args) -> {
+ throw new AssertionError("Unexpected BlockData method call: " + method.getName());
+ }
+ );
+ }
+}