From 2c724bafe6160039cabb15433838f0c787e8507b Mon Sep 17 00:00:00 2001 From: Bukkit/Spigot Date: Sat, 26 Mar 2016 09:27:04 +1100 Subject: [PATCH] SPIGOT-2013 Added Comparator/Hopper material, enhanced Diode By: ryanbennitt --- .../src/main/java/org/bukkit/Material.java | 8 +- .../java/org/bukkit/material/Comparator.java | 200 ++++++++++++++++++ .../main/java/org/bukkit/material/Diode.java | 144 ++++++++++--- .../main/java/org/bukkit/material/Hopper.java | 181 ++++++++++++++++ .../bukkit/materials/MaterialDataTest.java | 109 ++++++++++ 5 files changed, 609 insertions(+), 33 deletions(-) create mode 100644 paper-api/src/main/java/org/bukkit/material/Comparator.java create mode 100644 paper-api/src/main/java/org/bukkit/material/Hopper.java diff --git a/paper-api/src/main/java/org/bukkit/Material.java b/paper-api/src/main/java/org/bukkit/Material.java index 6de16317c6..914d9e89ed 100644 --- a/paper-api/src/main/java/org/bukkit/Material.java +++ b/paper-api/src/main/java/org/bukkit/Material.java @@ -13,6 +13,7 @@ import org.bukkit.material.Chest; import org.bukkit.material.Coal; import org.bukkit.material.CocoaPlant; import org.bukkit.material.Command; +import org.bukkit.material.Comparator; import org.bukkit.material.Crops; import org.bukkit.material.DetectorRail; import org.bukkit.material.Diode; @@ -23,6 +24,7 @@ import org.bukkit.material.EnderChest; import org.bukkit.material.FlowerPot; import org.bukkit.material.Furnace; import org.bukkit.material.Gate; +import org.bukkit.material.Hopper; import org.bukkit.material.Ladder; import org.bukkit.material.Leaves; import org.bukkit.material.Lever; @@ -216,12 +218,12 @@ public enum Material { TRAPPED_CHEST(146, Chest.class), GOLD_PLATE(147), IRON_PLATE(148), - REDSTONE_COMPARATOR_OFF(149), - REDSTONE_COMPARATOR_ON(150), + REDSTONE_COMPARATOR_OFF(149, Comparator.class), + REDSTONE_COMPARATOR_ON(150, Comparator.class), DAYLIGHT_DETECTOR(151), REDSTONE_BLOCK(152), QUARTZ_ORE(153), - HOPPER(154), + HOPPER(154, Hopper.class), QUARTZ_BLOCK(155), QUARTZ_STAIRS(156, Stairs.class), ACTIVATOR_RAIL(157, PoweredRail.class), diff --git a/paper-api/src/main/java/org/bukkit/material/Comparator.java b/paper-api/src/main/java/org/bukkit/material/Comparator.java new file mode 100644 index 0000000000..b5602ee67a --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/material/Comparator.java @@ -0,0 +1,200 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a comparator in the on or off state, in normal or subtraction mode and facing in a specific direction. + * + * @see Material#REDSTONE_COMPARATOR_OFF + * @see Material#REDSTONE_COMPARATOR_ON + */ +public class Comparator extends MaterialData implements Directional, Redstone { + protected static final BlockFace DEFAULT_DIRECTION = BlockFace.NORTH; + protected static final boolean DEFAULT_SUBTRACTION_MODE = false; + protected static final boolean DEFAULT_STATE = false; + + /** + * Constructs a comparator switched off, with the default mode (normal) and facing the default direction (north). + */ + public Comparator() { + this(DEFAULT_DIRECTION, DEFAULT_SUBTRACTION_MODE, false); + } + + /** + * Constructs a comparator switched off, with the default mode (normal) and facing the specified direction. + * + * @param facingDirection the direction the comparator is facing + * + * @see BlockFace + */ + public Comparator(BlockFace facingDirection) { + this(facingDirection, DEFAULT_SUBTRACTION_MODE, DEFAULT_STATE); + } + + /** + * Constructs a comparator switched off, with the specified mode and facing the specified direction. + * + * @param facingDirection the direction the comparator is facing + * @param isSubtraction True if the comparator is in subtraction mode, false for normal comparator operation + * + * @see BlockFace + */ + public Comparator(BlockFace facingDirection, boolean isSubtraction) { + this(facingDirection, isSubtraction, DEFAULT_STATE); + } + + /** + * Constructs a comparator switched on or off, with the specified mode and facing the specified direction. + * + * @param facingDirection the direction the comparator is facing + * @param isSubtraction True if the comparator is in subtraction mode, false for normal comparator operation + * @param state True if the comparator is in the on state + * + * @see BlockFace + */ + public Comparator(BlockFace facingDirection, boolean isSubtraction, boolean state) { + super(state ? Material.REDSTONE_COMPARATOR_ON : Material.REDSTONE_COMPARATOR_OFF); + setFacingDirection(facingDirection); + setSubtractionMode(isSubtraction); + } + + /** + * @param type the raw type id + * @deprecated Magic value + */ + @Deprecated + public Comparator(int type) { + super(type); + } + + public Comparator(Material type) { + super(type); + } + + /** + * @param type the raw type id + * @param data the raw data value + * @deprecated Magic value + */ + @Deprecated + public Comparator(int type, byte data) { + super(type, data); + } + + /** + * @param type the type + * @param data the raw data value + * @deprecated Magic value + */ + @Deprecated + public Comparator(Material type, byte data) { + super(type, data); + } + + /** + * Sets whether the comparator is in subtraction mode. + * + * @param isSubtraction True if the comparator is in subtraction mode, false for normal comparator operation + */ + public void setSubtractionMode(boolean isSubtraction) { + setData((byte)(getData() & 0xB | (isSubtraction ? 0x4 : 0x0))); + } + + /** + * Checks whether the comparator is in subtraction mode + * + * @return True if the comparator is in subtraction mode, false if normal comparator operation + */ + public boolean isSubtractionMode() { + return (getData() & 0x4) != 0; + } + + /** + * Sets the direction this comparator is facing + * + * @param face The direction to set this comparator to + * + * @see BlockFace + */ + @Override + public void setFacingDirection(BlockFace face) { + int data = getData() & 0xC; + + switch (face) { + case EAST: + data |= 0x1; + break; + + case SOUTH: + data |= 0x2; + break; + + case WEST: + data |= 0x3; + break; + + case NORTH: + default: + data |= 0x0; + } + + setData((byte)data); + } + + /** + * Gets the direction this comparator is facing + * + * @return The direction this comparator is facing + * + * @see BlockFace + */ + @Override + public BlockFace getFacing() { + byte data = (byte) (getData() & 0x3); + + switch (data) { + case 0x0: + default: + return BlockFace.NORTH; + + case 0x1: + return BlockFace.EAST; + + case 0x2: + return BlockFace.SOUTH; + + case 0x3: + return BlockFace.WEST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + " in " + (isSubtractionMode() ? "subtraction" : "comparator") + " mode"; + } + + @Override + public Comparator clone() { + return (Comparator) super.clone(); + } + + /** + * Checks if the comparator is powered + * + * @return true if the comparator is powered + */ + @Override + public boolean isPowered() { + return getItemType() == Material.REDSTONE_COMPARATOR_ON; + } + + /** + * Checks if the comparator is being powered + * + * @return true if the comparator is being powered + */ + public boolean isBeingPowered() { + return (getData() & 0x8) != 0; + } +} diff --git a/paper-api/src/main/java/org/bukkit/material/Diode.java b/paper-api/src/main/java/org/bukkit/material/Diode.java index fbfacc06ed..84014c4f8f 100644 --- a/paper-api/src/main/java/org/bukkit/material/Diode.java +++ b/paper-api/src/main/java/org/bukkit/material/Diode.java @@ -3,9 +3,71 @@ package org.bukkit.material; import org.bukkit.Material; import org.bukkit.block.BlockFace; -public class Diode extends MaterialData implements Directional { +/** + * Represents a diode/repeater in the on or off state, with a delay and facing + * in a specific direction. + * + * @see Material#DIODE_BLOCK_OFF + * @see Material#DIODE_BLOCK_ON + */ +public class Diode extends MaterialData implements Directional, Redstone { + + protected static final BlockFace DEFAULT_DIRECTION = BlockFace.NORTH; + protected static final int DEFAULT_DELAY = 1; + protected static final boolean DEFAULT_STATE = false; + + /** + * Constructs a diode switched on, with a delay of 1 and facing the default + * direction (north). + * + * By default this constructor creates a diode that is switched on for + * backwards compatibility with past implementations. + */ public Diode() { - super(Material.DIODE_BLOCK_ON); + this(DEFAULT_DIRECTION, DEFAULT_DELAY, true); + } + + /** + * Constructs a diode switched off, with a delay of 1 and facing the + * specified direction. + * + * @param facingDirection the direction the diode is facing + * + * @see BlockFace + */ + public Diode(BlockFace facingDirection) { + this(facingDirection, DEFAULT_DELAY, DEFAULT_STATE); + } + + /** + * Constructs a diode switched off, with the specified delay and facing the + * specified direction. + * + * @param facingDirection the direction the diode is facing + * @param delay The number of ticks (1-4) before the diode turns on after + * being powered + * + * @see BlockFace + */ + public Diode(BlockFace facingDirection, int delay) { + this(facingDirection, delay, DEFAULT_STATE); + } + + /** + * Constructs a diode switched on or off, with the specified delay and + * facing the specified direction. + * + * @param facingDirection the direction the diode is facing + * @param delay The number of ticks (1-4) before the diode turns on after + * being powered + * @param state True if the diode is in the on state + * + * @see BlockFace + */ + public Diode(BlockFace facingDirection, int delay, boolean state) { + super(state ? Material.DIODE_BLOCK_ON : Material.DIODE_BLOCK_OFF); + setFacingDirection(facingDirection); + setDelay(delay); } /** @@ -42,10 +104,9 @@ public class Diode extends MaterialData implements Directional { } /** - * Sets the delay of the repeater + * Sets the delay of the repeater. * - * @param delay - * The new delay (1-4) + * @param delay The new delay (1-4) */ public void setDelay(int delay) { if (delay > 4) { @@ -60,7 +121,7 @@ public class Diode extends MaterialData implements Directional { } /** - * Gets the delay of the repeater in ticks + * Gets the delay of the repeater in ticks. * * @return The delay (1-4) */ @@ -68,48 +129,61 @@ public class Diode extends MaterialData implements Directional { return (getData() >> 2) + 1; } + /** + * Sets the direction this diode is facing. + * + * @param face The direction to set this diode to + * + * @see BlockFace + */ + @Override public void setFacingDirection(BlockFace face) { int delay = getDelay(); byte data; switch (face) { - case EAST: - data = 0x1; - break; - - case SOUTH: - data = 0x2; - break; - - case WEST: - data = 0x3; - break; - - case NORTH: - default: - data = 0x0; + case EAST: + data = 0x1; + break; + case SOUTH: + data = 0x2; + break; + case WEST: + data = 0x3; + break; + case NORTH: + default: + data = 0x0; } setData(data); setDelay(delay); } + /** + * Gets the direction this diode is facing + * + * @return The direction this diode is facing + * + * @see BlockFace + */ + @Override public BlockFace getFacing() { byte data = (byte) (getData() & 0x3); switch (data) { - case 0x0: - default: - return BlockFace.NORTH; + case 0x0: + default: + return BlockFace.NORTH; - case 0x1: - return BlockFace.EAST; + case 0x1: + return BlockFace.EAST; - case 0x2: - return BlockFace.SOUTH; + case 0x2: + return BlockFace.SOUTH; - case 0x3: - return BlockFace.WEST; + case 0x3: + return BlockFace.WEST; } } @@ -122,4 +196,14 @@ public class Diode extends MaterialData implements Directional { public Diode clone() { return (Diode) super.clone(); } + + /** + * Checks if the diode is powered. + * + * @return true if the diode is powered + */ + @Override + public boolean isPowered() { + return getItemType() == Material.DIODE_BLOCK_ON; + } } diff --git a/paper-api/src/main/java/org/bukkit/material/Hopper.java b/paper-api/src/main/java/org/bukkit/material/Hopper.java new file mode 100644 index 0000000000..d1516f2515 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/material/Hopper.java @@ -0,0 +1,181 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a hopper in an active or deactivated state and facing in a + * specific direction. + * + * @see Material#HOPPER + */ +public class Hopper extends MaterialData implements Directional, Redstone { + + protected static final BlockFace DEFAULT_DIRECTION = BlockFace.DOWN; + protected static final boolean DEFAULT_ACTIVE = true; + + /** + * Constructs a hopper facing the default direction (down) and initially + * active. + */ + public Hopper() { + this(DEFAULT_DIRECTION, DEFAULT_ACTIVE); + } + + /** + * Constructs a hopper facing the specified direction and initially active. + * + * @param facingDirection the direction the hopper is facing + * + * @see BlockFace + */ + public Hopper(BlockFace facingDirection) { + this(facingDirection, DEFAULT_ACTIVE); + } + + /** + * Constructs a hopper facing the specified direction and either active or + * not. + * + * @param facingDirection the direction the hopper is facing + * @param isActive True if the hopper is initially active, false if + * deactivated + * + * @see BlockFace + */ + public Hopper(BlockFace facingDirection, boolean isActive) { + super(Material.HOPPER); + setFacingDirection(facingDirection); + setActive(isActive); + } + + /** + * @param type the raw type id + * @deprecated Magic value + */ + @Deprecated + public Hopper(int type) { + super(type); + } + + public Hopper(Material type) { + super(type); + } + + /** + * @param type the raw type id + * @param data the raw data value + * @deprecated Magic value + */ + @Deprecated + public Hopper(int type, byte data) { + super(type, data); + } + + /** + * @param type the type + * @param data the raw data value + * @deprecated Magic value + */ + @Deprecated + public Hopper(Material type, byte data) { + super(type, data); + } + + /** + * Sets whether the hopper is active or not. + * + * @param isActive True if the hopper is active, false if deactivated as if + * powered by redstone + */ + public void setActive(boolean isActive) { + setData((byte) (getData() & 0x7 | (isActive ? 0x0 : 0x8))); + } + + /** + * Checks whether the hopper is active or not. + * + * @return True if the hopper is active, false if deactivated + */ + public boolean isActive() { + return (getData() & 0x8) == 0; + } + + /** + * Sets the direction this hopper is facing + * + * @param face The direction to set this hopper to + * + * @see BlockFace + */ + @Override + public void setFacingDirection(BlockFace face) { + int data = getData() & 0x8; + + switch (face) { + case DOWN: + data |= 0x0; + break; + case NORTH: + data |= 0x2; + break; + case SOUTH: + data |= 0x3; + break; + case WEST: + data |= 0x4; + break; + case EAST: + data |= 0x5; + break; + } + + setData((byte) data); + } + + /** + * Gets the direction this hopper is facing + * + * @return The direction this hopper is facing + * + * @see BlockFace + */ + @Override + public BlockFace getFacing() { + byte data = (byte) (getData() & 0x7); + + switch (data) { + default: + case 0x0: + return BlockFace.DOWN; + case 0x2: + return BlockFace.NORTH; + case 0x3: + return BlockFace.SOUTH; + case 0x4: + return BlockFace.WEST; + case 0x5: + return BlockFace.EAST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing(); + } + + @Override + public Hopper clone() { + return (Hopper) super.clone(); + } + + /** + * Checks if the hopper is powered. + * + * @return true if the hopper is powered + */ + @Override + public boolean isPowered() { + return (getData() & 0x8) != 0; + } +} diff --git a/paper-api/src/test/java/org/bukkit/materials/MaterialDataTest.java b/paper-api/src/test/java/org/bukkit/materials/MaterialDataTest.java index 2be3ddc99c..0e7b6667e4 100644 --- a/paper-api/src/test/java/org/bukkit/materials/MaterialDataTest.java +++ b/paper-api/src/test/java/org/bukkit/materials/MaterialDataTest.java @@ -9,7 +9,10 @@ import org.bukkit.NetherWartsState; import org.bukkit.TreeSpecies; import org.bukkit.block.BlockFace; import org.bukkit.material.Crops; +import org.bukkit.material.Comparator; +import org.bukkit.material.Diode; import org.bukkit.material.Door; +import org.bukkit.material.Hopper; import org.bukkit.material.Leaves; import org.bukkit.material.Mushroom; import org.bukkit.material.NetherWarts; @@ -321,4 +324,110 @@ public class MaterialDataTest { assertThat("Constructed with correct wart state", warts.getState(), equalTo(allWartStates[s])); } } + + @Test + public void testDiode() { + Diode diode = new Diode(); + assertThat("Constructed with backward compatible diode state", diode.getItemType(), equalTo(Material.DIODE_BLOCK_ON)); + assertThat("Constructed with backward compatible powered", diode.isPowered(), equalTo(true)); + assertThat("Constructed with default delay", diode.getDelay(), equalTo(1)); + assertThat("Constructed with default direction", diode.getFacing(), equalTo(BlockFace.NORTH)); + + BlockFace[] directions = new BlockFace[] {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + int[] delays = new int[] {1, 2, 3, 4}; + boolean[] states = new boolean[] {false, true}; + for (BlockFace direction : directions) { + diode = new Diode(direction); + assertThat("Constructed with default diode state", diode.getItemType(), equalTo(Material.DIODE_BLOCK_OFF)); + assertThat("Constructed with default powered", diode.isPowered(), equalTo(false)); + assertThat("Constructed with default delay", diode.getDelay(), equalTo(1)); + assertThat("Constructed with correct direction", diode.getFacing(), equalTo(direction)); + for (int delay : delays) { + diode = new Diode(direction, delay); + assertThat("Constructed with default diode state", diode.getItemType(), equalTo(Material.DIODE_BLOCK_OFF)); + assertThat("Constructed with default powered", diode.isPowered(), equalTo(false)); + assertThat("Constructed with correct delay", diode.getDelay(), equalTo(delay)); + assertThat("Constructed with correct direction", diode.getFacing(), equalTo(direction)); + for (boolean state : states) { + diode = new Diode(direction, delay, state); + assertThat("Constructed with correct diode state", diode.getItemType(), equalTo(state ? Material.DIODE_BLOCK_ON : Material.DIODE_BLOCK_OFF)); + assertThat("Constructed with default powered", diode.isPowered(), equalTo(state)); + assertThat("Constructed with correct delay", diode.getDelay(), equalTo(delay)); + assertThat("Constructed with correct direction", diode.getFacing(), equalTo(direction)); + } + } + } + } + + @Test + public void testComparator() { + Comparator comparator = new Comparator(); + assertThat("Constructed with default comparator state", comparator.getItemType(), equalTo(Material.REDSTONE_COMPARATOR_OFF)); + assertThat("Constructed with default powered", comparator.isPowered(), equalTo(false)); + assertThat("Constructed with default being powered", comparator.isBeingPowered(), equalTo(false)); + assertThat("Constructed with default mode", comparator.isSubtractionMode(), equalTo(false)); + assertThat("Constructed with default direction", comparator.getFacing(), equalTo(BlockFace.NORTH)); + + BlockFace[] directions = new BlockFace[] {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + boolean[] modes = new boolean[] {false, true}; + boolean[] states = new boolean[] {false, true}; + for (BlockFace direction : directions) { + comparator = new Comparator(direction); + assertThat("Constructed with default comparator state", comparator.getItemType(), equalTo(Material.REDSTONE_COMPARATOR_OFF)); + assertThat("Constructed with default powered", comparator.isPowered(), equalTo(false)); + assertThat("Constructed with default being powered", comparator.isBeingPowered(), equalTo(false)); + assertThat("Constructed with default mode", comparator.isSubtractionMode(), equalTo(false)); + assertThat("Constructed with correct direction", comparator.getFacing(), equalTo(direction)); + for (boolean mode : modes) { + comparator = new Comparator(direction, mode); + assertThat("Constructed with default comparator state", comparator.getItemType(), equalTo(Material.REDSTONE_COMPARATOR_OFF)); + assertThat("Constructed with default powered", comparator.isPowered(), equalTo(false)); + assertThat("Constructed with default being powered", comparator.isBeingPowered(), equalTo(false)); + assertThat("Constructed with correct mode", comparator.isSubtractionMode(), equalTo(mode)); + assertThat("Constructed with correct direction", comparator.getFacing(), equalTo(direction)); + for (boolean state : states) { + comparator = new Comparator(direction, mode, state); + assertThat("Constructed with correct comparator state", comparator.getItemType(), equalTo(state ? Material.REDSTONE_COMPARATOR_ON : Material.REDSTONE_COMPARATOR_OFF)); + assertThat("Constructed with correct powered", comparator.isPowered(), equalTo(state)); + assertThat("Constructed with default being powered", comparator.isBeingPowered(), equalTo(false)); + assertThat("Constructed with correct mode", comparator.isSubtractionMode(), equalTo(mode)); + assertThat("Constructed with correct direction", comparator.getFacing(), equalTo(direction)); + + // Check if the game sets the fourth bit, that block data is still interpreted correctly + comparator.setData((byte)((comparator.getData() & 0x7) | 0x8)); + assertThat("Constructed with correct comparator state", comparator.getItemType(), equalTo(state ? Material.REDSTONE_COMPARATOR_ON : Material.REDSTONE_COMPARATOR_OFF)); + assertThat("Constructed with correct powered", comparator.isPowered(), equalTo(state)); + assertThat("Constructed with correct being powered", comparator.isBeingPowered(), equalTo(true)); + assertThat("Constructed with correct mode", comparator.isSubtractionMode(), equalTo(mode)); + assertThat("Constructed with correct direction", comparator.getFacing(), equalTo(direction)); + } + } + } + } + + @Test + public void testHopper() { + Hopper hopper = new Hopper(); + assertThat("Constructed with default hopper type", hopper.getItemType(), equalTo(Material.HOPPER)); + assertThat("Constructed with default active state", hopper.isActive(), equalTo(true)); + assertThat("Constructed with default powered state", hopper.isPowered(), equalTo(false)); + assertThat("Constructed with default direction", hopper.getFacing(), equalTo(BlockFace.DOWN)); + + BlockFace[] directions = new BlockFace[] {BlockFace.DOWN, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST}; + boolean[] activeStates = new boolean[] {true, false}; + for (BlockFace direction : directions) { + hopper = new Hopper(direction); + assertThat("Constructed with default hopper type", hopper.getItemType(), equalTo(Material.HOPPER)); + assertThat("Constructed with default active state", hopper.isActive(), equalTo(true)); + assertThat("Constructed with correct powered state", hopper.isPowered(), equalTo(false)); + assertThat("Constructed with correct direction", hopper.getFacing(), equalTo(direction)); + for(boolean isActive : activeStates) { + hopper = new Hopper(direction, isActive); + assertThat("Constructed with default hopper type", hopper.getItemType(), equalTo(Material.HOPPER)); + assertThat("Constructed with correct active state", hopper.isActive(), equalTo(isActive)); + assertThat("Constructed with correct powered state", hopper.isPowered(), equalTo(!isActive)); + assertThat("Constructed with correct direction", hopper.getFacing(), equalTo(direction)); + } + } + } }