Skip to content

Commit cef50f2

Browse files
committed
fix: chat name & text formatting now works again
No longer attempting to modify the text to change player name formatting; instead, simply override player.getDisplayName() (via Forge event or Fabric mixin).
1 parent 84b02c6 commit cef50f2

File tree

12 files changed

+202
-102
lines changed

12 files changed

+202
-102
lines changed

common/src/main/java/dev/ftb/mods/ftbranks/FTBRanks.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ public FTBRanks() {
2323
LifecycleEvent.SERVER_LEVEL_SAVE.register(FTBRanksAPIImpl::worldSaved);
2424
LifecycleEvent.SERVER_STARTING.register(FTBRanksAPIImpl::serverStarting);
2525
CommandRegistrationEvent.EVENT.register(FTBRanksCommands::register);
26-
// TODO: Register with LOWEST priority on forge
27-
// FIXME: add back by syncing the ranks.snbt file to clients so they can format the text on the client.
28-
ChatEvent.RECEIVED.register(FTBRanksAPIImpl::serverChat);
26+
ChatEvent.RECEIVED.register(FTBRanksAPIImpl::chatReceived);
2927
}
3028
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package dev.ftb.mods.ftbranks;
2+
3+
import dev.architectury.injectables.annotations.ExpectPlatform;
4+
import dev.ftb.mods.ftbranks.api.FTBRanksAPI;
5+
import dev.ftb.mods.ftbranks.impl.FTBRanksAPIImpl;
6+
import dev.ftb.mods.ftbranks.impl.TextComponentParser;
7+
import net.minecraft.ChatFormatting;
8+
import net.minecraft.network.chat.Component;
9+
import net.minecraft.network.chat.HoverEvent;
10+
import net.minecraft.network.chat.Style;
11+
import net.minecraft.server.level.ServerPlayer;
12+
import net.minecraft.world.entity.player.Player;
13+
14+
public class PlayerNameFormatting {
15+
public static Component formatPlayerName(Player player, Component originalName) {
16+
if (!(player instanceof ServerPlayer serverPlayer)) return originalName;
17+
18+
String format = FTBRanksAPI.getPermissionValue(serverPlayer, "ftbranks.name_format").asString().orElse("");
19+
20+
if (!format.isEmpty()) {
21+
if (format.startsWith("<")) {
22+
// TODO remove in 1.20
23+
FTBRanksAPIImpl.manager.migrateOldNameFormats();
24+
format = FTBRanksAPI.getPermissionValue(serverPlayer, "ftbranks.name_format").asString().orElse("");
25+
}
26+
27+
try {
28+
return TextComponentParser.parse(format, s -> s.equals("name") ? originalName : null);
29+
} catch (Exception ex) {
30+
String s = "Error parsing " + format + ": " + ex;
31+
FTBRanks.LOGGER.error(s);
32+
return Component.literal("BrokenFormatting").withStyle(Style.EMPTY
33+
.withColor(ChatFormatting.RED)
34+
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(s)))
35+
);
36+
}
37+
} else {
38+
return originalName;
39+
}
40+
}
41+
42+
@ExpectPlatform
43+
public static void refreshPlayerNames() {
44+
throw new AssertionError();
45+
}
46+
}
Lines changed: 20 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package dev.ftb.mods.ftbranks.impl;
22

3+
import com.mojang.datafixers.types.Type;
34
import dev.architectury.event.EventResult;
4-
import dev.ftb.mods.ftblibrary.util.TextComponentUtils;
5-
import dev.ftb.mods.ftbranks.FTBRanks;
5+
import dev.architectury.event.events.common.ChatEvent;
66
import dev.ftb.mods.ftbranks.api.FTBRanksAPI;
77
import dev.ftb.mods.ftbranks.api.RankManager;
88
import dev.ftb.mods.ftbranks.impl.condition.*;
99
import net.minecraft.ChatFormatting;
1010
import net.minecraft.network.chat.Component;
11-
import net.minecraft.network.chat.HoverEvent;
1211
import net.minecraft.network.chat.MutableComponent;
13-
import net.minecraft.network.chat.contents.TranslatableContents;
1412
import net.minecraft.server.MinecraftServer;
1513
import net.minecraft.server.level.ServerLevel;
1614
import net.minecraft.server.level.ServerPlayer;
15+
import org.jetbrains.annotations.Nullable;
1716

