Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
javaVersion=25
mcVersion=26.2
group=dev.slne.surf.api
version=3.44.0
version=3.45.0
relocationPrefix=dev.slne.surf.api.libs
snapshot=false
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
import dev.slne.surf.api.core.messages.Colors;
import dev.slne.surf.api.paper.scoreboard.SurfAutoUpdatablePlayerScoreboard;
import dev.slne.surf.api.paper.scoreboard.SurfScoreboardBuilder;
import net.kyori.adventure.text.Component;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Contract;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Contract;

public class CreateScoreboard extends CommandAPICommand {

Expand Down Expand Up @@ -40,6 +39,7 @@ public CreateScoreboard(String commandName) {
}
}))
.addUpdatableLine(() -> Component.text("Updatable Line: " + UUID.randomUUID()))
.addViewerLine(player -> Component.text("Viewer health: " + player.getHealth()))
.buildAutoUpdatablePlayer();

scoreboard.enable();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.slne.surf.api.paper.server.impl.scoreboard

import net.kyori.adventure.text.Component
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
import org.bukkit.entity.Player
import java.util.function.Function

sealed interface ScoreboardLine {
class Shared(val component: SidebarComponent) : ScoreboardLine
class Viewer(val factory: Function<Player, SidebarComponent>) : ScoreboardLine
}

/**
* Holds the value of a mutable line source as of the last [refresh].
*/
class LineSnapshot<T : Any>(private val source: () -> T) {
lateinit var value: T
private set

fun refresh() {
value = source()
}
}

