diff --git a/core/src/main/java/com/rexcantor64/triton/packetinterceptor/PacketEventsListener.java b/core/src/main/java/com/rexcantor64/triton/packetinterceptor/PacketEventsListener.java index c88e4fa1..4f0fdec3 100644 --- a/core/src/main/java/com/rexcantor64/triton/packetinterceptor/PacketEventsListener.java +++ b/core/src/main/java/com/rexcantor64/triton/packetinterceptor/PacketEventsListener.java @@ -39,6 +39,7 @@ public void setupHandlers() { if (config.isScoreboards()) { val scoreboardHandler = new ScoreboardPacketHandler(parser, config); updatedHandlers.put(PacketType.Play.Server.TEAMS, scoreboardHandler::onTeamsPacket); + updatedHandlers.put(PacketType.Play.Server.SCOREBOARD_OBJECTIVE, scoreboardHandler::onObjectivePacket); } receiveHandlers = Collections.unmodifiableMap(updatedHandlers); diff --git a/core/src/main/java/com/rexcantor64/triton/packetinterceptor/handlers/ScoreboardPacketHandler.java b/core/src/main/java/com/rexcantor64/triton/packetinterceptor/handlers/ScoreboardPacketHandler.java index 41ebda03..0f6beed9 100644 --- a/core/src/main/java/com/rexcantor64/triton/packetinterceptor/handlers/ScoreboardPacketHandler.java +++ b/core/src/main/java/com/rexcantor64/triton/packetinterceptor/handlers/ScoreboardPacketHandler.java @@ -1,6 +1,7 @@ package com.rexcantor64.triton.packetinterceptor.handlers; import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams; import com.rexcantor64.triton.config.MainConfig; import com.rexcantor64.triton.language.parser.AdventureParser; @@ -96,4 +97,42 @@ public void onTeamsPacket(@NotNull PacketSendEvent event, @NotNull TritonLanguag } } + public void onObjectivePacket(@NotNull PacketSendEvent event, @NotNull TritonLanguagePlayer languagePlayer) { + val packet = new WrapperPlayServerScoreboardObjective(event); + + val action = packet.getMode(); + if (action == WrapperPlayServerScoreboardObjective.ObjectiveMode.REMOVE) { + languagePlayer.getPacketEventsRefresh().discardScoreboardObjective(packet.getName()); + } + + if (action != WrapperPlayServerScoreboardObjective.ObjectiveMode.CREATE && action != WrapperPlayServerScoreboardObjective.ObjectiveMode.UPDATE) { + // we are only interested in new/update actions + return; + } + + val originalDisplayName = packet.getDisplayName(); + + parser.translateComponent( + originalDisplayName, + languagePlayer, + syntax + ) + .getResultOrToRemove(Component::empty) + .ifPresent(result -> { + packet.setDisplayName(result); + event.markForReEncode(true); + }); + + if (event.needsReEncode()) { + languagePlayer.getPacketEventsRefresh().saveScoreboardObjective( + packet.getName(), + originalDisplayName, + packet.getRenderType(), + packet.getScoreFormat() + ); + } else { + languagePlayer.getPacketEventsRefresh().discardScoreboardObjective(packet.getName()); + } + } + } diff --git a/core/src/main/java/com/rexcantor64/triton/player/PacketEventsRefresh.java b/core/src/main/java/com/rexcantor64/triton/player/PacketEventsRefresh.java index 6034ba93..8d3f611b 100644 --- a/core/src/main/java/com/rexcantor64/triton/player/PacketEventsRefresh.java +++ b/core/src/main/java/com/rexcantor64/triton/player/PacketEventsRefresh.java @@ -2,15 +2,20 @@ import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.protocol.score.ScoreFormat; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective.RenderType; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams.ScoreBoardTeamInfo; import com.rexcantor64.triton.Triton; import com.rexcantor64.triton.config.MainConfig; import com.rexcantor64.triton.language.parser.AdventureParser; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.val; import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -18,6 +23,7 @@ @RequiredArgsConstructor public class PacketEventsRefresh { private final Map teamsMap = new ConcurrentHashMap<>(); + private final Map objectivesMap = new ConcurrentHashMap<>(); private final TritonLanguagePlayer languagePlayer; @@ -44,6 +50,33 @@ public void discardScoreboardTeam(String teamName) { teamsMap.remove(teamName); } + /** + * Stores info of an objective for later (i.e., to refresh text when language is changed). + * + * @param objectiveName The name of the objective the info belongs to. + * @param displayName The untranslated display name of the team. + * @param renderType The last render type sent with the packet. + * @param scoreFormat The last score format sent with the packet. + * @since 4.0.0 + */ + public void saveScoreboardObjective(@NotNull String objectiveName, + @NotNull Component displayName, + @Nullable RenderType renderType, + @Nullable ScoreFormat scoreFormat) { + val info = new ScoreboardObjective(displayName, renderType, scoreFormat); + objectivesMap.put(objectiveName, info); + } + + /** + * Forget info about the given objective. + * + * @param objectiveName The name of the objective to forget. + * @since 4.0.0 + */ + public void discardScoreboardObjective(String objectiveName) { + objectivesMap.remove(objectiveName); + } + /** * Refresh all active features of a player. * @@ -60,7 +93,9 @@ public void refreshAll() { val cfg = Triton.get().getConfig(); if (cfg.isScoreboards()) { - updateScoreboardTeams(user, cfg.getScoreboardSyntax(), parser); + val syntax = cfg.getScoreboardSyntax(); + updateScoreboardTeams(user, syntax, parser); + updateScoreboardObjectives(user, syntax, parser); } } @@ -106,4 +141,36 @@ private void updateScoreboardTeams(@NotNull User user, @NotNull MainConfig.Featu user.sendPacketSilently(packet); } } + + private void updateScoreboardObjectives(@NotNull User user, @NotNull MainConfig.FeatureSyntax syntax, @NotNull AdventureParser parser) { + for (val entry : objectivesMap.entrySet()) { + val info = entry.getValue(); + + val packet = new WrapperPlayServerScoreboardObjective( + entry.getKey(), + WrapperPlayServerScoreboardObjective.ObjectiveMode.UPDATE, + info.getDisplayName(), + info.getRenderType(), + info.getScoreFormat() + ); + + parser.translateComponent( + packet.getDisplayName(), + languagePlayer, + syntax + ) + .getResultOrToRemove(Component::empty) + .ifPresent(packet::setDisplayName); + + user.sendPacketSilently(packet); + } + } + + @RequiredArgsConstructor + @Getter + private static class ScoreboardObjective { + private final Component displayName; + private final RenderType renderType; + private final ScoreFormat scoreFormat; + } }