Adds a copySign command and stuff
All checks were successful
KnarCraft/PlaceholderSigns/pipeline/head This commit looks good

Adds a new copySign command that copies all info about a sign, including current lines, placeholders, waxed state, dye for each side, and glow state for each side.
Renames editSign to setSignLine to avoid collision with EssentialsX's editSign
Reduces viewSign to a single command
Makes viewSign display the looked at sign, instead of creating a session
Adds some additional messages
Adds missing documentation to the README
This commit is contained in:
2024-04-23 17:29:35 +02:00
parent b33f514dca
commit 66c45e00e2
12 changed files with 449 additions and 265 deletions

View File

@@ -3,8 +3,8 @@ package net.knarcraft.placeholdersigns;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.Translator;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.placeholdersigns.command.CopySignCommand;
import net.knarcraft.placeholdersigns.command.EditSignCommand;
import net.knarcraft.placeholdersigns.command.ViewPlaceholderSignCommand;
import net.knarcraft.placeholdersigns.command.ViewSignCommand;
import net.knarcraft.placeholdersigns.config.PlaceholderSignMessage;
import net.knarcraft.placeholdersigns.handler.PlaceholderSignHandler;
@@ -92,11 +92,9 @@ public final class PlaceholderSigns extends JavaPlugin {
Bukkit.getPluginManager().registerEvents(new SignTextListener(), this);
Bukkit.getPluginManager().registerEvents(new SignClickListener(), this);
registerCommand("editSign", new EditSignCommand());
registerCommand("viewSign", new ViewSignCommand(false));
registerCommand("viewSignRaw", new ViewSignCommand(true));
registerCommand("viewPlaceholderSign", new ViewPlaceholderSignCommand(false));
registerCommand("viewPlaceholderSignRaw", new ViewPlaceholderSignCommand(true));
registerCommand("setSignLine", new EditSignCommand());
registerCommand("viewSign", new ViewSignCommand());
registerCommand("copySign", new CopySignCommand());
}
@Override

View File

@@ -0,0 +1,54 @@
package net.knarcraft.placeholdersigns.command;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.config.PlaceholderSignMessage;
import net.knarcraft.placeholdersigns.container.SignCopyRequest;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* A command for copying signs, including placeholders
*/
public class CopySignCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
PlaceholderSigns placeholderSigns = PlaceholderSigns.getInstance();
StringFormatter stringFormatter = placeholderSigns.getStringFormatter();
if (!(commandSender instanceof Player player)) {
stringFormatter.displayErrorMessage(commandSender, PlaceholderSignMessage.ERROR_PLAYER_ONLY);
return false;
}
Block targetBlock = player.getTargetBlockExact(7);
if (targetBlock == null || !(targetBlock.getState() instanceof Sign sign)) {
stringFormatter.displayErrorMessage(commandSender, PlaceholderSignMessage.ERROR_NOT_LOOKING_AT_SIGN);
return false;
}
SignCopyRequest signCopyRequest = new SignCopyRequest(player, sign);
placeholderSigns.getRequestHandler().addSignCopyRequest(signCopyRequest);
stringFormatter.displaySuccessMessage(commandSender, PlaceholderSignMessage.SUCCESS_CLICK_SIGN_TO_PASTE);
return true;
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
return new ArrayList<>();
}
}

View File

@@ -1,5 +1,6 @@
package net.knarcraft.placeholdersigns.command;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.config.PlaceholderSignMessage;
import net.knarcraft.placeholdersigns.container.SignLineChangeRequest;
@@ -30,7 +31,13 @@ public class EditSignCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) {
if (args.length < 1 || !(commandSender instanceof Player player)) {
PlaceholderSigns placeholderSigns = PlaceholderSigns.getInstance();
StringFormatter stringFormatter = placeholderSigns.getStringFormatter();
if (!(commandSender instanceof Player player)) {
stringFormatter.displayErrorMessage(commandSender, PlaceholderSignMessage.ERROR_PLAYER_ONLY);
return false;
}
if (args.length < 1) {
return false;
}
@@ -61,11 +68,9 @@ public class EditSignCommand implements TabExecutor {
// Register the line change request
SignLineChangeRequest request = new SignLineChangeRequest(player, lineNumber - 1, builder.toString());
PlaceholderSigns placeholderSigns = PlaceholderSigns.getInstance();
placeholderSigns.getRequestHandler().addSignChangeRequest(request);
placeholderSigns.getStringFormatter().displaySuccessMessage(commandSender,
PlaceholderSignMessage.SUCCESS_CLICK_SIGN_TO_EDIT);
stringFormatter.displaySuccessMessage(commandSender, PlaceholderSignMessage.SUCCESS_CLICK_SIGN_TO_EDIT);
return true;
}

View File

@@ -1,53 +0,0 @@
package net.knarcraft.placeholdersigns.command;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.config.PlaceholderSignMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* A command for viewing placeholders on a sign
*/
public class ViewPlaceholderSignCommand implements TabExecutor {
private final boolean raw;
/**
* Instantiates a new view sign command
*
* @param raw <p>Whether this sign displays the raw text or not</p>
*/
public ViewPlaceholderSignCommand(boolean raw) {
this.raw = raw;
}
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
if (!(commandSender instanceof Player player)) {
return false;
}
// Register the sign view request
PlaceholderSigns.getInstance().getRequestHandler().addPlaceholderSignViewRequest(player, this.raw);
PlaceholderSigns.getInstance().getStringFormatter().displaySuccessMessage(commandSender,
PlaceholderSignMessage.SUCCESS_CLICK_SIGN_TO_VIEW);
return true;
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
return new ArrayList<>();
}
}

View File

@@ -1,7 +1,19 @@
package net.knarcraft.placeholdersigns.command;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.StringReplacer;
import net.knarcraft.knarlib.util.ColorHelper;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.config.PlaceholderSignMessage;
import net.knarcraft.placeholdersigns.container.PlaceholderSign;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -11,43 +23,172 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* A command for viewing lines on a sign
* A generic sign view command
*/
public class ViewSignCommand implements TabExecutor {
private final boolean raw;
/**
* Instantiates a new view sign command
*
* @param raw <p>Whether this sign displays the raw text or not</p>
*/
public ViewSignCommand(boolean raw) {
this.raw = raw;
}
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
@NotNull String[] arguments) {
StringFormatter stringFormatter = PlaceholderSigns.getInstance().getStringFormatter();
if (!(commandSender instanceof Player player)) {
stringFormatter.displayErrorMessage(commandSender, PlaceholderSignMessage.ERROR_PLAYER_ONLY);
return false;
}
// Register the sign view request
PlaceholderSigns.getInstance().getRequestHandler().addSignViewRequest(player, this.raw);
boolean showRawText = true;
boolean showPlaceholders = true;
if (arguments.length > 0) {
showRawText = Boolean.parseBoolean(arguments[0]);
}
if (arguments.length > 1) {
showPlaceholders = Boolean.parseBoolean(arguments[1]);
}
PlaceholderSigns.getInstance().getStringFormatter().displaySuccessMessage(commandSender,
PlaceholderSignMessage.SUCCESS_CLICK_SIGN_TO_VIEW);
Block targetBlock = player.getTargetBlockExact(7);
if (targetBlock == null || !(targetBlock.getState() instanceof Sign sign)) {
stringFormatter.displayErrorMessage(commandSender, PlaceholderSignMessage.ERROR_NOT_LOOKING_AT_SIGN);
return false;
}
printSign(sign, player, showRawText, showPlaceholders);
return true;
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
return new ArrayList<>();
@NotNull String[] arguments) {
if (arguments.length == 1) {
return getBooleanTabCompletions(arguments[0]);
} else if (arguments.length == 2) {
return getBooleanTabCompletions(arguments[1]);
} else {
return new ArrayList<>();
}
}
/**
* Gets boolean tab-completions
*
* @param filter <p>The input argument to filter by</p>
* @return <p>The the resulting boolean tab-completions</p>
*/
@NotNull
private List<String> getBooleanTabCompletions(@NotNull String filter) {
return TabCompletionHelper.filterMatchingStartsWith(List.of("true", "false"), filter);
}
/**
* Prints the current contents of a sign to a player
*
* @param sign <p>The sign to print</p>
* @param player <p>The player to display the contents to</p>
*/
private void printSign(@NotNull Sign sign, @NotNull Player player, boolean showRawText, boolean showPlaceholders) {
Location location = sign.getLocation();
SignSide front = sign.getSide(Side.FRONT);
SignSide back = sign.getSide(Side.BACK);
String frontLines = showPlaceholders ? getPlaceholderSignText(location, front, Side.FRONT, showRawText) :
getSignText(front.getLines(), showRawText);
String backLines = showPlaceholders ? getPlaceholderSignText(location, back, Side.BACK, showRawText) :
getSignText(back.getLines(), showRawText);
StringFormatter stringFormatter = PlaceholderSigns.getInstance().getStringFormatter();
StringReplacer replacer = new StringReplacer(stringFormatter.getUnformattedColoredMessage(
PlaceholderSignMessage.SUCCESS_SIGN_CONTENTS));
replacer.add("{frontLines}", frontLines);
replacer.add("{backLines}", backLines);
replacer.add("{frontDye}", getDye(front));
replacer.add("{frontGlow}", getStatus(front.isGlowingText()));
replacer.add("{backDye}", getDye(back));
replacer.add("{backGlow}", getStatus(back.isGlowingText()));
replacer.add("{waxed}", getStatus(sign.isWaxed()));
player.sendMessage(replacer.replace());
}
/**
* Gets a description of the status of a sign property
*
* @param isTrue <p>Whether the property is true</p>
* @return <p>A description of the property's current status</p>
*/
@NotNull
private String getStatus(boolean isTrue) {
StringFormatter stringFormatter = PlaceholderSigns.getInstance().getStringFormatter();
if (isTrue) {
return stringFormatter.getUnformattedColoredMessage(PlaceholderSignMessage.SIGN_PROPERTY_CONFIRM);
} else {
return stringFormatter.getUnformattedColoredMessage(PlaceholderSignMessage.SIGN_PROPERTY_DENY);
}
}
/**
* Gets a description of a dye applied to a sign
*
* @param signSide <p>The sign side to get the dye of</p>
* @return <p>The description of the applied dye</p>
*/
@NotNull
private String getDye(@NotNull SignSide signSide) {
DyeColor dyeColor = signSide.getColor();
if (dyeColor == null) {
return "None";
}
ChatColor color = ColorHelper.fromColor(dyeColor.getColor());
return color + signSide.getColor().name();
}
/**
* Gets text from a sign side, and appends it to the given string builder
*
* @param signLocation <p>The location of the sign</p>
* @param signSide <p>The sign side to get text from</p>
* @param side <p>The side of the sign to get placeholders from</p>
* @param raw <p>Whether to get the raw text with used formatting codes</p>
*/
@NotNull
private String getPlaceholderSignText(@NotNull Location signLocation, @NotNull SignSide signSide,
@NotNull Side side, boolean raw) {
String[] lines = signSide.getLines();
PlaceholderSign placeholderSign = PlaceholderSigns.getInstance().getSignHandler().getFromLocation(signLocation);
if (placeholderSign == null) {
return getSignText(lines, raw);
}
Map<Integer, String> placeholders = placeholderSign.placeholders().get(side);
for (Map.Entry<Integer, String> entry : placeholders.entrySet()) {
lines[entry.getKey()] = entry.getValue();
}
return getSignText(lines, raw);
}
/**
* Gets text from a sign side, and appends it to the given string builder
*
* @param lines <p>The lines on the sign</p>
* @param raw <p>Whether to get the raw text with used formatting codes</p>
*/
@NotNull
private String getSignText(@NotNull String[] lines, boolean raw) {
StringBuilder output = new StringBuilder();
for (int i = 0; i < 4; i++) {
output.append(i + 1).append(". ");
String line = lines[i];
if (raw) {
output.append(line.replace(ChatColor.COLOR_CHAR, '&'));
} else {
output.append(line);
}
output.append(ChatColor.COLOR_CHAR).append("r\n");
}
return output.toString();
}
}

