From ea84d5ae3f44d2264c45aba815c814720faab965 Mon Sep 17 00:00:00 2001 From: Philipp Bolte Date: Sun, 12 Sep 2021 21:50:03 +0200 Subject: [PATCH] HEX support - Added HEX support, thanks to @harry0198 and his HexiTextLib! He did 99% of the work and I just needed to make so small adjustments. #21 - Catch possible NPE with getOfflinePlayer #32 - Use Logger instead of sysout #31 - Let ServerlistMOTD shut up and stop spamming console :laughing: --- plugin.yml | 2 +- .../bolte/serverlistmotd/SpigotConfig.java | 20 +-- .../events/ProtocolLibImplementation.java | 3 +- .../serverlistmotd/motd/BanManagerMotd.java | 2 + .../bolte/serverlistmotd/motd/BanMotd.java | 2 + .../serverlistmotd/motd/ClassicMotd.java | 2 + .../serverlistmotd/motd/MaxBansMotd.java | 2 + .../bolte/serverlistmotd/motd/RandomMotd.java | 2 + .../motd/RestrictedModeMotd.java | 3 +- .../serverlistmotd/motd/WhitelistMotd.java | 3 +- .../bolte/serverlistmotd/util/Gradient.java | 80 +++++++++++ .../serverlistmotd/util/HexResolver.java | 135 ++++++++++++++++++ src/cloud/bolte/serverlistmotd/util/IO.java | 9 +- .../serverlistmotd/util/PapiIntegration.java | 2 +- .../variables/PlayerVariable.java | 14 +- 15 files changed, 257 insertions(+), 24 deletions(-) create mode 100644 src/cloud/bolte/serverlistmotd/util/Gradient.java create mode 100644 src/cloud/bolte/serverlistmotd/util/HexResolver.java diff --git a/plugin.yml b/plugin.yml index adba364..8c7a497 100755 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: ServerlistMOTD author: Strumswell -version: X-2020-09-05 +version: X-2021-09-12-BETA description: Change your Serverlist Motd! load: POSTWORLD depend: [ProtocolLib] diff --git a/src/cloud/bolte/serverlistmotd/SpigotConfig.java b/src/cloud/bolte/serverlistmotd/SpigotConfig.java index ec09a98..5941639 100644 --- a/src/cloud/bolte/serverlistmotd/SpigotConfig.java +++ b/src/cloud/bolte/serverlistmotd/SpigotConfig.java @@ -392,11 +392,11 @@ public static void saveSmotdConfig() { */ public void configWorldCheck() { if (Bukkit.getWorld(getWeatherWorld()) == null || Bukkit.getWorld(getTimeWorld()) == null) { - System.out.println("[ServerlistMOTD] ------------------------"); + Bukkit.getLogger().info("------------------------"); //Informing user of mismatch Bukkit.getLogger().severe( - "[ServerlistMOTD] CAN'T FIND THE DEFINED WORLD FROM YOUR CONFIG!"); - System.out.println("[ServerlistMOTD] Searching for available world..."); + "CAN'T FIND THE DEFINED WORLD FROM YOUR CONFIG!"); + Bukkit.getLogger().info("Searching for available world..."); //Search shortest world name String worldName = ""; @@ -416,9 +416,9 @@ public void configWorldCheck() { main.getConfig().set("Variables.TimeVariable.World", worldName); main.getConfig().set("Variables.WeatherVariable.World", worldName); SpigotConfig.saveSmotdConfig(); - System.out.println("[ServerlistMOTD] Found '" + worldName + "‘ and saved it to config."); - System.out.println("[ServerlistMOTD] We're good now. ;-)"); - System.out.println("[ServerlistMOTD] ------------------------"); + Bukkit.getLogger().info("Found '" + worldName + "‘ and saved it to config."); + Bukkit.getLogger().info("We're good now. ;-)"); + Bukkit.getLogger().info("------------------------"); } } @@ -431,7 +431,7 @@ public static void migrateConfig() { File newFile = new File(main.getDataFolder(), "config_old.yml"); if (newFile.exists()) { - System.out.println("[ServerlistMOTD] Remove your old config.yml!"); + Bukkit.getLogger().severe("Remove your old config.yml!"); Bukkit.getPluginManager().disablePlugin(main); } @@ -439,16 +439,16 @@ public static void migrateConfig() { if (fileRenamed) { main.saveDefaultConfig(); main.reloadConfig(); - System.out.println("[ServerlistMOTD] Renamed old config and created new config!"); + Bukkit.getLogger().info("Renamed old config and created new config!"); } } if (main.getConfig().getDouble("DoNOTtouchMe") == 10.0) { - System.out.println("[ServerlistMOTD] Adding new features to config.yml..."); + Bukkit.getLogger().info("Adding new features to config.yml..."); main.getConfig().set("Slots.OutdatedClientText.Enable", false); main.getConfig().set("Slots.OutdatedClientText.Message", "Use Minecraft 1.16 &r&7%realonline%&8/&7%realslots%"); main.getConfig().set("DoNOTtouchMe", 10.1); saveSmotdConfig(); - System.out.println("[ServerlistMOTD] Config successfully migrated to new version."); + Bukkit.getLogger().info("Config successfully migrated to new version."); } } } diff --git a/src/cloud/bolte/serverlistmotd/events/ProtocolLibImplementation.java b/src/cloud/bolte/serverlistmotd/events/ProtocolLibImplementation.java index 2e6af22..c276495 100644 --- a/src/cloud/bolte/serverlistmotd/events/ProtocolLibImplementation.java +++ b/src/cloud/bolte/serverlistmotd/events/ProtocolLibImplementation.java @@ -14,6 +14,7 @@ import cloud.bolte.serverlistmotd.slots.OutdatedClientText; import cloud.bolte.serverlistmotd.slots.SlotsPlusOne; import cloud.bolte.serverlistmotd.slots.VersionText; +import org.bukkit.Bukkit; /* * ServerlistMOTD (c) by Strumswell, Philipp Bolte @@ -37,7 +38,7 @@ private static void setPlugin(Main plugin) { } public void listenToServerlistPackets() { - System.out.println("[ServerlistMOTD] Hooking into ProtocolLib."); + Bukkit.getLogger().info("Hooking into ProtocolLib."); ProtocolLibrary.getProtocolManager().addPacketListener( new PacketAdapter(PacketAdapter.params(main, PacketType.Status.Server.SERVER_INFO).optionAsync()) { @Override diff --git a/src/cloud/bolte/serverlistmotd/motd/BanManagerMotd.java b/src/cloud/bolte/serverlistmotd/motd/BanManagerMotd.java index 264b7f6..e98b5f5 100644 --- a/src/cloud/bolte/serverlistmotd/motd/BanManagerMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/BanManagerMotd.java @@ -3,6 +3,7 @@ import java.net.InetAddress; import java.util.Date; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; @@ -74,6 +75,7 @@ public String formatMotd(String motd, InetAddress ip) { .replace("%expyear%", ban.banExpDateYear(playerName)); } formattedMotd = PapiIntegration.replaceVariables(player, formattedMotd); + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/motd/BanMotd.java b/src/cloud/bolte/serverlistmotd/motd/BanMotd.java index d4f5b47..48d70bb 100644 --- a/src/cloud/bolte/serverlistmotd/motd/BanMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/BanMotd.java @@ -2,6 +2,7 @@ import java.net.InetAddress; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.BanList.Type; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -67,6 +68,7 @@ public String formatMotd(String motd, InetAddress ip) { // FULL BAN } else formattedMotd = formattedMotd.replace("%reason%", ban.banReason(playerName)); formattedMotd = PapiIntegration.replaceVariables(player, formattedMotd); + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/motd/ClassicMotd.java b/src/cloud/bolte/serverlistmotd/motd/ClassicMotd.java index 70bdfc2..7b9889c 100644 --- a/src/cloud/bolte/serverlistmotd/motd/ClassicMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/ClassicMotd.java @@ -2,6 +2,7 @@ import java.net.InetAddress; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.event.server.ServerListPingEvent; @@ -50,6 +51,7 @@ public String formatMotd(String motd, InetAddress ip) { } else { formattedMotd = PapiIntegration.replaceVariables(null, formattedMotd); } + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/motd/MaxBansMotd.java b/src/cloud/bolte/serverlistmotd/motd/MaxBansMotd.java index d9bdf88..e92b87e 100644 --- a/src/cloud/bolte/serverlistmotd/motd/MaxBansMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/MaxBansMotd.java @@ -2,6 +2,7 @@ import java.net.InetAddress; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; @@ -69,6 +70,7 @@ public String formatMotd(String motd, InetAddress ip) { .replace("%expyear%", ban.banExpDateYear(playerName)); } formattedMotd = PapiIntegration.replaceVariables(player, formattedMotd); + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/motd/RandomMotd.java b/src/cloud/bolte/serverlistmotd/motd/RandomMotd.java index 8dab101..580262b 100644 --- a/src/cloud/bolte/serverlistmotd/motd/RandomMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/RandomMotd.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Random; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.event.server.ServerListPingEvent; @@ -56,6 +57,7 @@ public String formatMotd(String motd, InetAddress ip) { } else { formattedMotd = PapiIntegration.replaceVariables(null, formattedMotd); } + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/motd/RestrictedModeMotd.java b/src/cloud/bolte/serverlistmotd/motd/RestrictedModeMotd.java index 6e4f499..a67470d 100644 --- a/src/cloud/bolte/serverlistmotd/motd/RestrictedModeMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/RestrictedModeMotd.java @@ -2,6 +2,7 @@ import java.net.InetAddress; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; @@ -53,7 +54,7 @@ public String formatMotd(String motd, InetAddress ip) { } else { formattedMotd = PapiIntegration.replaceVariables(null, formattedMotd); } - + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/motd/WhitelistMotd.java b/src/cloud/bolte/serverlistmotd/motd/WhitelistMotd.java index 51cc795..46ea272 100644 --- a/src/cloud/bolte/serverlistmotd/motd/WhitelistMotd.java +++ b/src/cloud/bolte/serverlistmotd/motd/WhitelistMotd.java @@ -2,6 +2,7 @@ import java.net.InetAddress; +import cloud.bolte.serverlistmotd.util.HexResolver; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; @@ -51,7 +52,7 @@ public String formatMotd(String motd, InetAddress ip) { } else { formattedMotd = PapiIntegration.replaceVariables(null, formattedMotd); } - + formattedMotd = HexResolver.parseHexString(formattedMotd); return formattedMotd; } diff --git a/src/cloud/bolte/serverlistmotd/util/Gradient.java b/src/cloud/bolte/serverlistmotd/util/Gradient.java new file mode 100644 index 0000000..64bad8f --- /dev/null +++ b/src/cloud/bolte/serverlistmotd/util/Gradient.java @@ -0,0 +1,80 @@ +package cloud.bolte.serverlistmotd.util; + +import java.awt.Color; +import java.util.List; + +/** + * Author: https://github.com/harry0198/HexiTextLib MIT License + * + * NOTICE FROM harry0198: + * ------------------------- + * Gradient class + * Derived from: + * https://github.com/Rosewood-Development/RoseStacker/blob/master/Plugin/src/main/java/dev/rosewood/rosestacker/utils/HexUtils.java + * https://github.com/Oribuin/ChatEmojis/blob/master/src/main/java/xyz/oribuin/chatemojis/utils/HexUtils.java + * + * Unsure who original author is. + * @author unknown + */ +public class Gradient { + + private final List colors; + private final int stepSize; + private int step, stepIndex; + + public Gradient(List colors, int totalColors) { + if (colors.size() < 2) + throw new IllegalArgumentException("Please provide at least 2 colors, e.g. "); + + if (totalColors < 1) + throw new IllegalArgumentException("Must have at least 1 total color"); + + this.colors = colors; + this.stepSize = totalColors / (colors.size() - 1); + this.step = this.stepIndex = 0; + } + + /** + * @return the next color in the gradient + */ + public Color next() { + + Color color; + if (this.stepIndex + 1 < this.colors.size()) { + Color start = this.colors.get(this.stepIndex); + Color end = this.colors.get(this.stepIndex + 1); + float interval = (float) this.step / this.stepSize; + + color = getGradientInterval(start, end, interval); + } else { + color = this.colors.get(this.colors.size() - 1); + } + + this.step += 1; + if (this.step >= this.stepSize) { + this.step = 0; + this.stepIndex++; + } + + return color; + } + + /** + * Gets a color along a linear gradient between two colors + * + * @param start The start color + * @param end The end color + * @param interval The interval to get, between 0 and 1 inclusively + * @return A Color at the interval between the start and end colors + */ + public static Color getGradientInterval(Color start, Color end, float interval) { + if (0 > interval || interval > 1) + throw new IllegalArgumentException("Interval must be between 0 and 1 inclusively."); + + int r = (int) (end.getRed() * interval + start.getRed() * (1 - interval)); + int g = (int) (end.getGreen() * interval + start.getGreen() * (1 - interval)); + int b = (int) (end.getBlue() * interval + start.getBlue() * (1 - interval)); + + return new Color(r, g, b); + } +} diff --git a/src/cloud/bolte/serverlistmotd/util/HexResolver.java b/src/cloud/bolte/serverlistmotd/util/HexResolver.java new file mode 100644 index 0000000..6f3c097 --- /dev/null +++ b/src/cloud/bolte/serverlistmotd/util/HexResolver.java @@ -0,0 +1,135 @@ +package cloud.bolte.serverlistmotd.util; + +import net.md_5.bungee.api.ChatColor; +import java.util.regex.Matcher; +import java.util.Arrays; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.awt.Color; +import java.util.List; + +/** + * Credits go to harry0198 https://github.com/harry0198/HexiTextLib MIT License + * Small edits done by Strumswell + */ +public class HexResolver { + private static final Pattern GRADIENT_PATTERN = Pattern.compile("<(gradient|g)(:#([a-fA-F0-9]){6})+>"); + private static final Pattern HEX_PATTERN = Pattern.compile("<(#[a-fA-F0-9]{6})>"); + private static final Pattern STOPPING_PATTERN = Pattern.compile("<(gradient|g)(:#([a-fA-F0-9]){6})+>|(§[a-fA-F0-9])|<(#[a-fA-F0-9]{6})>"); + + /** + * Checks if hex colour codes are supported for the version of minecraft. + * @return True if supports hex. False if not. + */ + public static boolean serverSupportsHex() { + try { + ChatColor.of(Color.BLACK); + return true; + } catch (NoSuchMethodError ignore) { + return false; + } + } + + /** + * Parses string into hex colours where applicable using custom pattern matcher + * @param text String to parse into hex + * @return Parsed Hex where applicable otherwise returns inputted string + */ + public static String parseHexString(String text, Pattern hexPattern) { + if (serverSupportsHex()) { + text = parseGradients(text); + Matcher hexColorMatcher = hexPattern.matcher(text); + while (hexColorMatcher.find()) { + String hex = hexColorMatcher.group(1); + ChatColor color = ChatColor.of(hex); + + String before = text.substring(0, hexColorMatcher.start()); + String after = text.substring(hexColorMatcher.end()); + text = before + color + after; + hexColorMatcher = hexPattern.matcher(text); + + } + } + return org.bukkit.ChatColor.translateAlternateColorCodes('&',text); + } + + /** + * Parses string into hex colours where applicable using default pattern matcher + * @param text String to parse into hex + * @return Parsed Hex where applicable otherwise returns inputted string + */ + public static String parseHexString(String text) { + return parseHexString(text, HexResolver.HEX_PATTERN); + } + + /** + * Parses string into a gradient colour coded string + * @param text String to parse + * @return String with gradient applied + */ + private static String parseGradients(String text) { + List formatCodes = Arrays.asList("§o", "§k", "§l", "§n", "§r", "§m"); + String parsed = text; + + Matcher matcher = GRADIENT_PATTERN.matcher(parsed); + while (matcher.find()) { + StringBuilder parsedGradient = new StringBuilder(); + + String match = matcher.group(); + int tagLength = match.startsWith(""); + String hexContent = match.substring(tagLength, indexOfClose); + List hexSteps = Arrays.stream(hexContent.split(":")).map(Color::decode).collect(Collectors.toList()); + + int stop = findGradientStop(parsed, matcher.end()); + String content = parsed.substring(matcher.end(), stop); + + String cleanedContent = content; + for (String code: formatCodes) { + cleanedContent = cleanedContent.replace(code, ""); + } + + Gradient gradient = new Gradient(hexSteps, cleanedContent.length()); + + String tempFormat = ""; + for (char c : content.toCharArray()) { + if (c != '§' && tempFormat != "§") { + // This is a normal char + parsedGradient + .append(ChatColor.of(gradient.next()).toString()) + .append(tempFormat) + .append(c); + } else if (c == '§') { + // A custom formatting is starting + tempFormat = "§"; + } else if (c != '§' && tempFormat.contains("§")) { + // Type of custom formatting defined now + tempFormat += c; + } + } + + String before = parsed.substring(0, matcher.start()); + String after = parsed.substring(stop); + parsed = before + parsedGradient + after; + matcher = GRADIENT_PATTERN.matcher(parsed); + } + return parsed; + } + + /** + * Returns the index of the colour that is about to change to the next + * + * @param content The content to search through + * @param searchAfter The index at which to search after + * @return the index of the color stop, or the end of the string index if none is found + */ + private static int findGradientStop(String content, int searchAfter) { + Matcher matcher = STOPPING_PATTERN.matcher(content); + while (matcher.find()) { + if (matcher.start() > searchAfter) + return matcher.start(); + } + return content.length() - 1; + } +} diff --git a/src/cloud/bolte/serverlistmotd/util/IO.java b/src/cloud/bolte/serverlistmotd/util/IO.java index 7a72e09..ccdff33 100755 --- a/src/cloud/bolte/serverlistmotd/util/IO.java +++ b/src/cloud/bolte/serverlistmotd/util/IO.java @@ -13,6 +13,7 @@ import java.util.UUID; import cloud.bolte.serverlistmotd.Main; +import org.bukkit.Bukkit; /* * ServerlistMOTD (c) by Strumswell, Philipp Bolte @@ -51,8 +52,8 @@ public static void loadFlatfileIntoHashMap(File f, Map m) { } catch (IOException ioe) { ioe.printStackTrace(); } - System.out.println("[ServerlistMOTD] Loaded userdata from IP_UUID.dat("+f.length()+"bytes) into memory in " - +(System.currentTimeMillis()-start)+"ms."); + Bukkit.getLogger().info("[ServerlistMOTD] Loaded userdata from IP_UUID.dat(" + f.length() + "bytes) into memory in " + + (System.currentTimeMillis() - start) + "ms."); } } @@ -76,7 +77,7 @@ public static void saveHashMapIntoFlatfile(File f, Map m) { } catch (IOException ioe) { ioe.printStackTrace(); } - System.out.println("[ServerlistMOTD] Saved userdata into IP_UUID.dat in "+(System.currentTimeMillis()-start)+"ms."); + // SHUT UP :) Bukkit.getLogger().info(("[ServerlistMOTD] Saved userdata into IP_UUID.dat in "+(System.currentTimeMillis()-start)+"ms."); } /** @@ -102,7 +103,7 @@ public static void removeUnusedEntries() { int elementsStart = Main.IP_UUID.size(); Main.IP_UUID = invert(invert(Main.IP_UUID)); int elementsEnd = Main.IP_UUID.size(); - System.out.println("[ServerlistMOTD] Removed "+ (elementsStart - elementsEnd) +" duplicates in userdata in "+(System.currentTimeMillis()-start)+"ms."); + // SHUT UP :) Bukkit.getLogger().info("[ServerlistMOTD] Removed "+ (elementsStart - elementsEnd) +" duplicates in userdata in "+(System.currentTimeMillis()-start)+"ms."); } /** diff --git a/src/cloud/bolte/serverlistmotd/util/PapiIntegration.java b/src/cloud/bolte/serverlistmotd/util/PapiIntegration.java index ace5287..65d1d53 100644 --- a/src/cloud/bolte/serverlistmotd/util/PapiIntegration.java +++ b/src/cloud/bolte/serverlistmotd/util/PapiIntegration.java @@ -23,7 +23,7 @@ private PapiIntegration() { public static void setupIntegration() { if (Bukkit.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { - System.out.println("[ServerlistMOTD] Hooking into PlaceholderAPI."); + Bukkit.getLogger().info("[ServerlistMOTD] Hooking into PlaceholderAPI."); papiIsEnabled = true; } } diff --git a/src/cloud/bolte/serverlistmotd/variables/PlayerVariable.java b/src/cloud/bolte/serverlistmotd/variables/PlayerVariable.java index e5e038d..e1502fb 100644 --- a/src/cloud/bolte/serverlistmotd/variables/PlayerVariable.java +++ b/src/cloud/bolte/serverlistmotd/variables/PlayerVariable.java @@ -40,11 +40,15 @@ public static boolean isKnownPlayer(InetAddress ip) { * @return Name from player */ public static String getNameFromIP(InetAddress ip) { - OfflinePlayer p = Bukkit.getOfflinePlayer(Main.IP_UUID.get(ip)); - if (p.hasPlayedBefore()) { - return p.getName(); - }else { - //User is uncached by Spigot + try { + OfflinePlayer p = Bukkit.getOfflinePlayer(Main.IP_UUID.get(ip)); + if (p.hasPlayedBefore()) { + return p.getName(); + }else { + //User is uncached by Spigot + return ""; + } + } catch(NullPointerException npe) { return ""; } }