Keep server tab names and team formatting in the tab list

TabListMixin @Overwrote PlayerListEntry.getDisplayName() and always
returned the bare profile name, dropping server-set display names (rank
prefixes, colours) and, by never returning null, vanilla's team
decoration, for every player. Inject at RETURN instead: non-Saturn
players keep the original value, and Saturn players get the icon in
front of the original name, or the team-decorated profile name when the
server set none.

Also bump common for the player-lookup and thread-safety fixes.

Fixes #3. Refs #2, #6.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-25 14:00:39 +02:00
co-authored by claude
parent 333c408d1b
commit 68734f6ac5
9 changed files with 201 additions and 153 deletions
+1 -1
Submodule common updated: 2cfd93c197...9e3bf14ffa
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.name();
UUID uuid = profile.id();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.name();
UUID uuid = profile.id();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.getName();
UUID uuid = profile.getId();
if (SaturnPlayer.get(name, uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.getName();
UUID uuid = profile.getId();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.getName();
UUID uuid = profile.getId();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.getName();
UUID uuid = profile.getId();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.getName();
UUID uuid = profile.getId();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}
@@ -1,43 +1,49 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.client.player.Roles;
import org.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.authlib.GameProfile;
@Mixin(PlayerListEntry.class)
public abstract class TabListMixin {
@Shadow
private GameProfile profile;
/**
* @author HexLeo
* @reason Adds the Saturn Client icon to the player's name if they are online
* with Saturn Client
* Adds the Saturn Client icon to the player's name if they are online with
* Saturn Client, keeping any name, rank prefix or team formatting the server
* set.
*/
@Overwrite
@Nullable
public Text getDisplayName() {
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
@Inject(method = "getDisplayName", at = @At("RETURN"), cancellable = true)
private void saturn$addSaturnIcon(CallbackInfoReturnable<Text> cir) {
PlayerListEntry entry = (PlayerListEntry) (Object) this;
GameProfile profile = entry.getProfile();
String name = profile.name();
UUID uuid = profile.id();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
if (SaturnPlayer.get(name, uuid) == null) {
return;
}
return Text.literal(name);
Text displayName = cir.getReturnValue();
// With no server-set name, vanilla shows the team-decorated profile name.
if (displayName == null) {
displayName = Team.decorateName(entry.getScoreboardTeam(), Text.literal(name));
}
cir.setReturnValue(Text.empty()
.append(Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid))))
.append(displayName));
}
}