class ScoreboardDefinition(
val title: Component,
val maxLines: Int,
val lines: List<ScoreboardLine>,
val snapshots: List<LineSnapshot<*>>,
val animations: List<SidebarAnimation<Component>>
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,23 @@ package dev.slne.surf.api.paper.server.impl.scoreboard

import dev.slne.surf.api.paper.scoreboard.SurfAutoUpdatablePlayerScoreboard
import dev.slne.surf.api.paper.util.forEachPlayer
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.logger.slf4j.ComponentLogger
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
import org.bukkit.entity.Player

class SurfAutoUpdatablePlayerScoreboardImpl(
title: Component,
maxLines: Int,
sidebarComponent: SidebarComponent,
animations: List<SidebarAnimation<Component>>
) : SurfAutoUpdatableScoreboardImpl(
title, maxLines, sidebarComponent, animations
), SurfAutoUpdatablePlayerScoreboard {
definition: ScoreboardDefinition
) : SurfAutoUpdatableScoreboardImpl(definition), SurfAutoUpdatablePlayerScoreboard {
override fun addViewer(viewer: Player) {
ComponentLogger.logger()
.warn("You are not allowed to add viewers to this scoreboard. This Scoreboard automatically adds viewers.")
}

override fun removeViewer(viewer: Player) {
ComponentLogger.logger().warn(
"You are not allowed to remove viewers from this scoreboard. This Scoreboard automatically removes viewers."
)
ComponentLogger.logger()
.warn("You are not allowed to remove viewers from this scoreboard. This Scoreboard automatically removes viewers.")
}

override fun update() {
super.update()
val scoreboard = scoreboard ?: return
forEachPlayer { scoreboard.addPlayer(it) }
override fun onUpdate() {
forEachPlayer { addViewerInternal(it) }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
package dev.slne.surf.api.paper.server.impl.scoreboard

import com.github.shynixn.mccoroutine.folia.launch
import com.github.shynixn.mccoroutine.folia.ticks
import com.github.shynixn.mccoroutine.folia.scope
import dev.slne.surf.api.core.util.runAtFixedRate
import dev.slne.surf.api.paper.scoreboard.SurfAutoUpdatableScoreboard
import dev.slne.surf.api.paper.server.plugin
import io.papermc.paper.util.Tick
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import net.kyori.adventure.text.Component
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
import kotlin.concurrent.withLock
import kotlin.time.toKotlinDuration

open class SurfAutoUpdatableScoreboardImpl(
title: Component,
maxLines: Int,
sidebarComponent: SidebarComponent,
animations: List<SidebarAnimation<Component>>
) : SurfScoreboardImpl(title, maxLines, sidebarComponent, animations), SurfAutoUpdatableScoreboard {
definition: ScoreboardDefinition
) : SurfScoreboardImpl(definition), SurfAutoUpdatableScoreboard {
private var updater: Job? = null

override fun enable() {
super.enable()

this.updater = launchUpdater()
lock.withLock {
super.enable()
updater = launchUpdater()
}
}

override fun disable() {
super.disable()
lock.withLock {
super.disable()
updater?.cancel()
updater = null
}
}

updater!!.cancel()
private fun launchUpdater(): Job = plugin.scope.runAtFixedRate(
fiveTicks,
taskName = "SurfAutoUpdatableScoreboardUpdater"
) {
updateIfEnabled()
}

private fun launchUpdater() = plugin.launch {
while (true) {
update()
delay(5.ticks)
}
companion object {
private val fiveTicks = Tick.of(5).toKotlinDuration()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation
import org.bukkit.entity.Player
import java.util.function.Function
import java.util.function.Supplier

class SurfScoreboardBuilderImpl(private val title: Component) : SurfScoreboardBuilder {
private val sidebarComponentBuilder = SidebarComponent.builder()
private val lines = mutableObjectListOf<ScoreboardLine>()
private val snapshots = mutableObjectListOf<LineSnapshot<*>>()
private val animations = mutableObjectListOf<SidebarAnimation<Component>>()
private var maxLines = SurfScoreboardBuilder.DEFAULT_MAX_LINES

Expand All @@ -21,52 +24,48 @@ class SurfScoreboardBuilderImpl(private val title: Component) : SurfScoreboardBu
this.maxLines = maxLines
}

override fun addLine(line: Component) = apply {
sidebarComponentBuilder.addStaticLine(line)
}
override fun addLine(line: Component) = addShared(SidebarComponent.staticLine(line))

override fun addUpdatableLine(line: Supplier<Component>) = apply {
sidebarComponentBuilder.addDynamicLine(line)
val snapshot = LineSnapshot(line::get).also { snapshots.add(it) }
addShared { drawable -> drawable.drawLine(snapshot.value) }
Comment on lines 29 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Give each built scoreboard its own line snapshots

When one builder containing an updatable line is used to build multiple scoreboards, this closure and snapshots.toList() retain the same mutable LineSnapshot instance in every result. The scoreboards have separate locks, so concurrent updates can overwrite one another's snapshot between refresh and draw, causing one scoreboard to display another scoreboard's supplier result. Create the snapshots and the components that capture them per definition()/build instead of sharing them through the builder.

Useful? React with 👍 / 👎.

}

override fun addAnimatedLine(animation: SidebarAnimation<SidebarComponent>) = apply {
sidebarComponentBuilder.addAnimatedComponent(animation)
override fun addViewerComponent(component: Function<Player, SidebarComponent>) = apply {
lines.add(ScoreboardLine.Viewer(component))
}

override fun addAnimatedLine(animation: SidebarAnimation<SidebarComponent>) =
addShared(SidebarComponent.animatedComponent(animation))

override fun addAnimatedLine(frames: MutableList<Component>) = apply {
check(frames.isNotEmpty()) { "frames cannot be empty" }
addAnimation(CollectionSidebarAnimation(frames))
}

val animation = CollectionSidebarAnimation(frames)
sidebarComponentBuilder.addAnimatedLine(animation)
override fun addGradientLine(text: Component, start: TextColor, end: TextColor) =
addAnimation(createGradientAnimation(text, start.asHexString(), end.asHexString()))

private fun addAnimation(animation: SidebarAnimation<Component>) = apply {
animations.add(animation)
addShared(SidebarComponent.animatedLine(animation))
}

override fun addGradientLine(text: Component, start: TextColor, end: TextColor) = apply {
val gradient = createGradientAnimation(text, start.asHexString(), end.asHexString())
sidebarComponentBuilder.addAnimatedLine(gradient)
animations.add(gradient)
private fun addShared(component: SidebarComponent) = apply {
lines.add(ScoreboardLine.Shared(component))
}

override fun build() = SurfScoreboardImpl(
title, maxLines, sidebarComponentBuilder.build(), animations
)


override fun buildAutoUpdatable() = SurfAutoUpdatableScoreboardImpl(
title,
maxLines,
sidebarComponentBuilder.build(),
animations
)


override fun buildAutoUpdatablePlayer() = SurfAutoUpdatablePlayerScoreboardImpl(
private fun definition() = ScoreboardDefinition(
title,
maxLines,
sidebarComponentBuilder.build(),
animations
lines.toList(),
snapshots.toList(),
animations.toList(),
)

override fun build() = SurfScoreboardImpl(definition())
override fun buildAutoUpdatable() = SurfAutoUpdatableScoreboardImpl(definition())
override fun buildAutoUpdatablePlayer() = SurfAutoUpdatablePlayerScoreboardImpl(definition())

companion object {
private fun createGradientAnimation(
Expand Down Expand Up @@ -95,4 +94,4 @@ class SurfScoreboardBuilderImpl(private val title: Component) : SurfScoreboardBu
return CollectionSidebarAnimation(frames)
}
}
}
}
Loading
Loading