Begin implementing below name support and better name display

This commit is contained in:
Camotoy 2021-01-18 00:24:04 -05:00
parent cbe2bf7480
commit 01babd636a
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
9 changed files with 174 additions and 16 deletions

View file

@ -27,6 +27,7 @@ package org.geysermc.connector.entity.player;
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.scoreboard.ScoreboardPosition;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
@ -50,9 +51,11 @@ import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.scoreboard.Objective;
import org.geysermc.connector.scoreboard.Score;
import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.ArrayList;
import java.util.List;
@ -111,6 +114,22 @@ public class PlayerEntity extends LivingEntity {
updateEquipment(session);
updateBedrockAttributes(session);
// Check to see if the player should have a belowname counterpart added
Objective objective = session.getWorldCache().getScoreboard().getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME);
if (objective != null) {
boolean hasScore = false;
for (Score score : objective.getScores().values()) {
if (score.getName().equals(this.username)) {
hasScore = true;
session.getWorldCache().getScoreboard().sendBelowNameUpdate(objective, score, this);
break;
}
}
if (!hasScore) {
//TODO session.getWorldCache().getScoreboard().sendBelowNameUpdate(objective, null, this);
}
}
}
public void sendPlayer(GeyserSession session) {
@ -257,12 +276,7 @@ public class PlayerEntity extends LivingEntity {
}
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);
metadata.put(EntityData.NAMETAG, team.getDisplayNameFor(username, session.getPlayerEntity().getUsername()));
}
}

View file

@ -124,10 +124,26 @@ public class EntityCache {
playerEntities.put(entity.getUuid(), entity);
}
public Collection<PlayerEntity> getPlayerEntities() {
return playerEntities.values();
}
public PlayerEntity getPlayerEntity(UUID uuid) {
return playerEntities.get(uuid);
}
/**
* @return a {@link PlayerEntity} with the given username
*/
public PlayerEntity getPlayerEntity(String username) {
for (PlayerEntity entity : playerEntities.values()) {
if (username.equals(entity.getUsername())) {
return entity;
}
}
return null;
}
public void removePlayerEntity(UUID uuid) {
playerEntities.remove(uuid);
}

View file

@ -70,7 +70,7 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
break;
}
if (objective == null || !objective.isActive()) {
if (objective == null) {
return;
}

View file

@ -25,19 +25,24 @@
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.packet.ingame.server.scoreboard.ServerTeamPacket;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
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 org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.Arrays;
import java.util.Set;
@ -55,7 +60,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
int pps = session.getWorldCache().increaseAndGetScoreboardPacketsPerSecond();
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
Team team = scoreboard.getTeam(packet.getTeamName());
Team team = null;
if (packet.getAction() != TeamAction.CREATE) {
team = scoreboard.getTeam(packet.getTeamName());
}
switch (packet.getAction()) {
case CREATE:
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
@ -64,6 +73,7 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
.setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()));
updatePlayerNameTags(session, team, packet.getPlayers(), false);
break;
case UPDATE:
if (team == null) {
@ -73,6 +83,7 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
));
return;
}
NameTagVisibility oldVisibility = team.getNameTagVisibility();
team.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
.setColor(packet.getColor())
@ -80,6 +91,9 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()))
.setUpdateType(UpdateType.UPDATE);
if (!oldVisibility.equals(packet.getNameTagVisibility())) {
updatePlayerNameTags(session, team, team.getEntities(), false);
}
break;
case ADD_PLAYER:
if (team == null) {
@ -90,6 +104,7 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
return;
}
team.addEntities(packet.getPlayers());
updatePlayerNameTags(session, team, packet.getPlayers(), false);
break;
case REMOVE_PLAYER:
if (team == null) {
@ -100,9 +115,13 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
return;
}
team.removeEntities(packet.getPlayers());
updatePlayerNameTags(session, team, packet.getPlayers(), true);
break;
case REMOVE:
scoreboard.removeTeam(packet.getTeamName());
if (team != null) {
updatePlayerNameTags(session, team, team.getEntities(), true);
}
break;
}
@ -113,6 +132,31 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
}
}
private void updatePlayerNameTags(GeyserSession session, Team team, String[] teamEntities, boolean remove) {
for (PlayerEntity entity : session.getEntityCache().getPlayerEntities()) {
for (String name : teamEntities) {
if (entity.getUsername().equals(name)) {
String currentName = entity.getMetadata().getString(EntityData.NAMETAG);
String newName;
if (remove) {
// Set the nametag to their default
newName = entity.getUsername();
} else {
newName = team.getDisplayNameFor(currentName, session.getPlayerEntity().getUsername());
}
if (!newName.equals(currentName)) {
entity.getMetadata().put(EntityData.NAMETAG, newName);
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.getMetadata().put(EntityData.NAMETAG, newName);
packet.setRuntimeEntityId(entity.getGeyserId());
session.sendUpstreamPacket(packet);
}
break;
}
}
}
}
private Set<String> toPlayerSet(String[] players) {
return new ObjectOpenHashSet<>(players);
}

