3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-20 06:50:09 +01:00

Update display names for team players after team updates

Resolves #1912
Dieser Commit ist enthalten in:
Camotoy 2021-10-05 17:06:15 -04:00
Ursprung 175d9aff48
Commit b65ba2cb52
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
7 geänderte Dateien mit 249 neuen und 157 gelöschten Zeilen

Datei anzeigen

@ -41,7 +41,6 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.geysermc.connector.entity.living.ArmorStandEntity;
import org.geysermc.connector.entity.player.PlayerEntity; import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
@ -256,10 +255,7 @@ public class Entity {
setAir((int) entityMetadata.getValue()); setAir((int) entityMetadata.getValue());
break; break;
case 2: // custom name case 2: // custom name
if (entityMetadata.getValue() instanceof Component message) { setDisplayName(session, (Component) entityMetadata.getValue());
// Always translate even if it's a TextMessage since there could be translatable parameters
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message, session.getLocale()));
}
break; break;
case 3: // is custom name visible case 3: // is custom name visible
if (!this.is(PlayerEntity.class)) if (!this.is(PlayerEntity.class))
@ -310,6 +306,21 @@ public class Entity {
return false; return false;
} }
/**
* @return the translated string display
*/
protected String setDisplayName(GeyserSession session, Component name) {
if (name != null) {
String displayName = MessageTranslator.convertMessage(name, session.getLocale());
metadata.put(EntityData.NAMETAG, displayName);
return displayName;
} else if (!metadata.getString(EntityData.NAMETAG).isEmpty()) {
// Clear nametag
metadata.put(EntityData.NAMETAG, "");
}
return null;
}
/** /**
* Set the height and width of the entity's bounding box * Set the height and width of the entity's bounding box
*/ */

Datei anzeigen

