Skip to content

Commit

Permalink
Support editing the backside of a sign
Browse files Browse the repository at this point in the history
  • Loading branch information
rutgerkok committed Jun 11, 2023
1 parent bfba62e commit 71e5569
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 36 deletions.
12 changes: 9 additions & 3 deletions src/main/java/nl/rutgerkok/blocklocker/SignParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
Expand All @@ -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<SignType> getSignType(Sign sign);
Optional<SignType> getSignType(Sign sign, Side side);

/**
* Gets the type of the sign from the {@link SignChangeEvent}. The event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Profile> profiles;
private final Location location;
Expand Down
99 changes: 73 additions & 26 deletions src/main/java/nl/rutgerkok/blocklocker/impl/SignParserImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -52,8 +56,8 @@ public String[] getDisplayLines(ProtectionSign sign) {
}

@Override
public Optional<SignType> getSignType(Sign sign) {
String header = sign.getLine(0);
public Optional<SignType> getSignType(Sign sign, Side side) {
String header = sign.getSide(side).getLine(0);
return Optional.ofNullable(getSignTypeOrNull(header));
}

Expand All @@ -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<ProtectionSign> 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 {
Expand All @@ -95,29 +108,42 @@ private Optional<ProtectionSign> 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<Profile> 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
Expand All @@ -135,32 +161,42 @@ public Optional<ProtectionSign> 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<ProtectionSign> parseSimpleSign(Location location, String[] lines) {
SignType signType = getSignTypeOrNull(lines[0]);
private Optional<ProtectionSign> parseSimpleSign(Location location, String[] frontLines, String[] backLines) {
SignType signType = getSignTypeOrNull(frontLines[0]);
if (signType == null) {
return Optional.empty();
}

List<Profile> 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.<ProtectionSign>of(protectionSignImpl);
Expand All @@ -177,15 +213,26 @@ public void saveSign(ProtectionSign sign) {
}

// Update sign, both visual and using raw JSON
List<Profile> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand All @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,10 +29,10 @@ public SignChangeListener(BlockLockerPluginImpl plugin) {
}


private Optional<SignType> getExistingSignType(Block block) {
private Optional<SignType> 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();
}
Expand All @@ -42,7 +43,7 @@ private void handleSignNearbyProtection(SignChangeEvent event, Protection protec

Profile playerProfile = plugin.getProfileFactory().fromPlayer(player);
Optional<SignType> newSignType = plugin.getSignParser().getSignType(event);
Optional<SignType> oldSignType = this.getExistingSignType(event.getBlock());
Optional<SignType> oldSignType = this.getExistingSignType(event.getBlock(), event.getSide());

// Only protection signs should be handled
if (!newSignType.isPresent() && !oldSignType.isPresent()) {
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 71e5569

Please sign in to comment.