繁體中文 | English
A server-side Fabric mod that shows a ranked leaderboard in the scoreboard sidebar, with the player's own rank appended at the bottom.
Built on fabric-mod-template, using a preprocessor for multi-version support. The core version is Minecraft 1.17.
| Minecraft | Status |
|---|---|
| 1.17.x | ✅ core |
| 1.18.x | ✅ |
| 1.19.x | ✅ |
| 1.20 – 1.20.1 | ✅ |
| 1.20.2 | ✅ |
| 1.20.3/4 | ✅ |
| 1.20.5/6 | ✅ |
| 1.21 – 1.21.1 | ✅ |
- Driven entirely by vanilla commands: set the leaderboard with
/scoreboard objectives setdisplay sidebar <objective>, clear the sidebar to disable it;sidebar.team.<color>slots are supported — players on a colored team see their team's board first (matching the vanilla client logic) - Server side only (
environment: "*", installing on both sides is fine too; the integrated server in singleplayer works as well) - No Fabric API dependency, pure mixin implementation
- Every player sees a personalized sidebar (the objective is faked via packets and never
touches the real server scoreboard):
- shows the top N entries of the objective (default 10; ranks 1/2/3 get distinct colors, your own name is highlighted in green)
- if you are below the top list, an extra
#rank yournameline with your score is appended
- Event-driven updates: scoreboard changes are reflected within the same tick (≤50ms), with snapshot short-circuiting and per-line diffing so high-frequency changes stay cheap; the display slot is re-claimed once per second, self-healing against vanilla sidebar broadcasts
- Persistent settings: the ranking mode and each player's top count are stored in
myrank.jsonin the world directory and restored on restart (which objective is displayed already lives in the vanilla scoreboard data) - Localized messages: translated on the server using each player's reported client
language (works for vanilla clients too), with built-in
en_us/zh_tw/zh_cnand automatic fallback to en_us; language files live insrc/main/resources/assets/myrank/lang/*.yml(yamlang format) — add a file to add a language - Pinned rows:
/myrank pin add <name>pins a score holder (e.g. a datapack-maintained fake player like "Total") to the top of the sidebar — no#rankprefix, excluded from ranking and player counts; pairs seamlessly with datapacks that sum totals every tick (see "Pinned rows & datapack totals" below) - Permission nodes: with a fabric-permissions-api provider installed (e.g. LuckPerms), the OP commands check permission nodes; without one, vanilla OP levels are used
The leaderboard itself is toggled with vanilla commands:
/scoreboard objectives setdisplay sidebar <objective> enable / switch the leaderboard
/scoreboard objectives setdisplay sidebar.team.aqua <obj> a board only the aqua team sees
/scoreboard objectives setdisplay sidebar disable| Command | Permission | Description |
|---|---|---|
/myrank display <slot> on|off |
OP (2) | whether MyRank takes over that slot (off = show the vanilla scoreboard; all slots default on, persisted) |
/myrank mode competition |
OP (2) | ranks skip after ties: 1, 2, 2, 4 (default) |
/myrank mode dense |
OP (2) | ranks stay consecutive after ties: 1, 2, 2, 3 |
/myrank pin add <name> |
OP (2) | pin a score holder to the top of the sidebar (no rank, excluded from ranking) |
/myrank pin remove <name> |
OP (2) | unpin a score holder |
/myrank pin list |
OP (2) | list all pinned names |
/myrank top <1-15> |
everyone | how many top entries your own sidebar shows (persisted) |
/myrank query |
everyone | your current rank, score and the number of ranked players |
/myrank query <name> |
everyone | query any ranked player (with tab completion) |
/myrank list [page] |
everyone | the full leaderboard in chat, paginated (10 per page) |
/myrank |
everyone | which leaderboard you are shown and the current ranking mode |
query/list operate on the board the command sender currently sees
(team-color slot first, same as the sidebar).
Tied players always share a rank; mode only decides how ranks continue after a tie group.
With a fabric-permissions-api provider (e.g. LuckPerms) installed, the OP commands map to the nodes below, defaulting to vanilla OP level 2; without a provider, OP levels are checked directly:
| Node | Command |
|---|---|
myrank.command.display |
/myrank display |
myrank.command.mode |
/myrank mode |
myrank.command.pin |
/myrank pin |
Leaderboard data keeps accumulating in real time via scoreboard criteria (e.g.
minecraft.mined:minecraft.deepslate), while a datapack sums the server-wide total into a
fake player (e.g. "Total") every tick. Pin that fake player with /myrank pin add, and it:
- shows at the very top of the sidebar, in gold, without a
#rankprefix - takes no part in ranking, and is excluded from
/myrank query//myrank listcounts
Datapack example (an mcfunction hooked into minecraft:tick; mine_count is a criteria
objective):
scoreboard players set Total mine_count 0
execute as @a run scoreboard players operation Total mine_count += @s mine_count/myrank pin add Total
/scoreboard objectives setdisplay sidebar mine_countThe sidebar sorts by score (determined by the client). On 1.20.3+, pinned rows' sorting
scores are automatically bumped above every ranked line, and a number format makes the display
always show the real value — guaranteeing they stay on top. On 1.20.2 and earlier, pinned
rows use their real score directly, so the displayed value is always correct, but they may not
render on top when tied with a ranked entry (as the client then uses alphabetical order). Names
containing non-ASCII or special characters must be double-quoted in commands. Pinned names are
stored in the world's myrank.json and apply to holders with that name in every objective.
/scoreboard objectives add test dummy "xxx"
/scoreboard players set Alice test 100
/scoreboard players set Bob test 80
/scoreboard objectives setdisplay sidebar test.\gradlew buildAndGatherThe jars of every version are gathered into build/libs/; for a single version use
.\gradlew :1.17.1:build, whose output lands in versions/1.17.1/build/libs/.
- Add the new version to
versionsinsettings.json - Copy
versions/<closest version>/gradle.propertiestoversions/<new version>/and adjust it (see the template repo's counterpart and https://fallen-breath.github.io/fabric-versions/) - Add a
createNodein thepreprocessblock ofbuild.gradleand connect it withlink - Handle version differences in code with preprocessor comments like
//#if MC >= 11900(e.g.MyRankTexts.literalwraps the 1.19TextComponent→Component.literalchange)
Where version differences live: text building in MyRankTexts, the sendSuccess wrapper at the
end of MyRankCommand, and all scoreboard/packet APIs in the "Version-specific scoreboard access"
section at the top of MyRankManager. Extending to 1.21.2+ should only require touching these.