mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Translate scoreboard display name and number format (#4567)
The following caveats apply because selectively removing or modifying the score numbers on Bedrock cannot be done without removing all of them: * "blank" and "styled" number formats are ignored for "list" and "sidebar" display slots * The "fixed" number format is appended to the end of the "list" and "sidebar" entry names instead of replacing the score number * The "below_name" slot has no limitations and displays identically to Java
This commit is contained in:
parent
ae96ad2ec4
commit
0cc2921eda
7 changed files with 157 additions and 35 deletions
|
@ -25,6 +25,11 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type.player;
|
||||
|
||||
import com.github.steveice10.mc.protocol.codec.NbtComponentSerializer;
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.BlankFormat;
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.FixedFormat;
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.NumberFormat;
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.StyledFormat;
|
||||
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.type.BooleanEntityMetadata;
|
||||
|
@ -33,6 +38,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEnt
|
|||
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.StringTag;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
@ -64,6 +70,7 @@ import org.geysermc.geyser.scoreboard.Score;
|
|||
import org.geysermc.geyser.scoreboard.Team;
|
||||
import org.geysermc.geyser.scoreboard.UpdateType;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
|
||||
|
@ -415,14 +422,36 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
|
||||
public void setBelowNameText(Objective objective) {
|
||||
if (objective != null && objective.getUpdateType() != UpdateType.REMOVE) {
|
||||
int amount;
|
||||
Score score = objective.getScores().get(username);
|
||||
String numberString;
|
||||
NumberFormat numberFormat;
|
||||
int amount;
|
||||
if (score != null) {
|
||||
amount = score.getCurrentData().getScore();
|
||||
amount = score.getScore();
|
||||
numberFormat = score.getNumberFormat();
|
||||
if (numberFormat == null) {
|
||||
numberFormat = objective.getNumberFormat();
|
||||
}
|
||||
} else {
|
||||
amount = 0;
|
||||
numberFormat = objective.getNumberFormat();
|
||||
}
|
||||
String displayString = amount + " " + objective.getDisplayName();
|
||||
|
||||
if (numberFormat instanceof BlankFormat) {
|
||||
numberString = "";
|
||||
} else if (numberFormat instanceof FixedFormat fixedFormat) {
|
||||
numberString = MessageTranslator.convertMessage(fixedFormat.getValue());
|
||||
} else if (numberFormat instanceof StyledFormat styledFormat) {
|
||||
CompoundTag styledAmount = styledFormat.getStyle().clone();
|
||||
styledAmount.put(new StringTag("text", String.valueOf(amount)));
|
||||
|
||||
numberString = MessageTranslator.convertJsonMessage(
|
||||
NbtComponentSerializer.tagComponentToJson(styledAmount).toString());
|
||||
} else {
|
||||
numberString = String.valueOf(amount);
|
||||
}
|
||||
|
||||
String displayString = numberString + " " + ChatColor.RESET + objective.getDisplayName();
|
||||
|
||||
if (valid) {
|
||||
// Already spawned - we still need to run the rest of this code because the spawn packet will be
|
||||
|
@ -431,13 +460,22 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
packet.setRuntimeEntityId(geyserId);
|
||||
packet.getMetadata().put(EntityDataTypes.SCORE, displayString);
|
||||
session.sendUpstreamPacket(packet);
|
||||
} else {
|
||||
// Not spawned yet, store score value in dirtyMetadata to be picked up by #spawnEntity
|
||||
dirtyMetadata.put(EntityDataTypes.SCORE, displayString);
|
||||
}
|
||||
} else {
|
||||
if (valid) {
|
||||
SetEntityDataPacket packet = new SetEntityDataPacket();
|
||||
packet.setRuntimeEntityId(geyserId);
|
||||
packet.getMetadata().put(EntityDataTypes.SCORE, "");
|
||||
session.sendUpstreamPacket(packet);
|
||||
} else {
|
||||
// Not spawned yet, store score value in dirtyMetadata to be picked up by #spawnEntity
|
||||
dirtyMetadata.put(EntityDataTypes.SCORE, "");
|
||||
}
|
||||
} else if (valid) {
|
||||
SetEntityDataPacket packet = new SetEntityDataPacket();
|
||||
packet.setRuntimeEntityId(geyserId);
|
||||
packet.getMetadata().put(EntityDataTypes.SCORE, "");
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,13 +25,16 @@
|
|||
|
||||
package org.geysermc.geyser.scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.NumberFormat;
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Getter
|
||||
|
@ -47,6 +50,7 @@ public final class Objective {
|
|||
private ScoreboardPosition displaySlot;
|
||||
private String displaySlotName;
|
||||
private String displayName = "unknown";
|
||||
private NumberFormat numberFormat;
|
||||
private int type = 0; // 0 = integer, 1 = heart
|
||||
|
||||
private Map<String, Score> scores = new ConcurrentHashMap<>();
|
||||
|
@ -85,25 +89,29 @@ public final class Objective {
|
|||
};
|
||||
}
|
||||
|
||||
public void registerScore(String id, int score) {
|
||||
public void registerScore(String id, int score, Component displayName, NumberFormat numberFormat) {
|
||||
if (!scores.containsKey(id)) {
|
||||
long scoreId = scoreboard.getNextId().getAndIncrement();
|
||||
Score scoreObject = new Score(scoreId, id)
|
||||
.setScore(score)
|
||||
.setTeam(scoreboard.getTeamFor(id))
|
||||
.setDisplayName(displayName)
|
||||
.setNumberFormat(numberFormat)
|
||||
.setUpdateType(UpdateType.ADD);
|
||||
scores.put(id, scoreObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void setScore(String id, int score) {
|
||||
public void setScore(String id, int score, Component displayName, NumberFormat numberFormat) {
|
||||
Score stored = scores.get(id);
|
||||
if (stored != null) {
|
||||
stored.setScore(score)
|
||||
.setDisplayName(displayName)
|
||||
.setNumberFormat(numberFormat)
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
return;
|
||||
}
|
||||
registerScore(id, score);
|
||||
registerScore(id, score, displayName, numberFormat);
|
||||
}
|
||||
|
||||
public void removeScore(String id) {
|
||||
|
@ -128,6 +136,26 @@ public final class Objective {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Objective setNumberFormat(NumberFormat numberFormat) {
|
||||
if (Objects.equals(this.numberFormat, numberFormat)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.numberFormat = numberFormat;
|
||||
if (updateType == UpdateType.NOTHING) {
|
||||
updateType = UpdateType.UPDATE;
|
||||
}
|
||||
|
||||
// Update the number format for scores that are following this objective's number format
|
||||
for (Score score : scores.values()) {
|
||||
if (score.getNumberFormat() == null) {
|
||||
score.setUpdateType(UpdateType.UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Objective setType(int type) {
|
||||
this.type = type;
|
||||
if (updateType == UpdateType.NOTHING) {
|
||||
|
|
|
@ -25,9 +25,16 @@
|
|||
|
||||
package org.geysermc.geyser.scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.FixedFormat;
|
||||
import com.github.steveice10.mc.protocol.data.game.chat.numbers.NumberFormat;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.cloudburstmc.protocol.bedrock.data.ScoreInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
|
@ -52,6 +59,10 @@ public final class Score {
|
|||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
String displayName = cachedData.displayName;
|
||||
if (displayName != null) {
|
||||
return displayName;
|
||||
}
|
||||
Team team = cachedData.team;
|
||||
if (team != null) {
|
||||
return team.getDisplayName(name);
|
||||
|
@ -88,6 +99,35 @@ public final class Score {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Score setDisplayName(Component displayName) {
|
||||
if (currentData.displayName != null && displayName != null) {
|
||||
String convertedDisplayName = MessageTranslator.convertMessage(displayName);
|
||||
if (!currentData.displayName.equals(convertedDisplayName)) {
|
||||
currentData.displayName = convertedDisplayName;
|
||||
setUpdateType(UpdateType.UPDATE);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
// simplified from (this.displayName != null && displayName == null) || (this.displayName == null && displayName != null)
|
||||
if (currentData.displayName != null || displayName != null) {
|
||||
currentData.displayName = MessageTranslator.convertMessage(displayName);
|
||||
setUpdateType(UpdateType.UPDATE);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public NumberFormat getNumberFormat() {
|
||||
return currentData.numberFormat;
|
||||
}
|
||||
|
||||
public Score setNumberFormat(NumberFormat numberFormat) {
|
||||
if (!Objects.equals(currentData.numberFormat, numberFormat)) {
|
||||
currentData.numberFormat = numberFormat;
|
||||
setUpdateType(UpdateType.UPDATE);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public UpdateType getUpdateType() {
|
||||
return currentData.updateType;
|
||||
}
|
||||
|
@ -105,7 +145,7 @@ public final class Score {
|
|||
(currentData.team != null && currentData.team.shouldUpdate());
|
||||
}
|
||||
|
||||
public void update(String objectiveName) {
|
||||
public void update(Objective objective) {
|
||||
if (cachedData == null) {
|
||||
cachedData = new ScoreData();
|
||||
cachedData.updateType = UpdateType.ADD;
|
||||
|
@ -119,13 +159,26 @@ public final class Score {
|
|||
currentData.changed = false;
|
||||
cachedData.team = currentData.team;
|
||||
cachedData.score = currentData.score;
|
||||
cachedData.displayName = currentData.displayName;
|
||||
cachedData.numberFormat = currentData.numberFormat;
|
||||
|
||||
String name = this.name;
|
||||
if (cachedData.team != null) {
|
||||
if (cachedData.displayName != null) {
|
||||
name = cachedData.displayName;
|
||||
} else if (cachedData.team != null) {
|
||||
cachedData.team.prepareUpdate();
|
||||
name = cachedData.team.getDisplayName(name);
|
||||
}
|
||||
cachedInfo = new ScoreInfo(id, objectiveName, cachedData.score, name);
|
||||
|
||||
NumberFormat numberFormat = cachedData.numberFormat;
|
||||
if (numberFormat == null) {
|
||||
numberFormat = objective.getNumberFormat();
|
||||
}
|
||||
if (numberFormat instanceof FixedFormat fixedFormat) {
|
||||
name += " " + ChatColor.RESET + MessageTranslator.convertMessage(fixedFormat.getValue());
|
||||
}
|
||||
|
||||
cachedInfo = new ScoreInfo(id, objective.getObjectiveName(), cachedData.score, name);
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
@ -136,6 +189,9 @@ public final class Score {
|
|||
private Team team;
|
||||
private int score;
|
||||
|
||||
private String displayName;
|
||||
private NumberFormat numberFormat;
|
||||
|
||||
private ScoreData() {
|
||||
updateType = UpdateType.ADD;
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ public final class Scoreboard {
|
|||
boolean update = score.shouldUpdate();
|
||||
|
||||
if (update) {
|
||||
score.update(objective.getObjectiveName());
|
||||
score.update(objective);
|
||||
}
|
||||
|
||||
if (score.getUpdateType() != REMOVE && update) {
|
||||
|
@ -281,7 +281,7 @@ public final class Scoreboard {
|
|||
}
|
||||
|
||||
if (score.shouldUpdate()) {
|
||||
score.update(objective.getObjectiveName());
|
||||
score.update(objective);
|
||||
add = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public class JavaResetScorePacket extends PacketTranslator<ClientboundResetScore
|
|||
|
||||
// as described below
|
||||
if (belowName != null) {
|
||||
JavaSetScoreTranslator.setBelowName(session, belowName, packet.getOwner(), 0);
|
||||
JavaSetScoreTranslator.setBelowName(session, belowName, packet.getOwner());
|
||||
}
|
||||
} else {
|
||||
Objective objective = scoreboard.getObjective(packet.getObjective());
|
||||
|
@ -64,7 +64,7 @@ public class JavaResetScorePacket extends PacketTranslator<ClientboundResetScore
|
|||
// attached to this score.
|
||||
if (objective == belowName) {
|
||||
// Update the score on this player to now reflect 0
|
||||
JavaSetScoreTranslator.setBelowName(session, objective, packet.getOwner(), 0);
|
||||
JavaSetScoreTranslator.setBelowName(session, objective, packet.getOwner());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ public class JavaSetObjectiveTranslator extends PacketTranslator<ClientboundSetO
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundSetObjectivePacket packet) {
|
||||
// todo 1.20.3 unused NumberFormat ?
|
||||
WorldCache worldCache = session.getWorldCache();
|
||||
Scoreboard scoreboard = worldCache.getScoreboard();
|
||||
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
@ -64,8 +63,19 @@ public class JavaSetObjectiveTranslator extends PacketTranslator<ClientboundSetO
|
|||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case ADD, UPDATE -> objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||
.setType(packet.getType().ordinal());
|
||||
case ADD, UPDATE -> {
|
||||
objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||
.setNumberFormat(packet.getNumberFormat())
|
||||
.setType(packet.getType().ordinal());
|
||||
if (objective == scoreboard.getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME)) {
|
||||
// Update the score tag of all players
|
||||
for (PlayerEntity entity : session.getEntityCache().getAllPlayerEntities()) {
|
||||
if (entity.isValid()) {
|
||||
entity.setBelowNameText(objective);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case REMOVE -> {
|
||||
scoreboard.unregisterObjective(packet.getName());
|
||||
if (objective != null && objective == scoreboard.getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME)) {
|
||||
|
|
|
@ -28,8 +28,6 @@ package org.geysermc.geyser.translator.protocol.java.scoreboard;
|
|||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
|
@ -54,7 +52,6 @@ public class JavaSetScoreTranslator extends PacketTranslator<ClientboundSetScore
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundSetScorePacket packet) {
|
||||
// todo 1.20.3 unused display and number format?
|
||||
WorldCache worldCache = session.getWorldCache();
|
||||
Scoreboard scoreboard = worldCache.getScoreboard();
|
||||
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
@ -71,10 +68,10 @@ public class JavaSetScoreTranslator extends PacketTranslator<ClientboundSetScore
|
|||
// attached to this score.
|
||||
boolean isBelowName = objective == scoreboard.getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME);
|
||||
|
||||
objective.setScore(packet.getOwner(), packet.getValue());
|
||||
objective.setScore(packet.getOwner(), packet.getValue(), packet.getDisplay(), packet.getNumberFormat());
|
||||
if (isBelowName) {
|
||||
// Update the below name score on this player
|
||||
setBelowName(session, objective, packet.getOwner(), packet.getValue());
|
||||
setBelowName(session, objective, packet.getOwner());
|
||||
}
|
||||
|
||||
// ScoreboardUpdater will handle it for us if the packets per second
|
||||
|
@ -87,20 +84,13 @@ public class JavaSetScoreTranslator extends PacketTranslator<ClientboundSetScore
|
|||
/**
|
||||
* @param objective the objective that currently resides on the below name display slot
|
||||
*/
|
||||
static void setBelowName(GeyserSession session, Objective objective, String username, int count) {
|
||||
static void setBelowName(GeyserSession session, Objective objective, String username) {
|
||||
PlayerEntity entity = getOtherPlayerEntity(session, username);
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String displayString = count + " " + objective.getDisplayName();
|
||||
|
||||
// Of note: unlike Bedrock, if there is an objective in the below name slot, everyone has a display
|
||||
entity.getDirtyMetadata().put(EntityDataTypes.SCORE, displayString);
|
||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
entityDataPacket.getMetadata().put(EntityDataTypes.SCORE, displayString);
|
||||
session.sendUpstreamPacket(entityDataPacket);
|
||||
entity.setBelowNameText(objective);
|
||||
}
|
||||
|
||||
private static @Nullable PlayerEntity getOtherPlayerEntity(GeyserSession session, String username) {
|
||||
|
|
Loading…
Reference in a new issue