View File

@@ -13,11 +13,6 @@ public enum PlaceholderSignMessage implements TranslatableMessage {
*/
SUCCESS_CLICK_SIGN_TO_EDIT,
/**
* The message to display when waiting for a sign selection (view)
*/
SUCCESS_CLICK_SIGN_TO_VIEW,
/**
* The message displayed when a player tries to edit a waxed sign without the necessary permission
*/
@@ -42,6 +37,31 @@ public enum PlaceholderSignMessage implements TranslatableMessage {
* The string displayed when a sign property is denied
*/
SIGN_PROPERTY_DENY,
/**
* The message displayed when a player command is used from the console
*/
ERROR_PLAYER_ONLY,
/**
* The message to display when the player is not looking at a sign, which is required
*/
ERROR_NOT_LOOKING_AT_SIGN,
/**
* The message displayed when ready to paste a sign
*/
SUCCESS_CLICK_SIGN_TO_PASTE,
/**
* The message displayed when a sign is successfully pasted
*/
SUCCESS_SIGN_PASTED,
/**
* The message displayed when a protection plugin cancels the sign change event
*/
ERROR_CANCELLED_BY_PROTECTION,
;
@Override

View File

@@ -0,0 +1,14 @@
package net.knarcraft.placeholdersigns.container;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A record of a player's request to copy a sign
*
* @param player <p>The player requesting the sign copy</p>
* @param sign <p>The sign the player wants to copy</p>
*/
public record SignCopyRequest(@NotNull Player player, @NotNull Sign sign) {
}

View File

@@ -1,5 +1,6 @@
package net.knarcraft.placeholdersigns.handler;
import net.knarcraft.placeholdersigns.container.SignCopyRequest;
import net.knarcraft.placeholdersigns.container.SignLineChangeRequest;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -13,17 +14,38 @@ import java.util.Map;
*/
public class PlaceholderSignRequestHandler {
private final Map<Player, SignLineChangeRequest> signChangeRequests;
private final Map<Player, Boolean> signViewRequests;
private final Map<Player, Boolean> placeholderSignViewRequests;
private final @NotNull Map<Player, SignLineChangeRequest> signChangeRequests;
private final Map<Player, SignCopyRequest> signCopyRequests;
/**
* Instantiates a new placeholder sign request handler
*/
public PlaceholderSignRequestHandler() {
this.signChangeRequests = new HashMap<>();
this.signViewRequests = new HashMap<>();
this.placeholderSignViewRequests = new HashMap<>();
this.signCopyRequests = new HashMap<>();
}
/**
* Registers a sign copy request
*
* <p>A sign copy request is basically the result of running the copySign command, which must be stored until the
* player clicks a sign.</p>
*
* @param request <p>The sign copy request to register</p>
*/
public void addSignCopyRequest(@NotNull SignCopyRequest request) {
this.signCopyRequests.put(request.player(), request);
}
/**
* Gets a sign copy request
*
* @param player <p>The player to get the request for</p>
* @return <p>The sign copy request, or null if not found</p>
*/
@Nullable
public SignCopyRequest getSignCopyRequest(@NotNull Player player) {
return this.signCopyRequests.remove(player);
}
/**
@@ -49,54 +71,4 @@ public class PlaceholderSignRequestHandler {
return this.signChangeRequests.remove(player);
}
/**
* Registers a sign view request
*
* @param player <p>The player requesting to view a sign</p>
* @param raw <p>Whether to display the raw text (visible formatting codes)</p>
*/
public void addSignViewRequest(@NotNull Player player, boolean raw) {
this.signViewRequests.put(player, raw);
}
/**
* Checks whether the given player has a sign view request
*
* @param player <p>The player to check</p>
* @return <p>Not null if the player has requested to view a sign</p>
*/
@Nullable
public Boolean getSignViewRequest(@NotNull Player player) {
if (this.signViewRequests.containsKey(player)) {
return this.signViewRequests.remove(player);
} else {
return null;
}
}
/**
* Registers a placeholder view request
*
* @param player <p>The player requesting to view a placeholder sign</p>
* @param raw <p>Whether to display the raw text (visible formatting codes)</p>
*/
public void addPlaceholderSignViewRequest(Player player, boolean raw) {
this.placeholderSignViewRequests.put(player, raw);
}
/**
* Checks whether the given player has a placeholder sign view request
*
* @param player <p>The player to check</p>
* @return <p>Not null if the player has requested to view a sign</p>
*/
@Nullable
public Boolean getPlaceholderSignViewRequest(@NotNull Player player) {
if (this.placeholderSignViewRequests.containsKey(player)) {
return this.placeholderSignViewRequests.remove(player);
} else {
return null;
}
}
}

View File

@@ -1,19 +1,16 @@
package net.knarcraft.placeholdersigns.listener;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.StringReplacer;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.config.PlaceholderSignMessage;
import net.knarcraft.placeholdersigns.container.PlaceholderSign;
import net.knarcraft.placeholdersigns.container.SignCopyRequest;
import net.knarcraft.placeholdersigns.container.SignLineChangeRequest;
import net.knarcraft.placeholdersigns.handler.PlaceholderSignHandler;
import net.knarcraft.placeholdersigns.handler.PlaceholderSignRequestHandler;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Location;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
@@ -24,6 +21,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Objects;
@@ -44,17 +42,13 @@ public class SignClickListener implements Listener {
Player player = event.getPlayer();
PlaceholderSignRequestHandler requestHandler = PlaceholderSigns.getInstance().getRequestHandler();
Boolean hasSignViewRequest = requestHandler.getSignViewRequest(player);
if (hasSignViewRequest != null) {
printSign(sign, player, hasSignViewRequest, false);
// Cancel the event to prevent vanilla behavior
event.setCancelled(true);
return;
}
SignCopyRequest signCopyRequest = requestHandler.getSignCopyRequest(player);
if (signCopyRequest != null) {
if (checkWaxEdit(sign, player, "placeholdersigns.copy.bypass-waxed")) {
return;
}
Boolean hasPlaceholderSignViewRequest = requestHandler.getPlaceholderSignViewRequest(player);
if (hasPlaceholderSignViewRequest != null) {
printSign(sign, player, hasPlaceholderSignViewRequest, true);
pasteSign(signCopyRequest.sign(), sign, player);
event.setCancelled(true);
return;
}
@@ -62,9 +56,7 @@ public class SignClickListener implements Listener {
// Check if the player has run the /editSign command
SignLineChangeRequest request = requestHandler.getSignChangeRequest(player);
if (request != null) {
if (sign.isWaxed() && !player.hasPermission("placeholdersigns.edit.bypass-waxed")) {
PlaceholderSigns.getInstance().getStringFormatter().displayErrorMessage(player,
PlaceholderSignMessage.ERROR_WAXED_NO_PERMISSION);
if (checkWaxEdit(sign, player, "placeholdersigns.edit.bypass-waxed")) {
return;
}
@@ -75,114 +67,138 @@ public class SignClickListener implements Listener {
}
/**
* Prints the current contents of a sign to a player
* Checks if the player is trying to edit/modify a waxed sign without permission
*
* @param sign <p>The sign to print</p>
* @param player <p>The player to display the contents to</p>
* @param raw <p>Whether to get the raw text with used formatting codes</p>
* @param showPlaceholders <p>Whether to show the actual placeholders stored on the sign</p>
* @param sign <p>The sign the player is trying to edit</p>
* @param player <p>The player trying to edit the sign</p>
* @return <p>True if the player is trying to edit a waxed sign without permission</p>
*/
private void printSign(@NotNull Sign sign, @NotNull Player player, boolean raw, boolean showPlaceholders) {
Location location = sign.getLocation();
SignSide front = sign.getSide(Side.FRONT);
SignSide back = sign.getSide(Side.BACK);
String frontLines = showPlaceholders ? getPlaceholderSignText(location, front, Side.FRONT, raw) :
getSignText(front.getLines(), raw);
String backLines = showPlaceholders ? getPlaceholderSignText(location, back, Side.BACK, raw) :
getSignText(back.getLines(), raw);
StringFormatter stringFormatter = PlaceholderSigns.getInstance().getStringFormatter();
StringReplacer replacer = new StringReplacer(stringFormatter.getUnformattedColoredMessage(
PlaceholderSignMessage.SUCCESS_SIGN_CONTENTS));
replacer.add("{frontLines}", frontLines);
replacer.add("{backLines}", backLines);
replacer.add("{frontDye}", getDye(front));
replacer.add("{frontGlow}", getStatus(front.isGlowingText()));
replacer.add("{backDye}", getDye(back));
replacer.add("{backGlow}", getStatus(back.isGlowingText()));
replacer.add("{waxed}", getStatus(sign.isWaxed()));
player.sendMessage(replacer.replace());
}
/**
* Gets a description of the status of a sign property
*
* @param isTrue <p>Whether the property is true</p>
* @return <p>A description of the property's current status</p>
*/
@NotNull
private String getStatus(boolean isTrue) {
StringFormatter stringFormatter = PlaceholderSigns.getInstance().getStringFormatter();
if (isTrue) {
return stringFormatter.getUnformattedColoredMessage(PlaceholderSignMessage.SIGN_PROPERTY_CONFIRM);
private boolean checkWaxEdit(@NotNull Sign sign, @NotNull Player player, @NotNull String permissionNode) {
if (sign.isWaxed() && !player.hasPermission(permissionNode)) {
PlaceholderSigns.getInstance().getStringFormatter().displayErrorMessage(player,
PlaceholderSignMessage.ERROR_WAXED_NO_PERMISSION);
return true;
} else {
return stringFormatter.getUnformattedColoredMessage(PlaceholderSignMessage.SIGN_PROPERTY_DENY);
return false;
}
}
/**
* Gets a description of a dye applied to a sign
* Pastes the data from the source sign to the target sign
*
* @param signSide <p>The sign side to get the dye of</p>
* @return <p>The description of the applied dye</p>
* @param source <p>The source sign to copy data from</p>
* @param target <p>The target sign to paste to</p>
* @param player <p>The player pasting the sign</p>
*/
@NotNull
private String getDye(@NotNull SignSide signSide) {
DyeColor dyeColor = signSide.getColor();
if (dyeColor == null) {
return "None";
private void pasteSign(@NotNull Sign source, @NotNull Sign target, @NotNull Player player) {
StringFormatter stringFormatter = PlaceholderSigns.getInstance().getStringFormatter();
PlaceholderSign placeholderSign = PlaceholderSigns.getInstance().getSignHandler().getFromLocation(source.getLocation());
SignSide sourceFront = source.getSide(Side.FRONT);
SignSide sourceBack = source.getSide(Side.BACK);
SignSide targetFront = target.getSide(Side.FRONT);
SignSide targetBack = target.getSide(Side.BACK);
// Get the lines, with placeholders replaced, and the sign change event processed
String[] frontLines = getFinalLines(target, sourceFront, Side.FRONT, placeholderSign, player);
if (frontLines == null) {
stringFormatter.displayErrorMessage(player, PlaceholderSignMessage.ERROR_CANCELLED_BY_PROTECTION);
return;
}
String[] backLines = getFinalLines(target, sourceBack, Side.BACK, placeholderSign, player);
if (backLines == null) {
stringFormatter.displayErrorMessage(player, PlaceholderSignMessage.ERROR_CANCELLED_BY_PROTECTION);
return;
}
ChatColor color = ColorHelper.fromColor(dyeColor.getColor());
return color + signSide.getColor().name();
// Update the lines on both sides of the target sign
for (int i = 0; i < frontLines.length; i++) {
targetFront.setLine(i, ColorHelper.translateColorCodes(frontLines[i], ColorConversion.RGB));
}
for (int i = 0; i < backLines.length; i++) {
targetBack.setLine(i, ColorHelper.translateColorCodes(backLines[i], ColorConversion.RGB));
}
// Copy options
copySignOptions(source, target);
// Apply changes
target.update();
stringFormatter.displaySuccessMessage(player, PlaceholderSignMessage.SUCCESS_SIGN_PASTED);
}
/**
* Gets text from a sign side, and appends it to the given string builder
* Gets the final lines from a sign side, after inserting placeholders and running a sign change event
*
* @param signLocation <p>The location of the sign</p>
* @param signSide <p>The sign side to get text from</p>
* @param side <p>The side of the sign to get placeholders from</p>
* @param raw <p>Whether to get the raw text with used formatting codes</p>
* @param sign <p>The sign that's changed</p>
* @param signSide <p>The side of the sign to get lines from</p>
* @param side <p>The side that's processed</p>
* @param placeholderSign <p>The placeholder sign corresponding to the sign, if any</p>
* @param player <p>The player attempting to paste the sign</p>
* @return <p>The final lines, or null if the event was cancelled</p>
*/
@NotNull
private String getPlaceholderSignText(@NotNull Location signLocation, @NotNull SignSide signSide,
@NotNull Side side, boolean raw) {
String[] lines = signSide.getLines();
PlaceholderSign placeholderSign = PlaceholderSigns.getInstance().getSignHandler().getFromLocation(signLocation);
if (placeholderSign == null) {
return getSignText(lines, raw);
@Nullable
private String[] getFinalLines(@NotNull Sign sign, @NotNull SignSide signSide, @NotNull Side side,
@Nullable PlaceholderSign placeholderSign, @NotNull Player player) {
String[] sideLines = signSide.getLines();
if (placeholderSign != null) {
Map<Side, Map<Integer, String>> placeholders = placeholderSign.placeholders();
loadPlaceholders(side, sideLines, placeholders);
}
Map<Integer, String> placeholders = placeholderSign.placeholders().get(side);
for (Map.Entry<Integer, String> entry : placeholders.entrySet()) {
lines[entry.getKey()] = entry.getValue();
// Run the sign change event to allow protection plugins to cancel, and allow the sign text listener to trigger
SignChangeEvent changeEvent = new SignChangeEvent(Objects.requireNonNull(sign.getBlock()), player, sideLines, side);
Bukkit.getPluginManager().callEvent(changeEvent);
if (changeEvent.isCancelled()) {
return null;
}
return getSignText(lines, raw);
return changeEvent.getLines();
}
/**
* Gets text from a sign side, and appends it to the given string builder
* Copies all sign option (dye, glowing, waxed) from the source to the target
*
* @param lines <p>The lines on the sign</p>
* @param raw <p>Whether to get the raw text with used formatting codes</p>
* @param source <p>The source sign</p>
* @param target <p>The target sign</p>
*/
@NotNull
private String getSignText(@NotNull String[] lines, boolean raw) {
StringBuilder output = new StringBuilder();
private void copySignOptions(@NotNull Sign source, @NotNull Sign target) {
SignSide sourceFront = source.getSide(Side.FRONT);
SignSide sourceBack = source.getSide(Side.BACK);
SignSide targetFront = target.getSide(Side.FRONT);
SignSide targetBack = target.getSide(Side.BACK);
// Copy glowing state
targetFront.setGlowingText(sourceFront.isGlowingText());
targetBack.setGlowingText(sourceBack.isGlowingText());
// Copy applied dye
targetFront.setColor(sourceFront.getColor());
targetBack.setColor(sourceBack.getColor());
// Apply waxed state
target.setWaxed(source.isWaxed());
}
/**
* Loads placeholders from one side of a sign
*
* @param side <p>The side to load from</p>
* @param lines <p>The array to write placeholders to</p>
* @param placeholders <p>The placeholders to load</p>
*/
private void loadPlaceholders(@NotNull Side side, @NotNull String[] lines,
@NotNull Map<Side, Map<Integer, String>> placeholders) {
if (!placeholders.containsKey(side)) {
return;
}
Map<Integer, String> sidePlaceholders = placeholders.get(side);
for (int i = 0; i < 4; i++) {
output.append(i + 1).append(". ");
String line = lines[i];
if (raw) {
output.append(line.replace(ChatColor.COLOR_CHAR, '&'));
} else {
output.append(line);
if (sidePlaceholders.containsKey(i)) {
lines[i] = sidePlaceholders.get(i);
}
output.append(ChatColor.COLOR_CHAR).append("r\n");
}
return output.toString();
}
/**
@@ -220,6 +236,8 @@ public class SignClickListener implements Listener {
// Restore the old placeholder if the action didn't complete
placeholderSign.placeholders().get(side).put(request.line(), oldPlaceholder);
}
PlaceholderSigns.getInstance().getStringFormatter().displayErrorMessage(player,
PlaceholderSignMessage.ERROR_CANCELLED_BY_PROTECTION);
return;
}