Skip to content

Instantly share code, notes, and snippets.

@nathanfranke
Last active March 1, 2022 14:32
Show Gist options
  • Save nathanfranke/1ddd81ad8913d33b8b7c46f931ee5250 to your computer and use it in GitHub Desktop.
Save nathanfranke/1ddd81ad8913d33b8b7c46f931ee5250 to your computer and use it in GitHub Desktop.
All Zeros Sidebar in Paper 1.18.2 (1.8 Compatible)
package me.nathanfranke.sidebarplugin
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
import org.bukkit.ChatColor
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scoreboard.DisplaySlot
import org.bukkit.scoreboard.Objective
private val objectiveEntries = mutableMapOf<Objective, Set<String>>()
fun Player.updateSidebar(title: Component, items: List<Component>) {
// Create the sidebar objective if it does not exist.
val objective = scoreboard.getObjective(DisplaySlot.SIDEBAR) ?: run {
scoreboard.registerNewObjective("@sidebar", "dummy", null).apply {
displaySlot = DisplaySlot.SIDEBAR
}
}
objective.displayName(title)
// Prefix all scoreboard prefixes with this text to ensure they are ordered properly.
val orderedLetters = ('a'..'z').toList()
fun getOrderedPrefix(index: Int) = "\u00A7${orderedLetters[index]}\u00A7r"
// Keep track of added objective scores, so they can be removed later.
val entries = mutableSetOf<String>()
for ((i, item) in items.withIndex()) {
// Legacy players (pre-1.13) can only have 42 characters per line (14 prefix, 12 entry, 16 suffix).
if (protocolVersion < 393) {
val prefixLength = 14 // Maximum of 16, with 2 characters reserved for the team color (used internally).
val entryLength = 12 // Maximum of 16, with 4 characters reserved for the ordering prefix.
val suffixLength = 16 // Maximum of 16.
// Convert item text into legacy text.
var text = LegacyComponentSerializer.legacySection().serialize(item)
// Separate as much text as possible into the prefix.
val (prefix, prefixRemaining) = run {
for (n in Integer.min(text.length, prefixLength) downTo 0) {
val prefix = text.substring(0, n)
if (prefix.endsWith(ChatColor.COLOR_CHAR)) {
continue
}
val lastColors = ChatColor.getLastColors(prefix)
val remaining = text.substring(n)
return@run Pair(prefix, lastColors + remaining)
}
throw IllegalStateException()
}
text = prefixRemaining
// Separate as much text as possible into the suffix.
val (suffix, suffixRemaining) = run {
for (n in Integer.min(text.length, suffixLength) downTo 0) {
val suffix = text.substring(text.length - n)
val remaining = text.substring(0, text.length - n)
if (remaining.endsWith(ChatColor.COLOR_CHAR) && !suffix.startsWith(ChatColor.COLOR_CHAR)) {
continue
}
val lastColors = ChatColor.getLastColors(remaining)
if (lastColors.length + suffix.length > suffixLength) {
continue
}
return@run Pair(lastColors + suffix, remaining)
}
throw IllegalStateException()
}
text = suffixRemaining
// Ensure remaining text (entry) is not too long.
if (text.length > entryLength) {
text = text.substring(0, entryLength)
}
// Prepend the entry with the ordering prefix.
val entry = getOrderedPrefix(i) + text
// Create the entry team if it does not exist.
val team = scoreboard.getTeam("@sidebar$i") ?: run {
scoreboard.registerNewTeam("@sidebar$i")
}
team.addEntry(entry)
team.prefix(LegacyComponentSerializer.legacySection().deserialize(prefix))
team.suffix(LegacyComponentSerializer.legacySection().deserialize(suffix))
objective.getScore(entry).score = 0
entries.add(entry)
} else {
val entry = getOrderedPrefix(i)
val team = scoreboard.getTeam("@sidebar$i") ?: run {
scoreboard.registerNewTeam("@sidebar$i")
}
team.addEntry(entry)
team.prefix(item)
objective.getScore(entry).score = 0
entries.add(entry)
}
}
// Remove obsolete entries.
for (entry in objectiveEntries[objective] ?: setOf()) {
if (entry !in entries) {
objective.getScore(entry).resetScore()
}
}
objectiveEntries[objective] = entries
}
@Suppress("UNUSED")
class SidebarPlugin : JavaPlugin(), Listener {
override fun onEnable() {
server.scheduler.scheduleSyncRepeatingTask(
this,
{
for (p in server.onlinePlayers) {
val lines = (Math.random() * 10).toInt() + 1
p.updateSidebar(
Component.text("Lines: ").append(Component.text(lines)),
(1..lines).map { i ->
Component.text("Line #").append(Component.text(i)).color(NamedTextColor.NAMES.values().random())
},
)
}
},
0, 20,
)
server.pluginManager.registerEvents(this, this)
}
@EventHandler
fun onPlayerJoin(event: PlayerJoinEvent) {
event.player.scoreboard = server.scoreboardManager.newScoreboard
}
}
@nathanfranke
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment