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:
Ursprung
175d9aff48
Commit
b65ba2cb52
@ -41,7 +41,6 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
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.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
@ -256,10 +255,7 @@ public class Entity {
|
||||
setAir((int) entityMetadata.getValue());
|
||||
break;
|
||||
case 2: // custom name
|
||||
if (entityMetadata.getValue() instanceof Component message) {
|
||||
// Always translate even if it's a TextMessage since there could be translatable parameters
|
||||
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message, session.getLocale()));
|
||||
}
|
||||
setDisplayName(session, (Component) entityMetadata.getValue());
|
||||
break;
|
||||
case 3: // is custom name visible
|
||||
if (!this.is(PlayerEntity.class))
|
||||
@ -310,6 +306,21 @@ public class Entity {
|
||||
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
|
||||
*/
|
||||
|
@ -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.Pose;
|
||||
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.nukkitx.math.vector.Vector3f;
|
||||
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.EntityLinkData;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.LivingEntity;
|
||||
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.UpdateType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -64,6 +68,9 @@ public class PlayerEntity extends LivingEntity {
|
||||
private String username;
|
||||
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
|
||||
*/
|
||||
@ -79,6 +86,7 @@ public class PlayerEntity extends LivingEntity {
|
||||
profile = gameProfile;
|
||||
uuid = gameProfile.getId();
|
||||
username = gameProfile.getName();
|
||||
displayName = username;
|
||||
|
||||
// For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior
|
||||
metadata.put(EntityData.MARK_VARIANT, 0xff);
|
||||
@ -240,23 +248,6 @@ public class PlayerEntity extends LivingEntity {
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession 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
|
||||
if (entityMetadata.getId() == 15) {
|
||||
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
|
||||
protected void setDimensions(Pose pose) {
|
||||
float height;
|
||||
|
@ -25,13 +25,16 @@
|
||||
|
||||
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.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.entity.Tickable;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.Tickable;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
|
@ -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.game.scoreboard.TeamColor;
|
||||
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.serializer.gson.GsonComponentSerializer;
|
||||
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.utils.LanguageUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MessageTranslator {
|
||||
|
||||
@ -54,7 +53,7 @@ public class MessageTranslator {
|
||||
.build();
|
||||
|
||||
// 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
|
||||
private static final String BASE = "\u00a7";
|
||||
@ -62,11 +61,36 @@ public class MessageTranslator {
|
||||
// Reset character
|
||||
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 {
|
||||
TEAM_FORMATS.put(TeamColor.OBFUSCATED, TextDecoration.OBFUSCATED);
|
||||
TEAM_FORMATS.put(TeamColor.BOLD, TextDecoration.BOLD);
|
||||
TEAM_FORMATS.put(TeamColor.STRIKETHROUGH, TextDecoration.STRIKETHROUGH);
|
||||
TEAM_FORMATS.put(TeamColor.ITALIC, TextDecoration.ITALIC);
|
||||
TEAM_COLORS.put(TeamColor.NONE, "");
|
||||
|
||||
TEAM_COLORS.put(TeamColor.BLACK, BASE + "0");
|
||||
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
|
||||
DefaultComponentSerializer.set(GSON_SERIALIZER);
|
||||
@ -88,12 +112,12 @@ public class MessageTranslator {
|
||||
String legacy = LegacyComponentSerializer.legacySection().serialize(message);
|
||||
|
||||
// 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
|
||||
// See https://minecraft.gamepedia.com/Formatting_codes#Usage
|
||||
legacy = legacy.replaceAll("\u00a7([0-9a-f])", "\u00a7r\u00a7$1");
|
||||
legacy = legacy.replaceAll("\u00a7r\u00a7r", "\u00a7r");
|
||||
legacy = COLOR_CHARACTERS.matcher(legacy).replaceAll("\u00a7r\u00a7$1");
|
||||
legacy = DOUBLE_RESET.matcher(legacy).replaceAll("\u00a7r");
|
||||
|
||||
return legacy;
|
||||
} catch (Exception e) {
|
||||
@ -158,84 +182,6 @@ public class MessageTranslator {
|
||||
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
|
||||
*
|
||||
@ -243,16 +189,7 @@ public class MessageTranslator {
|
||||
* @return The chat color character
|
||||
*/
|
||||
public static String toChatColor(TeamColor teamColor) {
|
||||
if (teamColor.equals(TeamColor.NONE)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
NamedTextColor textColor = NamedTextColor.NAMES.value(teamColor.name().toLowerCase());
|
||||
if (textColor != null) {
|
||||
return getColor(textColor);
|
||||
}
|
||||
|
||||
return getFormat(TEAM_FORMATS.get(teamColor));
|
||||
return TEAM_COLORS.getOrDefault(teamColor, "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,9 @@
|
||||
|
||||
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.TeamColor;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
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.Team;
|
||||
import org.geysermc.connector.scoreboard.UpdateType;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
@Translator(packet = ServerTeamPacket.class)
|
||||
public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
@ -60,48 +62,77 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||
Team team = scoreboard.getTeam(packet.getTeamName());
|
||||
switch (packet.getAction()) {
|
||||
case CREATE ->
|
||||
scoreboard.registerNewTeam(packet.getTeamName(), packet.getPlayers())
|
||||
case CREATE -> {
|
||||
team = scoreboard.registerNewTeam(packet.getTeamName(), packet.getPlayers())
|
||||
.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), 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 -> {
|
||||
if (team == null) {
|
||||
logger.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
if (logger.isDebug()) {
|
||||
logger.debug("Error while translating Team Packet " + packet.getAction()
|
||||
+ "! Scoreboard Team " + packet.getTeamName() + " is not registered."
|
||||
);
|
||||
}
|
||||
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()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()))
|
||||
.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 -> {
|
||||
if (team == null) {
|
||||
logger.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
if (logger.isDebug()) {
|
||||
logger.debug("Error while translating Team Packet " + packet.getAction()
|
||||
+ "! Scoreboard Team " + packet.getTeamName() + " is not registered."
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
team.addEntities(packet.getPlayers());
|
||||
Set<String> added = team.addEntities(packet.getPlayers());
|
||||
scoreboard.updateEntityNames(team, added, true);
|
||||
}
|
||||
case REMOVE_PLAYER -> {
|
||||
if (team == null) {
|
||||
logger.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
if (logger.isDebug()) {
|
||||
logger.debug("Error while translating Team Packet " + packet.getAction()
|
||||
+ "! Scoreboard Team " + packet.getTeamName() + " is not registered."
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
team.removeEntities(packet.getPlayers());
|
||||
Set<String> removed = team.removeEntities(packet.getPlayers());
|
||||
scoreboard.updateEntityNames(null, removed, true);
|
||||
}
|
||||
case REMOVE -> scoreboard.removeTeam(packet.getTeamName());
|
||||
}
|
||||
|
@ -33,10 +33,12 @@ import com.nukkitx.protocol.bedrock.packet.SetScorePacket;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -127,7 +129,8 @@ public final class Scoreboard {
|
||||
return team;
|
||||
}
|
||||
|
||||
team = new Team(this, teamName).addEntities(players);
|
||||
team = new Team(this, teamName);
|
||||
team.addEntities(players);
|
||||
teams.put(teamName, team);
|
||||
return team;
|
||||
}
|
||||
@ -155,6 +158,9 @@ public final class Scoreboard {
|
||||
Team remove = teams.remove(teamName);
|
||||
if (remove != null) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@ -43,7 +42,7 @@ public final class Team {
|
||||
private final Scoreboard scoreboard;
|
||||
private final String id;
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final Set<String> entities;
|
||||
@Setter private NameTagVisibility nameTagVisibility;
|
||||
@Setter private TeamColor color;
|
||||
@ -60,16 +59,16 @@ public final class Team {
|
||||
entities = new ObjectOpenHashSet<>();
|
||||
}
|
||||
|
||||
public Team addEntities(String... names) {
|
||||
List<String> added = new ArrayList<>();
|
||||
public Set<String> addEntities(String... names) {
|
||||
Set<String> added = new HashSet<>();
|
||||
for (String name : names) {
|
||||
if (entities.add(name)) {
|
||||
added.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (added.size() == 0) {
|
||||
return this;
|
||||
if (added.isEmpty()) {
|
||||
return added;
|
||||
}
|
||||
// we don't have to change the updateType,
|
||||
// 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) {
|
||||
entities.remove(name);
|
||||
if (entities.remove(name)) {
|
||||
removed.add(name);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
public boolean hasEntity(String name) {
|
||||
return entities.contains(name);
|
||||
@ -172,7 +179,11 @@ public final class Team {
|
||||
|
||||
public boolean isVisibleFor(String entity) {
|
||||
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 ALWAYS -> true;
|
||||
case NEVER -> false;
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren