{"id":14969597,"url":"https://github.com/megavexnetwork/scoreboard-library","last_synced_at":"2025-04-04T19:11:07.618Z","repository":{"id":37426456,"uuid":"441892801","full_name":"MegavexNetwork/scoreboard-library","owner":"MegavexNetwork","description":"Powerful packet-level scoreboard library for Paper/Spigot servers","archived":false,"fork":false,"pushed_at":"2025-03-27T18:00:37.000Z","size":1893,"stargazers_count":174,"open_issues_count":11,"forks_count":14,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T18:15:32.710Z","etag":null,"topics":["adventure","api","bukkit","java","library","minecraft","objectives","packets","papermc","scoreboard","sidebar","spigotmc","teams"],"latest_commit_sha":null,"homepage":"https://megavex.net","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MegavexNetwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-26T13:05:42.000Z","updated_at":"2025-03-27T18:00:11.000Z","dependencies_parsed_at":"2024-04-26T01:26:24.974Z","dependency_job_id":"e5460e60-99cc-44ae-b1bd-af6efaa3c0bd","html_url":"https://github.com/MegavexNetwork/scoreboard-library","commit_stats":{"total_commits":329,"total_committers":3,"mean_commits":"109.66666666666667","dds":"0.43465045592705165","last_synced_commit":"4f6d1dc5b17d899709be32890eba3cb2c164b3c6"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MegavexNetwork%2Fscoreboard-library","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MegavexNetwork%2Fscoreboard-library/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MegavexNetwork%2Fscoreboard-library/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MegavexNetwork%2Fscoreboard-library/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MegavexNetwork","download_url":"https://codeload.github.com/MegavexNetwork/scoreboard-library/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247234921,"owners_count":20905854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adventure","api","bukkit","java","library","minecraft","objectives","packets","papermc","scoreboard","sidebar","spigotmc","teams"],"created_at":"2024-09-24T13:42:05.798Z","updated_at":"2025-04-04T19:11:07.587Z","avatar_url":"https://github.com/MegavexNetwork.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# scoreboard-library\n\nPowerful scoreboard library for Minecraft Paper/Spigot servers using\nthe [adventure](https://github.com/KyoriPowered/adventure)\ncomponent library\n\nJoin the [Discord](https://discord.gg/v7nmTDTW8W) or create an issue for support\n\n## Features\n\n- Sidebars. Up to 42 characters (depends on the formatting) for 1.12.2-, no limit for newer versions\n- Teams. Supports showing different properties (display name, prefix, entries etc.) of the same team to different\n  players\n- Objectives.\n- Full support for new 1.20.4 features (score display names, custom score formats)\n- Doesn't require extra dependencies (assuming you're targeting modern versions of Paper)\n- Packet-level, meaning it works with other scoreboard plugins\n- Supports [Folia](https://github.com/PaperMC/Folia)\n- Fully async. All packet work is done asynchronously, so you can use the library from the main\n  thread without sacrificing any performance\n- Automatically works with `TranslatableComponent`s. All components are translated using `GlobalTranslator` for\n  each player's client locale and automatically update whenever the player changes it in their settings\n\n## Available Packet Adapters\n\n- **modern.** Supports 1.17-1.21.4. Can take advantage of [Paper](https://papermc.io)'s native adventure support to be more efficient.\n- **legacy.** Supports 1.7.10-1.12.2.\n- **PacketEvents.** Supports 1.8+. Requires [PacketEvents 2.0](https://github.com/retrooper/packetevents/tree/2.0) to be shaded or installed as a plugin.\n\n\u003e [!NOTE]  \n\u003e You can add multiple packet adapters, the best one will automatically be picked depending on the server version.\n\n## Installation\n\nSee installation instructions [here](https://github.com/MegavexNetwork/scoreboard-library/blob/dev/2.0/INSTALLATION.md)\n\n### Getting started\n\n```java\nScoreboardLibrary scoreboardLibrary;\ntry {\n  scoreboardLibrary = ScoreboardLibrary.loadScoreboardLibrary(plugin);\n} catch (NoPacketAdapterAvailableException e) {\n  // If no packet adapter was found, you can fallback to the no-op implementation:\n  scoreboardLibrary = new NoopScoreboardLibrary();\n  plugin.getLogger().warning(\"No scoreboard packet adapter available!\");\n}\n\n// On plugin shutdown:\nscoreboardLibrary.close();\n```\n\n### Thread safety warning (Folia)\n\n`Sidebar`s and `TeamManager`s are not thread safe, so you will need to add some\nsynchronisation to make sure you're using them from only one thread at a time.\n\n### Sidebar (low-level)\n\n```java\nSidebar sidebar = scoreboardLibrary.createSidebar();\n\nsidebar.title(Component.text(\"Sidebar Title\"));\nsidebar.line(0, Component.empty());\nsidebar.line(1, Component.text(\"Line 1\"));\nsidebar.line(2, Component.text(\"Line 2\"));\nsidebar.line(2, Component.empty());\nsidebar.line(3, Component.text(\"epicserver.net\"));\n\nsidebar.addPlayer(player); // Add the player to the sidebar\n\n// Don't forget to call sidebar.close() once you don't need it anymore!\n```\n\n### Component sidebars\n\nComponent sidebars are an abstraction over the low-level `Sidebar`. They allow you to design sidebars in a clean way.\nHere's how you create a `SidebarComponent` with a static line:\n\n```java\nSidebarComponent staticLine = SidebarComponent.staticLine(Component.text(\"A static line\"));\n```\n\nYou can chain multiple `SidebarComponent`s together using `SidebarComponent.builder()`:\n\n```java\nSidebarComponent lines = SidebarComponent.builder()\n  .addComponent(SidebarComponent.staticLine(Component.text(\"A static line\")))\n  .addStaticLine(Component.text(\"Another static line\")) // Shorthand for line above\n  .build();\n```\n\nTo use these components, create a `ComponentSidebarLayout`:\n\n```java\nComponentSidebarLayout layout = new ComponentSidebarLayout(\n  SidebarComponent.staticLine(Component.text(\"Sidebar Title\")),\n  lines\n);\n\nSidebar sidebar = scoreboardLibrary.createSidebar();\n\n// Apply the title \u0026 lines components to the Sidebar\n// Do this every time the title or any line needs to be updated\nlayout.apply(sidebar);\n```\n\nYou can make animations with `SidebarAnimation`:\n\n```java\nComponent lineComponent = Component.text(\"Line that changes colors\");\nSet\u003cNamedTextColor\u003e colors = NamedTextColor.NAMES.values();\nList\u003cComponent\u003e frames = new ArrayList\u003c\u003e(colors.size());\nfor (NamedTextColor color : colors) {\n  frames.add(lineComponent.color(color));\n}\n\nSidebarAnimation\u003cComponent\u003e animation = new CollectionSidebarAnimation\u003c\u003e(frames);\n// You can also implement SidebarAnimation yourself\n\nSidebarComponent line = SidebarComponent.animatedLine(animation);\n\n// Advance to the next frame of the animation\nanimation.nextFrame();\n```\n\nAnimations can be used for pagination:\n\n```java\nPlayer player = ...;\n\nSidebarComponent firstPage = SidebarComponent.builder()\n  .addStaticLine(Component.text(\"First page\"))\n  .addDynamicLine(() -\u003e Component.text(\"Level: \" + player.getLevel()))\n  .build();\n\nSidebarComponent secondPage = SidebarComponent.builder()\n  .addStaticLine(Component.text(\"Second page\"))\n  .addDynamicLine(() -\u003e Component.text(\"Health: \" + player.getHealth()))\n  .build();\n\nList\u003cSidebarComponent\u003e pages = Arrays.asList(firstPage, secondPage);\nSidebarAnimation\u003cSidebarComponent\u003e pageAnimation = new CollectionSidebarAnimation\u003c\u003e(pages);\nSidebarComponent paginatedComponent = SidebarComponent.animatedComponent(pageAnimation);\n\n// ...\n```\n\nYou can also create your own `SidebarComponent`s:\n\n```java\npublic class KeyValueSidebarComponent implements SidebarComponent {\n  private final Component key;\n  private final Supplier\u003cComponent\u003e valueSupplier;\n\n  public KeyValueSidebarComponent(@NotNull Component key, @NotNull Supplier\u003cComponent\u003e valueSupplier) {\n    this.key = key;\n    this.valueSupplier = valueSupplier;\n  }\n\n  @Override\n  public void draw(@NotNull LineDrawable drawable) {\n    var value = valueSupplier.get();\n    var line = Component.text()\n      .append(key)\n      .append(Component.text(\": \"))\n      .append(value.colorIfAbsent(NamedTextColor.AQUA))\n      .build();\n\n    drawable.drawLine(line);\n  }\n}\n```\n\nHere's a full sidebar example:\n\n```java\npublic class ExampleComponentSidebar {\n  private final Sidebar sidebar;\n  private final ComponentSidebarLayout componentSidebar;\n  private final SidebarAnimation\u003cComponent\u003e titleAnimation;\n\n  public ExampleComponentSidebar(@NotNull Plugin plugin, @NotNull Sidebar sidebar) {\n    this.sidebar = sidebar;\n\n    this.titleAnimation = createGradientAnimation(Component.text(\"Sidebar Example\", Style.style(TextDecoration.BOLD)));\n    var title = SidebarComponent.animatedLine(titleAnimation);\n\n    SimpleDateFormat dtf = new SimpleDateFormat(\"HH:mm:ss\");\n\n    // Custom SidebarComponent, see below for how an implementation might look like\n    SidebarComponent onlinePlayers = new KeyValueSidebarComponent(\n      Component.text(\"Online players\"),\n      () -\u003e Component.text(plugin.getServer().getOnlinePlayers().size())\n    );\n\n    SidebarComponent lines = SidebarComponent.builder()\n      .addDynamicLine(() -\u003e {\n        var time = dtf.format(new Date());\n        return Component.text(time, NamedTextColor.GRAY);\n      })\n      .addBlankLine()\n      .addStaticLine(Component.text(\"A static line\"))\n      .addComponent(onlinePlayers)\n      .addBlankLine()\n      .addStaticLine(Component.text(\"epicserver.net\", NamedTextColor.AQUA))\n      .build();\n\n\n    this.componentSidebar = new ComponentSidebarLayout(title, lines);\n  }\n\n  // Called every tick\n  public void tick() {\n    // Advance title animation to the next frame\n    titleAnimation.nextFrame();\n\n    // Update sidebar title \u0026 lines\n    componentSidebar.apply(sidebar);\n  }\n\n  private @NotNull SidebarAnimation\u003cComponent\u003e createGradientAnimation(@NotNull Component text) {\n    float step = 1f / 8f;\n\n    TagResolver.Single textPlaceholder = Placeholder.component(\"text\", text);\n    List\u003cComponent\u003e frames = new ArrayList\u003c\u003e((int) (2f / step));\n\n    float phase = -1f;\n    while (phase \u003c 1) {\n      frames.add(MiniMessage.miniMessage().deserialize(\"\u003cgradient:yellow:gold:\" + phase + \"\u003e\u003ctext\u003e\", textPlaceholder));\n      phase += step;\n    }\n\n    return new CollectionSidebarAnimation\u003c\u003e(frames);\n  }\n}\n```\n\n![Sidebar example](./assets/sidebar.gif)`\n\n### TeamManager\n\n```java\nTeamManager teamManager = scoreboardLibrary.createTeamManager();\nScoreboardTeam team = teamManager.createIfAbsent(\"team_name\");\n\n// A TeamDisplay holds all the display properties that a team can have (prefix, suffix etc.).\n// You can apply different TeamDisplays for each player so different players can see\n// different properties on a single ScoreboardTeam. However if you don't need that you can\n// use the default TeamDisplay that is created in every ScoreboardTeam.\nTeamDisplay teamDisplay = team.defaultDisplay();\nteamDisplay.displayName(Component.text(\"Team Display Name\"));\nteamDisplay.prefix(Component.text(\"[Prefix] \"));\nteamDisplay.suffix(Component.text(\" [Suffix]\"));\nteamDisplay.playerColor(NamedTextColor.RED);\nteamDisplay.addEntry(player.getName());\n\nteamManager.addPlayer(player); // Player will be added to the default TeamDisplay of each ScoreboardTeam\n\n// Create a new TeamDisplay like this:\nTeamDisplay newTeamDisplay = team.createDisplay();\nnewTeamDisplay.displayName(Component.text(\"Other Team Display Name\"));\n\n// Change the TeamDisplay a player sees like this:\nteam.display(player, newTeamDisplay);\n\n// Don't forget to call teamManager.close() once you don't need it anymore!\n```\n\n### ObjectiveManager\n\n```java\nObjectiveManager objectiveManager = scoreboardLibrary.createObjectiveManager();\nScoreboardObjective objective = objectiveManager.create(\"coolobjective\");\nobjective.value(Component.text(\"Display name\"));\nobjective.score(player.getName(), 69420);\nobjectiveManager.display(ObjectiveDisplaySlot.belowName(), objective);\n\nobjectiveManager.addPlayer(player); // Make a player see the objectives\n\n// Don't forget to call objectiveManager.close() once you don't need it anymore!\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmegavexnetwork%2Fscoreboard-library","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmegavexnetwork%2Fscoreboard-library","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmegavexnetwork%2Fscoreboard-library/lists"}