@ -29,6 +29,7 @@ import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition; import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
@ -39,9 +40,11 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.LivingEntity; import org.geysermc.connector.entity.LivingEntity;
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity; import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
@ -53,6 +56,7 @@ import org.geysermc.connector.scoreboard.Score;
import org.geysermc.connector.scoreboard.Team; import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.scoreboard.UpdateType; import org.geysermc.connector.scoreboard.UpdateType;
import javax.annotation.Nullable;
import java.util.Collections; import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -64,6 +68,9 @@ public class PlayerEntity extends LivingEntity {
private String username; private String username;
private boolean playerList = true; // Player is in the player list private boolean playerList = true; // Player is in the player list
@Getter(AccessLevel.NONE)
private String displayName;
/** /**
* Saves the parrot currently on the player's left shoulder; otherwise null * Saves the parrot currently on the player's left shoulder; otherwise null
*/ */
@ -79,6 +86,7 @@ public class PlayerEntity extends LivingEntity {
profile = gameProfile; profile = gameProfile;
uuid = gameProfile.getId(); uuid = gameProfile.getId();
username = gameProfile.getName(); username = gameProfile.getName();
displayName = username;
// For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior // For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior
metadata.put(EntityData.MARK_VARIANT, 0xff); metadata.put(EntityData.MARK_VARIANT, 0xff);
@ -240,23 +248,6 @@ public class PlayerEntity extends LivingEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
if (entityMetadata.getId() == 2) {
String username = this.username;
Component name = (Component) entityMetadata.getValue();
if (name != null) {
username = MessageTranslator.convertMessage(name);
}
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
if (team != null) {
String displayName = "";
if (team.isVisibleFor(session.getPlayerEntity().getUsername())) {
displayName = MessageTranslator.toChatColor(team.getColor()) + username;
displayName = team.getCurrentData().getDisplayName(displayName);
}
metadata.put(EntityData.NAMETAG, displayName);
}
}
// Extra hearts - is not metadata but an attribute on Bedrock // Extra hearts - is not metadata but an attribute on Bedrock
if (entityMetadata.getId() == 15) { if (entityMetadata.getId() == 15) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
@ -319,6 +310,65 @@ public class PlayerEntity extends LivingEntity {
} }
} }
@Override
protected String setDisplayName(GeyserSession session, Component name) {
String displayName = super.setDisplayName(session, name);
this.displayName = displayName != null ? displayName : username;
// Update if we know this player has a team
updateDisplayName(session, null, false);
return this.displayName;
}
//todo this will become common entity logic once UUID support is implemented for them
/**
* @param useGivenTeam even if there is no team, update the username in the entity metadata anyway, and don't look for a team
*/
public void updateDisplayName(GeyserSession session, @Nullable Team team, boolean useGivenTeam) {
if (team == null && !useGivenTeam) {
// Only search for the team if we are not supposed to use the given team
// If the given team is null, this is intentional that we are being removed from the team
team = session.getWorldCache().getScoreboard().getTeamFor(username);
}
boolean needsUpdate;
String newDisplayName = this.displayName;
if (team != null) {
if (team.isVisibleFor(session.getPlayerEntity().getUsername())) {
TeamColor color = team.getColor();
String chatColor;
if (color == TeamColor.NONE) {
chatColor = ChatColor.RESET;
} else {
chatColor = MessageTranslator.toChatColor(color);
}
// We have to emulate what modern Java text already does for us and add the color to each section
String prefix = team.getCurrentData().getPrefix();
String suffix = team.getCurrentData().getSuffix();
newDisplayName = chatColor + prefix + chatColor + this.displayName + chatColor + suffix;
} else {
// The name is not visible to the session player; clear name
newDisplayName = "";
}
needsUpdate = useGivenTeam && !newDisplayName.equals(metadata.getString(EntityData.NAMETAG, null));
metadata.put(EntityData.NAMETAG, newDisplayName);
} else if (useGivenTeam) {
// The name has reset, if it was previously something else
needsUpdate = !newDisplayName.equals(metadata.getString(EntityData.NAMETAG));
metadata.put(EntityData.NAMETAG, this.displayName);
} else {
needsUpdate = false;
}
if (needsUpdate) {
// Update the metadata as it won't be updated later
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.getMetadata().put(EntityData.NAMETAG, newDisplayName);
packet.setRuntimeEntityId(geyserId);
session.sendUpstreamPacket(packet);
}
}
@Override @Override
protected void setDimensions(Pose pose) { protected void setDimensions(Pose pose) {
float height; float height;

Datei anzeigen

@ -25,13 +25,16 @@
package org.geysermc.connector.network.session.cache; package org.geysermc.connector.network.session.cache;
import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.Long2LongMap;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.entity.Tickable;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.Tickable;
import org.geysermc.connector.entity.player.PlayerEntity; import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;

Datei anzeigen

@ -28,8 +28,6 @@ package org.geysermc.connector.network.translators.chat;
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer; import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.gson.legacyimpl.NBTLegacyHoverEventSerializer; import net.kyori.adventure.text.serializer.gson.legacyimpl.NBTLegacyHoverEventSerializer;
@ -38,9 +36,10 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LanguageUtils;
import java.util.HashMap; import java.util.EnumMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern;
public class MessageTranslator { public class MessageTranslator {
@ -54,7 +53,7 @@ public class MessageTranslator {
.build(); .build();
// Store team colors for player names // Store team colors for player names
private static final Map<TeamColor, TextDecoration> TEAM_FORMATS = new HashMap<>(); private static final Map<TeamColor, String> TEAM_COLORS = new EnumMap<>(TeamColor.class);
// Legacy formatting character // Legacy formatting character
private static final String BASE = "\u00a7"; private static final String BASE = "\u00a7";
@ -62,11 +61,36 @@ public class MessageTranslator {
// Reset character // Reset character
private static final String RESET = BASE + "r"; private static final String RESET = BASE + "r";
/* Various regexes to fix formatting for Bedrock's specifications */
private static final Pattern STRIKETHROUGH_UNDERLINE = Pattern.compile("\u00a7[mn]");
private static final Pattern COLOR_CHARACTERS = Pattern.compile("\u00a7([0-9a-f])");
private static final Pattern DOUBLE_RESET = Pattern.compile("\u00a7r\u00a7r");
static { static {
TEAM_FORMATS.put(TeamColor.OBFUSCATED, TextDecoration.OBFUSCATED); TEAM_COLORS.put(TeamColor.NONE, "");
TEAM_FORMATS.put(TeamColor.BOLD, TextDecoration.BOLD);
TEAM_FORMATS.put(TeamColor.STRIKETHROUGH, TextDecoration.STRIKETHROUGH); TEAM_COLORS.put(TeamColor.BLACK, BASE + "0");
TEAM_FORMATS.put(TeamColor.ITALIC, TextDecoration.ITALIC); TEAM_COLORS.put(TeamColor.DARK_BLUE, BASE + "1");
TEAM_COLORS.put(TeamColor.DARK_GREEN, BASE + "2");
TEAM_COLORS.put(TeamColor.DARK_AQUA, BASE + "3");
TEAM_COLORS.put(TeamColor.DARK_RED, BASE + "4");
TEAM_COLORS.put(TeamColor.DARK_PURPLE, BASE + "5");
TEAM_COLORS.put(TeamColor.GOLD, BASE + "6");
TEAM_COLORS.put(TeamColor.GRAY, BASE + "7");
TEAM_COLORS.put(TeamColor.DARK_GRAY, BASE + "8");
TEAM_COLORS.put(TeamColor.BLUE, BASE + "9");
TEAM_COLORS.put(TeamColor.GREEN, BASE + "a");
TEAM_COLORS.put(TeamColor.AQUA, BASE + "b");
TEAM_COLORS.put(TeamColor.RED, BASE + "c");
TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, BASE + "d");
TEAM_COLORS.put(TeamColor.YELLOW, BASE + "e");
TEAM_COLORS.put(TeamColor.WHITE, BASE + "f");
// Formats, not colors
TEAM_COLORS.put(TeamColor.OBFUSCATED, BASE + "k");
TEAM_COLORS.put(TeamColor.BOLD, BASE + "l");
TEAM_COLORS.put(TeamColor.STRIKETHROUGH, BASE + "m");
TEAM_COLORS.put(TeamColor.ITALIC, BASE + "o");
// Tell MCProtocolLib to use our serializer // Tell MCProtocolLib to use our serializer
DefaultComponentSerializer.set(GSON_SERIALIZER); DefaultComponentSerializer.set(GSON_SERIALIZER);
@ -88,12 +112,12 @@ public class MessageTranslator {
String legacy = LegacyComponentSerializer.legacySection().serialize(message); String legacy = LegacyComponentSerializer.legacySection().serialize(message);
// Strip strikethrough and underline as they are not supported on bedrock // Strip strikethrough and underline as they are not supported on bedrock
legacy = legacy.replaceAll("\u00a7[mn]", ""); legacy = STRIKETHROUGH_UNDERLINE.matcher(legacy).replaceAll("");
// Make color codes reset formatting like Java // Make color codes reset formatting like Java
// See https://minecraft.gamepedia.com/Formatting_codes#Usage // See https://minecraft.gamepedia.com/Formatting_codes#Usage
legacy = legacy.replaceAll("\u00a7([0-9a-f])", "\u00a7r\u00a7$1"); legacy = COLOR_CHARACTERS.matcher(legacy).replaceAll("\u00a7r\u00a7$1");
legacy = legacy.replaceAll("\u00a7r\u00a7r", "\u00a7r"); legacy = DOUBLE_RESET.matcher(legacy).replaceAll("\u00a7r");
return legacy; return legacy;
} catch (Exception e) { } catch (Exception e) {
@ -158,84 +182,6 @@ public class MessageTranslator {
return GSON_SERIALIZER.serialize(component); return GSON_SERIALIZER.serialize(component);
} }
/**
* Convert a {@link NamedTextColor} into a string for inserting into messages
*
* @param color {@link NamedTextColor} to convert
* @return The converted color string
*/
private static String getColor(NamedTextColor color) {
StringBuilder str = new StringBuilder(BASE);
if (color.equals(NamedTextColor.BLACK)) {
str.append("0");
} else if (color.equals(NamedTextColor.DARK_BLUE)) {
str.append("1");
} else if (color.equals(NamedTextColor.DARK_GREEN)) {
str.append("2");
} else if (color.equals(NamedTextColor.DARK_AQUA)) {
str.append("3");
} else if (color.equals(NamedTextColor.DARK_RED)) {
str.append("4");
} else if (color.equals(NamedTextColor.DARK_PURPLE)) {
str.append("5");
} else if (color.equals(NamedTextColor.GOLD)) {
str.append("6");
} else if (color.equals(NamedTextColor.GRAY)) {
str.append("7");
} else if (color.equals(NamedTextColor.DARK_GRAY)) {
str.append("8");
} else if (color.equals(NamedTextColor.BLUE)) {
str.append("9");
} else if (color.equals(NamedTextColor.GREEN)) {
str.append("a");
} else if (color.equals(NamedTextColor.AQUA)) {
str.append("b");
} else if (color.equals(NamedTextColor.RED)) {
str.append("c");
} else if (color.equals(NamedTextColor.LIGHT_PURPLE)) {
str.append("d");
} else if (color.equals(NamedTextColor.YELLOW)) {
str.append("e");
} else if (color.equals(NamedTextColor.WHITE)) {
str.append("f");
} else {
return "";
}
return str.toString();
}
/**
* Convert a {@link TextDecoration} into a string for inserting into messages
*
* @param format {@link TextDecoration} to convert
* @return The converted chat formatting string
*/
private static String getFormat(TextDecoration format) {
StringBuilder str = new StringBuilder(BASE);
switch (format) {
case OBFUSCATED:
str.append("k");
break;
case BOLD:
str.append("l");
break;
case STRIKETHROUGH:
str.append("m");
break;
case UNDERLINED:
str.append("n");
break;
case ITALIC:
str.append("o");
break;
default:
return "";
}
return str.toString();
}
/** /**
* Convert a team color to a chat color * Convert a team color to a chat color
* *
@ -243,16 +189,7 @@ public class MessageTranslator {
* @return The chat color character * @return The chat color character
*/ */
public static String toChatColor(TeamColor teamColor) { public static String toChatColor(TeamColor teamColor) {
if (teamColor.equals(TeamColor.NONE)) { return TEAM_COLORS.getOrDefault(teamColor, "");
return "";
}
NamedTextColor textColor = NamedTextColor.NAMES.value(teamColor.name().toLowerCase());
if (textColor != null) {
return getColor(textColor);
}
return getFormat(TEAM_FORMATS.get(teamColor));
} }
/** /**

Datei anzeigen

@ -25,7 +25,9 @@
package org.geysermc.connector.network.translators.java.scoreboard; package org.geysermc.connector.network.translators.java.scoreboard;
import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamAction; import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamAction;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.GeyserLogger;
@ -37,9 +39,9 @@ import org.geysermc.connector.scoreboard.Scoreboard;
import org.geysermc.connector.scoreboard.ScoreboardUpdater; import org.geysermc.connector.scoreboard.ScoreboardUpdater;
import org.geysermc.connector.scoreboard.Team; import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.scoreboard.UpdateType; import org.geysermc.connector.scoreboard.UpdateType;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Set;
@Translator(packet = ServerTeamPacket.class) @Translator(packet = ServerTeamPacket.class)
public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> { public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
@ -60,48 +62,77 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
Scoreboard scoreboard = session.getWorldCache().getScoreboard(); Scoreboard scoreboard = session.getWorldCache().getScoreboard();
Team team = scoreboard.getTeam(packet.getTeamName()); Team team = scoreboard.getTeam(packet.getTeamName());
switch (packet.getAction()) { switch (packet.getAction()) {
case CREATE -> case CREATE -> {
scoreboard.registerNewTeam(packet.getTeamName(), packet.getPlayers()) team = scoreboard.registerNewTeam(packet.getTeamName(), packet.getPlayers())
.setName(MessageTranslator.convertMessage(packet.getDisplayName())) .setName(MessageTranslator.convertMessage(packet.getDisplayName()))
.setColor(packet.getColor()) .setColor(packet.getColor())
.setNameTagVisibility(packet.getNameTagVisibility()) .setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale())) .setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale())); .setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()));
if (packet.getPlayers().length != 0) {
if ((team.getNameTagVisibility() != NameTagVisibility.ALWAYS && !team.isVisibleFor(session.getPlayerEntity().getUsername()))
|| team.getColor() != TeamColor.NONE
|| !team.getCurrentData().getPrefix().isEmpty()
|| !team.getCurrentData().getSuffix().isEmpty()) {
// Something is here that would modify entity names
scoreboard.updateEntityNames(team, true);
}
}
}
case UPDATE -> { case UPDATE -> {
if (team == null) { if (team == null) {
logger.debug(LanguageUtils.getLocaleStringLog( if (logger.isDebug()) {
"geyser.network.translator.team.failed_not_registered", logger.debug("Error while translating Team Packet " + packet.getAction()
packet.getAction(), packet.getTeamName() + "! Scoreboard Team " + packet.getTeamName() + " is not registered."
)); );
}
return; return;
} }
TeamColor oldColor = team.getColor();
NameTagVisibility oldVisibility = team.getNameTagVisibility();
String oldPrefix = team.getCurrentData().getPrefix();
String oldSuffix = team.getCurrentData().getSuffix();
team.setName(MessageTranslator.convertMessage(packet.getDisplayName())) team.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
.setColor(packet.getColor()) .setColor(packet.getColor())
.setNameTagVisibility(packet.getNameTagVisibility()) .setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale())) .setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale())) .setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()))
.setUpdateType(UpdateType.UPDATE); .setUpdateType(UpdateType.UPDATE);
if (oldVisibility != team.getNameTagVisibility()
|| oldColor != team.getColor()
|| !oldPrefix.equals(team.getCurrentData().getPrefix())
|| !oldSuffix.equals(team.getCurrentData().getSuffix())) {
// Update entities attached to this team as something about their nameplates have changed
scoreboard.updateEntityNames(team, false);
}
} }
case ADD_PLAYER -> { case ADD_PLAYER -> {
if (team == null) { if (team == null) {
logger.debug(LanguageUtils.getLocaleStringLog( if (logger.isDebug()) {
"geyser.network.translator.team.failed_not_registered", logger.debug("Error while translating Team Packet " + packet.getAction()
packet.getAction(), packet.getTeamName() + "! Scoreboard Team " + packet.getTeamName() + " is not registered."
)); );
}
return; return;
} }
team.addEntities(packet.getPlayers()); Set<String> added = team.addEntities(packet.getPlayers());
scoreboard.updateEntityNames(team, added, true);
} }
case REMOVE_PLAYER -> { case REMOVE_PLAYER -> {
if (team == null) { if (team == null) {
logger.debug(LanguageUtils.getLocaleStringLog( if (logger.isDebug()) {
"geyser.network.translator.team.failed_not_registered", logger.debug("Error while translating Team Packet " + packet.getAction()
packet.getAction(), packet.getTeamName() + "! Scoreboard Team " + packet.getTeamName() + " is not registered."
)); );
}
return; return;
} }
team.removeEntities(packet.getPlayers()); Set<String> removed = team.removeEntities(packet.getPlayers());
scoreboard.updateEntityNames(null, removed, true);
} }
case REMOVE -> scoreboard.removeTeam(packet.getTeamName()); case REMOVE -> scoreboard.removeTeam(packet.getTeamName());
} }

Datei anzeigen

@ -33,10 +33,12 @@ import com.nukkitx.protocol.bedrock.packet.SetScorePacket;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.player.PlayerEntity; import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LanguageUtils;
import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -127,7 +129,8 @@ public final class Scoreboard {
return team; return team;
} }
team = new Team(this, teamName).addEntities(players); team = new Team(this, teamName);
team.addEntities(players);
teams.put(teamName, team); teams.put(teamName, team);
return team; return team;
} }
@ -155,6 +158,9 @@ public final class Scoreboard {
Team remove = teams.remove(teamName); Team remove = teams.remove(teamName);
if (remove != null) { if (remove != null) {
remove.setUpdateType(REMOVE); remove.setUpdateType(REMOVE);
// We need to use the direct entities list here, so #refreshSessionPlayerDisplays also updates accordingly
// With the player's lack of a team in visibility checks
updateEntityNames(remove, remove.getEntities(), true);
} }
} }
@ -326,4 +332,47 @@ public final class Scoreboard {
} }
return null; return null;
} }
/**
* Updates the display names of all entities in a given team.
* @param teamChange the players have either joined or left the team. Used for optimizations when just the display name updated.
*/
public void updateEntityNames(Team team, boolean teamChange) {
Set<String> names = new HashSet<>(team.getEntities());
updateEntityNames(team, names, teamChange);
}
/**
* Updates the display name of a set of entities within a given team. The team may also be null if the set is being removed
* from a team.
*/
public void updateEntityNames(@Nullable Team team, Set<String> names, boolean teamChange) {
if (names.remove(session.getPlayerEntity().getUsername()) && teamChange) {
// If the player's team changed, then other entities' teams may modify their visibility based on team status
refreshSessionPlayerDisplays();
}
if (!names.isEmpty()) {
for (Entity entity : session.getEntityCache().getEntities().values()) {
// This more complex logic is for the future to iterate over all entities, not just players
if (entity instanceof PlayerEntity player && names.remove(player.getUsername())) {
player.updateDisplayName(session, team, true);
if (names.isEmpty()) {
break;
}
}
}
}
}
/**
* If the team's player was refreshed, then we need to go through every entity and check...
*/
private void refreshSessionPlayerDisplays() {
for (Entity entity : session.getEntityCache().getEntities().values()) {
if (entity instanceof PlayerEntity player) {
Team playerTeam = session.getWorldCache().getScoreboard().getTeamFor(player.getUsername());
player.updateDisplayName(session, playerTeam, true);
}
}
}
} }

Datei anzeigen

@ -33,8 +33,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.ArrayList; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
@Getter @Getter
@ -43,7 +42,7 @@ public final class Team {
private final Scoreboard scoreboard; private final Scoreboard scoreboard;
private final String id; private final String id;
@Getter(AccessLevel.NONE) @Getter(AccessLevel.PACKAGE)
private final Set<String> entities; private final Set<String> entities;
@Setter private NameTagVisibility nameTagVisibility; @Setter private NameTagVisibility nameTagVisibility;
@Setter private TeamColor color; @Setter private TeamColor color;
@ -60,16 +59,16 @@ public final class Team {
entities = new ObjectOpenHashSet<>(); entities = new ObjectOpenHashSet<>();
} }
public Team addEntities(String... names) { public Set<String> addEntities(String... names) {
List<String> added = new ArrayList<>(); Set<String> added = new HashSet<>();
for (String name : names) { for (String name : names) {
if (entities.add(name)) { if (entities.add(name)) {
added.add(name); added.add(name);
} }
} }
if (added.size() == 0) { if (added.isEmpty()) {
return this; return added;
} }
// we don't have to change the updateType, // we don't have to change the updateType,
// because the scores itself need updating, not the team // because the scores itself need updating, not the team
@ -81,14 +80,22 @@ public final class Team {
} }
} }
} }
return this;
return added;
} }
public void removeEntities(String... names) { /**
* @return all removed entities from this team
*/
public Set<String> removeEntities(String... names) {
Set<String> removed = new HashSet<>();
for (String name : names) { for (String name : names) {
entities.remove(name); if (entities.remove(name)) {
removed.add(name);
} }
} }
return removed;
}
public boolean hasEntity(String name) { public boolean hasEntity(String name) {
return entities.contains(name); return entities.contains(name);
@ -172,7 +179,11 @@ public final class Team {
public boolean isVisibleFor(String entity) { public boolean isVisibleFor(String entity) {
return switch (nameTagVisibility) { return switch (nameTagVisibility) {
case HIDE_FOR_OTHER_TEAMS -> hasEntity(entity); case HIDE_FOR_OTHER_TEAMS -> {
// Player must be in a team in order for HIDE_FOR_OTHER_TEAMS to be triggered
Team team = scoreboard.getTeamFor(entity);
yield team == null || team == this;
}
case HIDE_FOR_OWN_TEAM -> !hasEntity(entity); case HIDE_FOR_OWN_TEAM -> !hasEntity(entity);
case ALWAYS -> true; case ALWAYS -> true;
case NEVER -> false; case NEVER -> false;