diff --git a/common/src/main/java/net/minescript/common/Minescript.java b/common/src/main/java/net/minescript/common/Minescript.java index 749df800..55a546f9 100644 --- a/common/src/main/java/net/minescript/common/Minescript.java +++ b/common/src/main/java/net/minescript/common/Minescript.java @@ -2778,6 +2778,50 @@ private static ScriptValue runScriptFunction( return ScriptValue.of(result); } + case "get_scoreboard": + { + args.expectSize(0); + var scoreboard = world.getScoreboard(); + var objective = scoreboard.getDisplayObjective(net.minecraft.world.scores.DisplaySlot.SIDEBAR); + if (objective == null) { + return ScriptValue.NULL; + } + var playerScores = scoreboard.listPlayerScores(objective); + var entries = new ArrayList(); + for (var playerScore : playerScores) { + String owner = playerScore.owner(); + int score = playerScore.value(); + // Get the display text by checking if the owner belongs to a team + // Servers often use teams to set the visible text via prefix/suffix + var team = scoreboard.getPlayersTeam(owner); + String displayText; + if (team != null) { + // Team exists - get prefix + suffix which contains the visible text + var prefix = team.getPlayerPrefix(); + var suffix = team.getPlayerSuffix(); + String prefixStr = prefix != null ? prefix.getString() : ""; + String suffixStr = suffix != null ? suffix.getString() : ""; + displayText = prefixStr + suffixStr; + // If still empty, fall back to owner name + if (displayText.isEmpty()) { + var ownerName = playerScore.ownerName(); + displayText = ownerName != null ? ownerName.getString() : owner; + } + } else { + // No team - use ownerName if available, otherwise owner + var ownerName = playerScore.ownerName(); + displayText = ownerName != null ? ownerName.getString() : owner; + } + entries.add(new ScoreboardEntry(owner, score, displayText)); + } + Collections.sort(entries, (a, b) -> Integer.compare(b.score, a.score)); + var result = new ScoreboardData( + objective.getName(), + objective.getDisplayName().getString(), + entries.toArray(ScoreboardEntry[]::new)); + return ScriptValue.of(result); + } + case "screenshot": { args.expectArgs("filename"); diff --git a/common/src/main/java/net/minescript/common/dataclasses/ScoreboardData.java b/common/src/main/java/net/minescript/common/dataclasses/ScoreboardData.java new file mode 100644 index 00000000..dda6d7c1 --- /dev/null +++ b/common/src/main/java/net/minescript/common/dataclasses/ScoreboardData.java @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: © 2022-2025 Greg Christiana +// SPDX-License-Identifier: GPL-3.0-only + +package net.minescript.common.dataclasses; + +import net.minescript.common.Jsonable; + +public class ScoreboardData extends Jsonable { + public String objective_name; + public String display_name; + public ScoreboardEntry[] entries; + + public ScoreboardData(String objectiveName, String displayName, ScoreboardEntry[] entries) { + this.objective_name = objectiveName; + this.display_name = displayName; + this.entries = entries; + } +} diff --git a/common/src/main/java/net/minescript/common/dataclasses/ScoreboardEntry.java b/common/src/main/java/net/minescript/common/dataclasses/ScoreboardEntry.java new file mode 100644 index 00000000..248367b7 --- /dev/null +++ b/common/src/main/java/net/minescript/common/dataclasses/ScoreboardEntry.java @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: © 2022-2025 Greg Christiana +// SPDX-License-Identifier: GPL-3.0-only + +package net.minescript.common.dataclasses; + +import net.minescript.common.Jsonable; + +public class ScoreboardEntry extends Jsonable { + public String name; + public int score; + public String display_name; + + public ScoreboardEntry(String name, int score, String displayName) { + this.name = name; + this.score = score; + this.display_name = displayName; + } +} diff --git a/common/src/main/resources/system/lib/minescript.py b/common/src/main/resources/system/lib/minescript.py index 5b6ff36e..5a547cd6 100644 --- a/common/src/main/resources/system/lib/minescript.py +++ b/common/src/main/resources/system/lib/minescript.py @@ -810,6 +810,41 @@ def _world_info_result_transform(info): world_info = ScriptFunction("world_info", world_info, _world_info_result_transform) +@dataclass +class ScoreboardEntry: + name: str + score: int + display_name: str + +@dataclass +class ScoreboardData: + objective_name: str + display_name: str + entries: List['ScoreboardEntry'] + +def get_scoreboard() -> ScoreboardData: + """Gets the scoreboard data displayed on the sidebar. + + Returns: + `ScoreboardData` with objective name, display name, and list of entries, + or `None` if no scoreboard is displayed on the sidebar. + + Since: v5.1 + """ + return () + +def _get_scoreboard_result_transform(data): + if data is None: + return None + entries = [ScoreboardEntry(**e) for e in data['entries']] + return ScoreboardData( + objective_name=data['objective_name'], + display_name=data['display_name'], + entries=entries) + +get_scoreboard = ScriptFunction("get_scoreboard", get_scoreboard, _get_scoreboard_result_transform) + + def getblock(x: int, y: int, z: int) -> str: """Gets the type of block at position (x, y, z). diff --git a/common/src/main/resources/system/pyj/minescript.py b/common/src/main/resources/system/pyj/minescript.py index 9df2f8e7..5f014544 100644 --- a/common/src/main/resources/system/pyj/minescript.py +++ b/common/src/main/resources/system/pyj/minescript.py @@ -566,6 +566,18 @@ def world_info() -> WorldInfo: return __mcall__("world_info", []) +def get_scoreboard() -> ScoreboardData: + """Gets the scoreboard data displayed on the sidebar. + + Returns: + `ScoreboardData` with objective name, display name, and list of entries, + or `None` if no scoreboard is displayed on the sidebar. + + Since: v5.1 + """ + return __mcall__("get_scoreboard", []) + + def getblock(x: int, y: int, z: int) -> str: """Gets the type of block at position (x, y, z).