diff --git a/README.md b/README.md index 248a81e..ae2c48a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,21 @@ # Styled Chat It's a simple mod that allows server owners to change how their chat looks! -It supports changing style per player with permissions (supports LuckPerms and PlayerRoles) -If you have any questions, you can ask them on my [Discord](https://discord.com/invite/AbqPPppgrd) -![Example image](https://i.imgur.com/y0KGyVT.png) -![Example image2](https://i.imgur.com/ObepOhW.png) +It adds support for [modern chat formatting](https://placeholders.pb4.eu/user/text-format/) supported by Minecraft, +but ignored by many chat mods/plugins. + +It's also compatible with any mods using [Placeholder API](https://placeholders.pb4.eu/user/general/). + +It also supports changing style per player with permissions (supports LuckPerms and PlayerRoles) + +*This mod works only on Fabric Mod Loader and compatible!* + +If you have any questions, you can ask them on my [Discord](https://pb4.eu/discord) + +[Also check out my other mods and project, as you might find them useful!](https://pb4.eu) + +![Example image](https://i.imgur.com/HPSMaS8.png) +![Example image2](https://i.imgur.com/mSWzIV4.png) ## Commands (and permissions): @@ -13,8 +24,8 @@ If you have any questions, you can ask them on my [Discord](https://discord.com/ ## Configuration: You can find config file in `./config/styled-chat.json`. -[Formatting uses PlaceholderAPI's Text Parser for which docs you can find here](https://github.com/Patbox/FabricPlaceholderAPI/blob/1.17/TEXT_FORMATTING.md). -It supports usage of placeholders from [Placeholder API](https://github.com/Patbox/FabricPlaceholderAPI/wiki). +[Formatting uses Simplified Text Format](https://placeholders.pb4.eu/user/text-format/). +It supports usage of placeholders from [Placeholder API](https://placeholders.pb4.eu/user/general/). Additionally, every message type has few own local variables. ```json5 @@ -66,61 +77,111 @@ It supports all default ones with addition of `` tag. "joinRenamed": " ", "left": " ", "death": " ${default_message}", - "advancementTask": " ", - "advancementChallenge": " ", - "advancementGoal": " " + "advancementTask": "", + "advancementChallenge": "", + "advancementGoal": "", + "teamChatSent": "\\'>${team}':'${displayName}':'${message}'>", + "teamChatReceived": "\\'>${team}':'${displayName}':'${message}'>", + "privateMessageSent": "[PM → ${receiver}] » ${message}", + "privateMessageReceived": "[PM ← ${sender}] » ${message}", + "sayCommand": "[${player}] ${message}", + "meCommand": "* ${player} ${message}" }, "permissionStyles": [ { - "permission": "group.test", + "permission": "group.admin", + "opLevel": 4, "style": { - "displayName": "[Admin | %player:playtime%] ${vanillaDisplayName}" + "displayName": "[Admin] ${vanillaDisplayName}", + "chat": "${player} » ${message}", + "death": "" } }, { - "permission": "group.admin", + "permission": "group.default", + "opLevel": 0, "style": { - "displayName": "[Admin] ${vanillaDisplayName}", - "chat": "${player} » ${message}" + "displayName": "[Player] ${vanillaDisplayName}" } } ], + "petDeathMessage": "Oh no! ${default_message}", + "emoticons": { + "bucket": "🪣", + "sword": "🗡", + "potion": "🧪", + "trident": "🔱", + "shears": "✂", + "rod": "🎣", + "fire": "🔥", + "shrug": "¯\\_(ツ)_/¯", + "bow": "🏹", + "bell": "🔔", + "table": "(╯°□°)╯︵ ┻━┻", + "heart": "❤" + }, + "permissionEmoticons": [ + { + "permission": "group.vip", + "opLevel": 3, + "emotes": {} + } + ], "legacyChatFormatting": true, + "parseLinksInChat": true, + "enableMarkdown": true, + "formattingInPrivateMessages": true, + "formattingInTeamMessages": true, + "linkStyle": "${link}", + "spoilerStyle": "${spoiler}", + "spoilerSymbol": "▌", "defaultEnabledFormatting": { - "dark_red": true, + "dark_red": false, "color": false, "underline": true, "yellow": false, - "insert": false, - "italic": false, + "italic": true, "dark_blue": false, - "dark_purple": true, - "gold": true, + "dark_purple": false, + "gold": false, "red": false, "aqua": false, "hover": false, "gray": false, "light_purple": false, "white": false, + "pos": true, "dark_gray": false, - "strikethrough": false, + "spoiler": true, + "strikethrough": true, "lang": false, "obfuscated": false, + "dark_grey": false, "key": false, + "change_page": false, + "st": true, + "b": true, "item": true, "green": false, - "c": true, + "c": false, "dark_green": false, "gradient": false, "black": false, - "bold": false, + "em": false, + "i": true, + "bold": true, "gr": false, - "click": false, + "grey": false, + "orange": false, "rb": false, "rainbow": false, + "obf": false, + "colour": false, "blue": false, "dark_aqua": false, - "reset": true, + "underlined": false, + "reset": false, + "page": false, "font": false } } diff --git a/build.gradle b/build.gradle index 43105e5..342859a 100644 --- a/build.gradle +++ b/build.gradle @@ -30,10 +30,11 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" // Fabric API. This is technically optional, but you probably want it anyway. - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + modImplementation include(fabricApi.module("fabric-api-base", project.fabric_version)) + modImplementation include(fabricApi.module("fabric-lifecycle-events-v1", project.fabric_version)) + modImplementation include(fabricApi.module("fabric-command-api-v1", project.fabric_version)) - - modImplementation include("eu.pb4:placeholder-api:1.0.0-rc2-1.17") + modImplementation include("eu.pb4:placeholder-api:1.1.1+1.17.1") modImplementation include("me.lucko:fabric-permissions-api:0.1-SNAPSHOT") modRuntime "supercoder79:databreaker:0.2.7" diff --git a/gradle.properties b/gradle.properties index e3489bf..77e7b2f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,14 +3,14 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://fabricmc.net/use - minecraft_version=1.17 - yarn_mappings=1.17+build.13 + minecraft_version=1.17.1 + yarn_mappings=1.17.1+build.1 loader_version=0.11.3 # Mod Properties - mod_version = 1.1.0 + mod_version = 1.2.0 maven_group = eu.pb4 archives_base_name = styled-chat # Dependencies - fabric_version=0.34.9+1.17 + fabric_version=0.40.1+1.17 diff --git a/src/main/java/eu/pb4/styledchat/StyledChatEvents.java b/src/main/java/eu/pb4/styledchat/StyledChatEvents.java new file mode 100644 index 0000000..8d656d1 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/StyledChatEvents.java @@ -0,0 +1,69 @@ +package eu.pb4.styledchat; + +import eu.pb4.placeholders.TextParser; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; + +import java.util.function.BiConsumer; + +public class StyledChatEvents { + /** + * Event ran for message content before they are formatted (Strings) + */ + public static final Event PRE_MESSAGE_CONTENT_SEND = EventFactory.createArrayBacked(PreMessageEvent.class, callbacks -> (message, player, filtered) -> { + for (var callback : callbacks) { + message = callback.onPreMessage(message, player, filtered); + } + + return message; + }); + + /** + * Event ran for message content after it being formatted (Text) + */ + public static final Event MESSAGE_CONTENT_SEND = EventFactory.createArrayBacked(MessageEvent.class, callbacks -> (message, player, filtered) -> { + for (var callback : callbacks) { + message = callback.onMessage(message, player, filtered); + } + + return message; + }); + + /** + * Event ran before message is send to someone, it is fully formatted (including template) + */ + public static final Event MESSAGE_TO_SEND = EventFactory.createArrayBacked(MessageToEvent.class, callbacks -> (message, sender, receiver, filtered) -> { + for (var callback : callbacks) { + message = callback.onMessageTo(message, sender, receiver, filtered); + } + + return message; + }); + + /** + * This event can be used to allow custom formatting + */ + public static final Event FORMATTING_CREATION_EVENT = EventFactory.createArrayBacked(FormattingCreationEvent.class, callbacks -> (player, builder) -> { + for (var callback : callbacks) { + callback.onFormattingBuild(player, builder); + } + }); + + public interface FormattingCreationEvent { + void onFormattingBuild(ServerPlayerEntity player, BiConsumer builder); + } + + public interface PreMessageEvent { + String onPreMessage(String message, ServerPlayerEntity player, boolean filtered); + } + + public interface MessageEvent { + Text onMessage(Text message, ServerPlayerEntity player, boolean filtered); + } + + public interface MessageToEvent { + Text onMessageTo(Text message, ServerPlayerEntity sender, ServerPlayerEntity receiver, boolean filtered); + } +} diff --git a/src/main/java/eu/pb4/styledchat/StyledChatMod.java b/src/main/java/eu/pb4/styledchat/StyledChatMod.java index 8845b85..6a70d8d 100644 --- a/src/main/java/eu/pb4/styledchat/StyledChatMod.java +++ b/src/main/java/eu/pb4/styledchat/StyledChatMod.java @@ -13,11 +13,24 @@ public class StyledChatMod implements ModInitializer { public static final Logger LOGGER = LogManager.getLogger("Styled Chat"); public static String VERSION = FabricLoader.getInstance().getModContainer("styledchat").get().getMetadata().getVersion().getFriendlyString(); - public static final String URL_REGEX = "(https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]+\\.[^ ]+)"; - @Override public void onInitialize() { + this.crabboardDetection(); Commands.register(); - ServerLifecycleEvents.SERVER_STARTING.register((s) -> ConfigManager.loadConfig()); + ServerLifecycleEvents.SERVER_STARTING.register((s) -> { + this.crabboardDetection(); + ConfigManager.loadConfig(); + }); + } + + private void crabboardDetection() { + if (FabricLoader.getInstance().isModLoaded("cardboard")) { + LOGGER.error(""); + LOGGER.error("Cardboard detected! This mod doesn't work with it!"); + LOGGER.error("You won't get any support as long as it's present!"); + LOGGER.error(""); + LOGGER.error("Read more: https://gist.github.com/Patbox/e44844294c358b614d347d369b0fc3bf"); + LOGGER.error(""); + } } } diff --git a/src/main/java/eu/pb4/styledchat/StyledChatUtils.java b/src/main/java/eu/pb4/styledchat/StyledChatUtils.java new file mode 100644 index 0000000..0730fe6 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/StyledChatUtils.java @@ -0,0 +1,147 @@ +package eu.pb4.styledchat; + +import eu.pb4.placeholders.PlaceholderAPI; +import eu.pb4.placeholders.TextParser; +import eu.pb4.placeholders.util.GeneralUtils; +import eu.pb4.placeholders.util.TextParserUtils; +import eu.pb4.styledchat.config.Config; +import eu.pb4.styledchat.config.ConfigManager; +import me.lucko.fabric.api.permissions.v0.Permissions; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.*; +import net.minecraft.util.Formatting; +import net.minecraft.util.Hand; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +public final class StyledChatUtils { + public static final String IGNORED_TEXT_KEY = "styled.chat.ignored.text.if.you.see.it.some.mod.is.bad"; + public static final TranslatableText IGNORED_TEXT = new TranslatableText(IGNORED_TEXT_KEY); + + public static final String URL_REGEX = "(https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]+\\.[^ ]+)"; + + public static final String ITEM_TAG = "item"; + public static final String POS_TAG = "pos"; + public static final String SPOILER_TAG = "spoiler"; + public static final String LINK_TAG = "sc-link"; + + public static final TextParser.TextFormatterHandler SPOILER_TAG_HANDLER = (tag, data, input, handlers, endAt) -> { + var out = TextParserUtils.recursiveParsing(input, handlers, endAt); + var config = ConfigManager.getConfig(); + + return new GeneralUtils.TextLengthPair( + ((MutableText) PlaceholderAPI.parsePredefinedText(config.spoilerStyle, + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("spoiler", new LiteralText(config.configData.spoilerSymbol.repeat(out.text().getString().length()))) + )).setStyle(Style.EMPTY.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, out.text()))), + out.length() + ); + }; + + + public static final String FORMAT_PERMISSION_BASE = "styledchat.format."; + public static final Pattern EMOTE_PATTERN = Pattern.compile("[:](?[^:]+)[:]"); + ; + + public static Text parseText(String input) { + return !input.isEmpty() ? TextParser.parse(input) : IGNORED_TEXT; + } + + public static Map getHandlers(ServerPlayerEntity player) { + HashMap handlers = new HashMap<>(); + ServerCommandSource source = player.getCommandSource(); + Config config = ConfigManager.getConfig(); + + for (Map.Entry entry : TextParser.getRegisteredSafeTags().entrySet()) { + if (config.defaultFormattingCodes.getBoolean(entry.getKey()) + || Permissions.check(source, FORMAT_PERMISSION_BASE + entry.getKey(), 2)) { + handlers.put(entry.getKey(), entry.getValue()); + } + } + + if (config.defaultFormattingCodes.getBoolean(ITEM_TAG) || + Permissions.check(source, FORMAT_PERMISSION_BASE + ITEM_TAG, 2)) { + handlers.put(ITEM_TAG, (tag, data, input, buildInHandlers, endAt) -> new GeneralUtils.TextLengthPair((MutableText) player.getStackInHand(Hand.MAIN_HAND).toHoverableText(), 0)); + } + + if (config.defaultFormattingCodes.getBoolean(SPOILER_TAG) || + Permissions.check(source, FORMAT_PERMISSION_BASE + SPOILER_TAG, 2)) { + handlers.put(SPOILER_TAG, SPOILER_TAG_HANDLER); + } + + if (config.defaultFormattingCodes.getBoolean(POS_TAG) || + Permissions.check(source, FORMAT_PERMISSION_BASE + POS_TAG, 2)) { + handlers.put(POS_TAG, (tag, data, input, buildInHandlers, endAt) -> + new GeneralUtils.TextLengthPair(new LiteralText(String.format("%.2f %.2f %.2f", player.getX(), player.getY(), player.getZ())), 0)); + } + + if (config.configData.parseLinksInChat + || Permissions.check(source, "styledchat.links", 2)) { + handlers.put(LINK_TAG, (tag, data, input, buildInHandlers, endAt) -> { + String url = TextParserUtils.cleanArgument(data); + return new GeneralUtils.TextLengthPair( + (MutableText) PlaceholderAPI.parsePredefinedText( + config.linkStyle, + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("link", new LiteralText(url).setStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)))) + ), 0); + }); + } + + StyledChatEvents.FORMATTING_CREATION_EVENT.invoker().onFormattingBuild(player, handlers::put); + + return handlers; + } + + + public static String formatMessage(String input, Map handlers) { + var config = ConfigManager.getConfig(); + if (handlers.containsKey(StyledChatUtils.LINK_TAG)) { + input = input.replaceAll(StyledChatUtils.URL_REGEX, "<" + StyledChatUtils.LINK_TAG + ":'$1'>"); + } + + if (config.configData.legacyChatFormatting) { + for (Formatting formatting : Formatting.values()) { + if (handlers.get(formatting.getName()) != null) { + input = input.replace(String.copyValueOf(new char[]{'&', formatting.getCode()}), "<" + formatting.getName() + ">"); + } + } + } + + try { + if (config.configData.enableMarkdown) { + if (handlers.containsKey(SPOILER_TAG)) { + input = input.replaceAll(getMarkdownRegex("||", "\\|\\|"), "$2"); + } + + if (handlers.containsKey("bold")) { + input = input.replaceAll(getMarkdownRegex("**", "\\*\\*"), "$2"); + } + + if (handlers.containsKey("underline")) { + input = input.replaceAll(getMarkdownRegex("__", "__"), "$2"); + } + + if (handlers.containsKey("strikethrough")) { + input = input.replaceAll(getMarkdownRegex("~~", "~~"), "$2"); + } + + if (handlers.containsKey("italic")) { + input = input.replaceAll(getMarkdownRegex("*", "\\*"), "$2"); + input = input.replaceAll(getMarkdownRegex("_", "_"), "$2"); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return input; + } + + private static String getMarkdownRegex(String base, String sides) { + return "(" + sides + ")(?[^" + base +"]+)(" + sides + ")"; + } +} diff --git a/src/main/java/eu/pb4/styledchat/command/Commands.java b/src/main/java/eu/pb4/styledchat/command/Commands.java index e052dca..26f91b7 100644 --- a/src/main/java/eu/pb4/styledchat/command/Commands.java +++ b/src/main/java/eu/pb4/styledchat/command/Commands.java @@ -1,17 +1,26 @@ package eu.pb4.styledchat.command; +import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; +import eu.pb4.placeholders.TextParser; import eu.pb4.styledchat.StyledChatMod; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.ConfigManager; import me.lucko.fabric.api.permissions.v0.Permissions; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.minecraft.command.argument.EntityArgumentType; +import net.minecraft.command.argument.TextArgumentType; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; +import net.minecraft.text.Texts; import net.minecraft.util.Formatting; +import net.minecraft.util.Util; +import java.util.Iterator; - +import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; public class Commands { @@ -27,6 +36,27 @@ public static void register() { .executes(Commands::reloadConfig) ) ); + + dispatcher.register( + literal("tellform") + .requires(Permissions.require("styledchat.tellform", 2)) + + .then(argument("targets", EntityArgumentType.players()) + .then(argument("message", StringArgumentType.greedyString()) + .executes((context) -> { + int i = 0; + var parsed = TextParser.parse(StyledChatUtils.formatMessage(context.getArgument("message", String.class), TextParser.getRegisteredTags())); + + for (var player : EntityArgumentType.getPlayers(context, "targets")) { + player.sendSystemMessage(parsed, Util.NIL_UUID); + } + + return i; + } + ) + ) + ) + ); }); } @@ -43,7 +73,7 @@ private static int reloadConfig(CommandContext context) { private static int about(CommandContext context) { context.getSource().sendFeedback(new LiteralText("Styled Chat") .formatted(Formatting.YELLOW) - .append(new LiteralText( " - " + StyledChatMod.VERSION) + .append(new LiteralText(" - " + StyledChatMod.VERSION) .formatted(Formatting.WHITE) ), false); diff --git a/src/main/java/eu/pb4/styledchat/config/ChatStyle.java b/src/main/java/eu/pb4/styledchat/config/ChatStyle.java index b58fc70..2c6876c 100644 --- a/src/main/java/eu/pb4/styledchat/config/ChatStyle.java +++ b/src/main/java/eu/pb4/styledchat/config/ChatStyle.java @@ -1,8 +1,10 @@ package eu.pb4.styledchat.config; import eu.pb4.placeholders.PlaceholderAPI; -import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.data.ChatStyleData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; @@ -20,38 +22,59 @@ public class ChatStyle { public final Text advancementTask; public final Text advancementChallenge; public final Text advancementGoal; + public final Text privateMessageSent; + public final Text privateMessageReceived; + public final Text teamChatSent; + public final Text teamChatReceived; + public final Text sayCommand; + public final Text meCommand; public ChatStyle(ChatStyleData data, ChatStyle defaultStyle) { - this.displayName = data.displayName != null ? TextParser.parse(data.displayName.replace("%player:displayname%", "")) : defaultStyle.displayName; - this.chat = data.chat != null ? TextParser.parse(data.chat) : defaultStyle.chat; - this.join = data.join != null ? TextParser.parse(data.join) : defaultStyle.join; - this.joinFirstTime = data.joinFirstTime != null ? TextParser.parse(data.joinFirstTime) : this.join; - this.joinRenamed = data.joinRenamed != null ? TextParser.parse(data.joinRenamed) : defaultStyle.joinRenamed; - this.left = data.left != null ? TextParser.parse(data.left) : defaultStyle.left; - this.death = data.death != null ? TextParser.parse(data.death) : defaultStyle.death; - this.advancementTask = data.advancementTask != null ? TextParser.parse(data.advancementTask) : defaultStyle.advancementTask; - this.advancementChallenge = data.advancementChallenge != null ? TextParser.parse(data.advancementChallenge) : defaultStyle.advancementChallenge; - this.advancementGoal = data.advancementGoal != null ? TextParser.parse(data.advancementGoal) : defaultStyle.advancementGoal; + this.displayName = data.displayName != null ? StyledChatUtils.parseText(data.displayName.replace("%player:displayname%", "")) : defaultStyle.displayName; + this.chat = data.chat != null ? StyledChatUtils.parseText(data.chat) : defaultStyle.chat; + this.join = data.join != null ? StyledChatUtils.parseText(data.join) : defaultStyle.join; + this.joinFirstTime = data.joinFirstTime != null ? StyledChatUtils.parseText(data.joinFirstTime) : this.join; + this.joinRenamed = data.joinRenamed != null ? StyledChatUtils.parseText(data.joinRenamed) : defaultStyle.joinRenamed; + this.left = data.left != null ? StyledChatUtils.parseText(data.left) : defaultStyle.left; + this.death = data.death != null ? StyledChatUtils.parseText(data.death) : defaultStyle.death; + this.advancementTask = data.advancementTask != null ? StyledChatUtils.parseText(data.advancementTask) : defaultStyle.advancementTask; + this.advancementChallenge = data.advancementChallenge != null ? StyledChatUtils.parseText(data.advancementChallenge) : defaultStyle.advancementChallenge; + this.advancementGoal = data.advancementGoal != null ? StyledChatUtils.parseText(data.advancementGoal) : defaultStyle.advancementGoal; + this.privateMessageSent = data.privateMessageSent != null ? StyledChatUtils.parseText(data.privateMessageSent) : defaultStyle.privateMessageSent; + this.privateMessageReceived = data.privateMessageReceived != null ? StyledChatUtils.parseText(data.privateMessageReceived) : defaultStyle.privateMessageReceived; + this.teamChatSent = data.teamChatSent != null ? StyledChatUtils.parseText(data.teamChatSent) : defaultStyle.teamChatSent; + this.teamChatReceived = data.teamChatReceived != null ? StyledChatUtils.parseText(data.teamChatReceived) : defaultStyle.teamChatReceived; + this.sayCommand = data.sayCommand != null ? StyledChatUtils.parseText(data.sayCommand) : defaultStyle.sayCommand; + this.meCommand = data.meCommand != null ? StyledChatUtils.parseText(data.meCommand) : defaultStyle.meCommand; } public ChatStyle(ChatStyleData data) { - this.displayName = data.displayName != null ? TextParser.parse(data.displayName.replace("%player:displayname%", "")) : null; - this.chat = data.chat != null ? TextParser.parse(data.chat) : null; - this.join = data.join != null ? TextParser.parse(data.join) : null; - this.joinRenamed = data.joinRenamed != null ? TextParser.parse(data.joinRenamed) : null; - this.joinFirstTime = data.joinFirstTime != null ? TextParser.parse(data.joinFirstTime) : null; - this.left = data.left != null ? TextParser.parse(data.left) : null; - this.death = data.death != null ? TextParser.parse(data.death) : null; - this.advancementTask = data.advancementTask != null ? TextParser.parse(data.advancementTask) : null; - this.advancementChallenge = data.advancementChallenge != null ? TextParser.parse(data.advancementChallenge) : null; - this.advancementGoal = data.advancementGoal != null ? TextParser.parse(data.advancementGoal) : null; - + this.displayName = data.displayName != null ? StyledChatUtils.parseText(data.displayName.replace("%player:displayname%", "")) : null; + this.chat = data.chat != null ? StyledChatUtils.parseText(data.chat) : null; + this.join = data.join != null ? StyledChatUtils.parseText(data.join) : null; + this.joinRenamed = data.joinRenamed != null ? StyledChatUtils.parseText(data.joinRenamed) : null; + this.joinFirstTime = data.joinFirstTime != null ? StyledChatUtils.parseText(data.joinFirstTime) : null; + this.left = data.left != null ? StyledChatUtils.parseText(data.left) : null; + this.death = data.death != null ? StyledChatUtils.parseText(data.death) : null; + this.advancementTask = data.advancementTask != null ? StyledChatUtils.parseText(data.advancementTask) : null; + this.advancementChallenge = data.advancementChallenge != null ? StyledChatUtils.parseText(data.advancementChallenge) : null; + this.advancementGoal = data.advancementGoal != null ? StyledChatUtils.parseText(data.advancementGoal) : null; + this.privateMessageSent = data.privateMessageSent != null ? StyledChatUtils.parseText(data.privateMessageSent) : null; + this.privateMessageReceived = data.privateMessageReceived != null ? StyledChatUtils.parseText(data.privateMessageReceived) : null; + this.teamChatSent = data.teamChatSent != null ? StyledChatUtils.parseText(data.teamChatSent) : null; + this.teamChatReceived = data.teamChatReceived != null ? StyledChatUtils.parseText(data.teamChatReceived) : null; + this.sayCommand = data.sayCommand != null ? StyledChatUtils.parseText(data.sayCommand) : null; + this.meCommand = data.meCommand != null ? StyledChatUtils.parseText(data.meCommand) : null; } + + public Text getDisplayName(ServerPlayerEntity player, Text vanillaDisplayName) { if (this.displayName == null) { return null; + } else if (this.advancementGoal == StyledChatUtils.IGNORED_TEXT) { + return vanillaDisplayName; } return PlaceholderAPI.parsePredefinedText( @@ -65,6 +88,8 @@ public Text getDisplayName(ServerPlayerEntity player, Text vanillaDisplayName) { public Text getChat(ServerPlayerEntity player, Text message) { if (this.chat == null) { return null; + } else if (this.chat == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -78,6 +103,8 @@ public Text getChat(ServerPlayerEntity player, Text message) { public Text getJoin(ServerPlayerEntity player) { if (this.join == null) { return null; + } else if (this.join == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -90,6 +117,8 @@ public Text getJoin(ServerPlayerEntity player) { public Text getJoinFirstTime(ServerPlayerEntity player) { if (this.joinFirstTime == null) { return null; + } else if (this.joinFirstTime == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -102,6 +131,8 @@ public Text getJoinFirstTime(ServerPlayerEntity player) { public Text getJoinRenamed(ServerPlayerEntity player, String oldName) { if (this.joinRenamed == null) { return null; + } else if (this.joinRenamed == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -115,6 +146,8 @@ public Text getJoinRenamed(ServerPlayerEntity player, String oldName) { public Text getLeft(ServerPlayerEntity player) { if (this.left == null) { return null; + } else if (this.left == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -127,6 +160,8 @@ public Text getLeft(ServerPlayerEntity player) { public Text getDeath(ServerPlayerEntity player, Text vanillaMessage) { if (this.death == null) { return null; + } else if (this.death == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -140,6 +175,8 @@ public Text getDeath(ServerPlayerEntity player, Text vanillaMessage) { public Text getAdvancementGoal(ServerPlayerEntity player, Text advancement) { if (this.advancementGoal == null) { return null; + } else if (this.advancementGoal == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -153,6 +190,8 @@ public Text getAdvancementGoal(ServerPlayerEntity player, Text advancement) { public Text getAdvancementTask(ServerPlayerEntity player, Text advancement) { if (this.advancementTask == null) { return null; + }else if (this.advancementTask == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -166,6 +205,8 @@ public Text getAdvancementTask(ServerPlayerEntity player, Text advancement) { public Text getAdvancementChallenge(ServerPlayerEntity player, Text advancement) { if (this.advancementChallenge == null) { return null; + }else if (this.advancementChallenge == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; } return PlaceholderAPI.parsePredefinedText( @@ -175,4 +216,138 @@ public Text getAdvancementChallenge(ServerPlayerEntity player, Text advancement) "advancement", advancement) ); } + + public Text getSayCommand(ServerCommandSource source, Text message) { + if (this.sayCommand == null) { + return null; + } else if (this.sayCommand == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; + } + + try { + var player = source.getPlayer(); + return PlaceholderAPI.parsePredefinedText( + PlaceholderAPI.parseText(this.sayCommand, player), + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("player", player.getDisplayName(), + "displayName", source.getDisplayName(), + "message", message) + ); + } catch (Exception e) { + return PlaceholderAPI.parsePredefinedText( + PlaceholderAPI.parseText(this.sayCommand, source.getServer()), + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("player", source.getDisplayName(), + "displayName", source.getDisplayName(), + "message", message) + ); + } + } + + public Text getMeCommand(ServerCommandSource source, Text message) { + if (this.meCommand == null) { + return null; + } else if (this.meCommand == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; + } + + try { + var player = source.getPlayer(); + return PlaceholderAPI.parsePredefinedText( + PlaceholderAPI.parseText(this.meCommand, player), + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("player", player.getDisplayName(), + "displayName", source.getDisplayName(), + "message", message) + ); + } catch (Exception e) { + return PlaceholderAPI.parsePredefinedText( + PlaceholderAPI.parseText(this.meCommand, source.getServer()), + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("player", source.getDisplayName(), + "displayName", source.getDisplayName(), + "message", message) + ); + } + } + + public Text getPrivateMessageSent(Text sender, Text receiver, Text message, Object placeholderContext) { + if (this.privateMessageSent == null) { + return null; + } else if (this.privateMessageSent == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; + } + + var template = placeholderContext instanceof ServerPlayerEntity player + ? PlaceholderAPI.parseText(this.privateMessageSent, player) + : PlaceholderAPI.parseText(this.privateMessageSent, (MinecraftServer) placeholderContext); + + return PlaceholderAPI.parsePredefinedText( + template, + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("sender", sender, + "receiver", receiver, + "message", message) + ); + } + + public Text getPrivateMessageReceived(Text sender, Text receiver, Text message, Object placeholderContext) { + if (this.privateMessageReceived == null) { + return null; + } else if (this.privateMessageReceived == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; + } + + var template = placeholderContext instanceof ServerPlayerEntity player + ? PlaceholderAPI.parseText(this.privateMessageReceived, player) + : PlaceholderAPI.parseText(this.privateMessageReceived, (MinecraftServer) placeholderContext); + + return PlaceholderAPI.parsePredefinedText( + template, + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("sender", sender, + "receiver", receiver, + "message", message) + ); + } + + public Text getTeamChatSent(Text team, Text displayName, Text message, Object placeholderContext) { + if (this.teamChatSent == null) { + return null; + } else if (this.teamChatSent == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; + } + + var template = placeholderContext instanceof ServerPlayerEntity player + ? PlaceholderAPI.parseText(this.teamChatSent, player) + : PlaceholderAPI.parseText(this.teamChatSent, (MinecraftServer) placeholderContext); + + return PlaceholderAPI.parsePredefinedText( + template, + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("team", team, + "displayName", displayName, + "message", message) + ); + } + + public Text getTeamChatReceived(Text team, Text displayName, Text message, Object placeholderContext) { + if (this.teamChatReceived == null) { + return null; + } else if (this.teamChatReceived == StyledChatUtils.IGNORED_TEXT) { + return StyledChatUtils.IGNORED_TEXT; + } + + var template = placeholderContext instanceof ServerPlayerEntity player + ? PlaceholderAPI.parseText(this.teamChatReceived, player) + : PlaceholderAPI.parseText(this.teamChatReceived, (MinecraftServer) placeholderContext); + + return PlaceholderAPI.parsePredefinedText( + template, + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("team", team, + "displayName", displayName, + "message", message) + ); + } } diff --git a/src/main/java/eu/pb4/styledchat/config/Config.java b/src/main/java/eu/pb4/styledchat/config/Config.java index 27dda1b..0815a06 100644 --- a/src/main/java/eu/pb4/styledchat/config/Config.java +++ b/src/main/java/eu/pb4/styledchat/config/Config.java @@ -1,18 +1,23 @@ package eu.pb4.styledchat.config; +import eu.pb4.placeholders.PlaceholderAPI; import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.data.ChatStyleData; import eu.pb4.styledchat.config.data.ConfigData; import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap; import me.lucko.fabric.api.permissions.v0.Permissions; +import net.minecraft.entity.passive.TameableEntity; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; -import net.minecraft.util.math.MathHelper; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public final class Config { public final ConfigData configData; @@ -20,6 +25,11 @@ public final class Config { private final List permissionStyle; public final Object2BooleanArrayMap defaultFormattingCodes; public final Text linkStyle; + public final Text spoilerStyle; + private final Text petDeathMessage; + + private final Map emotes; + private final List permissionEmotes; public Config(ConfigData data) { this.configData = data; @@ -27,15 +37,30 @@ public Config(ConfigData data) { this.permissionStyle = new ArrayList<>(); this.linkStyle = TextParser.parse(data.linkStyle); + this.spoilerStyle = TextParser.parse(data.spoilerStyle); for (ConfigData.PermissionPriorityStyle entry : data.permissionStyles) { - try { - this.permissionStyle.add(new PermissionStyle(entry.permission, MathHelper.clamp(Integer.parseInt(entry.permission), 1, 4), new ChatStyle(entry.style))); - } catch (Exception e) { - this.permissionStyle.add(new PermissionStyle(entry.permission, 4, new ChatStyle(entry.style))); + this.permissionStyle.add(new PermissionStyle(entry.permission, entry.opLevel, new ChatStyle(entry.style))); + } + + this.petDeathMessage = StyledChatUtils.parseText(configData.petDeathMessage); + + this.emotes = new HashMap<>(); + + for (var entry : data.emoticons.entrySet()) { + this.emotes.put(entry.getKey(), TextParser.parse(entry.getValue())); + } + + this.permissionEmotes = new ArrayList<>(); + for (var entry : data.permissionEmoticons) { + var emotes = PermissionEmotes.of(entry.permission, entry.opLevel); + + for (var emote : entry.emotes.entrySet()) { + emotes.emotes().put(emote.getKey(), TextParser.parse(emote.getValue())); } } + this.defaultFormattingCodes = new Object2BooleanArrayMap<>(this.configData.defaultEnabledFormatting); } @@ -173,6 +198,141 @@ public Text getAdvancementChallenge(ServerPlayerEntity player, Text advancement) return this.defaultStyle.getAdvancementChallenge(player, advancement); } + public Text getSayCommand(ServerCommandSource source, Text message) { + for (PermissionStyle entry : this.permissionStyle) { + if (Permissions.check(source, entry.permission, entry.opLevel)) { + Text text = entry.style.getSayCommand(source, message); + if (text != null) { + return text; + } + } + } + return this.defaultStyle.getSayCommand(source, message); + } + + public Text getMeCommand(ServerCommandSource source, Text message) { + for (PermissionStyle entry : this.permissionStyle) { + if (Permissions.check(source, entry.permission, entry.opLevel)) { + Text text = entry.style.getMeCommand(source, message); + if (text != null) { + return text; + } + } + } + return this.defaultStyle.getMeCommand(source, message); + } + + public Text getPrivateMessageSent(Text sender, Text receiver, Text message, ServerCommandSource context) { + Object placeholderContext; + + try { + placeholderContext = context.getPlayer(); + } catch (Exception e) { + placeholderContext = context.getServer(); + } + + for (PermissionStyle entry : this.permissionStyle) { + if (Permissions.check(context, entry.permission, entry.opLevel)) { + Text text = entry.style.getPrivateMessageSent(sender, receiver, message, placeholderContext); + if (text != null) { + return text; + } + } + } + return this.defaultStyle.getPrivateMessageSent(sender, receiver, message, placeholderContext); + } + + public Text getPrivateMessageReceived(Text sender, Text receiver, Text message, ServerCommandSource context) { + Object placeholderContext; + + try { + placeholderContext = context.getPlayer(); + } catch (Exception e) { + placeholderContext = context.getServer(); + } + + for (PermissionStyle entry : this.permissionStyle) { + if (Permissions.check(context, entry.permission, entry.opLevel)) { + Text text = entry.style.getPrivateMessageReceived(sender, receiver, message, placeholderContext); + if (text != null) { + return text; + } + } + } + return this.defaultStyle.getPrivateMessageReceived(sender, receiver, message, placeholderContext); + } + + public Text getTeamChatSent(Text team, Text displayName, Text message, ServerCommandSource context) { + Object placeholderContext; + + try { + placeholderContext = context.getPlayer(); + } catch (Exception e) { + placeholderContext = context.getServer(); + } + + for (PermissionStyle entry : this.permissionStyle) { + if (Permissions.check(context, entry.permission, entry.opLevel)) { + Text text = entry.style.getTeamChatSent(team, displayName, message, placeholderContext); + if (text != null) { + return text; + } + } + } + return this.defaultStyle.getTeamChatSent(team, displayName, message, placeholderContext); + } + + public Text getTeamChatReceived(Text team, Text displayName, Text message, ServerCommandSource context) { + Object placeholderContext; + + try { + placeholderContext = context.getPlayer(); + } catch (Exception e) { + placeholderContext = context.getServer(); + } + + for (PermissionStyle entry : this.permissionStyle) { + if (Permissions.check(context, entry.permission, entry.opLevel)) { + Text text = entry.style.getTeamChatReceived(team, displayName, message, placeholderContext); + if (text != null) { + return text; + } + } + } + return this.defaultStyle.getTeamChatReceived(team, displayName, message, placeholderContext); + } + + public Text getPetDeath(TameableEntity entity, Text vanillaMessage) { + if (this.petDeathMessage == null) { + return null; + } + + return PlaceholderAPI.parsePredefinedText( + PlaceholderAPI.parseText(this.petDeathMessage, entity.getServer()), + PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, + Map.of("pet", entity.getDisplayName(), + "default_message", vanillaMessage) + ); + } + + public Map getEmotes(ServerCommandSource source) { + var base = new HashMap<>(this.emotes); + + for (var entry : this.permissionEmotes) { + if (Permissions.check(source, entry.permission, entry.opLevel)) { + base.putAll(entry.emotes()); + } + } + + return base; + } + private static record PermissionStyle(String permission, int opLevel, ChatStyle style) { } + + private static record PermissionEmotes(String permission, int opLevel, Map emotes) { + public static PermissionEmotes of(String permission, int opLevel) { + return new PermissionEmotes(permission, opLevel, new HashMap<>()); + } + } } diff --git a/src/main/java/eu/pb4/styledchat/config/ConfigManager.java b/src/main/java/eu/pb4/styledchat/config/ConfigManager.java index 8f1ace3..572a8ae 100644 --- a/src/main/java/eu/pb4/styledchat/config/ConfigManager.java +++ b/src/main/java/eu/pb4/styledchat/config/ConfigManager.java @@ -13,7 +13,7 @@ public class ConfigManager { public static final int VERSION = 2; - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().setLenient().create(); private static Config CONFIG; @@ -36,6 +36,8 @@ public static boolean loadConfig() { case 1 -> GSON.fromJson(json, ConfigDataV1.class).updateToV2(); default -> GSON.fromJson(json, ConfigData.class); }); + + config.defaultStyle.fillMissing(); } else { config = new ConfigData(); } diff --git a/src/main/java/eu/pb4/styledchat/config/data/ChatStyleData.java b/src/main/java/eu/pb4/styledchat/config/data/ChatStyleData.java index 2332602..abbd06f 100644 --- a/src/main/java/eu/pb4/styledchat/config/data/ChatStyleData.java +++ b/src/main/java/eu/pb4/styledchat/config/data/ChatStyleData.java @@ -1,6 +1,8 @@ package eu.pb4.styledchat.config.data; +import java.util.Objects; + public class ChatStyleData { public static ChatStyleData DEFAULT = getDefault(); @@ -14,6 +16,12 @@ public class ChatStyleData { public String advancementTask; public String advancementChallenge; public String advancementGoal; + public String teamChatSent; + public String teamChatReceived; + public String privateMessageSent; + public String privateMessageReceived; + public String sayCommand; + public String meCommand; private static ChatStyleData getDefault() { @@ -25,10 +33,34 @@ private static ChatStyleData getDefault() { data.joinFirstTime = ""; data.left = ""; data.death = "${default_message}"; - data.advancementTask = ""; + data.advancementTask = "<>"; data.advancementGoal = ""; data.advancementChallenge = ""; + data.teamChatSent = "\\'>${team}':'${displayName}':'${message}'>"; + data.teamChatReceived = "\\'>${team}':'${displayName}':'${message}'>"; + data.privateMessageSent = ""; + data.privateMessageReceived = ""; + data.sayCommand = "[${player}] ${message}"; + data.meCommand = ""; return data; } + + public void fillMissing() { + this.displayName = Objects.requireNonNullElse(this.displayName, DEFAULT.displayName); + this.chat = Objects.requireNonNullElse(this.chat, DEFAULT.chat); + this.join = Objects.requireNonNullElse(this.join, DEFAULT.join); + this.joinRenamed = Objects.requireNonNullElse(this.joinRenamed, DEFAULT.joinRenamed); + this.left = Objects.requireNonNullElse(this.left, DEFAULT.left); + this.death = Objects.requireNonNullElse(this.death, DEFAULT.death); + this.advancementTask = Objects.requireNonNullElse(this.advancementTask, DEFAULT.advancementTask); + this.advancementChallenge = Objects.requireNonNullElse(this.advancementChallenge, DEFAULT.advancementChallenge); + this.advancementGoal = Objects.requireNonNullElse(this.advancementGoal, DEFAULT.advancementGoal); + this.privateMessageReceived = Objects.requireNonNullElse(this.privateMessageReceived, DEFAULT.privateMessageReceived); + this.privateMessageSent = Objects.requireNonNullElse(this.privateMessageSent, DEFAULT.privateMessageSent); + this.teamChatSent = Objects.requireNonNullElse(this.teamChatSent, DEFAULT.teamChatSent); + this.teamChatReceived = Objects.requireNonNullElse(this.teamChatReceived, DEFAULT.teamChatReceived); + this.sayCommand = Objects.requireNonNullElse(this.sayCommand, DEFAULT.sayCommand); + this.meCommand = Objects.requireNonNullElse(this.meCommand, DEFAULT.meCommand); + } } diff --git a/src/main/java/eu/pb4/styledchat/config/data/ConfigData.java b/src/main/java/eu/pb4/styledchat/config/data/ConfigData.java index 91e2412..1024cdd 100644 --- a/src/main/java/eu/pb4/styledchat/config/data/ConfigData.java +++ b/src/main/java/eu/pb4/styledchat/config/data/ConfigData.java @@ -1,34 +1,69 @@ package eu.pb4.styledchat.config.data; +import com.mojang.serialization.RecordBuilder; import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.ConfigManager; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class ConfigData { public int CONFIG_VERSION_DONT_TOUCH_THIS = ConfigManager.VERSION; public String _comment = "Before changing anything, see https://github.com/Patbox/StyledChat#configuration"; public ChatStyleData defaultStyle = ChatStyleData.DEFAULT; public List permissionStyles = new ArrayList<>(); + public String petDeathMessage = "${default_message}"; + + public Map emoticons = getDefaultEmoticons(); + + public List permissionEmoticons = new ArrayList<>(); + public boolean legacyChatFormatting = false; public boolean parseLinksInChat = true; - public String linkStyle = "${link}"; + public boolean enableMarkdown = true; + public boolean formattingInPrivateMessages = true; + public boolean formattingInTeamMessages = true; + public String linkStyle = "${link}"; + public String spoilerStyle = "${spoiler}"; + public String spoilerSymbol = "▌"; public HashMap defaultEnabledFormatting = getDefaultFormatting(); + private static HashMap getDefaultFormatting() { HashMap map = new HashMap<>(); - for (String string : TextParser.getRegisteredTags().keySet()) { - if (string.equals("click")) { - continue; - } + for (String string : TextParser.getRegisteredSafeTags().keySet()) { map.put(string, false); } - map.put("item", true); - map.put("pos", true); + map.put(StyledChatUtils.ITEM_TAG, true); + map.put(StyledChatUtils.POS_TAG, true); + map.put(StyledChatUtils.SPOILER_TAG, true); + map.put("bold", true); + map.put("b", true); + map.put("italic", true); + map.put("i", true); + map.put("strikethrough", true); + map.put("st", true); + map.put("underline", true); + return map; + } + + private static Map getDefaultEmoticons() { + HashMap map = new HashMap<>(); + + map.put("shrug", "¯\\_(ツ)_/¯"); + map.put("table", "(╯°□°)╯︵ ┻━┻"); + map.put("heart", "❤"); + map.put("sword", "\uD83D\uDDE1"); + map.put("fire", "\uD83D\uDD25"); + map.put("bow", "\uD83C\uDFF9"); + map.put("trident", "\uD83D\uDD31"); + map.put("rod", "\uD83C\uDFA3"); + map.put("potion", "\uD83E\uDDEA"); + map.put("shears", "✂"); + map.put("bucket", "\uD83E\uDEA3"); + map.put("bell", "\uD83D\uDD14"); + return map; } @@ -41,6 +76,7 @@ public static ConfigData transform(ConfigData configData) { public static class PermissionPriorityStyle { public String permission = ""; + public int opLevel = 3; public ChatStyleData style = ChatStyleData.DEFAULT; public static PermissionPriorityStyle of(String permission, ChatStyleData style) { @@ -50,4 +86,10 @@ public static PermissionPriorityStyle of(String permission, ChatStyleData style) return priorityStyle; } } + + public static class PermissionEmotes { + public String permission = ""; + public int opLevel = 3; + public Map emotes = Collections.EMPTY_MAP; + } } diff --git a/src/main/java/eu/pb4/styledchat/mixin/MeCommandMixin.java b/src/main/java/eu/pb4/styledchat/mixin/MeCommandMixin.java new file mode 100644 index 0000000..b2f51cb --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/mixin/MeCommandMixin.java @@ -0,0 +1,53 @@ +package eu.pb4.styledchat.mixin; + +import com.mojang.brigadier.context.CommandContext; +import eu.pb4.placeholders.PlaceholderAPI; +import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; +import eu.pb4.styledchat.config.ConfigManager; +import net.minecraft.server.command.MeCommand; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; + +@Mixin(MeCommand.class) +public class MeCommandMixin { + @Inject(method = "getEmoteText", at = @At("HEAD"), cancellable = true) + private static void styledChat_formatText(CommandContext context, String arg, CallbackInfoReturnable cir) { + var config = ConfigManager.getConfig(); + var source = context.getSource(); + + Text message; + Map formatting; + + try { + var player = source.getPlayer(); + formatting = StyledChatUtils.getHandlers(player); + } catch (Exception e) { + formatting = TextParser.getRegisteredSafeTags(); + } + var emotes = config.getEmotes(source); + + + if (formatting.size() != 0) { + var formattedMessage = StyledChatUtils.formatMessage(arg, formatting); + + message = TextParser.parse(formattedMessage, formatting); + } else { + message = new LiteralText(arg); + } + + if (emotes.size() != 0) { + message = PlaceholderAPI.parsePredefinedText(message, StyledChatUtils.EMOTE_PATTERN, emotes); + } + + + cir.setReturnValue(config.getMeCommand(source, message)); + } +} diff --git a/src/main/java/eu/pb4/styledchat/mixin/MessageCommandMixin.java b/src/main/java/eu/pb4/styledchat/mixin/MessageCommandMixin.java new file mode 100644 index 0000000..faa0c25 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/mixin/MessageCommandMixin.java @@ -0,0 +1,70 @@ +package eu.pb4.styledchat.mixin; + +import eu.pb4.placeholders.PlaceholderAPI; +import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; +import eu.pb4.styledchat.config.ConfigManager; +import net.minecraft.server.command.MessageCommand; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Collection; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +@Mixin(MessageCommand.class) +public class MessageCommandMixin { + @ModifyVariable(method = "execute", at = @At("HEAD"), index = 2) + private static Text styledChat_formatText(Text message, ServerCommandSource source, Collection targets) { + var config = ConfigManager.getConfig(); + if (config.configData.formattingInPrivateMessages) { + Map formatting; + + try { + var player = source.getPlayer(); + formatting = StyledChatUtils.getHandlers(player); + } catch (Exception e) { + formatting = TextParser.getRegisteredSafeTags(); + } + var emotes = config.getEmotes(source); + + if (formatting.size() != 0) { + var ogMessage = message.getString(); + var parsed = PlaceholderAPI.parsePredefinedText(TextParser.parse(StyledChatUtils.formatMessage(ogMessage, formatting), formatting), StyledChatUtils.EMOTE_PATTERN, emotes); ; + + + if (!ogMessage.equals(parsed.getString())) { + return parsed; + } + } + } + + return message; + } + + @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V")) + private static void styledChat_redirectSending(Consumer consumer, T t, ServerCommandSource source, Collection targets, Text message) { + var entity = source.getEntity(); + + var receiver = (Text) t; + var output = ConfigManager.getConfig().getPrivateMessageSent(source.getDisplayName(), receiver, message, source); + + if (entity instanceof ServerPlayerEntity player) { + player.sendSystemMessage(output, player.getUuid()); + } else { + source.sendFeedback(output, false); + } + } + + @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;sendSystemMessage(Lnet/minecraft/text/Text;Ljava/util/UUID;)V")) + private static void styledChat_redirectReceiving(ServerPlayerEntity receiver, Text _ignored, UUID sender, ServerCommandSource source, Collection targets, Text message) { + var output = ConfigManager.getConfig().getPrivateMessageReceived(source.getDisplayName(), receiver.getDisplayName(), message, source); + receiver.sendSystemMessage(output, sender); + } +} diff --git a/src/main/java/eu/pb4/styledchat/mixin/MinecraftServerMixin.java b/src/main/java/eu/pb4/styledchat/mixin/MinecraftServerMixin.java new file mode 100644 index 0000000..d7e2078 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/mixin/MinecraftServerMixin.java @@ -0,0 +1,23 @@ +package eu.pb4.styledchat.mixin; + +import eu.pb4.styledchat.StyledChatUtils; +import net.minecraft.network.MessageType; +import net.minecraft.server.MinecraftServer; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.UUID; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + @Inject(method = "sendSystemMessage", at = @At("HEAD"), cancellable = true) + private void styledChat_excludeSendingOfHiddenMessages(Text message, UUID sender, CallbackInfo ci) { + if (message instanceof TranslatableText text && text.getKey().equals(StyledChatUtils.IGNORED_TEXT_KEY)) { + ci.cancel(); + } + } +} diff --git a/src/main/java/eu/pb4/styledchat/mixin/PlayerAdvancementTrackerMixin.java b/src/main/java/eu/pb4/styledchat/mixin/PlayerAdvancementTrackerMixin.java index e121fc3..75e2c0c 100644 --- a/src/main/java/eu/pb4/styledchat/mixin/PlayerAdvancementTrackerMixin.java +++ b/src/main/java/eu/pb4/styledchat/mixin/PlayerAdvancementTrackerMixin.java @@ -17,7 +17,7 @@ public class PlayerAdvancementTrackerMixin { @Shadow private ServerPlayerEntity owner; @ModifyArg(method = "grantCriterion", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcastChatMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/MessageType;Ljava/util/UUID;)V")) - private Text changeAdvancementMessage(Text text) { + private Text styledChat_changeAdvancementMessage(Text text) { TranslatableText translatableText = (TranslatableText) text; Config config = ConfigManager.getConfig(); diff --git a/src/main/java/eu/pb4/styledchat/mixin/PlayerEntityMixin.java b/src/main/java/eu/pb4/styledchat/mixin/PlayerEntityMixin.java index bea02d3..019b979 100644 --- a/src/main/java/eu/pb4/styledchat/mixin/PlayerEntityMixin.java +++ b/src/main/java/eu/pb4/styledchat/mixin/PlayerEntityMixin.java @@ -15,7 +15,7 @@ public class PlayerEntityMixin { @Unique private boolean ignoreNextCalls = false; @Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true) - private void replaceDisplayName(CallbackInfoReturnable cir) { + private void styledChat_replaceDisplayName(CallbackInfoReturnable cir) { if (!this.ignoreNextCalls && ((Object) this) instanceof ServerPlayerEntity player) { this.ignoreNextCalls = true; cir.setReturnValue(ConfigManager.getConfig().getDisplayName(player, cir.getReturnValue())); diff --git a/src/main/java/eu/pb4/styledchat/mixin/PlayerManagerMixin.java b/src/main/java/eu/pb4/styledchat/mixin/PlayerManagerMixin.java index 3b9308b..b7d9a77 100644 --- a/src/main/java/eu/pb4/styledchat/mixin/PlayerManagerMixin.java +++ b/src/main/java/eu/pb4/styledchat/mixin/PlayerManagerMixin.java @@ -1,7 +1,9 @@ package eu.pb4.styledchat.mixin; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.ConfigManager; import net.minecraft.network.ClientConnection; +import net.minecraft.network.MessageType; import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.stat.Stats; @@ -14,6 +16,9 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.UUID; +import java.util.function.Function; + @Mixin(PlayerManager.class) public class PlayerManagerMixin { @@ -21,17 +26,17 @@ public class PlayerManagerMixin { @Unique private ServerPlayerEntity temporaryPlayer = null; @Inject(method = "onPlayerConnect", at = @At(value = "HEAD")) - private void storePlayer(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { + private void styledChat_storePlayer(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { this.temporaryPlayer = player; } @Inject(method = "onPlayerConnect", at = @At("RETURN")) - private void removeStoredPlayer(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { + private void styledChat_removeStoredPlayer(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { this.temporaryPlayer = null; } @ModifyArg(method = "onPlayerConnect", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcastChatMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/MessageType;Ljava/util/UUID;)V")) - private Text updatePlayerNameAfterMessage(Text text) { + private Text styledChat_updatePlayerNameAfterMessage(Text text) { if (this.temporaryPlayer.getStatHandler().getStat(Stats.CUSTOM.getOrCreateStat(Stats.LEAVE_GAME)) == 0) { return ConfigManager.getConfig().getJoinFirstTime(this.temporaryPlayer); } @@ -43,4 +48,11 @@ private Text updatePlayerNameAfterMessage(Text text) { return ConfigManager.getConfig().getJoinRenamed(this.temporaryPlayer, (String) args[1]); } } + + @Inject(method = "broadcastChatMessage", at = @At("HEAD"), cancellable = true) + private void styledChat_excludeSendingOfHiddenMessages(Text message, MessageType type, UUID sender, CallbackInfo ci) { + if (message instanceof TranslatableText text && text.getKey().equals(StyledChatUtils.IGNORED_TEXT_KEY)) { + ci.cancel(); + } + } } diff --git a/src/main/java/eu/pb4/styledchat/mixin/SayCommandMixin.java b/src/main/java/eu/pb4/styledchat/mixin/SayCommandMixin.java new file mode 100644 index 0000000..464ed80 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/mixin/SayCommandMixin.java @@ -0,0 +1,64 @@ +package eu.pb4.styledchat.mixin; + +import com.mojang.brigadier.context.CommandContext; +import eu.pb4.placeholders.PlaceholderAPI; +import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; +import eu.pb4.styledchat.config.ConfigManager; +import net.minecraft.command.argument.MessageArgumentType; +import net.minecraft.server.command.MeCommand; +import net.minecraft.server.command.SayCommand; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; + +@Mixin(SayCommand.class) +public class SayCommandMixin { + @ModifyVariable(method = "method_13563", at = @At("STORE"), ordinal = 1) + private static Text styledChat_formatText(Text inputX, CommandContext context) { + var input = (Text) ((TranslatableText) inputX).getArgs()[1]; + + var inputAsString = input.getString(); + + var config = ConfigManager.getConfig(); + var source = context.getSource(); + + Text message; + Map formatting; + + try { + var player = source.getPlayer(); + formatting = StyledChatUtils.getHandlers(player); + } catch (Exception e) { + formatting = TextParser.getRegisteredSafeTags(); + } + var emotes = config.getEmotes(source); + + if (formatting.size() != 0) { + var formattedMessage = StyledChatUtils.formatMessage(inputAsString, formatting); + var tmpMessage = TextParser.parse(formattedMessage, formatting); + + if (!tmpMessage.getString().equals(inputAsString)) { + message = tmpMessage; + } else { + message = input; + } + } else { + message = input; + } + + if (emotes.size() != 0) { + message = PlaceholderAPI.parsePredefinedText(message, StyledChatUtils.EMOTE_PATTERN, emotes); + } + + return config.getSayCommand(source, message); + } +} diff --git a/src/main/java/eu/pb4/styledchat/mixin/ServerPlayNetworkManagerMixin.java b/src/main/java/eu/pb4/styledchat/mixin/ServerPlayNetworkManagerMixin.java index 263073c..a63d9a6 100644 --- a/src/main/java/eu/pb4/styledchat/mixin/ServerPlayNetworkManagerMixin.java +++ b/src/main/java/eu/pb4/styledchat/mixin/ServerPlayNetworkManagerMixin.java @@ -3,33 +3,26 @@ import eu.pb4.placeholders.PlaceholderAPI; import eu.pb4.placeholders.TextParser; -import eu.pb4.placeholders.util.GeneralUtils; -import eu.pb4.placeholders.util.TextParserUtils; -import eu.pb4.styledchat.StyledChatMod; + +import eu.pb4.styledchat.StyledChatEvents; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.Config; import eu.pb4.styledchat.config.ConfigManager; -import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.network.MessageType; import net.minecraft.server.PlayerManager; -import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.filter.TextStream; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.*; import net.minecraft.util.Formatting; -import net.minecraft.util.Hand; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; @Mixin(ServerPlayNetworkHandler.class) @@ -39,68 +32,64 @@ public class ServerPlayNetworkManagerMixin { public ServerPlayerEntity player; @ModifyArg(method = "onDisconnected", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcastChatMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/MessageType;Ljava/util/UUID;)V")) - private Text replaceDisconnectMessage(Text text) { + private Text styledChat_replaceDisconnectMessage(Text text) { return ConfigManager.getConfig().getLeft(this.player); } @Redirect(method = "handleMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Ljava/util/function/Function;Lnet/minecraft/network/MessageType;Ljava/util/UUID;)V")) - private void replaceChatMessage(PlayerManager playerManager, Text serverMessage, Function playerMessageFactory, MessageType playerMessageType, UUID sender, TextStream.Message message) { - HashMap handlers = new HashMap<>(); - ServerCommandSource source = this.player.getCommandSource(); + private void styledChat_replaceChatMessage(PlayerManager playerManager, Text serverMessage, Function playerMessageFactory, MessageType playerMessageType, UUID sender, TextStream.Message message) { + var handlers = StyledChatUtils.getHandlers(this.player); Config config = ConfigManager.getConfig(); + var emotes = config.getEmotes(this.player.getCommandSource()); - String rawMessage = message.getRaw(); - String filteredMessage = message.getRaw(); + String rawMessage = message.getRaw(); + String filteredMessage = message.getFiltered(); - for (Map.Entry entry : TextParser.getRegisteredTags().entrySet()) { - if (!entry.getKey().equals("click") - && config.defaultFormattingCodes.getBoolean(entry.getKey()) - || Permissions.check(source, "styledchat.format." + entry.getKey(), 2)) { - handlers.put(entry.getKey(), entry.getValue()); - } - } - if (config.defaultFormattingCodes.getBoolean("item") || - Permissions.check(source, "styledchat.format.item", 2)) { - handlers.put("item", (tag, data, input, buildInHandlers, endAt) -> new GeneralUtils.TextLengthPair((MutableText) player.getStackInHand(Hand.MAIN_HAND).toHoverableText(), 0)); - } + // You might say, that it's useless and you would be kinda right + // However in case of other mods or vanilla implementing these, it should work without any modifications! + if (rawMessage.equals(filteredMessage)) { + rawMessage = StyledChatEvents.PRE_MESSAGE_CONTENT_SEND.invoker().onPreMessage(message.getRaw(), player, false); - if (config.defaultFormattingCodes.getBoolean("pos") || - Permissions.check(source, "styledchat.format.pos", 2)) { - handlers.put("pos", (tag, data, input, buildInHandlers, endAt) -> new GeneralUtils.TextLengthPair(new LiteralText(player.getBlockPos().toShortString()), 0)); - } + rawMessage = StyledChatUtils.formatMessage(rawMessage, handlers); - if (config.configData.parseLinksInChat - || Permissions.check(source, "styledchat.links", 2)) { - handlers.put("sc-link", (tag, data, input, buildInHandlers, endAt) -> { - String url = TextParserUtils.cleanArgument(data); - return new GeneralUtils.TextLengthPair( - (MutableText) PlaceholderAPI.parsePredefinedText( - config.linkStyle, - PlaceholderAPI.PREDEFINED_PLACEHOLDER_PATTERN, - Map.of("link", new LiteralText(url).setStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)))) - ), 0); - }); - - - rawMessage = rawMessage.replaceAll(StyledChatMod.URL_REGEX, ""); - filteredMessage = filteredMessage.replaceAll(StyledChatMod.URL_REGEX, ""); - } + Text rawText = config.getChat(this.player, + StyledChatEvents.MESSAGE_CONTENT_SEND.invoker().onMessage(handlers.size() > 0 + ? PlaceholderAPI.parsePredefinedText(TextParser.parse(rawMessage, handlers), StyledChatUtils.EMOTE_PATTERN, emotes) + : new LiteralText(message.getRaw()), + player, false) + ); - if (config.configData.legacyChatFormatting) { - for (Formatting formatting : Formatting.values()) { - if (handlers.get(formatting.getName()) != null) { - rawMessage = rawMessage.replace(String.copyValueOf(new char[] {'&', formatting.getCode()}), "<" + formatting.getName() + ">"); - filteredMessage = filteredMessage.replace(String.copyValueOf(new char[] {'&', formatting.getCode()}), "<" + formatting.getName() + ">"); - } + if (rawText != null ) { + playerManager.broadcast(rawText, (receiver) -> StyledChatEvents.MESSAGE_TO_SEND.invoker().onMessageTo(rawText, this.player, receiver, false), playerMessageType, sender); + } + } else { + rawMessage = StyledChatEvents.PRE_MESSAGE_CONTENT_SEND.invoker().onPreMessage(message.getRaw(), player, false); + filteredMessage = StyledChatEvents.PRE_MESSAGE_CONTENT_SEND.invoker().onPreMessage(message.getFiltered(), player, true); + + rawMessage = StyledChatUtils.formatMessage(rawMessage, handlers); + filteredMessage = StyledChatUtils.formatMessage(filteredMessage, handlers); + + Text rawText = config.getChat(this.player, + StyledChatEvents.MESSAGE_CONTENT_SEND.invoker().onMessage(handlers.size() > 0 + ? TextParser.parse(rawMessage, handlers) + : new LiteralText(message.getRaw()), + player, false) + ); + Text filteredText = config.getChat(this.player, + StyledChatEvents.MESSAGE_CONTENT_SEND.invoker().onMessage(handlers.size() > 0 + ? TextParser.parse(filteredMessage, handlers) + : new LiteralText(message.getFiltered()), + player, true) + ); + + if (rawText != null && filteredText != null) { + playerManager.broadcast(rawText, (receiver) -> { + var filtered = this.player.shouldFilterMessagesSentTo(receiver); + return StyledChatEvents.MESSAGE_TO_SEND.invoker().onMessageTo(filtered ? filteredText : rawText, this.player, receiver, filtered); + + }, playerMessageType, sender); } - } - - Text rawText = config.getChat(this.player, handlers.size() > 0 ? TextParser.parse(rawMessage, handlers) : new LiteralText(message.getRaw())); - Text filteredText = config.getChat(this.player, handlers.size() > 0 ? TextParser.parse(filteredMessage, handlers) : new LiteralText(message.getFiltered())); - - if (rawText != null && filteredText != null) { - playerManager.broadcast(rawText, (player) -> this.player.shouldFilterMessagesSentTo(player) ? filteredText : rawText, playerMessageType, sender); } } } diff --git a/src/main/java/eu/pb4/styledchat/mixin/ServerPlayerEntityMixin.java b/src/main/java/eu/pb4/styledchat/mixin/ServerPlayerEntityMixin.java index f175321..43cc179 100644 --- a/src/main/java/eu/pb4/styledchat/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/eu/pb4/styledchat/mixin/ServerPlayerEntityMixin.java @@ -1,19 +1,32 @@ package eu.pb4.styledchat.mixin; +import eu.pb4.styledchat.StyledChatUtils; import eu.pb4.styledchat.config.ConfigManager; +import net.minecraft.network.MessageType; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.UUID; @Mixin(ServerPlayerEntity.class) public class ServerPlayerEntityMixin { @ModifyArg(method = "onDeath", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcastChatMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/MessageType;Ljava/util/UUID;)V")) - private Text replaceDeathMessage(Text text) { + private Text styledChat_replaceDeathMessage(Text text) { return ConfigManager.getConfig().getDeath((ServerPlayerEntity) (Object) this, text); } + + @Inject(method = "sendMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/MessageType;Ljava/util/UUID;)V", at = @At("HEAD"), cancellable = true) + private void styledChat_excludeSendingOfHiddenMessages(Text message, MessageType type, UUID sender, CallbackInfo ci) { + if (message instanceof TranslatableText text && text.getKey().equals(StyledChatUtils.IGNORED_TEXT_KEY)) { + ci.cancel(); + } + } } diff --git a/src/main/java/eu/pb4/styledchat/mixin/TameableEntityMixin.java b/src/main/java/eu/pb4/styledchat/mixin/TameableEntityMixin.java new file mode 100644 index 0000000..b2a32d9 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/mixin/TameableEntityMixin.java @@ -0,0 +1,16 @@ +package eu.pb4.styledchat.mixin; + +import eu.pb4.styledchat.config.ConfigManager; +import net.minecraft.entity.passive.TameableEntity; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(TameableEntity.class) +public class TameableEntityMixin { + @ModifyArg(method = "onDeath", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;sendSystemMessage(Lnet/minecraft/text/Text;Ljava/util/UUID;)V")) + private Text styledChat_replaceDeathMessage(Text text) { + return ConfigManager.getConfig().getPetDeath((TameableEntity) (Object) this, text); + } +} diff --git a/src/main/java/eu/pb4/styledchat/mixin/TeamMsgCommandMixin.java b/src/main/java/eu/pb4/styledchat/mixin/TeamMsgCommandMixin.java new file mode 100644 index 0000000..7e77818 --- /dev/null +++ b/src/main/java/eu/pb4/styledchat/mixin/TeamMsgCommandMixin.java @@ -0,0 +1,79 @@ +package eu.pb4.styledchat.mixin; + +import eu.pb4.placeholders.PlaceholderAPI; +import eu.pb4.placeholders.TextParser; +import eu.pb4.styledchat.StyledChatUtils; +import eu.pb4.styledchat.config.ConfigManager; +import net.minecraft.scoreboard.Team; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.command.TeamMsgCommand; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; + +@Mixin(TeamMsgCommand.class) +public class TeamMsgCommandMixin { + + @Unique + private static Text styledChat_sentFormatted = null; + @Unique + private static Text styledChat_receivedFormatted = null; + + @ModifyVariable(method = "execute", at = @At("HEAD"), index = 1) + private static Text styledChat_formatText(Text message, ServerCommandSource source) { + var config = ConfigManager.getConfig(); + if (config.configData.formattingInTeamMessages) { + Map formatting; + + try { + var player = source.getPlayer(); + formatting = StyledChatUtils.getHandlers(player); + } catch (Exception e) { + formatting = TextParser.getRegisteredSafeTags(); + } + + var emotes = config.getEmotes(source); + + if (formatting.size() != 0) { + var ogMessage = message.getString(); + var parsed = PlaceholderAPI.parsePredefinedText(TextParser.parse(StyledChatUtils.formatMessage(ogMessage, formatting), formatting), StyledChatUtils.EMOTE_PATTERN, emotes); ; + + if (!ogMessage.equals(parsed.getString())) { + return parsed; + } + } + } + + return message; + } + + @Inject(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/MutableText;fillStyle(Lnet/minecraft/text/Style;)Lnet/minecraft/text/MutableText;")) + private static void styledChat_storeFormatted(ServerCommandSource source, Text message, CallbackInfoReturnable cir) { + var config = ConfigManager.getConfig(); + var team = ((Team) source.getEntity().getScoreboardTeam()).getFormattedName(); + team.setStyle(team.getStyle().withHoverEvent(null)); + + styledChat_sentFormatted = config.getTeamChatSent(team, source.getDisplayName(), message, source); + styledChat_receivedFormatted = config.getTeamChatReceived(team, source.getDisplayName(), message, source); + } + + @Inject(method = "execute", at = @At("RETURN")) + private static void styledChat_clearCache(ServerCommandSource source, Text message, CallbackInfoReturnable cir) { + styledChat_receivedFormatted = null; + styledChat_sentFormatted = null; + } + + @ModifyArg(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;sendSystemMessage(Lnet/minecraft/text/Text;Ljava/util/UUID;)V", ordinal = 0)) + private static Text styledChat_replaceText(Text _unused) { + return styledChat_sentFormatted; + } + + @ModifyArg(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;sendSystemMessage(Lnet/minecraft/text/Text;Ljava/util/UUID;)V", ordinal = 1)) + private static Text styledChat_replaceText2(Text _unused) { + return styledChat_receivedFormatted; + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index fb7c409..883eebe 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -26,8 +26,6 @@ ], "depends": { - "fabricloader": ">=0.11.3", - "fabric": "*", "minecraft": "1.17.x" } } diff --git a/src/main/resources/styledchat.mixins.json b/src/main/resources/styledchat.mixins.json index dab4693..24105c3 100644 --- a/src/main/resources/styledchat.mixins.json +++ b/src/main/resources/styledchat.mixins.json @@ -4,11 +4,17 @@ "package": "eu.pb4.styledchat.mixin", "compatibilityLevel": "JAVA_16", "mixins": [ + "MeCommandMixin", + "MessageCommandMixin", + "MinecraftServerMixin", "PlayerAdvancementTrackerMixin", "PlayerEntityMixin", "PlayerManagerMixin", + "SayCommandMixin", "ServerPlayerEntityMixin", - "ServerPlayNetworkManagerMixin" + "ServerPlayNetworkManagerMixin", + "TameableEntityMixin", + "TeamMsgCommandMixin" ], "client": [ ],