From cfc67965f397a7d1b930f4212eba07120a43b5f9 Mon Sep 17 00:00:00 2001 From: md_5 Date: Mon, 6 Aug 2018 14:40:06 +1000 Subject: [PATCH] Add merging and matching of parsed BlockData --- .../block/data/CraftBlockData.java | 50 +++++++++++- src/test/java/org/bukkit/BlockDataTest.java | 80 +++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java index cfe5903cda..472e89c43c 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java @@ -27,6 +27,7 @@ import org.bukkit.craftbukkit.util.CraftMagicNumbers; public class CraftBlockData implements BlockData { private IBlockData state; + private Set> parsedStates; protected CraftBlockData() { throw new AssertionError("Template Constructor"); @@ -86,9 +87,51 @@ public class CraftBlockData implements BlockData { * @param the NMS type */ protected , N extends Enum & INamable> void set(BlockStateEnum nms, Enum bukkit) { + this.parsedStates = null; this.state = this.state.set(nms, toNMS(bukkit, nms.b())); } + @Override + public BlockData merge(BlockData data) { + CraftBlockData craft = (CraftBlockData) data; + Preconditions.checkArgument(craft.parsedStates != null, "Data not created via string parsing"); + Preconditions.checkArgument(this.state.getBlock() == craft.state.getBlock(), "States have different types (got %s, expected %s)", data, this); + + CraftBlockData clone = (CraftBlockData) this.clone(); + clone.parsedStates = null; + + for (IBlockState parsed : craft.parsedStates) { + clone.state = clone.state.set(parsed, craft.state.get(parsed)); + } + + return clone; + } + + @Override + public boolean matches(BlockData data) { + if (data == null) { + return false; + } + if (!(data instanceof CraftBlockData)) { + return false; + } + + CraftBlockData craft = (CraftBlockData) data; + if (this.state.getBlock() != craft.state.getBlock()) { + return false; + } + + // Fastpath an exact match + boolean exactMatch = this.equals(data); + + // If that failed, do a merge and check + if (!exactMatch && craft.parsedStates != null) { + return this.merge(data).equals(this); + } + + return exactMatch; + } + private static final Map, Enum>> classMappings = new HashMap<>(); /** @@ -187,6 +230,7 @@ public class CraftBlockData implements BlockData { */ public , V extends T> void set(IBlockState ibs, V v) { // Straight integer or boolean setter + this.parsedStates = null; this.state = this.state.set(ibs, v); } @@ -421,6 +465,7 @@ public class CraftBlockData implements BlockData { IBlockData blockData; Block block = CraftMagicNumbers.getBlock(material); + Set> parsed = null; // Data provided, use it if (data != null) { @@ -435,6 +480,7 @@ public class CraftBlockData implements BlockData { Preconditions.checkArgument(!reader.canRead(), "Spurious trailing data"); blockData = arg.b(); + parsed = arg.a().keySet(); } catch (CommandSyntaxException ex) { throw new IllegalArgumentException("Could not parse data: " + data, ex); } @@ -442,7 +488,9 @@ public class CraftBlockData implements BlockData { blockData = block.getBlockData(); } - return fromData(blockData); + CraftBlockData craft = fromData(blockData); + craft.parsedStates = parsed; + return craft; } public static CraftBlockData fromData(IBlockData data) { diff --git a/src/test/java/org/bukkit/BlockDataTest.java b/src/test/java/org/bukkit/BlockDataTest.java index e43de76569..fb30a9e238 100644 --- a/src/test/java/org/bukkit/BlockDataTest.java +++ b/src/test/java/org/bukkit/BlockDataTest.java @@ -1,9 +1,13 @@ package org.bukkit; import net.minecraft.server.BlockCake; +import net.minecraft.server.BlockChest; import net.minecraft.server.Blocks; +import net.minecraft.server.EnumDirection; +import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Cake; +import org.bukkit.block.data.type.Chest; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.support.AbstractTestingBase; import static org.hamcrest.Matchers.*; @@ -72,4 +76,80 @@ public class BlockDataTest extends AbstractTestingBase { clone.setBites(1); Assert.assertThat("Clone is not actually clone", clone, is(not(cakeTest))); } + + @Test + public void testMerge() { + Chest trueTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest falseTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]"); + Chest waterlogged = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + + BlockData candidate; + + Assert.assertFalse("Target and match are not yet equal", trueTarget.equals(waterlogged)); + candidate = trueTarget.merge(waterlogged); + Assert.assertTrue("Target and candidate are now equal", trueTarget.equals(candidate)); + + Assert.assertFalse("Target and match are not yet equal", falseTarget.equals(waterlogged)); + candidate = falseTarget.merge(waterlogged); + Assert.assertFalse("Target and candidate are still not equal", falseTarget.equals(candidate)); + } + + @Test + public void testMergeAny() { + Chest trueTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest falseTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]"); + Chest any = (Chest) CraftBlockData.newData(null, "minecraft:chest"); + + BlockData candidate; + + Assert.assertFalse("Target and match are not yet equal", trueTarget.equals(any)); + candidate = trueTarget.merge(any); + Assert.assertTrue("Target and candidate are now equal", trueTarget.equals(candidate)); + + Assert.assertFalse("Target and match are not yet equal", falseTarget.equals(any)); + candidate = falseTarget.merge(any); + Assert.assertTrue("Target and candidate are now equal", falseTarget.equals(candidate)); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotMerge1() { + Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest two = (Chest) CraftBlockData.fromData(Blocks.CHEST.getBlockData()); + + one.merge(two); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotMerge2() { + Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + Chest two = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + + one.merge(two); + + two.setFacing(BlockFace.NORTH); + one.merge(two); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotMerge3() { + Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + Chest two = (Chest) CraftBlockData.newData(null, "minecraft:trapped_chest[waterlogged=true]"); + + one.merge(two); + } + + @Test + public void testMatch() { + Assert.assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + Assert.assertFalse(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + Assert.assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest"))); + Assert.assertFalse(CraftBlockData.newData(null, "minecraft:trapped_chest[facing=east,waterlogged=false]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + Assert.assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true,facing=east]"))); + + Chest one = (Chest) CraftBlockData.fromData(Blocks.CHEST.getBlockData().set(BlockChest.FACING, EnumDirection.EAST)); + Chest two = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=false]"); + + Assert.assertTrue(one.matches(two)); + Assert.assertFalse(two.matches(one)); + } }