View file

@ -48,6 +48,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator<ServerUpdateScor
@Override
public void translate(ServerUpdateScorePacket packet, GeyserSession session) {
session.getConnector().getLogger().warning(packet.toString());
WorldCache worldCache = session.getWorldCache();
Scoreboard scoreboard = worldCache.getScoreboard();
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();

View file

@ -49,7 +49,6 @@ public final class Objective {
private int type = 0; // 0 = integer, 1 = heart
private Map<String, Score> scores = new ConcurrentHashMap<>();
// todo add a 'to add' map so that we don't have to use a concurrentHashMap
private Objective(Scoreboard scoreboard) {
this.id = scoreboard.getNextId().getAndIncrement();

View file

@ -35,11 +35,13 @@ public final class Score {
private final long id;
private final String name;
private ScoreInfo cachedInfo;
@Getter
private boolean hasSentBelowName = false;
/**
* Changes that have been made since the last cached data.
*/
private Score.ScoreData currentData;
private final Score.ScoreData currentData;
/**
* The data that is currently displayed to the Bedrock client.
*/
@ -72,14 +74,14 @@ public final class Score {
if (currentData.team != null && team != null) {
if (!currentData.team.equals(team)) {
currentData.team = team;
currentData.updateType = UpdateType.UPDATE;
setUpdateType(UpdateType.UPDATE);
}
return this;
}
// simplified from (this.team != null && team == null) || (this.team == null && team != null)
if (currentData.team != null || team != null) {
currentData.team = team;
currentData.updateType = UpdateType.UPDATE;
setUpdateType(UpdateType.UPDATE);
}
return this;
}
@ -124,6 +126,11 @@ public final class Score {
cachedInfo = new ScoreInfo(id, objectiveName, cachedData.score, name);
}
public Score setHasSentBelowName(boolean hasSentBelowName) {
this.hasSentBelowName = hasSentBelowName;
return this;
}
@Getter
public static final class ScoreData {
protected UpdateType updateType;

View file

@ -27,15 +27,19 @@ package org.geysermc.connector.scoreboard;
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
import com.nukkitx.protocol.bedrock.data.ScoreInfo;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket;
import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
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.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;
@ -49,9 +53,12 @@ public final class Scoreboard {
private final AtomicLong nextId = new AtomicLong(0);
private final Map<String, Objective> objectives = new HashMap<>();
@Getter
private final Map<ScoreboardPosition, Objective> objectiveSlots = new HashMap<>();
private final Map<String, Team> teams = new ConcurrentHashMap<>(); // updated on multiple threads
// todo add a 'to add' map so that we don't have to use a concurrentHashMap
/**
* Updated on multiple threads
*/
private final Map<String, Team> teams = new ConcurrentHashMap<>();
private int lastAddScoreCount = 0;
private int lastRemoveScoreCount = 0;
@ -183,6 +190,7 @@ public final class Scoreboard {
setScorePacket.setAction(SetScorePacket.Action.REMOVE);
setScorePacket.setInfos(removeScores);
session.sendUpstreamPacket(setScorePacket);
System.out.println(setScorePacket);
}
if (!addScores.isEmpty()) {
@ -190,6 +198,7 @@ public final class Scoreboard {
setScorePacket.setAction(SetScorePacket.Action.SET);
setScorePacket.setInfos(addScores);
session.sendUpstreamPacket(setScorePacket);
System.out.println(setScorePacket);
}
// prevents crashes in some cases
@ -241,6 +250,8 @@ public final class Scoreboard {
boolean objectiveAdd = objective.getUpdateType() == ADD;
boolean objectiveRemove = objective.getUpdateType() == REMOVE;
boolean isBelowName = objective.getDisplaySlot() == ScoreboardPosition.BELOW_NAME;
for (Score score : objective.getScores().values()) {
Team team = score.getTeam();
@ -285,6 +296,15 @@ public final class Scoreboard {
objective.removeScore0(score.getName());
}
if (score.getUpdateType() != REMOVE && !objectiveRemove && isBelowName && add) {
PlayerEntity entity = session.getEntityCache().getPlayerEntity(score.getName());
if (entity != null) {
sendBelowNameUpdate(objective, score, entity);
}
} else if ((objectiveRemove || score.getUpdateType() == REMOVE) && (isBelowName || score.isHasSentBelowName())) {
removeBelowName(score);
}
score.setUpdateType(NOTHING);
}
@ -296,6 +316,7 @@ public final class Scoreboard {
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
session.sendUpstreamPacket(removeObjectivePacket);
System.out.println(removeObjectivePacket);
}
if ((objectiveAdd || objectiveUpdate) && !objectiveRemove) {
@ -306,12 +327,14 @@ public final class Scoreboard {
displayObjectivePacket.setDisplaySlot(objective.getDisplaySlotName());
displayObjectivePacket.setSortOrder(1); // ??
session.sendUpstreamPacket(displayObjectivePacket);
System.out.println(displayObjectivePacket);
}
objective.setUpdateType(NOTHING);
}
private void deactivateObjective(Objective objective) {
System.out.println("Deactivating objective " + objective.getObjectiveName());
// Scoreboard has been removed already
if (objective.getScores() == null) {
return;
@ -320,6 +343,9 @@ public final class Scoreboard {
List<ScoreInfo> removedScores = new ArrayList<>(objective.getScores().size());
for (Score score : objective.getScores().values()) {
removedScores.add(score.getCachedInfo());
if (score.isHasSentBelowName()) {
removeBelowName(score);
}
}
objective.deactivate();
@ -328,13 +354,16 @@ public final class Scoreboard {
scorePacket.setAction(SetScorePacket.Action.REMOVE);
scorePacket.setInfos(removedScores);
session.sendUpstreamPacket(scorePacket);
System.out.println(scorePacket);
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
session.sendUpstreamPacket(removeObjectivePacket);
System.out.println(removeObjectivePacket);
}
public void deleteObjective(Objective objective) {
System.out.println("Deleting objective " + objective.getObjectiveName());
objectives.remove(objective.getObjectiveName());
Objective storedSlot = objectiveSlots.get(objective.getDisplaySlot());
@ -349,6 +378,7 @@ public final class Scoreboard {
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
session.sendUpstreamPacket(removeObjectivePacket);
System.out.println(removeObjectivePacket);
}
public Team getTeamFor(String entity) {
@ -359,4 +389,30 @@ public final class Scoreboard {
}
return null;
}
public void sendBelowNameUpdate(Objective objective, @Nullable Score score, PlayerEntity entity) {
// Show the belowname information this player
// Even if this player doesn't have a score, the display string is still updated for them
String displayString = score == null ? "0" : score.getCurrentData().getScore() + " " + objective.getDisplayName();
entity.getMetadata().put(EntityData.SCORE_TAG, displayString);
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(entity.getGeyserId());
packet.getMetadata().put(EntityData.SCORE_TAG, displayString);
session.sendUpstreamPacket(packet);
if (score != null) {
score.setHasSentBelowName(true);
}
}
public void removeBelowName(Score score) {
// Clear the tag from the player
PlayerEntity entity = session.getEntityCache().getPlayerEntity(score.getName());
if (entity != null) {
entity.getMetadata().remove(EntityData.SCORE_TAG);
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(entity.getGeyserId());
packet.getMetadata().put(EntityData.SCORE_TAG, "");
session.sendUpstreamPacket(packet);
}
}
}

View file

@ -32,6 +32,7 @@ import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.ArrayList;
import java.util.List;
@ -76,6 +77,10 @@ public final class Team {
}
}
public String[] getEntities() {
return entities.toArray(new String[0]);
}
public Team addEntities(String... names) {
List<String> added = new ArrayList<>();
for (String name : names) {
@ -198,6 +203,22 @@ public final class Team {
return true;
}
/**
* Get the display name of an entity on this team in relation to another entity.
*
* @param username the username of the player on this team
* @param otherEntity the other entity, for determining visibility
* @return the final display name
*/
public String getDisplayNameFor(String username, String otherEntity) {
String displayName = "";
if (isVisibleFor(otherEntity)) {
displayName = MessageTranslator.toChatColor(color) + username;
displayName = currentData.getDisplayName(displayName);
}
return displayName;
}
@Override
public int hashCode() {
return id.hashCode();