Skip to content

Commit 97a92a0

Browse files
committed
add scoreboard library integration and implement auto-updatable scoreboards
1 parent 6e1e785 commit 97a92a0

15 files changed

Lines changed: 607 additions & 1 deletion

File tree

gradle/libs.versions.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ commandapi = "11.2.0"
1818
# LuckPerms
1919
luckperms = "v5.5.0-bukkit"
2020

21+
# Scoreboard Library
22+
scoreboard-library = "2.7.1"
23+
scoreboard-library-implementation = "2.7.1"
24+
2125
# Adventure
2226
adventure-api = "4.26.1"
2327

@@ -95,6 +99,10 @@ commandapi-core = { module = "dev.jorel:commandapi-core", version.ref = "command
9599
commandapi-velocity = { module = "dev.jorel:commandapi-velocity-core", version.ref = "commandapi" }
96100
commandapi-velocity-kotlin = { module = "dev.jorel:commandapi-kotlin-velocity", version.ref = "commandapi" }
97101

102+
# Scoreboard Library
103+
scoreboard-library-api = { module = "net.megavex:scoreboard-library-api", version.ref = "scoreboard-library" }
104+
scoreboard-library-implementation = { module = "net.megavex:scoreboard-library-implementation", version.ref = "scoreboard-library-implementation" }
105+
98106
# Adventure
99107
adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure-api" }
100108
adventure-text-logger-slf4j = { module = "net.kyori:adventure-text-logger-slf4j", version.ref = "adventure-api" }

surf-api-paper/surf-api-paper-server/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ dependencies {
4444
paperLibrary(libs.aide.reflection)
4545
api(libs.mccoroutine.folia.api)
4646
api(libs.mccoroutine.folia.core)
47+
runtimeOnly(libs.scoreboard.library.implementation)
48+
paperLibrary(libs.scoreboard.library.api)
4749
}
4850

4951

surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperMain.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.slne.surf.api.paper.server
22

33
import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin
4+
import dev.slne.surf.api.paper.server.impl.scoreboard.SurfScoreboardApiImpl
45
import dev.slne.surf.api.paper.server.libs.LibLoader
56
import org.bukkit.plugin.java.JavaPlugin
67

@@ -12,9 +13,11 @@ class PaperMain : SuspendingJavaPlugin() {
1213

1314
override suspend fun onEnableAsync() {
1415
PaperInstance.onEnable()
16+
SurfScoreboardApiImpl.INSTANCE.onEnable()
1517
}
1618

1719
override suspend fun onDisableAsync() {
20+
SurfScoreboardApiImpl.INSTANCE.onDisable()
1821
PaperInstance.onDisable()
1922
}
2023
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dev.slne.surf.api.paper.server.impl.scoreboard
2+
3+
import dev.slne.surf.api.paper.scoreboard.SurfAutoUpdatablePlayerScoreboard
4+
import dev.slne.surf.api.paper.util.forEachPlayer
5+
import net.kyori.adventure.text.Component
6+
import net.kyori.adventure.text.logger.slf4j.ComponentLogger
7+
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
8+
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
9+
import org.bukkit.entity.Player
10+
11+
class SurfAutoUpdatablePlayerScoreboardImpl(
12+
title: Component,
13+
maxLines: Int,
14+
sidebarComponent: SidebarComponent,
15+
animations: List<SidebarAnimation<Component>>
16+
) : SurfAutoUpdatableScoreboardImpl(
17+
title, maxLines, sidebarComponent, animations
18+
), SurfAutoUpdatablePlayerScoreboard {
19+
override fun addViewer(viewer: Player) {
20+
ComponentLogger.logger()
21+
.warn("You are not allowed to add viewers to this scoreboard. This Scoreboard automatically adds viewers.")
22+
}
23+
24+
override fun removeViewer(viewer: Player) {
25+
ComponentLogger.logger().warn(
26+
"You are not allowed to remove viewers from this scoreboard. This Scoreboard automatically removes viewers."
27+
)
28+
}
29+
30+
override fun update() {
31+
super.update()
32+
val scoreboard = scoreboard ?: return
33+
forEachPlayer { scoreboard.addPlayer(it) }
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package dev.slne.surf.api.paper.server.impl.scoreboard
2+
3+
import com.github.shynixn.mccoroutine.folia.launch
4+
import com.github.shynixn.mccoroutine.folia.ticks
5+
import dev.slne.surf.api.paper.scoreboard.SurfAutoUpdatableScoreboard
6+
import dev.slne.surf.api.paper.server.plugin
7+
import kotlinx.coroutines.Job
8+
import kotlinx.coroutines.delay
9+
import net.kyori.adventure.text.Component
10+
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
11+
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
12+
13+
open class SurfAutoUpdatableScoreboardImpl(
14+
title: Component,
15+
maxLines: Int,
16+
sidebarComponent: SidebarComponent,
17+
animations: List<SidebarAnimation<Component>>
18+
) : SurfScoreboardImpl(title, maxLines, sidebarComponent, animations), SurfAutoUpdatableScoreboard {
19+
private var updater: Job? = null
20+
21+
override fun enable() {
22+
super.enable()
23+
24+
this.updater = launchUpdater()
25+
}
26+
27+
override fun disable() {
28+
super.disable()
29+
30+
updater!!.cancel()
31+
}
32+
33+
private fun launchUpdater() = plugin.launch {
34+
while (true) {
35+
update()
36+
delay(5.ticks)
37+
}
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package dev.slne.surf.api.paper.server.impl.scoreboard
2+
3+
import com.google.auto.service.AutoService
4+
import dev.slne.surf.api.core.util.logger
5+
import dev.slne.surf.api.paper.scoreboard.SurfScoreboardApi
6+
import dev.slne.surf.api.paper.server.plugin
7+
import net.kyori.adventure.text.Component
8+
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary
9+
import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException
10+
import net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary
11+
12+
@AutoService(SurfScoreboardApi::class)
13+
class SurfScoreboardApiImpl : SurfScoreboardApi {
14+
private lateinit var scoreboardLibrary: ScoreboardLibrary
15+
16+
fun onEnable() {
17+
try {
18+
scoreboardLibrary = ScoreboardLibrary.loadScoreboardLibrary(plugin)
19+
} catch (exception: NoPacketAdapterAvailableException) {
20+
log.atSevere().withCause(exception)
21+
.log("No packet adapter available, using NooScoreboardLibrary...")
22+
scoreboardLibrary = NoopScoreboardLibrary()
23+
}
24+
}
25+
26+
fun onDisable() {
27+
scoreboardLibrary.close()
28+
}
29+
30+
override fun scoreboardLibrary(): ScoreboardLibrary {
31+
TODO("Not yet implemented")
32+
}
33+
34+
override fun createScoreboard(title: Component) = SurfScoreboardBuilderImpl(title)
35+
36+
companion object {
37+
private val log = logger()
38+
39+
val INSTANCE get() = SurfScoreboardApi.INSTANCE as SurfScoreboardApiImpl
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package dev.slne.surf.api.paper.server.impl.scoreboard
2+
3+
import dev.slne.surf.api.core.util.mutableObjectListOf
4+
import dev.slne.surf.api.paper.scoreboard.SurfScoreboardBuilder
5+
import net.kyori.adventure.text.Component
6+
import net.kyori.adventure.text.format.TextColor
7+
import net.kyori.adventure.text.minimessage.MiniMessage
8+
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder
9+
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
10+
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation
11+
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
12+
import java.util.function.Supplier
13+
14+
class SurfScoreboardBuilderImpl(private val title: Component) : SurfScoreboardBuilder {
15+
private val sidebarComponentBuilder = SidebarComponent.builder()
16+
private val animations = mutableObjectListOf<SidebarAnimation<Component>>()
17+
private var maxLines = SurfScoreboardBuilder.DEFAULT_MAX_LINES
18+
19+
override fun maxLines(maxLines: Int) = apply {
20+
require(maxLines in 1..15) { "maxLines must be between 1 and 15" }
21+
this.maxLines = maxLines
22+
}
23+
24+
override fun addLine(line: Component) = apply {
25+
sidebarComponentBuilder.addStaticLine(line)
26+
}
27+
28+
override fun addUpdatableLine(line: Supplier<Component>) = apply {
29+
sidebarComponentBuilder.addDynamicLine(line)
30+
}
31+
32+
override fun addAnimatedLine(animation: SidebarAnimation<SidebarComponent>) = apply {
33+
sidebarComponentBuilder.addAnimatedComponent(animation)
34+
}
35+
36+
override fun addAnimatedLine(frames: MutableList<Component>) = apply {
37+
check(frames.isNotEmpty()) { "frames cannot be empty" }
38+
39+
val animation = CollectionSidebarAnimation(frames)
40+
sidebarComponentBuilder.addAnimatedLine(animation)
41+
animations.add(animation)
42+
}
43+
44+
override fun addGradientLine(text: Component, start: TextColor, end: TextColor) = apply {
45+
val gradient = createGradientAnimation(text, start.asHexString(), end.asHexString())
46+
sidebarComponentBuilder.addAnimatedLine(gradient)
47+
animations.add(gradient)
48+
}
49+
50+
override fun build() = SurfScoreboardImpl(
51+
title, maxLines, sidebarComponentBuilder.build(), animations
52+
)
53+
54+
55+
override fun buildAutoUpdatable() = SurfAutoUpdatableScoreboardImpl(
56+
title,
57+
maxLines,
58+
sidebarComponentBuilder.build(),
59+
animations
60+
)
61+
62+
63+
override fun buildAutoUpdatablePlayer() = SurfAutoUpdatablePlayerScoreboardImpl(
64+
title,
65+
maxLines,
66+
sidebarComponentBuilder.build(),
67+
animations
68+
)
69+
70+
71+
companion object {
72+
private fun createGradientAnimation(
73+
component: Component,
74+
firstHex: String, secondHex: String
75+
): SidebarAnimation<Component> {
76+
val step = 1f / 20f
77+
val textPlaceholder = Placeholder.component("text", component)
78+
val frames = mutableObjectListOf<Component>()
79+
80+
// Animation from left to right
81+
var phase = -1f
82+
while (phase < 1) {
83+
frames.add(
84+
MiniMessage.miniMessage().deserialize(
85+
"<gradient:$firstHex:$secondHex:$phase><text></gradient>",
86+
textPlaceholder
87+
)
88+
)
89+
phase += step
90+
}
91+
92+
// Animation from right to left
93+
frames.addAll(frames.reversed())
94+
95+
return CollectionSidebarAnimation(frames)
96+
}
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dev.slne.surf.api.paper.server.impl.scoreboard
2+
3+
import dev.slne.surf.api.paper.scoreboard.SurfScoreboard
4+
import dev.slne.surf.api.paper.scoreboard.SurfScoreboardApi
5+
import net.kyori.adventure.text.Component
6+
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar
7+
import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout
8+
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
9+
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.FramedSidebarAnimation
10+
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
11+
import org.bukkit.entity.Player
12+
13+
open class SurfScoreboardImpl(
14+
protected val title: Component,
15+
protected val maxLines: Int,
16+
protected val sidebarComponent: SidebarComponent,
17+
protected val animations: List<SidebarAnimation<Component>>
18+
) : SurfScoreboard {
19+
protected var scoreboard: Sidebar? = null
20+
protected var sidebarLayout: ComponentSidebarLayout? = null
21+
protected var enabled: Boolean = false
22+
23+
override fun addViewer(viewer: Player) {
24+
check(enabled) { "Scoreboard is not enabled. Did you forget to call enable()?" }
25+
26+
scoreboard!!.addPlayer(viewer)
27+
}
28+
29+
override fun removeViewer(viewer: Player) {
30+
check(enabled) { "Scoreboard is not enabled. Did you forget to call enable()?" }
31+
32+
scoreboard!!.removePlayer(viewer)
33+
}
34+
35+
override fun enable() {
36+
check(!enabled) { "Scoreboard is already enabled" }
37+
38+
scoreboard = SurfScoreboardApi.scoreboardLibrary().createSidebar(maxLines)
39+
sidebarLayout = ComponentSidebarLayout(
40+
SidebarComponent.staticLine(title),
41+
sidebarComponent
42+
).also { it.apply(scoreboard!!) }
43+
44+
enabled = true
45+
}
46+
47+
override fun disable() {
48+
check(enabled) { "Scoreboard is not enabled. Did you forget to call enable()?" }
49+
50+
scoreboard!!.close()
51+
animations.forEach { (it as? FramedSidebarAnimation<Component>)?.switchFrame(0) }
52+
53+
this.scoreboard = null
54+
sidebarLayout = null
55+
enabled = false
56+
}
57+
58+
override fun update() {
59+
check(enabled) { "Scoreboard is not enabled. Did you forget to call enable()?" }
60+
61+
animations.forEach { it.nextFrame() }
62+
sidebarLayout!!.apply(scoreboard!!)
63+
}
64+
}

0 commit comments

Comments
 (0)