From 3cddba70c4222248f1daa7891d00f9a974a23305 Mon Sep 17 00:00:00 2001 From: iiAhmedYT <61851106+iiAhmedYT@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:45:52 +0300 Subject: [PATCH] Revamp Font system --- .../java/to/itsme/itsmyconfig/font/Font.java | 97 +------------------ .../to/itsme/itsmyconfig/font/FontImpl.java | 46 +++++++++ .../to/itsme/itsmyconfig/font/MappedFont.java | 60 ++++++++++++ .../to/itsme/itsmyconfig/hook/PAPIHook.java | 4 +- .../listener/impl/PacketChatListener.java | 10 +- .../to/itsme/itsmyconfig/util/Strings.java | 22 ++++- .../to/itsme/itsmyconfig/util/Utilities.java | 3 +- 7 files changed, 142 insertions(+), 100 deletions(-) create mode 100644 src/main/java/to/itsme/itsmyconfig/font/FontImpl.java create mode 100644 src/main/java/to/itsme/itsmyconfig/font/MappedFont.java diff --git a/src/main/java/to/itsme/itsmyconfig/font/Font.java b/src/main/java/to/itsme/itsmyconfig/font/Font.java index 40cb37b..97a95c4 100644 --- a/src/main/java/to/itsme/itsmyconfig/font/Font.java +++ b/src/main/java/to/itsme/itsmyconfig/font/Font.java @@ -1,100 +1,13 @@ package to.itsme.itsmyconfig.font; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.minimessage.tag.TagPattern; -import to.itsme.itsmyconfig.util.Strings; +import org.jetbrains.annotations.NotNull; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; +public interface Font { -public enum Font { - SMALL_CAPS( - "smallcaps", - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÑÓÚñóú", - "ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢɴᴏᴜɴᴏᴜ" - ), - UPSIDE_DOWN( - "upsidedown", - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - "∀qƆpƎℲפHIſʞ˥WNOԀQɹS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz" - ); - - private final Map characterMap; - private final TextReplacementConfig config; - private final @TagPattern String name; - - Font( - @TagPattern final String name, - final String original, - final String replacements - ) { - this.name = name; - this.characterMap = createMapping(original, replacements); - this.config = createConfig(); - } - - public @TagPattern String getName() { - return name; - } - - private TextReplacementConfig createConfig() { - final TextReplacementConfig.Builder config = TextReplacementConfig.builder(); - config.match(Strings.LETTERS_PATTERN).replacement((matchResult, builder) -> { - final String text = this.apply(matchResult.group()); - return Component.text().content(text); - }); - return config.build(); - } - - public String apply(final String text) { - final byte[] bytes = this.englishify(text).getBytes(StandardCharsets.UTF_8); - final StringBuilder builder = new StringBuilder(); - for (byte messageByte : bytes) { - final char originalChar = (char) messageByte; - final char styledChar = characterMap.getOrDefault(originalChar, originalChar); - builder.append(styledChar); - } - return builder.toString(); - } - - public String englishify(final String text) { - return text - .replace("À", "A") - .replace("Â", "A") - .replace("à", "a") - .replace("â", "a") - .replace("É", "E") - .replace("È", "E") - .replace("Ê", "E") - .replace("é", "e") - .replace("è", "e") - .replace("ê", "e") - .replace("Î", "I") - .replace("î", "i") - .replace("Ô", "O") - .replace("ô", "o") - .replace("Û", "U") - .replace("û", "u") - .replace("Ç", "C") - .replace("ç", "c"); - } - - public Component apply(final Component component) { - return component.replaceText(config); - } - - private static Map createMapping(final String original, final String replacements) { - if (original.length() != replacements.length()) { - throw new IllegalArgumentException("Original and replacement texts must be of the same length."); - } - - final Map mapping = new HashMap<>(); - for (int i = 0; i < original.length(); i++) { - mapping.put(original.charAt(i), replacements.charAt(i)); - } - return mapping; - } + @TagPattern String getName(); + @NotNull String apply(final @NotNull String text); + @NotNull Component apply(final @NotNull Component component); } diff --git a/src/main/java/to/itsme/itsmyconfig/font/FontImpl.java b/src/main/java/to/itsme/itsmyconfig/font/FontImpl.java new file mode 100644 index 0000000..262721a --- /dev/null +++ b/src/main/java/to/itsme/itsmyconfig/font/FontImpl.java @@ -0,0 +1,46 @@ +package to.itsme.itsmyconfig.font; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextReplacementConfig; +import net.kyori.adventure.text.minimessage.tag.TagPattern; +import org.jetbrains.annotations.NotNull; +import to.itsme.itsmyconfig.util.Strings; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public abstract class FontImpl implements Font { + + private static final Map FONTS = new HashMap<>(); + + private final @TagPattern String name; + private final TextReplacementConfig config = this.createConfig(); + + public FontImpl(final @TagPattern String name) { + this.name = name; + } + + public @TagPattern String getName() { + return name; + } + + @Override + public @NotNull Component apply(final @NotNull Component component) { + return component.replaceText(config); + } + + private TextReplacementConfig createConfig() { + final TextReplacementConfig.Builder config = TextReplacementConfig.builder(); + config.match(Strings.LETTERS_PATTERN).replacement((matchResult, builder) -> { + final String text = this.apply(matchResult.group()); + return Component.text().content(text); + }); + return config.build(); + } + + public static Collection values() { + return FONTS.values(); + } + +} diff --git a/src/main/java/to/itsme/itsmyconfig/font/MappedFont.java b/src/main/java/to/itsme/itsmyconfig/font/MappedFont.java new file mode 100644 index 0000000..83170f2 --- /dev/null +++ b/src/main/java/to/itsme/itsmyconfig/font/MappedFont.java @@ -0,0 +1,60 @@ +package to.itsme.itsmyconfig.font; + +import net.kyori.adventure.text.minimessage.tag.TagPattern; +import org.jetbrains.annotations.NotNull; +import to.itsme.itsmyconfig.util.Strings; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class MappedFont extends FontImpl { + + public static final MappedFont SMALL_CAPS = new MappedFont( + "smallcaps", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘꞯʀsᴛᴜᴠᴡxʏᴢᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘꞯʀsᴛᴜᴠᴡxʏᴢ" + ); + + public static final MappedFont UPSIDE_DOWN = new MappedFont( + "upsidedown", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "∀qƆpƎℲפHIſʞ˥WNOԀQɹS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz" + ); + + private final Map characterMap; + + MappedFont( + @TagPattern final String name, + final String original, + final String replacements + ) { + super(name); + this.characterMap = createMapping(original, replacements); + } + + @Override + public @NotNull String apply(final @NotNull String text) { + final byte[] bytes = Strings.englishify(text).getBytes(StandardCharsets.UTF_8); + final StringBuilder builder = new StringBuilder(); + for (final byte messageByte : bytes) { + final char originalChar = (char) messageByte; + final char styledChar = characterMap.getOrDefault(originalChar, originalChar); + builder.append(styledChar); + } + return builder.toString(); + } + + private static Map createMapping(final String original, final String replacements) { + if (original.length() != replacements.length()) { + throw new IllegalArgumentException("Original and replacement texts must be of the same length."); + } + + final Map mapping = new HashMap<>(); + for (int i = 0; i < original.length(); i++) { + mapping.put(original.charAt(i), replacements.charAt(i)); + } + return mapping; + } + +} diff --git a/src/main/java/to/itsme/itsmyconfig/hook/PAPIHook.java b/src/main/java/to/itsme/itsmyconfig/hook/PAPIHook.java index f15eec8..5ce605f 100644 --- a/src/main/java/to/itsme/itsmyconfig/hook/PAPIHook.java +++ b/src/main/java/to/itsme/itsmyconfig/hook/PAPIHook.java @@ -6,9 +6,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import to.itsme.itsmyconfig.ItsMyConfig; +import to.itsme.itsmyconfig.font.MappedFont; import to.itsme.itsmyconfig.placeholder.Placeholder; import to.itsme.itsmyconfig.placeholder.PlaceholderType; -import to.itsme.itsmyconfig.font.Font; import to.itsme.itsmyconfig.util.Strings; /** @@ -129,7 +129,7 @@ private String handleFont(final String[] splitParams) { } } else if ("smallcaps".equals(fontType)) { String message = splitParams[2].toLowerCase(); - return Font.SMALL_CAPS.apply(message); + return MappedFont.SMALL_CAPS.apply(message); } return "ERROR"; } diff --git a/src/main/java/to/itsme/itsmyconfig/listener/impl/PacketChatListener.java b/src/main/java/to/itsme/itsmyconfig/listener/impl/PacketChatListener.java index cd8f6bf..40b92aa 100644 --- a/src/main/java/to/itsme/itsmyconfig/listener/impl/PacketChatListener.java +++ b/src/main/java/to/itsme/itsmyconfig/listener/impl/PacketChatListener.java @@ -20,6 +20,8 @@ public final class PacketChatListener extends PacketListener { + private static final String DEBUG_HYPHEN = "###############################################"; + private final boolean internalAdventure; private final Method fromComponent; private final BungeeComponentSerializer bungee = BungeeComponentSerializer.get(); @@ -54,14 +56,14 @@ public void onPacketSending(final PacketEvent event) { Utilities.debug(() -> "################# CHAT PACKET #################\nProccessing packet " + container.getType().name()); final PacketResponse response = this.processPacket(container); if (response == null || response.message.isEmpty()) { - Utilities.debug(() -> "Packet is null or empty\n###############################################"); + Utilities.debug(() -> "Packet is null or empty\n" + DEBUG_HYPHEN); return; } final String message = response.message; Utilities.debug(() -> "Checking: " + message); if (!this.startsWithSymbol(message)) { - Utilities.debug(() -> "Message doesn't start w/ the symbol-prefix: " + message + "\n###############################################"); + Utilities.debug(() -> "Message doesn't start w/ the symbol-prefix: " + message + "\n" + DEBUG_HYPHEN); return; } @@ -69,7 +71,7 @@ public void onPacketSending(final PacketEvent event) { final Component parsed = Utilities.translate(this.processMessage(message), player); if (parsed.equals(Component.empty())) { event.setCancelled(true); - Utilities.debug(() -> "Component is empty, cancelling...\n###############################################"); + Utilities.debug(() -> "Component is empty, cancelling...\n" + DEBUG_HYPHEN); return; } @@ -95,7 +97,7 @@ public void onPacketSending(final PacketEvent event) { break; } - Utilities.debug(() -> "###############################################"); + Utilities.debug(() -> DEBUG_HYPHEN); } private PacketResponse processPacket(final PacketContainer container) { diff --git a/src/main/java/to/itsme/itsmyconfig/util/Strings.java b/src/main/java/to/itsme/itsmyconfig/util/Strings.java index 5c8a0e1..0fa1fae 100644 --- a/src/main/java/to/itsme/itsmyconfig/util/Strings.java +++ b/src/main/java/to/itsme/itsmyconfig/util/Strings.java @@ -3,6 +3,7 @@ import net.kyori.adventure.text.minimessage.tag.standard.StandardTags; import org.jetbrains.annotations.NotNull; +import java.text.Normalizer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -17,7 +18,8 @@ public final class Strings { public static final Pattern TAG_PATTERN = Pattern.compile("<(\\w+)(?::\"([^\"]*)\"|:([^<]*))*>"); private static final Pattern COLOR_FILTER = Pattern.compile("[§&][a-zA-Z0-9]"); - public static final Pattern ARGUMENT_PATTERN = Pattern.compile("\\{([0-9]+)}"); + private static final Pattern ARGUMENT_PATTERN = Pattern.compile("\\{([0-9]+)}"); + private static final Pattern DIACRITICAL_MARKS_PATTERN = Pattern.compile("\\p{M}"); private static final Pattern QUOTE_PATTERN = Pattern.compile("]*))?>(.*)"); /** @@ -226,4 +228,22 @@ public static String toString(final @NotNull List list) { return String.join(System.lineSeparator(), list.stream().map(Object::toString).toArray(String[]::new)); } + /** + * Converts a string containing accented characters into a string with plain + * English characters by removing diacritical marks (accents). + * + *

This method normalizes the input text by decomposing accented characters + * into their base characters followed by diacritical marks. It then removes + * the diacritical marks, resulting in a string composed of basic Latin letters.

+ * + *

For example, the input string "À la carte" would be converted to "A la carte".

+ * + * @param text the input string potentially containing accented characters + * @return a new string with the accented characters converted to plain English characters + */ + public static String englishify(final String text) { + final String normalized = Normalizer.normalize(text, Normalizer.Form.NFD); + return DIACRITICAL_MARKS_PATTERN.matcher(normalized).replaceAll(""); + } + } diff --git a/src/main/java/to/itsme/itsmyconfig/util/Utilities.java b/src/main/java/to/itsme/itsmyconfig/util/Utilities.java index f94dd14..0c804a0 100644 --- a/src/main/java/to/itsme/itsmyconfig/util/Utilities.java +++ b/src/main/java/to/itsme/itsmyconfig/util/Utilities.java @@ -16,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import to.itsme.itsmyconfig.ItsMyConfig; import to.itsme.itsmyconfig.font.Font; +import to.itsme.itsmyconfig.font.FontImpl; import to.itsme.itsmyconfig.font.FontTag; import to.itsme.itsmyconfig.placeholder.Placeholder; import to.itsme.itsmyconfig.placeholder.type.ColorPlaceholder; @@ -41,7 +42,7 @@ public final class Utilities { static { final TagResolver.Builder builder = TagResolver.builder(); - for (final @Subst("") Font font : Font.values()) { + for (final @Subst("") Font font : FontImpl.values()) { builder.tag(font.getName(), new FontTag(font)); } FONT_RESOLVER = builder.build();