Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c2e10c103 | ||
|
|
aeb29a7dc4
|
||
|
|
e7449ad436
|
||
|
|
fa87728993
|
||
|
|
0f9cd07dab
|
||
|
|
c0b7ec3dfa | ||
|
|
08f2b85274 | ||
|
|
da395ca2e0 | ||
|
|
1a61dbc537 | ||
|
|
aeed0fbbd5 | ||
|
|
ab01e7071f | ||
|
|
cada1a81d6 | ||
|
|
0835cb0d9e | ||
|
|
1caa0f12f5 | ||
|
|
e859f25729 | ||
|
|
146770ccbc | ||
|
|
d1de3a950e | ||
|
|
52ebddcad5 | ||
|
|
eec897db46 | ||
|
|
7719a3e9c0 | ||
|
|
5ec8c231d4 | ||
|
|
eb5a600b4d | ||
|
|
ae262ae9ff | ||
|
|
f74a5160a5 | ||
|
|
06d6f6f504 | ||
|
|
d7a9ee7033 | ||
|
|
ad218d0c7b | ||
|
|
277035f994 | ||
|
|
22378436cb | ||
|
|
1f25bd8fe3 | ||
|
|
8028bda73c | ||
|
|
a54efe2c06 | ||
|
|
8cc7b17842 | ||
|
|
8eb806f091 | ||
|
|
29c114e4b6 | ||
|
|
04945bedbb | ||
|
|
8ca62066b9 | ||
|
|
1f3d04eb8c | ||
|
|
819daf396f | ||
|
|
fe56b43f32 | ||
|
|
0c5e6ebc3f | ||
|
|
69789ee7ba | ||
|
|
3e27812b4a | ||
|
|
2ae797371a | ||
|
|
972d58f5c8 |
No files matched your search
@@ -0,0 +1 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
+1
-7
@@ -1,9 +1,3 @@
|
||||
[submodule "libs/Utilities-OG"]
|
||||
path = libs/Utilities-OG
|
||||
url = https://github.com/true-og/Utilities-OG
|
||||
[submodule "libs/DiamondBank-OG"]
|
||||
path = libs/DiamondBank-OG
|
||||
url = https://github.com/true-og/DiamondBank-OG
|
||||
[submodule "libs/Chat-OG"]
|
||||
path = libs/Chat-OG
|
||||
url = https://github.com/true-og/Chat-OG
|
||||
url = https://git.trueog.net/trueog/Utilities-OG
|
||||
@@ -0,0 +1,42 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Scoreboard-OG are documented here.
|
||||
|
||||
## 1.2.0 - 2026-09-12
|
||||
|
||||
### Changes
|
||||
|
||||
- Add a sidebar API for other plugins: `ScoreboardOGAPI.claim`, `release`,
|
||||
`releaseAll` and `getOwner`, with a `SidebarProvider` supplying a title and up
|
||||
to 15 lines. A claim replaces the network board with the provider's content,
|
||||
survives `/togglescoreboard` and world changes, and hands the sidebar back on
|
||||
release, quit, or when the provider throws.
|
||||
|
||||
## 1.1.0 - 2026-07-29
|
||||
|
||||
### Changes
|
||||
|
||||
- Restore the network sidebar after players return from minigames by refreshing
|
||||
it one tick after world transitions when no Bukkit scoreboard remains active.
|
||||
- Add `/togglescoreboard` with persistent per-player visibility preferences.
|
||||
- Add a dedicated aqua diamond-balance line.
|
||||
- Correct the SimpleClans union, BattleTracker PvP kill, and BattleTracker PvP
|
||||
death placeholders, and migrate DiamondBank balances to MiniPlaceholder
|
||||
expansion.
|
||||
- Upgrade the packet-based sidebar implementation to scoreboard-library 2.6.0
|
||||
and preserve its runtime adapters in shaded builds.
|
||||
- Update the DiamondBank-OG and Utilities-OG integrations and remove the unused
|
||||
Chat-OG dependency.
|
||||
|
||||
## 1.0.0 - 2025-12-11
|
||||
|
||||
### Changes
|
||||
|
||||
- Release a player-specific TrueOG Network sidebar for the Purpur 1.19.4
|
||||
server target.
|
||||
- Display each player's LuckPerms rank and colored name, SimpleClans union,
|
||||
diamond balance, kills, deaths, and the network address.
|
||||
- Refresh dynamic sidebar values every ten ticks and manage boards across
|
||||
player joins, quits, plugin reloads, and shutdowns.
|
||||
- Package the packet-backed scoreboard implementation and its required runtime
|
||||
adapter in the shaded plugin JAR.
|
||||
@@ -1,6 +1,8 @@
|
||||
# Scoreboard-OG
|
||||
|
||||
TrueOG Network's custom Scoreboard plugin supporting dynamic MiniPlaceholders. Based on the [ScoreboardLib](https://github.com/TigerHix/ScoreboardLib) API by TigerHix.
|
||||
TrueOG Network's custom Scoreboard plugin supporting dynamic MiniPlaceholders. Based on [scoreboard-library by MegavexNetwork](https://github.com/MegavexNetwork/scoreboard-library).
|
||||
|
||||
Current version: `1.2.0` ([changelog](CHANGELOG.md)).
|
||||
|
||||
Gradle Version: 8.14.3
|
||||
|
||||
@@ -10,4 +12,26 @@ Minecraft API: 1.19
|
||||
|
||||
API Jar: 1.19.4 Purpur
|
||||
|
||||
Scoreboard-OG is public domain software released under the terms of [The Unlicense](https://github.com/true-og/Template-OG/blob/main/LICENSE).
|
||||
## API for other plugins
|
||||
|
||||
A minigame can take over a player's sidebar for the length of a match and hand it back afterwards. Compile against the jar, soft-depend on `Scoreboard-OG`, and only touch the API after checking the plugin is enabled.
|
||||
|
||||
```java
|
||||
import plugin.api.ScoreboardOGAPI;
|
||||
import plugin.api.SidebarProvider;
|
||||
|
||||
ScoreboardOGAPI.claim(player, "MyGame-OG", new SidebarProvider() {
|
||||
public Component title(Player player) { return Component.text("My Game"); }
|
||||
public List<Component> lines(Player player) { return List.of(Component.text("Players: 4")); }
|
||||
});
|
||||
// later
|
||||
ScoreboardOGAPI.release(player, "MyGame-OG");
|
||||
// on disable
|
||||
ScoreboardOGAPI.releaseAll("MyGame-OG");
|
||||
```
|
||||
|
||||
`claim` replaces the network board with the provider's content, polled on the main thread every ten ticks; up to 15 lines, fewer lines shrink the board. `release` hands the sidebar back only when the named owner still holds it, and the network board returns on its own. A player who hid the sidebar with `/togglescoreboard` sees neither board. A provider that throws loses its claim.
|
||||
|
||||
TheHerobrine-OG, BuildBattle-OG and Splegg-OG use this API and fall back to their own scoreboards when Scoreboard-OG is absent.
|
||||
|
||||
Scoreboard-OG is public domain software released under the terms of [The Unlicense](https://git.trueog.net/trueog/Template-OG/src/branch/main/LICENSE).
|
||||
+21
-7
@@ -30,10 +30,12 @@ kotlin { jvmToolchain(17) }
|
||||
/* ----------------------------- Metadata ------------------------------ */
|
||||
group = "net.trueog.scoreboard-og" // Declare bundle identifier.
|
||||
|
||||
version = "1.0" // Declare plugin version (will be in .jar).
|
||||
version = "1.2.0" // Declare plugin version (will be in .jar).
|
||||
|
||||
val apiVersion = "1.19" // Declare minecraft server target version.
|
||||
|
||||
val scoreboardLibraryVersion = "2.6.0" // Declare vytskalt/scoreboard-library version.
|
||||
|
||||
/* ----------------------------- Resources ----------------------------- */
|
||||
tasks.named<ProcessResources>("processResources") {
|
||||
val props = mapOf("version" to version, "apiVersion" to apiVersion)
|
||||
@@ -47,7 +49,9 @@ repositories {
|
||||
mavenCentral() // Import the Maven Central Maven Repository.
|
||||
gradlePluginPortal() // Import the Gradle Plugin Portal Maven Repository.
|
||||
maven { url = uri("https://repo.purpurmc.org/snapshots") } // Import the PurpurMC Maven Repository.
|
||||
maven { url = uri("https://repo.hotten.cloud/snapshots") } // Import the ScoreboardLib library.
|
||||
maven { url = uri("https://repo.codemc.io/repository/maven-releases/") }
|
||||
maven { url = uri("https://repo.codemc.io/repository/maven-snapshots/") }
|
||||
maven { url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") }
|
||||
maven { url = uri("file://${System.getProperty("user.home")}/.m2/repository") }
|
||||
System.getProperty("SELF_MAVEN_LOCAL_REPO")?.let { // TrueOG Bootstrap mavenLocal().
|
||||
val dir = file(it)
|
||||
@@ -65,12 +69,16 @@ repositories {
|
||||
dependencies {
|
||||
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") // Declare Purpur API version to be packaged.
|
||||
compileOnly("io.github.miniplaceholders:miniplaceholders-api:2.2.3") // Import MiniPlaceholders API.
|
||||
compileOnly("me.clip:placeholderapi:2.11.6") // Import PlaceholderAPI for direct sidebar expansion.
|
||||
compileOnly("net.luckperms:api:5.5") // Import LuckPerms API.
|
||||
compileOnlyApi(project(":libs:Utilities-OG")) // Import TrueOG Network Utilities-OG Java API (from source).
|
||||
compileOnlyApi(project(":libs:DiamondBank-OG")) {
|
||||
attributes { attribute(kotlinAttribute, true) }
|
||||
} // Import TrueOG network DiamondBank-OG Kotlin API (from source).
|
||||
implementation("me.tigerhix.lib:scoreboard:1.0.1-SNAPSHOT")
|
||||
implementation("net.megavex:scoreboard-library-api:$scoreboardLibraryVersion") // Import scoreboard library API.
|
||||
runtimeOnly(
|
||||
"net.megavex:scoreboard-library-implementation:$scoreboardLibraryVersion"
|
||||
) // Import scoreboard library implementation.
|
||||
implementation(
|
||||
"net.megavex:scoreboard-library-extra-kotlin:$scoreboardLibraryVersion"
|
||||
) // Kotlin specific extensions.
|
||||
}
|
||||
|
||||
apply(from = "eclipse.gradle.kts") // Import eclipse classpath support script.
|
||||
@@ -85,7 +93,13 @@ tasks.withType<AbstractArchiveTask>().configureEach { // Ensure reproducible .ja
|
||||
tasks.shadowJar {
|
||||
exclude("io.github.miniplaceholders.*") // Exclude the MiniPlaceholders package from being shadowed.
|
||||
archiveClassifier.set("") // Use empty string instead of null.
|
||||
minimize()
|
||||
isEnableRelocation = true
|
||||
relocationPrefix = "${project.group}.shadow"
|
||||
minimize {
|
||||
exclude(dependency("net.megavex:scoreboard-library-api:.*"))
|
||||
exclude(dependency("net.megavex:scoreboard-library-extra-kotlin:.*"))
|
||||
exclude(dependency("net.megavex:scoreboard-library-implementation:.*"))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.jar { archiveClassifier.set("part") } // Applies to root jarfile only.
|
||||
|
||||
-1
Submodule libs/Chat-OG deleted from ca8cbae719.
Submodule libs/DiamondBank-OG deleted from 7db3767249.
+1
-1
Submodule libs/Utilities-OG updated: f30335fe62...8b5c86cc9c.
@@ -2,11 +2,15 @@
|
||||
// Author: NotAlexNoyle.
|
||||
package plugin;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.scoreboard.ScoreboardManager;
|
||||
|
||||
public class Listeners implements Listener {
|
||||
|
||||
@@ -21,6 +25,33 @@ public class Listeners implements Listener {
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
|
||||
ScoreboardOG.getInstance().closeBoard(event.getPlayer());
|
||||
ScoreboardOG.getInstance().forgetClaims(event.getPlayer());
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
|
||||
|
||||
final ScoreboardOG plugin = ScoreboardOG.getInstance();
|
||||
final Player player = event.getPlayer();
|
||||
|
||||
// Minigames can replace the sidebar with a Bukkit scoreboard. Wait until
|
||||
// every world-change listener has finished, then reclaim the sidebar only
|
||||
// when no plugin still owns the player's Bukkit scoreboard.
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
|
||||
final ScoreboardManager scoreboardManager = Bukkit.getScoreboardManager();
|
||||
if (!player.isOnline() || scoreboardManager == null
|
||||
|| player.getScoreboard() != scoreboardManager.getMainScoreboard())
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
plugin.openBoard(player);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Author: NotAlexNoyle.
|
||||
package plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -9,28 +11,47 @@ import java.util.UUID;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.tigerhix.lib.scoreboard.ScoreboardLib;
|
||||
import me.tigerhix.lib.scoreboard.common.EntryBuilder;
|
||||
import me.tigerhix.lib.scoreboard.type.Entry;
|
||||
import me.tigerhix.lib.scoreboard.type.Scoreboard;
|
||||
import me.tigerhix.lib.scoreboard.type.ScoreboardHandler;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.cacheddata.CachedMetaData;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import net.trueog.utilitiesog.UtilitiesOG;
|
||||
import plugin.api.SidebarProvider;
|
||||
|
||||
public class ScoreboardOG extends JavaPlugin {
|
||||
|
||||
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacyAmpersand();
|
||||
private static final Component EMPTY_LINE = Component.empty();
|
||||
private static final Component SIDEBAR_TITLE = legacyText("&4♥ &a&lTrue&c&lOG&r&e Network &4♥");
|
||||
private static final Component FOOTER_LINE = legacyText("&etrue-og.net");
|
||||
private static final int NETWORK_LINE_COUNT = 14;
|
||||
|
||||
private static ScoreboardOG instance;
|
||||
|
||||
private FileConfiguration config;
|
||||
private final Map<UUID, Scoreboard> boards = new HashMap<>();
|
||||
private final Map<UUID, PlayerSidebar> sidebars = new HashMap<>();
|
||||
// Sidebars other plugins currently own, kept apart from the boards so a toggle
|
||||
// or a world change never drops a running match's content.
|
||||
private final Map<UUID, SidebarClaim> claims = new HashMap<>();
|
||||
|
||||
private record SidebarClaim(String owner, SidebarProvider provider) {
|
||||
|
||||
}
|
||||
|
||||
private LuckPerms luckPerms;
|
||||
private ScoreboardLibrary scoreboardLibrary;
|
||||
private int updateTaskId = -1;
|
||||
|
||||
private File scoreboardPreferencesFile;
|
||||
private YamlConfiguration scoreboardPreferences;
|
||||
|
||||
public static ScoreboardOG getInstance() {
|
||||
|
||||
@@ -50,7 +71,35 @@ public class ScoreboardOG extends JavaPlugin {
|
||||
|
||||
saveDefaultConfig();
|
||||
this.config = getConfig();
|
||||
ScoreboardLib.setPluginInstance(this);
|
||||
|
||||
this.scoreboardPreferencesFile = new File(getDataFolder(), "scoreboardPreferences.yml");
|
||||
if (!scoreboardPreferencesFile.exists()) {
|
||||
|
||||
try {
|
||||
|
||||
getDataFolder().mkdirs();
|
||||
scoreboardPreferencesFile.createNewFile();
|
||||
|
||||
} catch (IOException error) {
|
||||
|
||||
throw new RuntimeException(error);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.scoreboardPreferences = YamlConfiguration.loadConfiguration(scoreboardPreferencesFile);
|
||||
|
||||
try {
|
||||
|
||||
this.scoreboardLibrary = ScoreboardLibrary.loadScoreboardLibrary(this);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
this.scoreboardLibrary = null;
|
||||
getLogger().warning("No scoreboard packet adapter available, disabling scoreboards.");
|
||||
|
||||
}
|
||||
|
||||
// Hook LuckPerms API.
|
||||
final RegisteredServiceProvider<LuckPerms> provider = Bukkit.getServicesManager()
|
||||
@@ -66,15 +115,43 @@ public class ScoreboardOG extends JavaPlugin {
|
||||
}
|
||||
|
||||
getServer().getPluginManager().registerEvents(new Listeners(), this);
|
||||
getCommand("togglescoreboard").setExecutor(new ToggleScoreboardCommand());
|
||||
Bukkit.getOnlinePlayers().forEach(this::openBoard);
|
||||
|
||||
this.updateTaskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(this,
|
||||
() -> Bukkit.getOnlinePlayers().forEach((Player online) ->
|
||||
{
|
||||
|
||||
final PlayerSidebar sidebar = sidebars.get(online.getUniqueId());
|
||||
if (sidebar != null) {
|
||||
|
||||
sidebar.tick();
|
||||
|
||||
}
|
||||
|
||||
}), 0L, 10L);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
boards.values().forEach(Scoreboard::deactivate);
|
||||
boards.clear();
|
||||
if (updateTaskId != -1) {
|
||||
|
||||
Bukkit.getScheduler().cancelTask(updateTaskId);
|
||||
updateTaskId = -1;
|
||||
|
||||
}
|
||||
|
||||
sidebars.values().forEach(PlayerSidebar::close);
|
||||
sidebars.clear();
|
||||
claims.clear();
|
||||
|
||||
if (scoreboardLibrary != null) {
|
||||
|
||||
scoreboardLibrary.close();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,57 +161,193 @@ public class ScoreboardOG extends JavaPlugin {
|
||||
|
||||
}
|
||||
|
||||
public boolean isSidebarReady() {
|
||||
|
||||
return scoreboardLibrary != null;
|
||||
|
||||
}
|
||||
|
||||
public void claimSidebar(Player player, String owner, SidebarProvider provider) {
|
||||
|
||||
claims.put(player.getUniqueId(), new SidebarClaim(owner, provider));
|
||||
final PlayerSidebar sidebar = sidebars.get(player.getUniqueId());
|
||||
if (sidebar != null) {
|
||||
|
||||
sidebar.tick();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean releaseSidebar(Player player, String owner) {
|
||||
|
||||
final SidebarClaim claim = claims.get(player.getUniqueId());
|
||||
if (claim == null || !claim.owner().equals(owner)) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
claims.remove(player.getUniqueId());
|
||||
final PlayerSidebar sidebar = sidebars.get(player.getUniqueId());
|
||||
if (sidebar != null) {
|
||||
|
||||
sidebar.tick();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public void releaseAllSidebars(String owner) {
|
||||
|
||||
for (Player online : Bukkit.getOnlinePlayers()) {
|
||||
|
||||
releaseSidebar(online, owner);
|
||||
|
||||
}
|
||||
|
||||
claims.values().removeIf(claim -> claim.owner().equals(owner));
|
||||
|
||||
}
|
||||
|
||||
public String getSidebarOwner(Player player) {
|
||||
|
||||
final SidebarClaim claim = claims.get(player.getUniqueId());
|
||||
return claim == null ? null : claim.owner();
|
||||
|
||||
}
|
||||
|
||||
public void forgetClaims(Player player) {
|
||||
|
||||
claims.remove(player.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
public YamlConfiguration getScoreboardPreferences() {
|
||||
|
||||
return scoreboardPreferences;
|
||||
|
||||
}
|
||||
|
||||
public File getScoreboardPreferencesFile() {
|
||||
|
||||
return scoreboardPreferencesFile;
|
||||
|
||||
}
|
||||
|
||||
public boolean isScoreboardHidden(Player player) {
|
||||
|
||||
return scoreboardPreferences.getBoolean(player.getUniqueId().toString());
|
||||
|
||||
}
|
||||
|
||||
public void openBoard(Player player) {
|
||||
|
||||
final Scoreboard board = ScoreboardLib.createScoreboard(player).setHandler(new ScoreboardHandler() {
|
||||
if (scoreboardLibrary == null) {
|
||||
|
||||
@Override
|
||||
public String getTitle(Player p) {
|
||||
return;
|
||||
|
||||
return "&4♥ &a&lTrue&c&lOG&r&e Network &4♥";
|
||||
}
|
||||
|
||||
}
|
||||
if (isScoreboardHidden(player)) {
|
||||
|
||||
@Override
|
||||
public java.util.List<Entry> getEntries(Player p) {
|
||||
return;
|
||||
|
||||
// Fetch LuckPerms prefix with formatting.
|
||||
String lpPrefix = getLuckPermsPrefixLegacy(p);
|
||||
lpPrefix = stripLeadingReset(stripTrailingReset(lpPrefix));
|
||||
}
|
||||
|
||||
// Bleed formatting from prefix into name.
|
||||
final String youTail = joinSpace(lpPrefix, p.getName());
|
||||
final String youLine = "&aYou:&r " + youTail + "&r";
|
||||
closeBoard(player);
|
||||
|
||||
// Independently formatted Union tag.
|
||||
final String unionTag = expandMM(p, "<placeholderapi_player:%simpleclans_clan_color_tag%>");
|
||||
final String unionLine = "&cUnion: &r" + unionTag + "&r";
|
||||
|
||||
return new EntryBuilder().next("&c&m----&6&m----&e&m----&2&m----&9&m----&5&m----").next(youLine).blank()
|
||||
.next("&bDiamonds: " + expandMM(p, "<diamondbankog_balance>")).blank().next(unionLine).blank()
|
||||
.next("&2Kills: " + expandMM(p, "<placeholderapi_player:%statistic_player_kills%>")).blank()
|
||||
.next("&4Deaths: " + expandMM(p, "<placeholderapi_player:%statistic_deaths%>")).blank()
|
||||
.next("&4&m--&0&m--&4> &etrue-og.net &4<&0&m--&4&m--").build();
|
||||
|
||||
}
|
||||
|
||||
}).setUpdateInterval(10L);
|
||||
board.activate();
|
||||
boards.put(player.getUniqueId(), board);
|
||||
final PlayerSidebar sidebar = new PlayerSidebar(player);
|
||||
sidebars.put(player.getUniqueId(), sidebar);
|
||||
|
||||
}
|
||||
|
||||
public void closeBoard(Player player) {
|
||||
|
||||
final Scoreboard board = boards.remove(player.getUniqueId());
|
||||
if (board != null) {
|
||||
final PlayerSidebar sidebar = sidebars.remove(player.getUniqueId());
|
||||
if (sidebar != null) {
|
||||
|
||||
board.deactivate();
|
||||
sidebar.close();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Component createRankLine(Player p) {
|
||||
|
||||
String lpPrefix = getLuckPermsPrefixLegacy(p);
|
||||
lpPrefix = stripLeadingReset(stripTrailingReset(lpPrefix));
|
||||
|
||||
return legacyText(lpPrefix);
|
||||
|
||||
}
|
||||
|
||||
private Component createYouLine(Player p) {
|
||||
|
||||
String lpPrefix = getLuckPermsPrefixLegacy(p);
|
||||
lpPrefix = stripLeadingReset(stripTrailingReset(lpPrefix));
|
||||
|
||||
final String colorCodes = extractLeadingColorCodes(lpPrefix);
|
||||
if (StringUtils.isEmpty(colorCodes)) {
|
||||
|
||||
return legacyText(p.getName());
|
||||
|
||||
}
|
||||
|
||||
return legacyText(colorCodes + p.getName());
|
||||
|
||||
}
|
||||
|
||||
private Component createUnionLine(Player p) {
|
||||
|
||||
final Component label = legacyText("&cUnion: &r");
|
||||
final Component unionTag = expandText(p, "<placeholderapi_player:%simpleclans_union_color_tag%>");
|
||||
return label.append(unionTag);
|
||||
|
||||
}
|
||||
|
||||
private Component createDiamondsLabelLine() {
|
||||
|
||||
return legacyText("&bDiamonds:");
|
||||
|
||||
}
|
||||
|
||||
private Component createDiamondsValueLine(Player p) {
|
||||
|
||||
return expandText(p, "<color:aqua><diamondbankog_balance>");
|
||||
|
||||
}
|
||||
|
||||
private Component createKillsLine(Player p) {
|
||||
|
||||
final Component label = legacyText("&2Kills: &r");
|
||||
final Component value = expandText(p, "<placeholderapi_player:%bt_pvp_kills%>");
|
||||
return label.append(value);
|
||||
|
||||
}
|
||||
|
||||
private Component createDeathsLine(Player p) {
|
||||
|
||||
final Component label = legacyText("&4Deaths: &r");
|
||||
final Component value = expandText(p, "<placeholderapi_player:%bt_pvp_deaths%>");
|
||||
return label.append(value);
|
||||
|
||||
}
|
||||
|
||||
private static Component legacyText(String input) {
|
||||
|
||||
if (input == null || StringUtils.isEmpty(input)) {
|
||||
|
||||
return Component.empty();
|
||||
|
||||
}
|
||||
|
||||
return LEGACY_SERIALIZER.deserialize(input);
|
||||
|
||||
}
|
||||
|
||||
// Resolve the player's LuckPerms prefix as legacy color codes with any
|
||||
// serializing.
|
||||
private String getLuckPermsPrefixLegacy(Player p) {
|
||||
@@ -169,11 +382,20 @@ public class ScoreboardOG extends JavaPlugin {
|
||||
|
||||
}
|
||||
|
||||
/// Return expanded text or null depending on if the String is populated.
|
||||
private static String expandMM(Player p, String miniMsg) {
|
||||
// Return expanded text or null depending on if the String is populated.
|
||||
private static Component expandText(Player p, String text) {
|
||||
|
||||
final String out = UtilitiesOG.trueogExpand(miniMsg, p).content();
|
||||
return out == null ? "" : out;
|
||||
if (text == null || StringUtils.isEmpty(text)) {
|
||||
|
||||
return Component.empty();
|
||||
|
||||
}
|
||||
|
||||
// Expand MiniPlaceholders and format the message.
|
||||
final Component out = UtilitiesOG.trueogExpand(text, p);
|
||||
|
||||
// Pass on the message.
|
||||
return out == null ? Component.empty() : out;
|
||||
|
||||
}
|
||||
|
||||
@@ -203,39 +425,141 @@ public class ScoreboardOG extends JavaPlugin {
|
||||
|
||||
}
|
||||
|
||||
// Ensure the prefix and name are spaced out.
|
||||
private static String joinSpace(String... parts) {
|
||||
private final class PlayerSidebar {
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (String part : parts) {
|
||||
private final Player player;
|
||||
private final Sidebar sidebar;
|
||||
private int renderedLines = 0;
|
||||
|
||||
if (part == null) {
|
||||
private PlayerSidebar(Player player) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
final String t = StringUtils.trim(part);
|
||||
if (StringUtils.isEmpty(t)) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
|
||||
sb.append(' ');
|
||||
|
||||
}
|
||||
|
||||
sb.append(t);
|
||||
first = false;
|
||||
this.player = player;
|
||||
this.sidebar = scoreboardLibrary.createSidebar();
|
||||
this.sidebar.addPlayer(player);
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
private void tick() {
|
||||
|
||||
final SidebarClaim claim = claims.get(player.getUniqueId());
|
||||
if (claim != null) {
|
||||
|
||||
tickClaim(claim);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
sidebar.title(SIDEBAR_TITLE);
|
||||
shrinkTo(NETWORK_LINE_COUNT);
|
||||
|
||||
setLine(0, EMPTY_LINE);
|
||||
setLine(1, createRankLine(player));
|
||||
setLine(2, createYouLine(player));
|
||||
setLine(3, EMPTY_LINE);
|
||||
setLine(4, createDiamondsLabelLine());
|
||||
setLine(5, createDiamondsValueLine(player));
|
||||
setLine(6, EMPTY_LINE);
|
||||
setLine(7, createUnionLine(player));
|
||||
setLine(8, EMPTY_LINE);
|
||||
setLine(9, createKillsLine(player));
|
||||
setLine(10, EMPTY_LINE);
|
||||
setLine(11, createDeathsLine(player));
|
||||
setLine(12, EMPTY_LINE);
|
||||
setLine(13, FOOTER_LINE);
|
||||
renderedLines = NETWORK_LINE_COUNT;
|
||||
|
||||
}
|
||||
|
||||
// A provider that throws loses its claim, so one bad plugin cannot blank the
|
||||
// board every ten ticks.
|
||||
private void tickClaim(SidebarClaim claim) {
|
||||
|
||||
final Component title;
|
||||
final java.util.List<Component> lines;
|
||||
try {
|
||||
|
||||
title = claim.provider().title(player);
|
||||
lines = claim.provider().lines(player);
|
||||
|
||||
} catch (Throwable error) {
|
||||
|
||||
getLogger().warning("Sidebar provider from " + claim.owner() + " failed for " + player.getName()
|
||||
+ ", handing the sidebar back: " + error.getMessage());
|
||||
claims.remove(player.getUniqueId());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
sidebar.title(title == null ? Component.empty() : title);
|
||||
|
||||
final int count = lines == null ? 0 : Math.min(lines.size(), sidebar.maxLines());
|
||||
shrinkTo(count);
|
||||
for (int index = 0; index < count; index++) {
|
||||
|
||||
final Component line = lines.get(index);
|
||||
setLine(index, line == null ? EMPTY_LINE : line);
|
||||
|
||||
}
|
||||
|
||||
renderedLines = count;
|
||||
|
||||
}
|
||||
|
||||
// The library keeps lines until they are cleared, so a shorter board has to
|
||||
// drop the surplus before it is redrawn.
|
||||
private void shrinkTo(int count) {
|
||||
|
||||
if (count < renderedLines) {
|
||||
|
||||
sidebar.clearLines();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void close() {
|
||||
|
||||
sidebar.close();
|
||||
|
||||
}
|
||||
|
||||
private void setLine(int index, Component component) {
|
||||
|
||||
sidebar.line(index, component);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
private String extractLeadingColorCodes(String input) {
|
||||
|
||||
if (input == null || input.length() < 2) {
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
final StringBuilder out = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < input.length() - 1; i++) {
|
||||
|
||||
final char c = input.charAt(i);
|
||||
if (c != '&' && c != '§') {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
final char code = input.charAt(i + 1);
|
||||
out.append(c).append(code);
|
||||
|
||||
// Skip the code character.
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// Author: NotAlexNoyle.
|
||||
package plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import net.trueog.utilitiesog.UtilitiesOG;
|
||||
import net.trueog.utilitiesog.utils.TextUtils;
|
||||
|
||||
public class ToggleScoreboardCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
|
||||
final String toggleScoreboardPermission = "scoreboard.toggle";
|
||||
|
||||
if (sender instanceof Player player) {
|
||||
|
||||
if (player.hasPermission(toggleScoreboardPermission)) {
|
||||
|
||||
final ScoreboardOG plugin = ScoreboardOG.getInstance();
|
||||
final YamlConfiguration preferences = plugin.getScoreboardPreferences();
|
||||
|
||||
final boolean currentlyHidden = preferences.getBoolean(player.getUniqueId().toString());
|
||||
|
||||
preferences.set(player.getUniqueId().toString(), !currentlyHidden);
|
||||
|
||||
if (currentlyHidden) {
|
||||
|
||||
plugin.openBoard(player);
|
||||
|
||||
UtilitiesOG.trueogMessage(player,
|
||||
"<#AAAAAA>[<#00AA00>Scoreboard<#AA0000>-OG<#AAAAAA>] <#55FF55>Scoreboard turned <#00AA00>ON<#55FF55>.");
|
||||
|
||||
} else {
|
||||
|
||||
plugin.closeBoard(player);
|
||||
|
||||
UtilitiesOG.trueogMessage(player,
|
||||
"<#AAAAAA>[<#00AA00>Scoreboard<#AA0000>-OG<#AAAAAA>] <#FFAA00>Scoreboard turned <#FF5555>OFF<#FFAA00>.");
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
preferences.save(plugin.getScoreboardPreferencesFile());
|
||||
|
||||
} catch (IOException error) {
|
||||
|
||||
throw new RuntimeException(error);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
TextUtils.permissionsErrorMessage(player, cmd.getName(), toggleScoreboardPermission);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
TextUtils.logToConsole(cmd.getName() + " " + toggleScoreboardPermission);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// Author: NotAlexNoyle.
|
||||
package plugin.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import plugin.ScoreboardOG;
|
||||
|
||||
// Lets a minigame take over a player's sidebar for the length of a match and hand
|
||||
// it back afterwards. The network board returns on its own once the claim is released.
|
||||
public final class ScoreboardOGAPI {
|
||||
|
||||
private ScoreboardOGAPI() {
|
||||
|
||||
}
|
||||
|
||||
// True once Scoreboard-OG is enabled and its packet sidebar is usable.
|
||||
public static boolean isAvailable() {
|
||||
|
||||
final ScoreboardOG plugin = ScoreboardOG.getInstance();
|
||||
return plugin != null && plugin.isEnabled() && plugin.isSidebarReady();
|
||||
|
||||
}
|
||||
|
||||
// Replaces the network board with the provider's content. A later claim by
|
||||
// any owner wins; the owner name is what release checks.
|
||||
public static void claim(Player player, String owner, SidebarProvider provider) {
|
||||
|
||||
if (!isAvailable() || player == null || owner == null || provider == null)
|
||||
return;
|
||||
|
||||
ScoreboardOG.getInstance().claimSidebar(player, owner, provider);
|
||||
|
||||
}
|
||||
|
||||
// Hands the sidebar back if this owner still holds it. Returns whether it did.
|
||||
public static boolean release(Player player, String owner) {
|
||||
|
||||
if (!isAvailable() || player == null || owner == null)
|
||||
return false;
|
||||
|
||||
return ScoreboardOG.getInstance().releaseSidebar(player, owner);
|
||||
|
||||
}
|
||||
|
||||
// Hands back every sidebar this owner holds; for a plugin's onDisable.
|
||||
public static void releaseAll(String owner) {
|
||||
|
||||
if (!isAvailable() || owner == null)
|
||||
return;
|
||||
|
||||
ScoreboardOG.getInstance().releaseAllSidebars(owner);
|
||||
|
||||
}
|
||||
|
||||
// The owner currently holding the player's sidebar, or null for the network
|
||||
// board.
|
||||
public static String getOwner(Player player) {
|
||||
|
||||
if (!isAvailable() || player == null)
|
||||
return null;
|
||||
|
||||
return ScoreboardOG.getInstance().getSidebarOwner(player);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// Author: NotAlexNoyle.
|
||||
package plugin.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
// Content another plugin wants on a player's sidebar instead of the network board.
|
||||
// Both methods are polled on the main thread every ten ticks while the claim holds.
|
||||
public interface SidebarProvider {
|
||||
|
||||
Component title(Player player);
|
||||
|
||||
// Top to bottom, at most 15 entries. Fewer entries shrink the sidebar.
|
||||
List<Component> lines(Player player);
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Scoreboard-OG
|
||||
author: NotAlexNoyle
|
||||
description: A simple Scoreboard plugin with MiniPlaceholder API support.
|
||||
website: https://github.com/true-og/Scoreboard-OG/
|
||||
website: https://git.trueog.net/trueog/Scoreboard-OG/
|
||||
version: '${version}'
|
||||
api-version: '${apiVersion}'
|
||||
main: plugin.ScoreboardOG
|
||||
@@ -9,5 +9,18 @@ depend:
|
||||
- DiamondBank-OG
|
||||
- Utilities-OG
|
||||
- LuckPerms
|
||||
- ViaVersion
|
||||
- ViaBackwards
|
||||
- ViaRewind
|
||||
softdepend:
|
||||
- PlaceholderAPI
|
||||
- ProtocolLib
|
||||
commands:
|
||||
togglescoreboard:
|
||||
description: Toggle the sidebar scoreboard on or off.
|
||||
usage: /togglescoreboard
|
||||
permission: scoreboard.toggle
|
||||
permissions:
|
||||
scoreboard.toggle:
|
||||
description: Allows a player to toggle their scoreboard visibility.
|
||||
default: true
|
||||
Reference in new issue
Block a user