Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legacy support & Minimessage configuration options #80

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/main/java/me/ryanhamshire/GPFlags/FlagManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.io.Files;
import me.ryanhamshire.GPFlags.flags.FlagDefinition;
import me.ryanhamshire.GPFlags.util.ChatUtil;
import me.ryanhamshire.GPFlags.util.MessagingUtil;
import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
Expand Down Expand Up @@ -116,8 +117,8 @@ public SetFlagResult setFlag(String claimId, FlagDefinition def, boolean isActiv
}
internalParameters.append(arg).append(" ");
}
internalParameters = new StringBuilder(internalParameters.toString().trim());
friendlyParameters = new StringBuilder(friendlyParameters.toString().trim());
internalParameters = new StringBuilder(ChatUtil.validatedHexString(sender, internalParameters.toString()).trim());
MaksyKun marked this conversation as resolved.
Show resolved Hide resolved
friendlyParameters = new StringBuilder(ChatUtil.validatedHexString(sender, friendlyParameters.toString()).trim());

SetFlagResult result;
if (isActive) {
Expand Down
225 changes: 225 additions & 0 deletions src/main/java/me/ryanhamshire/GPFlags/util/ChatUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package me.ryanhamshire.GPFlags.util;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ChatUtil {
private static final MiniMessage minimessage = MiniMessage.builder().build();

// Used to format chat-messages (legacy-support) of a specific sender related to his permissions
public static String validatedHexString(CommandSender sender, String message) {
message = message.replace("§", "&");
message = parseHexColorCodes(message);
message = normalizeColorCodes(message);
if (sender == null) return message;
message = validateMinimessage(sender, message);
return message;
}

// Used to format a chat-messages (legacy-support) without previous validation of somebody.
public static Component hexComp(String message) {
message = message.replace("§", "&");
message = parseHexColorCodes(message);
message = normalizeColorCodes(message);
return minimessage.deserialize(message);
}

// Parses legacy hex-colors to MiniMessage hex-colors
private static String parseHexColorCodes(String message) {
MaksyKun marked this conversation as resolved.
Show resolved Hide resolved
Pattern pattern = Pattern.compile("&#[a-fA-F0-9]{6}");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
String hexCode = message.substring(matcher.start(), matcher.end());
String replaceSharp = hexCode.replace("&", "");

message = message.replace(hexCode, "<color:" + replaceSharp + ">");
matcher = pattern.matcher(message);
}
return message;
}

// Normalized legacy color-codes and shortened Minimessage color-codes to written out MiniMessage color-codes
private static String normalizeColorCodes(String message) {
message = message.replace("&0", "<color:black>");
message = message.replace("&1", "<color:dark_blue>");
message = message.replace("&2", "<color:dark_green>");
message = message.replace("&3", "<color:dark_aqua>");
message = message.replace("&4", "<color:dark_red>");
message = message.replace("&5", "<color:dark_purple>");
message = message.replace("&6", "<color:gold>");
message = message.replace("&7", "<color:gray>");
message = message.replace("&8", "<color:dark_gray>");
message = message.replace("&9", "<color:blue>");
message = message.replace("&a", "<color:green>");
message = message.replace("&b", "<color:aqua>");
message = message.replace("&c", "<color:red>");
message = message.replace("&d", "<color:light_purple>");
message = message.replace("&e", "<color:yellow>");
message = message.replace("&f", "<color:white>");
message = message.replace("&l", "<bold>");
message = message.replace("&m", "<strikethrough>");
message = message.replace("&n", "<underlined>");
message = message.replace("&o", "<italic>");
message = message.replace("&k", "<obfuscated>");
message = message.replace("&r", "<reset>");
message = message.replace("<red>", "<color:red>");
message = message.replace("<green>", "<color:green>");
message = message.replace("<blue>", "<color:blue>");
message = message.replace("<yellow>", "<color:yellow>");
message = message.replace("<aqua>", "<color:aqua>");
message = message.replace("<gold>", "<color:gold>");
message = message.replace("<gray>", "<color:gray>");
message = message.replace("<dark_gray>", "<color:dark_gray>");
message = message.replace("<dark_red>", "<color:dark_red>");
message = message.replace("<dark_green>", "<color:dark_green>");
message = message.replace("<dark_blue>", "<color:dark_blue>");
message = message.replace("<dark_aqua>", "<color:dark_aqua>");
message = message.replace("<dark_purple>", "<color:dark_purple>");
message = message.replace("<light_purple>", "<color:light_purple>");
message = message.replace("<black>", "<color:black>");
message = message.replace("<white>", "<color:white>");
message = message.replace("<b>", "<bold>");
message = message.replace("<u>", "<underlined>");
message = message.replace("<i>", "<italic>");
message = message.replace("<st>", "<strikethrough>");
message = message.replace("<obf>", "<obfuscated>");
return message;
}

// Validates a minimessage for a specific sender, meaning we check for permissions and remove components if necessary
private static String validateMinimessage(CommandSender sender, String message) {

/*
// Check if there is a link in the message without click-component
Pattern pattern = Pattern.compile("(?<!<click:open_url:)(https?://\\S+)(?!>)");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
String link = message.substring(matcher.start(), matcher.end());
message = message.replace(link, "<click:open_url:" + link + ">" + link + "</click>");
matcher = pattern.matcher(message);
}*/

// Check if there are links
Pattern pattern = Pattern.compile("https?://(?:www\\.)?[\\w\\-.]+\\.[a-z]{2,}(?:/[\\w\\-.,@?^=%&:/~+#]*)?");
Matcher matcher = pattern.matcher(message);

String pseudoMessage = message;
while (matcher.find()) {
String link = pseudoMessage.substring(matcher.start(), matcher.end());
pseudoMessage = pseudoMessage.replace(link, "");
if(message.contains("<click:open_url:" + link + ">")) continue; // Skip if already a click-component
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there are two of the same links in the message? This'll only convert the first one

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not anymore, same iteration as everywhere until there is no link available.

message = message.replace(link, "<click:open_url:" + link + ">" + link + "</click>");
matcher = pattern.matcher(pseudoMessage);
}

// Check for click-components
pattern = Pattern.compile("<click:.*?>");
matcher = pattern.matcher(message);
while (matcher.find()) {
boolean requiredChanges = false;
String clickComponent = message.substring(matcher.start(), matcher.end());
if (clickComponent.contains("suggest_command") && !sender.hasPermission("gpflags.messages.click.suggest_command")) {
message = message.replace(clickComponent, "");
requiredChanges = true;
}
if (clickComponent.contains("run_command") && !sender.hasPermission("gpflags.messages.click.run_command")) {
message = message.replace(clickComponent, "");
requiredChanges = true;
}
if (clickComponent.contains("open_url") && !sender.hasPermission("gpflags.messages.click.open_url")) {
message = message.replace(clickComponent, "");
requiredChanges = true;
}
matcher = pattern.matcher(message);

if (!requiredChanges) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't tell what this variable is trying to do in this one. If I don't have permission for a click action I tried to use, it removes my click component, but leaves all the other click components in the message?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem was, when you find something in the pattern, but have the permission to use it, there is no need for change. But if there is no need for change, the while loop will keep iterating over and over until the server runs out of memory.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we could make the permission check before the actually pattern initialization. However it could become annoying with the click and hover components. What do you think about this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it makes sense for perm check before pattern initialization. What makes it annoying with click and hover components? That there are multiple types of them? Thats fine, just split it up more

if (!sender.hasPermission("gpflags.messages.hover.show_text")) {
// replace all "<hover:show_text>" with "\<hover:show_text>"
}
if (!sender.hasPermission("gpflags.messages.hover.show_item")) {
// replace all "<hover:show_item>" with "\<hover:show_item>"
}
...

As you see in my example above, I also chose to escape out the tag rather than repalcing it with "".
I think if you choose to replace it with "", it could result in some cases where for example <re<blue>d> would turn into <red> which we want to avoid.

Copy link
Author

@MaksyKun MaksyKun Aug 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont get this, Im sorry.

Do you want to put a \ before the hover:.... stuff? I think this would still setup the component itself. Also, we are having a while loop that repeats until there is no component left. So i think thats not bad at all atm.

All i could imagine to reduce iteration time:

if(!sender.hasPermission("gpflags.messages.hover.show_text") || !sender.hasPermission("gpflags.messages.hover.show_item") || !sender.hasPermission("gpflags.messages.hover.show_entity")) {
        pattern = Pattern.compile("<hover:.*?>");
        matcher = pattern.matcher(message);
        while (matcher.find()) {
            boolean requiredChanges = false;

            String hoverComponent = message.substring(matcher.start(), matcher.end());
            if (hoverComponent.contains("show_text") && !sender.hasPermission("gpflags.messages.hover.show_text")) {
                message = message.replace(hoverComponent, "");
                requiredChanges = true;
            }
            if (hoverComponent.contains("show_item") && !sender.hasPermission("gpflags.messages.hover.show_item")) {
                message = message.replace(hoverComponent, "");
                requiredChanges = true;
            }
            if (hoverComponent.contains("show_entity") && !sender.hasPermission("gpflags.messages.hover.show_entity")) {
                message = message.replace(hoverComponent, "");
                requiredChanges = true;
            }
            matcher = pattern.matcher(message);

            if (!requiredChanges) {
                break;
            }
        }
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to put a \ before the hover:.... stuff? I think this would still setup the component itself.

Yes that's exactly what Im saying. You can use the minimessage viewer at https://webui.advntr.dev/ to see that this does make the tag show without it serving as a tag.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, don't worry much about reducing iteration time here. This won't get called so often to where it's too big a consideration so I'd much rather it be easy to read and maintain. I still think three individual loops for the hover would be the best, and similar for the other parts

break;
}
}

// Check for hover-components
pattern = Pattern.compile("<hover:.*?>");
matcher = pattern.matcher(message);
while (matcher.find()) {
boolean requiredChanges = false;

String hoverComponent = message.substring(matcher.start(), matcher.end());
if (hoverComponent.contains("show_text") && !sender.hasPermission("gpflags.messages.hover.show_text")) {
message = message.replace(hoverComponent, "");
requiredChanges = true;
}
if (hoverComponent.contains("show_item") && !sender.hasPermission("gpflags.messages.hover.show_item")) {
message = message.replace(hoverComponent, "");
requiredChanges = true;
}
if (hoverComponent.contains("show_entity") && !sender.hasPermission("gpflags.messages.hover.show_entity")) {
message = message.replace(hoverComponent, "");
requiredChanges = true;
}
matcher = pattern.matcher(message);

if (!requiredChanges) {
break;
}
}

// Check for hex-colors
message = message.replace("<#", "<color:#");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone's using <# not as a tag, we wouldn't really want to convert this. Cant really imagine someone would do that but it's short enough a string to where I could see it coming up.

pattern = Pattern.compile("<color:#.*?>");
matcher = pattern.matcher(message);
while (matcher.find()) {
boolean requiredChanges = false;
String hexComponent = message.substring(matcher.start(), matcher.end());
if (!sender.hasPermission("gpflags.messages.hex-colors")) {
message = message.replace(hexComponent, "<color:white>");
MaksyKun marked this conversation as resolved.
Show resolved Hide resolved
requiredChanges = true;
}
matcher = pattern.matcher(message);

if (!requiredChanges) {
break;
}
}

// Check for colors
pattern = Pattern.compile("<color:.*?>");
MaksyKun marked this conversation as resolved.
Show resolved Hide resolved
matcher = pattern.matcher(message);
while (matcher.find()) {
boolean requiredChanges = false;
String colorComponent = message.substring(matcher.start(), matcher.end());
if (!sender.hasPermission("gpflags.messages.colors")) {
message = message.replace(colorComponent, "");
requiredChanges = true;
}
matcher = pattern.matcher(message);

if (!requiredChanges) {
break;
}
}

MaksyKun marked this conversation as resolved.
Show resolved Hide resolved
// Check for bold, italic, strikethrough, underline, obfuscated
pattern = Pattern.compile("<(bold|italic|strikethrough|underlined|obfuscated)>");
Copy link
Owner

@akdukaan akdukaan Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimessage tags are case insensitive so this wont catch stuff that are like <BOLD> or <bOLD>

matcher = pattern.matcher(message);
while (matcher.find()) {
boolean requiredChanges = false;
String styleComponent = message.substring(matcher.start(), matcher.end());
if (!sender.hasPermission("gpflags.messages.style." + styleComponent.replace("<", "").replace(">", ""))) {
message = message.replace(styleComponent, "");
requiredChanges = true;
}
matcher = pattern.matcher(message);

if (!requiredChanges) {
break;
}
}

return message;
}
}
7 changes: 3 additions & 4 deletions src/main/java/me/ryanhamshire/GPFlags/util/MessagingUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import me.ryanhamshire.GPFlags.hooks.PlaceholderApiHook;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -52,7 +51,7 @@ public static void sendMessage(@Nullable CommandSender receiver, String message)
message = PlaceholderApiHook.addPlaceholders(player, message);
} catch (Throwable ignored) {}
message = message.replace(COLOR_CHAR, '&');
Component component = MiniMessage.miniMessage().deserialize(message);
Component component = ChatUtil.hexComp(message);
GPFlags.getInstance().getAdventure().player(player).sendMessage(component);
// Audience.audience(player).sendMessage(component);
}
Expand All @@ -64,7 +63,7 @@ private static void logToConsole(String message) {

public static void sendActionbar(Player player, String message) {
message = message.replace(COLOR_CHAR, '&');
Component component = MiniMessage.miniMessage().deserialize(message);
Component component = ChatUtil.hexComp(message);
GPFlags.getInstance().getAdventure().player(player).sendActionBar(component);
}

Expand Down Expand Up @@ -94,7 +93,7 @@ private static String convertOriginalHexColors(String string) {

public static String reserialize(String ampersandMessage) {
ampersandMessage = convertOriginalHexColors(ampersandMessage);
Component component = LegacyComponentSerializer.legacyAmpersand().deserialize(ampersandMessage);
Component component = ChatUtil.hexComp(ampersandMessage);
return MiniMessage.miniMessage().serialize(component);
}
}