Skip to content

Commit 74098f7

Browse files
barr-israelSychic
andauthored
Fixing issues with player symbols in chat (#682)
* Modify only the username chatcomponent instead of the entire message * Fixed issues with player symbols. Including bad color formatting, spacing and hopefully click event breaks from other mods. * Fixed remaining known issues and tested with NEU profile viewer and patcher image preview * tree traversal for a more futureproof solution * Stack is very marginally better * unnull * Replaced stack based traversal with recursive traversal. --------- Co-authored-by: Sychic <47618543+Sychic@users.noreply.github.com>
1 parent 968b4da commit 74098f7

File tree

3 files changed

+89
-51
lines changed

3 files changed

+89
-51
lines changed

src/main/java/codes/biscuit/skyblockaddons/listeners/PlayerListener.java

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@
2929
import codes.biscuit.skyblockaddons.misc.scheduler.Scheduler;
3030
import codes.biscuit.skyblockaddons.misc.scheduler.SkyblockRunnable;
3131
import codes.biscuit.skyblockaddons.utils.*;
32-
import codes.biscuit.skyblockaddons.utils.objects.IntPair;
3332
import com.google.common.collect.Sets;
3433
import com.google.common.math.DoubleMath;
3534
import lombok.Getter;
36-
import lombok.Setter;
3735
import net.minecraft.block.Block;
3836
import net.minecraft.block.BlockPrismarine;
3937
import net.minecraft.block.BlockStone;
@@ -43,14 +41,11 @@
4341
import net.minecraft.client.audio.SoundCategory;
4442
import net.minecraft.client.entity.EntityOtherPlayerMP;
4543
import net.minecraft.client.entity.EntityPlayerSP;
46-
import net.minecraft.client.gui.GuiPlayerTabOverlay;
4744
import net.minecraft.client.gui.inventory.GuiChest;
48-
import net.minecraft.client.network.NetHandlerPlayClient;
4945
import net.minecraft.client.network.NetworkPlayerInfo;
5046
import net.minecraft.client.settings.KeyBinding;
5147
import net.minecraft.entity.Entity;
5248
import net.minecraft.entity.item.EntityArmorStand;
53-
import net.minecraft.entity.monster.EntityBlaze;
5449
import net.minecraft.entity.monster.EntityEnderman;
5550
import net.minecraft.entity.monster.EntityMagmaCube;
5651
import net.minecraft.entity.monster.EntitySlime;
@@ -64,9 +59,7 @@
6459
import net.minecraft.item.EnumDyeColor;
6560
import net.minecraft.item.ItemStack;
6661
import net.minecraft.nbt.NBTTagCompound;
67-
import net.minecraft.scoreboard.ScorePlayerTeam;
6862
import net.minecraft.util.*;
69-
import net.minecraft.world.WorldSettings;
7063
import net.minecraftforge.client.event.ClientChatReceivedEvent;
7164
import net.minecraftforge.client.event.GuiScreenEvent;
7265
import net.minecraftforge.client.event.sound.PlaySoundEvent;
@@ -79,7 +72,6 @@
7972
import net.minecraftforge.event.entity.player.AttackEntityEvent;
8073
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
8174
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
82-
import net.minecraftforge.event.world.ChunkEvent;
8375
import net.minecraftforge.fml.common.eventhandler.EventPriority;
8476
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
8577
import net.minecraftforge.fml.common.gameevent.InputEvent;
@@ -114,6 +106,8 @@ public class PlayerListener {
114106
private static final Pattern NEXT_TIER_PET_PROGRESS = Pattern.compile("Next tier: (?<total>[0-9,]+)/.*");
115107
private static final Pattern MAXED_TIER_PET_PROGRESS = Pattern.compile(".*: (?<total>[0-9,]+)");
116108
private static final Pattern SPIRIT_SCEPTRE_MESSAGE_PATTERN = Pattern.compile("Your (?:Implosion|Spirit Sceptre) hit (?<hitEnemies>[0-9]+) enem(?:y|ies) for (?<dealtDamage>[0-9]{1,3}(?:,[0-9]{3})*(?:\\.[0-9]+)*) damage\\.");
109+
private static final Pattern PROFILE_TYPE_SYMBOL = Pattern.compile("(?i)§[0-9A-FK-ORZ][♲Ⓑ]");
110+
private static final Pattern NETHER_FACTION_SYMBOL = Pattern.compile("(?i)§[0-9A-FK-ORZ][⚒ቾ]");
117111

118112
private static final Set<String> SOUP_RANDOM_MESSAGES = new HashSet<>(Arrays.asList("I feel like I can fly!", "What was in that soup?",
119113
"Hmm… tasty!", "Hmm... tasty!", "You can now fly for 2 minutes.", "Your flight has been extended for 2 extra minutes.",
@@ -174,7 +168,7 @@ public class PlayerListener {
174168
private final ActionBarParser actionBarParser = new ActionBarParser();
175169

176170
// For caching for the PROFILE_TYPE_IN_CHAT feature, saves the last MAX_SIZE names.
177-
private final LinkedHashMap<String, String> namesWithType = new LinkedHashMap<String, String>(){
171+
private final LinkedHashMap<String, String> namesWithSymbols = new LinkedHashMap<String, String>(){
178172
private final int MAX_SIZE = 80;
179173

180174
protected boolean removeEldestEntry(Map.Entry<String, String> eldest)
@@ -380,46 +374,7 @@ public void onChatReceive(ClientChatReceivedEvent e) {
380374
// Tries to check if a message is from a player to add the player profile icon
381375
} else if (main.getConfigValues().isEnabled(Feature.PLAYER_SYMBOLS_IN_CHAT) &&
382376
unformattedText.contains(":")) {
383-
// For some reason guild chat messages still contain color codes in the unformatted text
384-
String username = TextUtils.stripColor(unformattedText.split(":")[0]);
385-
// Remove chat channel prefix
386-
if(username.contains(">")){
387-
username = username.substring(username.indexOf('>')+1);
388-
}
389-
// Remove rank prefix and guild rank suffix if exists
390-
username = TextUtils.trimWhitespaceAndResets(username.replaceAll("\\[[^\\[\\]]*\\]",""));
391-
// Check if stripped username is a real username or the player
392-
if (TextUtils.isUsername(username) || username.equals("**MINECRAFTUSERNAME**")) {
393-
EntityPlayer chattingPlayer = Minecraft.getMinecraft().theWorld.getPlayerEntityByName(username);
394-
// Put player in cache if found nearby
395-
if(chattingPlayer != null) {
396-
namesWithType.put(username, chattingPlayer.getDisplayName().getSiblings().get(0).getUnformattedText());
397-
}
398-
// Otherwise search in tablist
399-
else {
400-
Collection<NetworkPlayerInfo> networkPlayerInfos = Minecraft.getMinecraft().thePlayer.sendQueue.getPlayerInfoMap();
401-
String finalUsername = username;
402-
Optional<NetworkPlayerInfo> result = networkPlayerInfos.stream().filter(npi -> npi.getDisplayName() != null).filter(npi -> TextUtils.stripUsername(npi.getDisplayName().getUnformattedText()).equals(finalUsername)).findAny();
403-
// Put in cache if found
404-
if(result.isPresent()){
405-
namesWithType.put(username, result.get().getDisplayName().getFormattedText());
406-
}
407-
}
408-
// Check cache regardless if found nearby
409-
if(namesWithType.containsKey(username)){
410-
IChatComponent oldMessage = e.message;
411-
String newName = namesWithType.get(username);
412-
if(main.getConfigValues().isDisabled(Feature.SHOW_PROFILE_TYPE)){
413-
newName = newName.replaceAll("(?i) *(§[0-9a-fk-orz])*[♲Ⓑ](§[0-9a-fk-orz])*","");
414-
}
415-
if(main.getConfigValues().isDisabled(Feature.SHOW_NETHER_FACTION)){
416-
newName = newName.replaceAll("(?i) *(§[0-9a-fk-orz])*[⚒ቾ](§[0-9a-fk-orz])*","");
417-
}
418-
newName = newName.replaceAll("(?i) *(§[0-9a-fk-orz])*\\[[^\\[\\]]*\\](§[0-9a-fk-orz])*", ""); // Soopyv2 compatibility
419-
e.message = new ChatComponentText(formattedText.replace(username, newName));
420-
e.message.setChatStyle(oldMessage.getChatStyle());
421-
}
422-
}
377+
playerSymbolsDisplay(e, unformattedText);
423378
}
424379

425380
if (main.getConfigValues().isEnabled(Feature.NO_ARROWS_LEFT_ALERT)) {
@@ -510,6 +465,64 @@ public void onChatReceive(ClientChatReceivedEvent e) {
510465
}
511466
}
512467

468+
private void playerSymbolsDisplay(ClientChatReceivedEvent e, String unformattedText) {
469+
// For some reason guild chat messages still contain color codes in the unformatted text
470+
String username = TextUtils.stripColor(unformattedText.split(":")[0]);
471+
// Remove chat channel prefix
472+
if(username.contains(">")){
473+
username = username.substring(username.indexOf('>')+1);
474+
}
475+
// Remove rank prefix and guild rank suffix if exists
476+
username = TextUtils.trimWhitespaceAndResets(username.replaceAll("\\[[^\\[\\]]*\\]",""));
477+
// Check if stripped username is a real username or the player
478+
if (TextUtils.isUsername(username) || username.equals("**MINECRAFTUSERNAME**")) {
479+
EntityPlayer chattingPlayer = Minecraft.getMinecraft().theWorld.getPlayerEntityByName(username);
480+
// Put player in cache if found nearby
481+
if(chattingPlayer != null) {
482+
namesWithSymbols.put(username, chattingPlayer.getDisplayName().getSiblings().get(0).getUnformattedText());
483+
}
484+
// Otherwise search in tablist
485+
else {
486+
Collection<NetworkPlayerInfo> networkPlayerInfos = Minecraft.getMinecraft().thePlayer.sendQueue.getPlayerInfoMap();
487+
String finalUsername = username;
488+
Optional<NetworkPlayerInfo> result = networkPlayerInfos.stream().filter(npi -> npi.getDisplayName() != null).filter(npi -> TextUtils.stripUsername(npi.getDisplayName().getUnformattedText()).equals(finalUsername)).findAny();
489+
// Put in cache if found
490+
if(result.isPresent()){
491+
namesWithSymbols.put(username, result.get().getDisplayName().getFormattedText());
492+
}
493+
}
494+
// Check cache regardless if found nearby
495+
if(namesWithSymbols.containsKey(username)){
496+
IChatComponent oldMessage = e.message;
497+
String usernameWithSymbols = namesWithSymbols.get(username);
498+
String suffix = " ";
499+
if(main.getConfigValues().isEnabled(Feature.SHOW_PROFILE_TYPE)){
500+
Matcher m = PROFILE_TYPE_SYMBOL.matcher(usernameWithSymbols);
501+
if(m.find()){
502+
suffix+=m.group(0);
503+
}
504+
}
505+
if(main.getConfigValues().isEnabled(Feature.SHOW_NETHER_FACTION)){
506+
Matcher m = NETHER_FACTION_SYMBOL.matcher(usernameWithSymbols);
507+
if(m.find()){
508+
suffix+=m.group(0);
509+
}
510+
}
511+
if(!suffix.equals(" ")) {
512+
String finalSuffix = suffix;
513+
String finalUsername = username;
514+
TextUtils.recursiveTransformChatComponent(oldMessage, component -> {
515+
if (component instanceof ChatComponentText & ((ChatComponentText)component).text.contains(finalUsername)) {
516+
ChatComponentText textComponent = (ChatComponentText) component;
517+
textComponent.text = textComponent.text.replace(finalUsername, finalUsername + finalSuffix);
518+
}
519+
}
520+
);
521+
}
522+
}
523+
}
524+
}
525+
513526
/**
514527
* Acts as a callback to set the actionbar message after other mods have a chance to look at the message
515528
*/

src/main/java/codes/biscuit/skyblockaddons/utils/TextUtils.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import codes.biscuit.skyblockaddons.SkyblockAddons;
44
import com.google.gson.JsonObject;
5+
import net.minecraft.util.IChatComponent;
56

67
import java.nio.charset.StandardCharsets;
78
import java.text.NumberFormat;
89
import java.text.ParseException;
910
import java.util.*;
11+
import java.util.function.Consumer;
1012
import java.util.regex.Matcher;
1113
import java.util.regex.Pattern;
1214

@@ -21,6 +23,7 @@ public class TextUtils {
2123

2224
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)§[0-9A-FK-ORZ]");
2325
private static final Pattern STRIP_ICONS_PATTERN = Pattern.compile("[♲Ⓑ⚒ቾ]+");
26+
private static final Pattern STRIP_PREFIX_PATTERN = Pattern.compile("\\[[^\\[\\]]*\\]");
2427
private static final Pattern REPEATED_COLOR_PATTERN = Pattern.compile("(?i)(§[0-9A-FK-ORZ])+");
2528
private static final Pattern NUMBERS_SLASHES = Pattern.compile("[^0-9 /]");
2629
private static final Pattern SCOREBOARD_CHARACTERS = Pattern.compile("[^a-z A-Z:0-9_/'.!§\\[\\]❤]");
@@ -75,7 +78,11 @@ public static String stripIcons(String input) {
7578
* @return Stripped Text
7679
*/
7780
public static String stripUsername(String input) {
78-
return trimWhitespaceAndResets(stripIcons(stripColor((input))));
81+
return trimWhitespaceAndResets(stripIcons(stripColor(stripPrefix((input)))));
82+
}
83+
84+
public static String stripPrefix(String input) {
85+
return STRIP_PREFIX_PATTERN.matcher(input).replaceAll("");
7986
}
8087

8188
/**
@@ -435,4 +442,21 @@ private static String mergeFormats(String firstFormat, String secondFormat) {
435442
}
436443
return builder.toString();
437444
}
445+
/**
446+
* Recursively performs an action upon a chat component and its siblings
447+
* This code is adapted from Skytils
448+
* <p>
449+
* https://github.com/Skytils/SkytilsMod/commit/35b1fbed1613f07bd422c61dbe3d261218b8edc6
450+
* <p>
451+
* I, Sychic, the author of this code grant usage under the terms of the MIT License.
452+
* @param chatComponent root chat component
453+
* @param action action to be performed
454+
* @author Sychic
455+
*/
456+
public static void recursiveTransformChatComponent(IChatComponent chatComponent, Consumer<IChatComponent> action) {
457+
action.accept(chatComponent);
458+
for (IChatComponent sibling : chatComponent.getSiblings()) {
459+
recursiveTransformChatComponent(sibling, action);
460+
}
461+
}
438462
}

src/main/resources/META-INF/skyblockaddons_at.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ public net.minecraft.client.particle.EffectRenderer field_110737_b # particleTex
4040
public net.minecraft.client.particle.EffectRenderer field_78877_c # renderer
4141
public net.minecraft.client.multiplayer.PlayerControllerMP field_78770_f # curBlockDamageMP
4242
public net.minecraft.client.renderer.RenderGlobal field_72738_E # damagedBlocks
43-
public net.minecraft.client.particle.EntityFX field_70544_f # particleScale
43+
public net.minecraft.client.particle.EntityFX field_70544_f # particleScale
44+
public-f net.minecraft.util.ChatComponentText field_150267_b # text

0 commit comments

Comments
 (0)