1817
/**
1918
* @author LatvianModder
@@ -69,90 +68,27 @@ public static void serverStarting(MinecraftServer server) {
6968
manager.registerCondition("creative_mode", (rank, tag) -> new CreativeModeCondition());
7069
}
7170

72-
73-
public static EventResult serverChat(ServerPlayer player, Component component) {
74-
if (player == null) {
75-
return EventResult.pass();
76-
}
77-
78-
String format = FTBRanksAPI.getPermissionValue(player, "ftbranks.name_format").asString().orElse("");
79-
80-
if (format.isEmpty()) {
81-
return EventResult.pass();
82-
}
83-
84-
MutableComponent main = Component.literal("");
85-
MutableComponent cachedNameForChat;
86-
87-
try {
88-
cachedNameForChat = TextComponentParser.parse(format, s -> {
89-
if (s.equals("name")) {
90-
return player.getDisplayName();
91-
}
92-
93-
return null;
94-
});
95-
} catch (Exception ex) {
96-
String s = "Error parsing " + format + ": " + ex;
97-
FTBRanks.LOGGER.error(s);
98-
cachedNameForChat = Component.literal("BrokenFormatting");
99-
cachedNameForChat.withStyle(ChatFormatting.RED);
100-
cachedNameForChat.setStyle(cachedNameForChat.getStyle().withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(s))));
101-
}
102-
103-
main.append(cachedNameForChat);
104-
main.append(" ");
105-
106-
MutableComponent text = null;
107-
108-
// In the easiest case, we have the vanilla chat format,
109-
// so we can just use the message from that.
110-
if (component.getContents() instanceof TranslatableContents tc && tc.getKey().equals("chat.type.text") && tc.getArgs().length > 1) {
111-
Object message = tc.getArgs()[1];
112-
if (message instanceof Component c) {
113-
text = c.copy();
114-
} else {
115-
text = Component.literal(message.toString());
71+
public static EventResult chatReceived(@Nullable ServerPlayer player, Component component) {
72+
if (component instanceof MutableComponent text) {
73+
// NOTE: only message text is decorated here; see PlayerNameFormatting for decoration of sender names
74+
ChatFormatting color = ChatFormatting.getByName(FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.color").asString().orElse(null));
75+
if (color != null) {
76+
text.setStyle(text.getStyle().applyFormat(color));
11677
}
117-
}
11878

119-
// Otherwise, fall back to parsing the message as a string and turning it back into a component.
120-
if (text == null) {
121-
FTBRanks.LOGGER.debug("Chat message format has been changed, fall back to parsing as string!");
122-
FTBRanks.LOGGER.debug("Since this may break formatting, feel free to remove the `ftbranks.name_format` permission node to stop this from happening.");
123-
text = TextComponentUtils.withLinks(component.getString()).copy();
124-
}
125-
126-
ChatFormatting color = ChatFormatting.getByName(FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.color").asString().orElse(null));
127-
if (color != null) {
128-
text.setStyle(text.getStyle().applyFormat(color));
129-
}
130-
131-
if (FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.bold").asBooleanOrFalse()) {
132-
text.setStyle(text.getStyle().applyFormat(ChatFormatting.BOLD));
133-
}
134-
135-
if (FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.italic").asBooleanOrFalse()) {
136-
text.setStyle(text.getStyle().applyFormat(ChatFormatting.ITALIC));
137-
}
138-
139-
if (FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.underlined").asBooleanOrFalse()) {
140-
text.setStyle(text.getStyle().applyFormat(ChatFormatting.UNDERLINE));
141-
}
142-
143-
if (FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.strikethrough").asBooleanOrFalse()) {
144-
text.setStyle(text.getStyle().applyFormat(ChatFormatting.STRIKETHROUGH));
79+
modifyText(player, text, "ftbranks.chat_text.bold", ChatFormatting.BOLD);
80+
modifyText(player, text, "ftbranks.chat_text.italic", ChatFormatting.ITALIC);
81+
modifyText(player, text, "ftbranks.chat_text.underlined", ChatFormatting.UNDERLINE);
82+
modifyText(player, text, "ftbranks.chat_text.strikethrough", ChatFormatting.STRIKETHROUGH);
83+
modifyText(player, text, "ftbranks.chat_text.obfuscated", ChatFormatting.OBFUSCATED);
84+
return EventResult.interruptTrue();
14585
}
86+
return EventResult.pass();
87+
}
14688

147-
if (FTBRanksAPI.getPermissionValue(player, "ftbranks.chat_text.obfuscated").asBooleanOrFalse()) {
148-
text.setStyle(text.getStyle().applyFormat(ChatFormatting.OBFUSCATED));
89+
private static void modifyText(ServerPlayer player, MutableComponent component, String node, ChatFormatting modifier) {
90+
if (FTBRanksAPI.getPermissionValue(player, node).asBooleanOrFalse()) {
91+
component.setStyle(component.getStyle().applyFormat(modifier));
14992
}
150-
151-
main.append(text);
152-
component = main;
153-
154-
System.out.println(main);
155-
156-
return EventResult.interruptTrue();
15793
}
15894
}

common/src/main/java/dev/ftb/mods/ftbranks/impl/RankImpl.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.ftb.mods.ftbranks.impl;
22

33
import com.mojang.authlib.GameProfile;
4+
import dev.ftb.mods.ftbranks.PlayerNameFormatting;
45
import dev.ftb.mods.ftbranks.api.PermissionValue;
56
import dev.ftb.mods.ftbranks.api.Rank;
67
import dev.ftb.mods.ftbranks.api.RankCondition;
@@ -80,6 +81,9 @@ public void setPermission(String node, PermissionValue value) {
8081
permissions.remove(node);
8182
}
8283
RankEvent.PERMISSION_CHANGED.invoker().accept(new PermissionNodeChangedEvent(manager, this, node, oldValue, value));
84+
if (node.equals("ftbranks.name_format")) {
85+
PlayerNameFormatting.refreshPlayerNames();
86+
}
8387
manager.saveRanks();
8488
}
8589
}
@@ -99,6 +103,7 @@ public void setCondition(RankCondition condition) {
99103
RankCondition oldCondition = this.condition;
100104
this.condition = condition;
101105
RankEvent.CONDITION_CHANGED.invoker().accept(new ConditionChangedEvent(manager, this, oldCondition, condition));
106+
PlayerNameFormatting.refreshPlayerNames();
102107
manager.saveRanks();
103108
}
104109

@@ -110,6 +115,7 @@ public boolean add(GameProfile profile) {
110115
data.added.put(this, Instant.now());
111116
manager.savePlayers();
112117
RankEvent.ADD_PLAYER.invoker().accept(new PlayerAddedToRankEvent(manager, this, profile));
118+
PlayerNameFormatting.refreshPlayerNames();
113119
return true;
114120
}
115121

@@ -121,8 +127,9 @@ public boolean remove(GameProfile profile) {
121127
PlayerRankData data = manager.getPlayerData(profile);
122128

123129
if (data.added.remove(this) != null) {
124-
RankEvent.REMOVE_PLAYER.invoker().accept(new PlayerRemovedFromRankEvent(manager,this, profile));
125130
manager.savePlayers();
131+
RankEvent.REMOVE_PLAYER.invoker().accept(new PlayerRemovedFromRankEvent(manager,this, profile));
132+
PlayerNameFormatting.refreshPlayerNames();
126133
return true;
127134
}
128135

common/src/main/java/dev/ftb/mods/ftbranks/impl/RankManagerImpl.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import dev.ftb.mods.ftblibrary.snbt.SNBTCompoundTag;
66
import dev.ftb.mods.ftblibrary.snbt.config.ConfigUtil;
77
import dev.ftb.mods.ftbranks.FTBRanks;
8+
import dev.ftb.mods.ftbranks.PlayerNameFormatting;
89
import dev.ftb.mods.ftbranks.api.*;
910
import dev.ftb.mods.ftbranks.api.event.RankCreatedEvent;
1011
import dev.ftb.mods.ftbranks.api.event.RankDeletedEvent;
@@ -25,16 +26,7 @@
2526
import java.nio.file.Files;
2627
import java.nio.file.Path;
2728
import java.time.Instant;
28-
import java.util.ArrayList;
29-
import java.util.Date;
30-
import java.util.HashMap;
31-
import java.util.HashSet;
32-
import java.util.LinkedHashMap;
33-
import java.util.List;
34-
import java.util.Map;
35-
import java.util.Optional;
36-
import java.util.Set;
37-
import java.util.UUID;
29+
import java.util.*;
3830

3931
import static dev.ftb.mods.ftbranks.FTBRanks.MOD_ID;
4032

@@ -297,20 +289,19 @@ public void reload() throws Exception {
297289
RankImpl memberRank = new RankImpl(this, "member");
298290
memberRank.setPermission("name", StringPermissionValue.of("Member"));
299291
memberRank.setPermission("power", NumberPermissionValue.of(1));
300-
memberRank.setPermission("ftbranks.name_format", StringPermissionValue.of("<{name}>"));
301292
memberRank.condition = AlwaysActiveCondition.INSTANCE;
302293
ranks.put("member", memberRank);
303294

304295
RankImpl vipRank = new RankImpl(this, "vip");
305296
vipRank.setPermission("name", StringPermissionValue.of("VIP"));
306297
vipRank.setPermission("power", NumberPermissionValue.of(50));
307-
vipRank.setPermission("ftbranks.name_format", StringPermissionValue.of("<&bVIP {name}&r>"));
298+
vipRank.setPermission("ftbranks.name_format", StringPermissionValue.of("&bVIP {name}"));
308299
ranks.put("vip", vipRank);
309300

310301
RankImpl adminRank = new RankImpl(this, "admin");
311302
adminRank.setPermission("name", StringPermissionValue.of("Admin"));
312303
adminRank.setPermission("power", NumberPermissionValue.of(1000));
313-
adminRank.setPermission("ftbranks.name_format", StringPermissionValue.of("<&2{name}&r>"));
304+
adminRank.setPermission("ftbranks.name_format", StringPermissionValue.of("&2{name}"));
314305
adminRank.condition = new OPCondition();
315306
ranks.put("admin", adminRank);
316307

@@ -420,6 +411,8 @@ public void reload() throws Exception {
420411

421412
RankEvent.RELOADED.invoker().accept(new RanksReloadedEvent(FTBRanksAPI.INSTANCE.getManager()));
422413

414+
PlayerNameFormatting.refreshPlayerNames();
415+
423416
FTBRanks.LOGGER.info("Loaded " + ranks.size() + " ranks");
424417
}
425418

@@ -542,4 +535,30 @@ private static PermissionValue ofTag(SNBTCompoundTag tag, String key) {
542535

543536
return StringPermissionValue.of(v.toString());
544537
}
538+
539+
/**
540+
* Go through all ranks and look for old-style (pre 1.19) name_format strings. Convert "<...{name}..[&r]>"
541+
* to simply "...{name}...". Since rank name formatting is now applied to the player's display name, and
542+
* not inserted into chat, which isn't really an option in 1.19+.
543+
*/
544+
public void migrateOldNameFormats() {
545+
boolean[] changesMade = new boolean[]{false};
546+
ranks.forEach((name, rank) -> {
547+
String format = rank.getPermission("ftbranks.name_format").asString().orElse("");
548+
if (!format.isEmpty()) {
549+
String newFormat = format
550+
.replaceAll("^<","")
551+
.replaceAll("(&r)?>$", "");
552+
if (!format.equals(newFormat)) {
553+
rank.setPermission("ftbranks.name_format", StringPermissionValue.of(newFormat));
554+
changesMade[0] = true;
555+
FTBRanks.LOGGER.info("migrated old ftbranks.name_format node for rank {}: '{}' -> '{}'", rank, format, newFormat);
556+
}
557+
}
558+
});
559+
if (changesMade[0]) {
560+
saveRanks();
561+
saveRanksNow();
562+
}
563+
}
545564
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dev.ftb.mods.ftbranks;
2+
3+
public interface PlayerDisplayNameCache {
4+
void clearCachedDisplayName();
5+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dev.ftb.mods.ftbranks.core.mixin.fabric;
2+
3+
import dev.ftb.mods.ftbranks.PlayerDisplayNameCache;
4+
import dev.ftb.mods.ftbranks.PlayerNameFormatting;
5+
import net.minecraft.network.chat.Component;
6+
import net.minecraft.network.chat.MutableComponent;
7+
import net.minecraft.world.entity.player.Player;
8+
import org.spongepowered.asm.mixin.Mixin;
9+
import org.spongepowered.asm.mixin.injection.At;
10+
import org.spongepowered.asm.mixin.injection.Inject;
11+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
12+
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
13+
14+
@Mixin(Player.class)
15+
public abstract class PlayerMixin implements PlayerDisplayNameCache {
16+
private Component cachedDisplayName = null;
17+
18+
@Inject(method = "getDisplayName", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILSOFT, cancellable = true)
19+
private void onGetDisplayName(CallbackInfoReturnable<Component> cir, MutableComponent mutableComponent) {
20+
if (cachedDisplayName == null) cachedDisplayName = PlayerNameFormatting.formatPlayerName((Player) (Object)this, mutableComponent);
21+
cir.setReturnValue(cachedDisplayName);
22+
}
23+
24+
@Override
25+
public void clearCachedDisplayName() {
26+
cachedDisplayName = null;
27+
}
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package dev.ftb.mods.ftbranks.fabric;
2+
3+
import dev.architectury.utils.GameInstance;
4+
import dev.ftb.mods.ftbranks.PlayerDisplayNameCache;
5+
6+
public class PlayerNameFormattingImpl {
7+
public static void refreshPlayerNames() {
8+
GameInstance.getServer().getPlayerList().getPlayers().forEach(p -> ((PlayerDisplayNameCache) p).clearCachedDisplayName());
9+
}
10+
}

fabric/src/main/resources/fabric.mod.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"ftblibrary": ">=1801.3.5-build.98"
2525
},
2626
"mixins": [
27-
"ftbranks-common.mixins.json"
27+
"ftbranks-common.mixins.json",
28+
"ftbranks-fabric.mixins.json"
2829
]
2930
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"required": true,
3+
"package": "dev.ftb.mods.ftbranks.core.mixin.fabric",
4+
"compatibilityLevel": "JAVA_8",
5+
"mixins": [
6+
"PlayerMixin"
7+
],
8+
"client": [
9+
],
10+
"injectors": {
11+
"defaultRequire": 1
12+
},
13+
"minVersion": "0.8"
14+
}

0 commit comments

Comments
 (0)