new scoreboard protocol number format conformance (#98)

* new scoreboard protocol conformance

* fix: pass number format through when reading scoreboard packet

---------

Co-authored-by: mworzala <mattheworzala@gmail.com>
(cherry picked from commit 2003f75f1f)
This commit is contained in:
DeidaraMC 2024-01-02 06:15:19 -05:00 committed by Matt Worzala
parent e6e16a7a08
commit ed13961e90
6 changed files with 200 additions and 39 deletions

View File

@ -65,6 +65,7 @@ public class Main {
commandManager.register(new NotificationCommand());
commandManager.register(new TestCommand2());
commandManager.register(new ConfigCommand());
commandManager.register(new SidebarCommand());
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));

View File

@ -0,0 +1,99 @@
package net.minestom.demo.commands;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.condition.Conditions;
import net.minestom.server.entity.Player;
import net.minestom.server.scoreboard.Sidebar;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SidebarCommand extends Command {
private final Sidebar sidebar = new Sidebar(Component.text("DEMO").decorate(TextDecoration.BOLD));
private int currentLine = 0;
public SidebarCommand() {
super("sidebar");
addLine("BLANK ", Sidebar.NumberFormat.blank());
addLine("STYLE ", Sidebar.NumberFormat.styled(Component.empty().decorate(TextDecoration.STRIKETHROUGH).color(NamedTextColor.GRAY)));
addLine("FIXED ", Sidebar.NumberFormat.fixed(Component.text("FIXED").color(NamedTextColor.GRAY)));
addLine("NULL ", null);
setDefaultExecutor((source, args) -> source.sendMessage(Component.text("Unknown syntax (note: title must be quoted)")));
setCondition(Conditions::playerOnly);
var option = ArgumentType.Word("option").from("add-line", "remove-line", "set-title", "toggle", "update-content", "update-score");
var content = ArgumentType.String("content").setDefaultValue("");
var targetLine = ArgumentType.Integer("target line").setDefaultValue(-1);
addSyntax(this::handleSidebar, option);
addSyntax(this::handleSidebar, option, content);
addSyntax(this::handleSidebar, option, content, targetLine);
}
private void handleSidebar(CommandSender source, CommandContext context) {
Player player = (Player) source;
String option = context.get("option");
String content = context.get("content");
int targetLine = context.get("target line");
if (targetLine == -1) targetLine = currentLine;
switch (option) {
case "add-line":
addLine(content, null);
break;
case "remove-line":
removeLine();
break;
case "set-title":
setTitle(content);
break;
case "toggle":
toggleSidebar(player);
break;
case "update-content":
updateLineContent(content, String.valueOf(targetLine));
break;
case "update-score":
updateLineScore(Integer.parseInt(content), String.valueOf(targetLine));
break;
}
}
private void addLine(@NotNull String content, @Nullable Sidebar.NumberFormat numberFormat) {
if (currentLine < 16) {
sidebar.createLine(new Sidebar.ScoreboardLine(String.valueOf(currentLine), Component.text(content).color(NamedTextColor.WHITE), currentLine, numberFormat));
currentLine++;
}
}
private void removeLine() {
if (currentLine > 0) {
sidebar.removeLine(String.valueOf(currentLine));
currentLine--;
}
}
private void setTitle(@NotNull String title) {
sidebar.setTitle(Component.text(title).decorate(TextDecoration.BOLD));
}
private void toggleSidebar(Player player) {
if (sidebar.getViewers().contains(player)) sidebar.removeViewer(player);
else sidebar.addViewer(player);
}
private void updateLineContent(@NotNull String content, @NotNull String lineId) {
sidebar.updateLineContent(lineId, Component.text(content).color(NamedTextColor.WHITE));
}
private void updateLineScore(int score, @NotNull String lineId) {
sidebar.updateLineScore(lineId, score);
}
}

View File

