diff --git a/common/src/main/java/net/draycia/carbon/common/command/commands/NicknameCommand.java b/common/src/main/java/net/draycia/carbon/common/command/commands/NicknameCommand.java index 57e92a801..76a4aa7be 100644 --- a/common/src/main/java/net/draycia/carbon/common/command/commands/NicknameCommand.java +++ b/common/src/main/java/net/draycia/carbon/common/command/commands/NicknameCommand.java @@ -23,9 +23,7 @@ import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.minecraft.extras.MinecraftExtrasMetaKeys; import cloud.commandframework.minecraft.extras.RichDescription; -import com.google.common.base.Suppliers; import com.google.inject.Inject; -import java.util.function.Supplier; import net.draycia.carbon.api.users.CarbonPlayer; import net.draycia.carbon.common.command.ArgumentFactory; import net.draycia.carbon.common.command.CarbonCommand; @@ -126,29 +124,27 @@ private void resetNickname(final Commander sender, final CarbonPlayer target) { } private void applyNickname(final Commander sender, final CarbonPlayer target, final String nick) { - - // Lazy since the sender might not have permission to set the nickname - final Supplier parsedNick = Suppliers.memoize(() -> parseNickname(sender, nick)); + final Component parsedNick = parseNickname(sender, nick); // If the nickname is caught in the character limit, return without setting a nickname. - final int nickNameLength = PlainTextComponentSerializer.plainText().serialize(parsedNick.get()).length(); + final int nickNameLength = PlainTextComponentSerializer.plainText().serialize(parsedNick).length(); final int minLength = this.config.primaryConfig().nickname().minLength(); final int maxLength = this.config.primaryConfig().nickname().maxLength(); if (nickNameLength < minLength || maxLength < nickNameLength) { - this.carbonMessages.nicknameErrorCharacterLimit(sender, parsedNick.get(), minLength, maxLength); + this.carbonMessages.nicknameErrorCharacterLimit(sender, parsedNick, minLength, maxLength); return; } - target.nickname(parsedNick.get()); + target.nickname(parsedNick); if (sender instanceof PlayerCommander playerCommander && playerCommander.carbonPlayer().uuid().equals(target.uuid())) { // Setting own nickname - this.carbonMessages.nicknameSet(sender, parsedNick.get()); + this.carbonMessages.nicknameSet(sender, parsedNick); } else { // Setting other player's nickname - this.carbonMessages.nicknameSet(target, parsedNick.get()); - this.carbonMessages.nicknameSetOthers(sender, target.username(), parsedNick.get()); + this.carbonMessages.nicknameSet(target, parsedNick); + this.carbonMessages.nicknameSetOthers(sender, target.username(), parsedNick); } } diff --git a/common/src/main/java/net/draycia/carbon/common/config/PrimaryConfig.java b/common/src/main/java/net/draycia/carbon/common/config/PrimaryConfig.java index 746b4f75f..8496b99e2 100644 --- a/common/src/main/java/net/draycia/carbon/common/config/PrimaryConfig.java +++ b/common/src/main/java/net/draycia/carbon/common/config/PrimaryConfig.java @@ -219,6 +219,12 @@ public static final class NicknameSettings { @Comment("Whether Carbon's nickname management should be used. Disable this if you wish to have another plugin manage nicknames.") private boolean useCarbonNicknames = true; + @Comment("Format used when displaying nicknames.") + public String format = "@'>~"; + + @Comment("Whether to skip applying 'format' when a nickname matches a players username, only differing in decoration.") + public boolean skipFormatWhenNameMatches = true; + public boolean useCarbonNicknames() { return this.useCarbonNicknames; } diff --git a/common/src/main/java/net/draycia/carbon/common/users/CarbonPlayerCommon.java b/common/src/main/java/net/draycia/carbon/common/users/CarbonPlayerCommon.java index b60f5cc73..45941047a 100644 --- a/common/src/main/java/net/draycia/carbon/common/users/CarbonPlayerCommon.java +++ b/common/src/main/java/net/draycia/carbon/common/users/CarbonPlayerCommon.java @@ -35,6 +35,7 @@ import net.draycia.carbon.api.util.InventorySlot; import net.draycia.carbon.common.PlatformScheduler; import net.draycia.carbon.common.config.ConfigManager; +import net.draycia.carbon.common.messages.CarbonMessageRenderer; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.identity.Identity; @@ -54,6 +55,7 @@ public class CarbonPlayerCommon implements CarbonPlayer, ForwardingAudience.Sing private transient @MonotonicNonNull @Inject ProfileResolver profileResolver; private transient @MonotonicNonNull @Inject PlatformScheduler scheduler; private transient @MonotonicNonNull @Inject ConfigManager config; + private transient @MonotonicNonNull @Inject CarbonMessageRenderer messageRenderer; private volatile transient long transientLoadedSince = -1; protected final PersistentUserProperty muted; @@ -443,6 +445,14 @@ public boolean hasNickname() { return this.displayName.hasValue(); } + public ConfigManager configManager() { + return this.config; + } + + public CarbonMessageRenderer messageRenderer() { + return this.messageRenderer; + } + @Override public UUID uuid() { return this.uuid; diff --git a/common/src/main/java/net/draycia/carbon/common/users/WrappedCarbonPlayer.java b/common/src/main/java/net/draycia/carbon/common/users/WrappedCarbonPlayer.java index 015f45fde..090ec65f2 100644 --- a/common/src/main/java/net/draycia/carbon/common/users/WrappedCarbonPlayer.java +++ b/common/src/main/java/net/draycia/carbon/common/users/WrappedCarbonPlayer.java @@ -31,12 +31,15 @@ import net.draycia.carbon.api.channels.ChatChannel; import net.draycia.carbon.api.users.CarbonPlayer; import net.draycia.carbon.api.util.InventorySlot; +import net.draycia.carbon.common.config.PrimaryConfig; +import net.draycia.carbon.common.messages.SourcedAudience; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.standard.StandardTags; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; @@ -229,7 +232,26 @@ public String username() { public Component displayName() { final @Nullable Component nick = this.nickname(); if (nick != null) { - return nick; + final PrimaryConfig.NicknameSettings nicknames = this.carbonPlayerCommon.configManager().primaryConfig().nickname(); + + if (nicknames.skipFormatWhenNameMatches) { + final String plainNick = PlainTextComponentSerializer.plainText().serialize(nick); + if (plainNick.equals(this.username())) { + return nick; + } + } + + try { + return this.carbonPlayerCommon.messageRenderer().render( + SourcedAudience.of(this, this), + nicknames.format, + Map.of("username", Tag.preProcessParsed(this.username()), "nickname", Tag.selfClosingInserting(nick)), + null, + null + ); + } catch (final StackOverflowError overflow) { + throw new RuntimeException("Invalid nickname format '%s'. Makes circular reference to CarbonPlayer#displayName().".formatted(nicknames.format), overflow); + } } return this.platformDisplayName().orElseGet(() -> Component.text(this.username())); } diff --git a/common/src/main/resources/locale/messages-en_US.properties b/common/src/main/resources/locale/messages-en_US.properties index 77a5d4c2c..17cab3ae7 100644 --- a/common/src/main/resources/locale/messages-en_US.properties +++ b/common/src/main/resources/locale/messages-en_US.properties @@ -25,7 +25,7 @@ command.ignore.description=Hides all incoming messages from ignored players. command.ignorelist.description=Displays a paginated list of who you are ignoring. command.ignorelist.none_ignored=You are not ignoring any players. command.ignorelist.pagination_header=Ignored players -command.ignorelist.pagination_element= - '> '>'>[unignore] +command.ignorelist.pagination_element= - '>'>[unignore] command.join.description=Join a channel you have previously left. command.leave.description=Leave a channel that you currently have access to. command.mute.argument.player=The name of the player to mute.