Changes and fixes a lot of things
All checks were successful
KnarCraft/PlaceholderSigns/pipeline/head This commit looks good

Adds a viewSign command which allows a player to see the entirety of a sign, including any part that overflows the character limit.
Changes the save structure for placeholder signs. This change was necessary to support storage of placeholders on both sides of signs, which is also implemented in this commit.
Updates Spigot
Probably fixes a few bugs
This commit is contained in:
Kristian Knarvik 2024-04-04 15:46:12 +02:00
parent 788be0cdcd
commit 51564570ad
12 changed files with 289 additions and 107 deletions

View File

@ -82,7 +82,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<version>1.20.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -2,8 +2,9 @@ package net.knarcraft.placeholdersigns;
import me.clip.placeholderapi.PlaceholderAPI;
import net.knarcraft.placeholdersigns.command.EditSignCommand;
import net.knarcraft.placeholdersigns.container.LineChangeRequest;
import net.knarcraft.placeholdersigns.command.ViewSignCommand;
import net.knarcraft.placeholdersigns.container.PlaceholderSign;
import net.knarcraft.placeholdersigns.container.SignLineChangeRequest;
import net.knarcraft.placeholdersigns.handler.PlaceholderSignHandler;
import net.knarcraft.placeholdersigns.listener.SignBreakListener;
import net.knarcraft.placeholdersigns.listener.SignClickListener;
@ -13,8 +14,8 @@ import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.command.PluginCommand;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@ -31,7 +32,8 @@ public final class PlaceholderSigns extends JavaPlugin {
private static PlaceholderSigns instance;
private PlaceholderSignHandler signHandler;
private Map<Player, LineChangeRequest> changeRequests;
private Map<Player, SignLineChangeRequest> signChangeRequests;
private Map<Player, Void> signViewRequests;
/**
* Gets an instance of this plugin
@ -59,8 +61,8 @@ public final class PlaceholderSigns extends JavaPlugin {
*
* @param request <p>The sign change request to register</p>
*/
public void addChangeRequest(@NotNull LineChangeRequest request) {
changeRequests.put(request.player(), request);
public void addSignChangeRequest(@NotNull SignLineChangeRequest request) {
signChangeRequests.put(request.player(), request);
}
/**
@ -69,23 +71,40 @@ public final class PlaceholderSigns extends JavaPlugin {
* @param player <p>The player to get the request for</p>
* @return <p>The sign change request, or null if not found</p>
*/
public @Nullable LineChangeRequest getChangeRequest(@NotNull Player player) {
return changeRequests.remove(player);
public @Nullable SignLineChangeRequest getSignChangeRequest(@NotNull Player player) {
return signChangeRequests.remove(player);
}
@Override
public void onLoad() {
super.onLoad();
// Register serialization classes
ConfigurationSerialization.registerClass(PlaceholderSign.class);
ConfigurationSerialization.registerClass(PlaceholderSignHandler.class);
/**
* Registers a sign view request
*
* @param player <p>The player requesting to view a sign</p>
*/
public void addSignViewRequest(@NotNull Player player) {
signViewRequests.put(player, null);
}
/**
* Checks whether the given player has a sign view request
*
* @param player <p>The player to check</p>
* @return <p>True if the player has requested to view a sign</p>
*/
public boolean hasSignViewRequest(@NotNull Player player) {
if (signViewRequests.containsKey(player)) {
signViewRequests.remove(player);
return true;
} else {
return false;
}
}
@Override
public void onEnable() {
instance = this;
signHandler = new PlaceholderSignHandler();
changeRequests = new HashMap<>();
signChangeRequests = new HashMap<>();
signViewRequests = new HashMap<>();
signHandler.load();
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") == null) {
@ -105,6 +124,11 @@ public final class PlaceholderSigns extends JavaPlugin {
if (editCommand != null) {
editCommand.setExecutor(new EditSignCommand());
}
PluginCommand viewCommand = Bukkit.getPluginCommand("viewSign");
if (viewCommand != null) {
viewCommand.setExecutor(new ViewSignCommand());
}
}
@Override
@ -130,36 +154,52 @@ public final class PlaceholderSigns extends JavaPlugin {
}
// Update placeholders
Map<Integer, String> placeholders = placeholderSign.placeholders();
String[] lines = sign.getSide(Side.FRONT).getLines();
boolean updateRequired = false;
for (int i = 0; i < lines.length; i++) {
String oldText = sign.getSide(Side.FRONT).getLine(i);
// The new text of the sign is either the same, or the original placeholder
String newText;
if (!placeholders.containsKey(i) || placeholders.get(i) == null) {
newText = oldText;
} else {
newText = PlaceholderAPI.setPlaceholders(null, placeholders.get(i));
}
// Convert color codes
newText = ColorHelper.translateAllColorCodes(newText);
// Only change the line if the text has changed
if (!newText.equals(oldText)) {
sign.getSide(Side.FRONT).setLine(i, newText);
updateRequired = true;
}
}
Map<Side, Map<Integer, String>> placeholders = placeholderSign.placeholders();
String[] frontLines = sign.getSide(Side.FRONT).getLines();
String[] backLines = sign.getSide(Side.BACK).getLines();
// Only update the sign if the text has changed
if (updateRequired) {
if (updatePlaceholders(frontLines, placeholders.get(Side.FRONT), sign.getSide(Side.FRONT)) ||
updatePlaceholders(backLines, placeholders.get(Side.BACK), sign.getSide(Side.BACK))) {
sign.update();
}
}
}
/**
* Updates the values of placeholders on a sign
*
* @param lines <p>The sign's current lines</p>
* @param placeholders <p>The sign's original placeholder lines</p>
* @param signSide <p>The side of the sign to update placeholders for</p>
* @return <p>True if text has been changed, and the sign needs to be updated</p>
*/
private boolean updatePlaceholders(@NotNull String[] lines, @NotNull Map<Integer, String> placeholders,
@NotNull SignSide signSide) {
boolean changed = false;
for (int i = 0; i < lines.length; i++) {
String oldText = signSide.getLine(i);
// The new text of the sign is either the same, or the original placeholder
String newText;
if (!placeholders.containsKey(i) || placeholders.get(i) == null) {
newText = oldText;
} else {
newText = PlaceholderAPI.setPlaceholders(null, placeholders.get(i));
}
// Convert color codes
newText = ColorHelper.translateAllColorCodes(newText);
// Only change the line if the text has changed
if (!newText.equals(oldText)) {
signSide.setLine(i, newText);
changed = true;
}
}
return changed;
}
}

View File

@ -1,7 +1,7 @@
package net.knarcraft.placeholdersigns.command;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.container.LineChangeRequest;
import net.knarcraft.placeholdersigns.container.SignLineChangeRequest;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@ -59,8 +59,8 @@ public class EditSignCommand implements TabExecutor {
}
// Register the line change request
LineChangeRequest request = new LineChangeRequest(player, lineNumber - 1, builder.toString());
PlaceholderSigns.getInstance().addChangeRequest(request);
SignLineChangeRequest request = new SignLineChangeRequest(player, lineNumber - 1, builder.toString());
PlaceholderSigns.getInstance().addSignChangeRequest(request);
commandSender.sendMessage("Please click the sign you want to change.");
return true;

View File

@ -0,0 +1,38 @@
package net.knarcraft.placeholdersigns.command;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
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 lines on a sign
*/
public class ViewSignCommand implements TabExecutor {
@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().addSignViewRequest(player);
commandSender.sendMessage("Please click the sign you want 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,10 +1,9 @@
package net.knarcraft.placeholdersigns.container;
import org.bukkit.Location;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.block.sign.Side;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
@ -13,27 +12,6 @@ import java.util.Map;
* @param location <p>The location of the sign</p>
* @param placeholders <p>The original placeholders typed on the sign</p>
*/
public record PlaceholderSign(Location location,
Map<Integer, String> placeholders) implements ConfigurationSerializable {
@Override
@NotNull
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("location", location);
data.put("placeholders", placeholders);
return data;
}
/**
* Deserializes the placeholder-sign specified in the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized placeholder sign</p>
*/
@SuppressWarnings({"unchecked", "unused"})
public static PlaceholderSign deserialize(Map<String, Object> data) {
return new PlaceholderSign((Location) data.get("location"), (Map<Integer, String>) data.get("placeholders"));
}
public record PlaceholderSign(@NotNull Location location, @NotNull Map<Side, Map<Integer, String>> placeholders) {
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.placeholdersigns.container;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A record of a player's request to change a sign
@ -9,5 +10,5 @@ import org.bukkit.entity.Player;
* @param line <p>The line the player wants to change</p>
* @param text <p>The new text the player provided for the line</p>
*/
public record LineChangeRequest(Player player, int line, String text) {
public record SignLineChangeRequest(@NotNull Player player, int line, @NotNull String text) {
}

View File

@ -2,10 +2,14 @@ package net.knarcraft.placeholdersigns.handler;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.container.PlaceholderSign;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.sign.Side;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
@ -13,12 +17,13 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* A handler for keeping track of placeholder signs
*/
public class PlaceholderSignHandler implements ConfigurationSerializable {
public class PlaceholderSignHandler {
private static final File signsFile = new File(PlaceholderSigns.getInstance().getDataFolder(), "signs.yml");
@ -40,6 +45,7 @@ public class PlaceholderSignHandler implements ConfigurationSerializable {
* @param location <p>The location of the sign</p>
* @return <p>The sign at the location, or null if no such sign exists</p>
*/
@Nullable
public PlaceholderSign getFromLocation(@NotNull Location location) {
return locationLookup.get(location);
}
@ -70,10 +76,58 @@ public class PlaceholderSignHandler implements ConfigurationSerializable {
* Loads all placeholder signs from disk
*/
public void load() {
this.placeholderSigns = new HashSet<>();
this.locationLookup = new HashMap<>();
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(signsFile);
PlaceholderSignHandler loadedHandler = (PlaceholderSignHandler) configuration.get("signHandler");
this.placeholderSigns = loadedHandler != null ? loadedHandler.placeholderSigns : new HashSet<>();
this.locationLookup = loadedHandler != null ? loadedHandler.locationLookup : new HashMap<>();
ConfigurationSection signSection = configuration.getConfigurationSection("signs");
if (signSection == null) {
PlaceholderSigns.getInstance().getLogger().log(Level.INFO, "PlaceholderSigns found no signs to load");
return;
}
for (String key : signSection.getKeys(false)) {
String[] locationInfo = key.split(",");
World world = Bukkit.getWorld(UUID.fromString(locationInfo[0]));
double x = Integer.parseInt(locationInfo[1]);
double y = Integer.parseInt(locationInfo[2]);
double z = Integer.parseInt(locationInfo[3]);
Location signLocation = new Location(world, x, y, z);
Map<Side, Map<Integer, String>> allPlaceholders = new HashMap<>();
Map<Integer, String> frontPlaceholders = new HashMap<>();
Map<Integer, String> backPlaceholders = new HashMap<>();
loadPlaceholders(signSection, key + ".placeholders.front.", frontPlaceholders);
loadPlaceholders(signSection, key + ".placeholders.back.", backPlaceholders);
allPlaceholders.put(Side.FRONT, frontPlaceholders);
allPlaceholders.put(Side.BACK, backPlaceholders);
PlaceholderSign sign = new PlaceholderSign(signLocation, allPlaceholders);
this.placeholderSigns.add(sign);
this.locationLookup.put(signLocation, sign);
}
}
/**
* Loads placeholders from one side of a sign
*
* @param signSection <p>The configuration section to read</p>
* @param configurationKey <p>The configuration key pointing to the placeholders</p>
* @param placeholderMap <p>The map to add read placeholders to</p>
*/
private void loadPlaceholders(@NotNull ConfigurationSection signSection, @NotNull String configurationKey,
@NotNull Map<Integer, String> placeholderMap) {
for (int i = 0; i < 4; i++) {
String placeholderKey = configurationKey + i;
if (!signSection.contains(placeholderKey)) {
continue;
}
String placeholder = signSection.getString(placeholderKey);
placeholderMap.put(i, placeholder);
}
}
/**
@ -82,37 +136,46 @@ public class PlaceholderSignHandler implements ConfigurationSerializable {
public void save() {
try {
YamlConfiguration configuration = new YamlConfiguration();
configuration.set("signHandler", this);
ConfigurationSection signsSection = configuration.createSection("signs");
for (PlaceholderSign sign : placeholderSigns) {
saveSign(signsSection, sign);
}
configuration.save(signsFile);
} catch (IOException exception) {
PlaceholderSigns.getInstance().getLogger().log(Level.SEVERE, "Unable to save placeholder signs!");
}
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("signs", this.placeholderSigns);
return data;
}
/**
* Deserializes the given placeholder sign handler data
* Saves a sign to the given configuration section
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized sign handler</p>
* @param section <p>The configuration section to save to</p>
* @param sign <p>The sign to save</p>
*/
@SuppressWarnings({"unchecked", "unused"})
public static PlaceholderSignHandler deserialize(@NotNull Map<String, Object> data) {
PlaceholderSignHandler placeholderSignHandler = new PlaceholderSignHandler();
placeholderSignHandler.placeholderSigns = (Set<PlaceholderSign>) data.get("signs");
Map<Location, PlaceholderSign> lookup = new HashMap<>();
for (PlaceholderSign sign : placeholderSignHandler.placeholderSigns) {
lookup.put(sign.location(), sign);
private void saveSign(@NotNull ConfigurationSection section, @NotNull PlaceholderSign sign) {
Location location = sign.location();
if (location.getWorld() == null) {
return;
}
String key = location.getWorld().getUID() + "," + location.getBlockX() + "," + location.getBlockY() +
"," + location.getBlockZ();
String frontKey = key + ".placeholders.front";
String backKey = key + ".placeholders.back";
Map<Integer, String> frontPlaceholders = sign.placeholders().get(Side.FRONT);
if (frontPlaceholders != null) {
for (Map.Entry<Integer, String> entry : frontPlaceholders.entrySet()) {
section.set(frontKey + "." + entry.getKey(), entry.getValue());
}
}
Map<Integer, String> backPlaceholders = sign.placeholders().get(Side.BACK);
if (backPlaceholders != null) {
for (Map.Entry<Integer, String> entry : backPlaceholders.entrySet()) {
section.set(backKey + "." + entry.getKey(), entry.getValue());
}
}
placeholderSignHandler.locationLookup = lookup;
return placeholderSignHandler;
}
}

View File

@ -9,6 +9,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.jetbrains.annotations.NotNull;
/**
* A listener for placeholder signs being broken
@ -16,7 +17,7 @@ import org.bukkit.event.block.BlockBreakEvent;
public class SignBreakListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onSignBreak(BlockBreakEvent event) {
public void onSignBreak(@NotNull BlockBreakEvent event) {
Block block = event.getBlock();
if (!(block.getState() instanceof Sign)) {

View File

@ -1,18 +1,22 @@
package net.knarcraft.placeholdersigns.listener;
import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.container.LineChangeRequest;
import net.knarcraft.placeholdersigns.container.SignLineChangeRequest;
import net.knarcraft.placeholdersigns.util.ColorHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* A listener for placeholder signs being clicked
@ -20,7 +24,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
public class SignClickListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onSignClick(PlayerInteractEvent event) {
public void onSignClick(@NotNull PlayerInteractEvent event) {
// Ignore if not a clicked sign
if (!event.hasBlock() || event.getClickedBlock() == null ||
!(event.getClickedBlock().getState() instanceof Sign sign)) {
@ -28,25 +32,70 @@ public class SignClickListener implements Listener {
}
Player player = event.getPlayer();
boolean hasSignViewRequest = PlaceholderSigns.getInstance().hasSignViewRequest(player);
if (hasSignViewRequest) {
printSign(sign, player);
}
if (sign.isWaxed() && !player.hasPermission("placeholdersigns.edit.bypass-waxed")) {
player.sendMessage(ChatColor.RED + "You do not have the necessary permissions to edit a waxed sign.");
return;
}
// Check if the player has run the /editSign command
LineChangeRequest request = PlaceholderSigns.getInstance().getChangeRequest(player);
if (request == null) {
return;
SignLineChangeRequest request = PlaceholderSigns.getInstance().getSignChangeRequest(player);
if (request != null) {
doSignChange(sign, request, event, player);
}
}
/**
* 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) {
StringBuilder builder = new StringBuilder();
getSignText(sign.getSide(Side.FRONT), "Front:", builder);
getSignText(sign.getSide(Side.BACK), "Back:", builder);
player.sendMessage(builder.toString());
}
/**
* Gets text from a sign side, and appends it to the given string builder
*
* @param signSide <p>The sign side to get text from</p>
* @param header <p>The header to display before printing the sign test</p>
* @param builder <p>The string builder to append the text to</p>
*/
private void getSignText(@NotNull SignSide signSide, @NotNull String header, @NotNull StringBuilder builder) {
builder.append(header).append("\n");
for (int i = 0; i < 4; i++) {
builder.append(signSide.getLine(i)).append("\n");
}
}
/**
* Perform the changing of a sign according to the given sign change request
*
* @param sign <p>The sign to be changed</p>
* @param request <p>The sign line change request to perform</p>
* @param event <p>The interaction event that triggered this</p>
* @param player <p>The player triggering the sign change</p>
*/
private void doSignChange(@NotNull Sign sign, @NotNull SignLineChangeRequest request,
@NotNull PlayerInteractEvent event, @NotNull Player player) {
// Cancel the event to prevent vanilla behavior
event.setCancelled(true);
String[] lines = sign.getSide(Side.FRONT).getLines();
lines[request.line()] = request.text();
// Run the sign change event to allow protection plugins to cancel, and allow the sign text listener to trigger
SignChangeEvent changeEvent = new SignChangeEvent(event.getClickedBlock(), player, lines, Side.FRONT);
SignChangeEvent changeEvent = new SignChangeEvent(Objects.requireNonNull(event.getClickedBlock()),
player, lines, Side.FRONT);
Bukkit.getPluginManager().callEvent(changeEvent);
if (changeEvent.isCancelled()) {
return;

View File

@ -5,10 +5,12 @@ import net.knarcraft.placeholdersigns.PlaceholderSigns;
import net.knarcraft.placeholdersigns.container.PlaceholderSign;
import net.knarcraft.placeholdersigns.handler.PlaceholderSignHandler;
import org.bukkit.Location;
import org.bukkit.block.sign.Side;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
@ -19,7 +21,7 @@ import java.util.Map;
public class SignTextListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSignCreateOrEdit(SignChangeEvent event) {
public void onSignCreateOrEdit(@NotNull SignChangeEvent event) {
// Only check for placeholders if the player is allowed to
if (!event.getPlayer().hasPermission("placeholdersigns.placeholder")) {
return;
@ -43,14 +45,16 @@ public class SignTextListener implements Listener {
PlaceholderSign existingSign = signHandler.getFromLocation(location);
if (!placeholders.isEmpty() && existingSign == null) {
Map<Side, Map<Integer, String>> placeholderSide = new HashMap<>();
placeholderSide.put(event.getSide(), placeholders);
// Register a new placeholder sign
PlaceholderSign placeholderSign = new PlaceholderSign(event.getBlock().getLocation(), placeholders);
PlaceholderSign placeholderSign = new PlaceholderSign(event.getBlock().getLocation(), placeholderSide);
signHandler.registerSign(placeholderSign);
} else if (!placeholders.isEmpty()) {
// Overwrite the placeholders of the existing placeholder sign
for (Map.Entry<Integer, String> entry : placeholders.entrySet()) {
existingSign.placeholders().put(entry.getKey(), entry.getValue());
}
Map<Integer, String> existing = existingSign.placeholders().get(event.getSide());
existing.putAll(placeholders);
signHandler.save();
}
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.placeholdersigns.util;
import net.md_5.bungee.api.ChatColor;
import org.jetbrains.annotations.NotNull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -20,7 +21,7 @@ public final class ColorHelper {
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
public static String translateAllColorCodes(@NotNull String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("&?(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);

View File

@ -10,6 +10,10 @@ commands:
usage: /<command> <line> [text] [text] ...
permission: placeholdersigns.edit
description: Changes the line of a sign, without a text limit, and with color conversion
viewSign:
usage: /<command> <line> [text] [text] ...
permission: placeholdersigns.view
description: Displays the contents of a sign in the chat (useful for lines exceeding the viewable area)
permissions:
placeholdersigns.*:
@ -32,3 +36,6 @@ permissions:
placeholdersigns.placeholder:
description: Allows a player to make signs containing placeholders
default: false
placeholdersigns.view:
description: Allows a player to see the full text of a sign in the chat
default: true