@ -6,6 +6,7 @@ import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.scoreboard.Sidebar;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -18,25 +19,28 @@ import static net.minestom.server.network.NetworkBuffer.*;
public record ScoreboardObjectivePacket(@NotNull String objectiveName, byte mode,
@Nullable Component objectiveValue,
@Nullable Type type) implements ComponentHoldingServerPacket {
@Nullable Type type,
@Nullable Sidebar.NumberFormat numberFormat) implements ComponentHoldingServerPacket {
public ScoreboardObjectivePacket(@NotNull NetworkBuffer reader) {
this(read(reader));
}
private ScoreboardObjectivePacket(ScoreboardObjectivePacket packet) {
this(packet.objectiveName, packet.mode, packet.objectiveValue, packet.type);
this(packet.objectiveName, packet.mode, packet.objectiveValue, packet.type, packet.numberFormat);
}
private static ScoreboardObjectivePacket read(@NotNull NetworkBuffer reader) {
var objectiveName = reader.read(STRING);
var mode = reader.read(BYTE);
String objectiveName = reader.read(STRING);
byte mode = reader.read(BYTE);
Component objectiveValue = null;
Type type = null;
Sidebar.NumberFormat numberFormat = null;
if (mode == 0 || mode == 2) {
objectiveValue = reader.read(COMPONENT);
type = Type.values()[reader.read(VAR_INT)];
numberFormat = reader.readOptional(Sidebar.NumberFormat::new);
}
return new ScoreboardObjectivePacket(objectiveName, mode, objectiveValue, type);
return new ScoreboardObjectivePacket(objectiveName, mode, objectiveValue, type, numberFormat);
}
@Override
@ -48,6 +52,7 @@ public record ScoreboardObjectivePacket(@NotNull String objectiveName, byte mode
writer.write(COMPONENT, objectiveValue);
assert type != null;
writer.write(VAR_INT, type.ordinal());
writer.writeOptional(numberFormat);
}
}
@ -68,7 +73,7 @@ public record ScoreboardObjectivePacket(@NotNull String objectiveName, byte mode
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return mode == 0 || mode == 2 ? new ScoreboardObjectivePacket(objectiveName, mode,
operator.apply(objectiveValue), type) : this;
operator.apply(objectiveValue), type, numberFormat) : this;
}
/**

View File

@ -5,6 +5,7 @@ import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.scoreboard.Sidebar;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -20,11 +21,11 @@ public record UpdateScorePacket(
@NotNull String objectiveName,
int score,
@Nullable Component displayName,
@Nullable NumberFormat numberFormat
@Nullable Sidebar.NumberFormat numberFormat
) implements ServerPacket {
public UpdateScorePacket(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), reader.read(STRING), reader.read(VAR_INT),
reader.readOptional(COMPONENT), new NumberFormat(reader));
reader.readOptional(COMPONENT), reader.readOptional(Sidebar.NumberFormat::new));
}
@Override
@ -43,17 +44,4 @@ public record UpdateScorePacket(
default -> PacketUtils.invalidPacketState(getClass(), state, ConnectionState.PLAY);
};
}
public record NumberFormat() implements Writer {
public NumberFormat(@NotNull NetworkBuffer reader) {
this();
throw new UnsupportedOperationException("TODO");
}
@Override
public void write(@NotNull NetworkBuffer writer) {
throw new UnsupportedOperationException("TODO");
}
}
}

View File

@ -6,7 +6,6 @@ import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket;
import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket;
import net.minestom.server.network.packet.server.play.UpdateScorePacket;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@ -38,7 +37,7 @@ public interface Scoreboard extends Viewable, PacketGroupingAudience {
* @return the creation objective packet
*/
default @NotNull ScoreboardObjectivePacket getCreationObjectivePacket(Component value, ScoreboardObjectivePacket.Type type) {
return new ScoreboardObjectivePacket(getObjectiveName(), (byte) 0, value, type);
return new ScoreboardObjectivePacket(getObjectiveName(), (byte) 0, value, type, null);
}
/**
@ -47,7 +46,7 @@ public interface Scoreboard extends Viewable, PacketGroupingAudience {
* @return the destruction objective packet
*/
default @NotNull ScoreboardObjectivePacket getDestructionObjectivePacket() {
return new ScoreboardObjectivePacket(getObjectiveName(), (byte) 1, null, null);
return new ScoreboardObjectivePacket(getObjectiveName(), (byte) 1, null, null, null);
}
/**

View File

@ -4,10 +4,8 @@ import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket;
import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket;
import net.minestom.server.network.packet.server.play.TeamsPacket;
import net.minestom.server.network.packet.server.play.UpdateScorePacket;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -98,7 +96,7 @@ public class Sidebar implements Scoreboard {
public void setTitle(@NotNull Component title) {
this.title = title;
sendPacketToViewers(new ScoreboardObjectivePacket(objectiveName, (byte) 2, title,
ScoreboardObjectivePacket.Type.INTEGER));
ScoreboardObjectivePacket.Type.INTEGER, null));
}
/**
@ -260,6 +258,10 @@ public class Sidebar implements Scoreboard {
* The score of the line
*/
private int line;
/**
* The number format of the line
*/
private NumberFormat numberFormat;
private final String teamName;
/**
@ -273,9 +275,14 @@ public class Sidebar implements Scoreboard {
private SidebarTeam sidebarTeam;
public ScoreboardLine(@NotNull String id, @NotNull Component content, int line) {
this(id, content, line, null);
}
public ScoreboardLine(@NotNull String id, @NotNull Component content, int line, @Nullable NumberFormat numberFormat) {
this.id = id;
this.content = content;
this.line = line;
this.numberFormat = numberFormat;
this.teamName = TEAM_PREFIX + COUNTER.incrementAndGet();
}
@ -335,9 +342,8 @@ public class Sidebar implements Scoreboard {
* @return a {@link UpdateScorePacket}
*/
private UpdateScorePacket getScoreCreationPacket(String objectiveName) {
//todo
throw new UnsupportedOperationException();
// return new UpdateScorePacket(entityName, (byte) 0, objectiveName, line);
//TODO displayName acts as a suffix to the objective name, find way to handle elegantly
return new UpdateScorePacket(entityName, objectiveName, line, Component.empty(), numberFormat);
}
/**
@ -346,10 +352,8 @@ public class Sidebar implements Scoreboard {
* @param objectiveName The objective name to be destroyed
* @return a {@link UpdateScorePacket}
*/
private UpdateScorePacket getScoreDestructionPacket(String objectiveName) {
//todo
throw new UnsupportedOperationException();
// return new UpdateScorePacket(entityName, (byte) 1, objectiveName, 0);
private ResetScorePacket getScoreDestructionPacket(String objectiveName) {
return new ResetScorePacket(entityName, objectiveName);
}
/**
@ -360,9 +364,8 @@ public class Sidebar implements Scoreboard {
* @return a {@link UpdateScorePacket}
*/
private UpdateScorePacket getLineScoreUpdatePacket(String objectiveName, int score) {
//todo
throw new UnsupportedOperationException();
// return new UpdateScorePacket(entityName, (byte) 0, objectiveName, score);
//TODO displayName acts as a suffix to the objective name, find way to handle elegantly
return new UpdateScorePacket(entityName, objectiveName, score, Component.empty(), numberFormat);
}
/**
@ -466,4 +469,70 @@ public class Sidebar implements Scoreboard {
this.prefix = prefix;
}
}
public static class NumberFormat implements NetworkBuffer.Writer {
private final FormatType formatType;
private final Component content;
private NumberFormat() {
this.content = null;
this.formatType = FormatType.BLANK;
}
private NumberFormat(@NotNull Component content, @NotNull FormatType formatType) {
this.content = content;
this.formatType = formatType;
}
public NumberFormat(NetworkBuffer reader) {
this.formatType = FormatType.values()[reader.read(NetworkBuffer.VAR_INT)];
if (formatType != FormatType.BLANK) this.content = reader.read(NetworkBuffer.COMPONENT);
else this.content = null;
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.VAR_INT, formatType.ordinal());
if (formatType == FormatType.STYLED) {
assert content != null;
writer.write(NetworkBuffer.COMPONENT, content);
}
else if (formatType == FormatType.FIXED) {
assert content != null;
writer.write(NetworkBuffer.COMPONENT, content);
}
}
/**
* A number format which has no sidebar score displayed
*
* @return a blank number format
*/
public static @NotNull NumberFormat blank() {
return new NumberFormat();
}
/**
* A number format which lets the sidebar scores be styled
*
* @param style a styled component
*/
public static @NotNull NumberFormat styled(@NotNull Component style) {
return new NumberFormat(style, FormatType.STYLED);
}
/**
* A number format which lets the sidebar scores be styled with explicit text
*
* @param content the fixed component
*/
public static @NotNull NumberFormat fixed(@NotNull Component content) {
return new NumberFormat(content, FormatType.FIXED);
}
private enum FormatType {
BLANK, STYLED, FIXED
}
}
}