diff --git a/src/main/java/nl/rutgerkok/blocklocker/SignParser.java b/src/main/java/nl/rutgerkok/blocklocker/SignParser.java index 4b73cf3..c5a8c93 100644 --- a/src/main/java/nl/rutgerkok/blocklocker/SignParser.java +++ b/src/main/java/nl/rutgerkok/blocklocker/SignParser.java @@ -4,6 +4,7 @@ import org.bukkit.block.Block; import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; import org.bukkit.event.block.SignChangeEvent; /** @@ -14,8 +15,11 @@ public interface SignParser { /** - * Gets the text-only display lines that should be displayed on the sign. - * @param sign The sign. + * Gets the text-only display lines that should be displayed on the sign at the + * front. + * + * @param sign + * The sign. * @return The display text. */ String[] getDisplayLines(ProtectionSign sign); @@ -25,9 +29,11 @@ public interface SignParser { * * @param sign * The sign. + * @param side + * The side of the sign to read. * @return The type of the sign. */ - Optional getSignType(Sign sign); + Optional getSignType(Sign sign, Side side); /** * Gets the type of the sign from the {@link SignChangeEvent}. The event diff --git a/src/main/java/nl/rutgerkok/blocklocker/impl/ProtectionSignImpl.java b/src/main/java/nl/rutgerkok/blocklocker/impl/ProtectionSignImpl.java index 525e359..aa0abca 100644 --- a/src/main/java/nl/rutgerkok/blocklocker/impl/ProtectionSignImpl.java +++ b/src/main/java/nl/rutgerkok/blocklocker/impl/ProtectionSignImpl.java @@ -14,7 +14,7 @@ final class ProtectionSignImpl implements ProtectionSign { - private static final int MAX_PROFILES = 3; + private static final int MAX_PROFILES = 6; private final SignType signType; private final List profiles; private final Location location; diff --git a/src/main/java/nl/rutgerkok/blocklocker/impl/SignParserImpl.java b/src/main/java/nl/rutgerkok/blocklocker/impl/SignParserImpl.java index 48c03f5..601a957 100644 --- a/src/main/java/nl/rutgerkok/blocklocker/impl/SignParserImpl.java +++ b/src/main/java/nl/rutgerkok/blocklocker/impl/SignParserImpl.java @@ -10,6 +10,8 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; @@ -30,7 +32,9 @@ class SignParserImpl implements SignParser { private static final NamespacedKey HEADER_KEY = NbtSecretSignEntry.key("header"); private static final NamespacedKey[] PROFILE_KEYS = { NbtSecretSignEntry.key("profile_1"), - NbtSecretSignEntry.key("profile_2"), NbtSecretSignEntry.key("profile_3") }; + NbtSecretSignEntry.key("profile_2"), NbtSecretSignEntry.key("profile_3"), + NbtSecretSignEntry.key("profile_4"), NbtSecretSignEntry.key("profile_5"), + NbtSecretSignEntry.key("profile_6") }; private final ChestSettings chestSettings; private final ProfileFactoryImpl profileFactory; @@ -52,8 +56,8 @@ public String[] getDisplayLines(ProtectionSign sign) { } @Override - public Optional getSignType(Sign sign) { - String header = sign.getLine(0); + public Optional getSignType(Sign sign, Side side) { + String header = sign.getSide(side).getLine(0); return Optional.ofNullable(getSignTypeOrNull(header)); } @@ -74,18 +78,27 @@ private SignType getSignTypeOrNull(String header) { @Override public boolean hasValidHeader(Sign sign) { - return getSignType(sign).isPresent(); + return getSignType(sign, Side.FRONT).isPresent(); } private Optional parseAdvancedSign(Sign sign) { - String[] displayedText = sign.getLines(); + // Get sign type PersistentDataContainer data = sign.getPersistentDataContainer(); - - - // Get sign type if (!data.has(HEADER_KEY, PersistentDataType.STRING)) { return Optional.empty(); } + + // Check whether back side is in use + boolean useBackside = getSignTypeOrNull(sign.getSide(Side.BACK).getLines()[0]) != null; + + // Collected displayed names of front (and back, if in use) + String[] displayedNames = new String[useBackside ? 6 : 3]; + System.arraycopy(sign.getSide(Side.FRONT).getLines(), 1, displayedNames, 0, 3); + if (useBackside) { + System.arraycopy(sign.getSide(Side.BACK).getLines(), 1, displayedNames, 3, 3); + } + + String signTypeString = data.get(HEADER_KEY, PersistentDataType.STRING); SignType type; try { @@ -95,29 +108,42 @@ private Optional parseAdvancedSign(Sign sign) { } // Check header - String header = sign.getLine(0); + String header = sign.getSide(Side.FRONT).getLine(0); boolean headerMismatch = !chestSettings.getFancyLocalizedHeader(type, header).equals(header); + if (useBackside) { + String backHeader = sign.getSide(Side.BACK).getLine(0); + if (!chestSettings.getFancyLocalizedHeader(SignType.MORE_USERS, header).equals(backHeader)) { + headerMismatch = true; + } + } // Get profiles List profiles = new ArrayList<>(); - int lineNumber = 1; + int i = 0; boolean signHadDataMismatch = false; // Set to true when the display text doesn't match the stored data for (NamespacedKey profileKey : PROFILE_KEYS) { NbtSecretSignEntry entry = data.get(profileKey, NbtSecretSignEntry.TAG_TYPE); + if (i >= 3 && !useBackside) { + if (entry != null) { + // Found more data, but backside of sign is not in use + signHadDataMismatch = true; + } + break; + } Profile profile = entry == null ? profileFactory.emptyProfile() : profileFactory.fromSavedObject(entry).orElseGet(profileFactory::emptyProfile); - if (profile.getDisplayName().equals(displayedText[lineNumber])) { + if (profile.getDisplayName().equals(displayedNames[i])) { // Text on sign matches, use stored profile profiles.add(profile); } else { // Text on sign doesn't match, so use the text - Profile newProfile = profileFactory.fromDisplayText(displayedText[lineNumber]); + Profile newProfile = profileFactory.fromDisplayText(displayedNames[i]); profiles.add(newProfile); signHadDataMismatch = true; } - lineNumber++; + i++; } return Optional @@ -135,32 +161,42 @@ public Optional parseSign(Block sign) { } // Try plain sign, written by the user - String[] lines = signState.getLines(); - return parseSimpleSign(sign.getLocation(), lines); + String[] frontLines = signState.getSide(Side.FRONT).getLines(); + String[] backLines = signState.getSide(Side.BACK).getLines(); + return parseSimpleSign(sign.getLocation(), frontLines, backLines); } /** - * Used for signs where the hidden information was never written or was - * lost. + * Used for signs where the hidden information was never written or was lost. * * @param location * The location on the sign. - * @param lines - * The lines on the sign. + * @param frontLines + * The lines on the front of the sign. + * @param backLines + * The lines on the back of the sign. * @return The protection sign, if the header format is correct. */ - private Optional parseSimpleSign(Location location, String[] lines) { - SignType signType = getSignTypeOrNull(lines[0]); + private Optional parseSimpleSign(Location location, String[] frontLines, String[] backLines) { + SignType signType = getSignTypeOrNull(frontLines[0]); if (signType == null) { return Optional.empty(); } List profiles = new ArrayList<>(); - for (int i = 1; i < lines.length; i++) { - String name = lines[i].trim(); + for (int i = 1; i < frontLines.length; i++) { + String name = frontLines[i].trim(); profiles.add(profileFactory.fromDisplayText(name)); } + // Try to read backside too + if (getSignTypeOrNull(backLines[0]) != null) { + for (int i = 1; i < backLines.length; i++) { + String name = backLines[i].trim(); + profiles.add(profileFactory.fromDisplayText(name)); + } + } + // Last argument == true: we want to save this sign in our own format ProtectionSignImpl protectionSignImpl = new ProtectionSignImpl(location, signType, profiles, true); return Optional.of(protectionSignImpl); @@ -177,15 +213,26 @@ public void saveSign(ProtectionSign sign) { } // Update sign, both visual and using raw JSON + List profiles = sign.getProfiles(); Sign signState = (Sign) blockState; + SignSide frontSide = signState.getSide(Side.FRONT); + SignSide backSide = signState.getSide(Side.BACK); - signState.setLine(0, chestSettings.getFancyLocalizedHeader(sign.getType(), signState.getLine(0))); + // Set headers correctly + frontSide.setLine(0, chestSettings.getFancyLocalizedHeader(sign.getType(), frontSide.getLine(0))); + if (profiles.size() > 3) { + backSide.setLine(0, chestSettings.getFancyLocalizedHeader(SignType.MORE_USERS, backSide.getLine(0))); + } PersistentDataContainer data = signState.getPersistentDataContainer(); data.set(HEADER_KEY, PersistentDataType.STRING, sign.getType().toString()); int i = 0; - for (Profile profile : sign.getProfiles()) { - signState.setLine(i + 1, profile.getDisplayName()); + for (Profile profile : profiles) { + if (i < 3) { + frontSide.setLine(i + 1, profile.getDisplayName()); + } else { + backSide.setLine(i - 2, profile.getDisplayName()); + } NbtSecretSignEntry signEntry = new NbtSecretSignEntry( data.getAdapterContext().newPersistentDataContainer()); profile.getSaveObject(signEntry); diff --git a/src/main/java/nl/rutgerkok/blocklocker/impl/event/InteractListener.java b/src/main/java/nl/rutgerkok/blocklocker/impl/event/InteractListener.java index 5a4447d..b4346b9 100644 --- a/src/main/java/nl/rutgerkok/blocklocker/impl/event/InteractListener.java +++ b/src/main/java/nl/rutgerkok/blocklocker/impl/event/InteractListener.java @@ -15,6 +15,8 @@ import org.bukkit.block.data.Levelled; import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.type.WallSign; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.entity.Player; import org.bukkit.entity.Villager; import org.bukkit.event.EventHandler; @@ -474,7 +476,7 @@ private boolean tryPlaceSign(Player player, Block block, BlockFace clickedSide, String[] newLines = plugin.getSignParser().getDisplayLines(protectionSign); // Test if we can place it - SignChangeEvent signChangeEvent = new SignChangeEvent(sign.getBlock(), player, newLines); + SignChangeEvent signChangeEvent = new SignChangeEvent(sign.getBlock(), player, newLines, Side.FRONT); Bukkit.getPluginManager().callEvent(signChangeEvent); if (sign.getBlock().getType() != sign.getType()) { @@ -490,8 +492,9 @@ private boolean tryPlaceSign(Player player, Block block, BlockFace clickedSide, } // Actually write the text + SignSide frontSide = sign.getSide(Side.FRONT); for (int i = 0; i < newLines.length; i++) { - sign.setLine(i, newLines[i]); + frontSide.setLine(i, newLines[i]); } sign.update(); diff --git a/src/main/java/nl/rutgerkok/blocklocker/impl/event/SignChangeListener.java b/src/main/java/nl/rutgerkok/blocklocker/impl/event/SignChangeListener.java index 821e88e..b6442b7 100644 --- a/src/main/java/nl/rutgerkok/blocklocker/impl/event/SignChangeListener.java +++ b/src/main/java/nl/rutgerkok/blocklocker/impl/event/SignChangeListener.java @@ -7,6 +7,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.block.SignChangeEvent; @@ -28,10 +29,10 @@ public SignChangeListener(BlockLockerPluginImpl plugin) { } - private Optional getExistingSignType(Block block) { + private Optional getExistingSignType(Block block, Side side) { BlockState blockState = block.getState(); if (blockState instanceof Sign) { - return plugin.getSignParser().getSignType((Sign) blockState); + return plugin.getSignParser().getSignType((Sign) blockState, side); } return Optional.empty(); } @@ -42,7 +43,7 @@ private void handleSignNearbyProtection(SignChangeEvent event, Protection protec Profile playerProfile = plugin.getProfileFactory().fromPlayer(player); Optional newSignType = plugin.getSignParser().getSignType(event); - Optional oldSignType = this.getExistingSignType(event.getBlock()); + Optional oldSignType = this.getExistingSignType(event.getBlock(), event.getSide()); // Only protection signs should be handled if (!newSignType.isPresent() && !oldSignType.isPresent()) { @@ -81,7 +82,7 @@ private void handleSignNearbyProtection(SignChangeEvent event, Protection protec } else { // Second main sign is not allowed plugin.getTranslator().sendMessage(player, Translation.PROTECTION_ADD_MORE_USERS_SIGN_INSTEAD); - block.breakNaturally(); + block.breakNaturally(); // Not ideal if other side is the main sign event.setCancelled(true); return; }