Skip to content

Commit

Permalink
Prototype leaderboard command
Browse files Browse the repository at this point in the history
  • Loading branch information
chatasma committed Jul 14, 2024
1 parent 5d86eb5 commit 087be9c
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 1 deletion.
11 changes: 11 additions & 0 deletions src/main/kotlin/network/warzone/mars/Mars.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ class Mars : JavaPlugin() {
return future
}

fun <T> asyncAsFutureWithResult(block: suspend() -> T) : CompletableFuture<T> {
val future = CompletableFuture<T>()
Bukkit.getScheduler().runTaskAsynchronously(get()) {
runBlocking {
val data = block.invoke()
future.complete(data)
}
}
return future
}

fun sync(block: () -> Unit) = Bukkit.getScheduler().runTask(get(), block) // Run next tick

fun get() = instance
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package network.warzone.mars.leaderboard

import network.warzone.mars.api.ApiClient

object LeaderboardClient {
suspend fun fetchLeaderboardEntries(scoreType: LeaderboardScoreType, period: LeaderboardPeriod) : List<LeaderboardEntry> {
return ApiClient.get("/mc/leaderboards/${scoreType.name}/${period.name}")
}
}

data class LeaderboardEntry(
val id: String,
val name: String,
val score: Int
)

enum class LeaderboardScoreType {
KILLS, DEATHS, XP
}

enum class LeaderboardPeriod {
ALL_TIME, DAILY
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package network.warzone.mars.leaderboard

import app.ashcon.intake.Command
import app.ashcon.intake.bukkit.parametric.annotation.Sender
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import network.warzone.mars.Mars
import network.warzone.mars.player.commands.ModCommands
import network.warzone.mars.player.models.PlayerStats
import network.warzone.mars.utils.enumify
import network.warzone.mars.utils.getUsername
import network.warzone.mars.utils.strategy.multiLine
import org.bukkit.ChatColor
import org.bukkit.command.CommandSender
import tc.oc.pgm.api.PGM
import java.util.*
import javax.annotation.Nullable

class LeaderboardCommands {
@Command(aliases = ["leaderboard", "lb"], desc = "View leaderboards", usage = "[scoreType] [period]")
fun onLeaderboardList(
@Sender sender: CommandSender,
@Nullable scoreType: String? = null,
@Nullable period: String? = null
) {
val match = PGM.get().matchManager.getMatch(sender)!!
val audience: tc.oc.pgm.util.Audience = tc.oc.pgm.util.Audience.get(sender) // PGM Audience required for name formatting
val scoreTypeAndPeriod = validateScoreTypeAndPeriod(scoreType, period)
if (scoreTypeAndPeriod == null) {
sender.sendMessage("${ChatColor.RED}/lb " +
"${formatVariantsToUsageString(LeaderboardScoreType::class.java)} " +
"${formatVariantsToUsageString(LeaderboardPeriod::class.java)}"
)
return
}
Mars.asyncAsFutureWithResult {
LeaderboardClient.fetchLeaderboardEntries(scoreTypeAndPeriod.first, scoreTypeAndPeriod.second)
}.thenApply { lbEntries ->
val message = audience.multiLine()
lbEntries.mapIndexed { idx, entry ->
val username = getUsername(UUID.fromString(entry.id), entry.name, match, offlineNameProvider = ModCommands.offlineNameProvider)
Component.text("${idx + 1}. ", NamedTextColor.GOLD)
.append(username).append(Component.text(": ", NamedTextColor.GRAY))
.append(formatScore(scoreTypeAndPeriod.first, entry.score))
}.forEach {
message.appendMultiLineComponent(it)
}
audience.sendMessage(
Component.text(
"${scoreTypeAndPeriod.first.name} leaderboard (${scoreTypeAndPeriod.second.name})",
NamedTextColor.GREEN
)
)
message.deliver()
}
}

private fun formatScore(scoreType: LeaderboardScoreType, score: Int) : Component =
when (scoreType) {
LeaderboardScoreType.XP -> {
Component.text(score, NamedTextColor.YELLOW).append(
Component.text(" (Level: ${PlayerStats.EXP_FORMULA.getLevelFromExp(score.toDouble())})",
NamedTextColor.GRAY)
)
}
else -> Component.text(score, NamedTextColor.YELLOW)
}

private fun validateScoreTypeAndPeriod(scoreType: String?, period: String?) : Pair<LeaderboardScoreType, LeaderboardPeriod>? {
if (scoreType == null) return null
val scoreType = getEnumVariant<LeaderboardScoreType>(scoreType)
val period = if (period == null) LeaderboardPeriod.ALL_TIME else getEnumVariant<LeaderboardPeriod>(period)
if (scoreType == null || period == null) return null
return scoreType to period
}

private fun <T : Enum<T>> formatVariantsToUsageString(enumClazz: Class<T>) : String {
return "(" + enumClazz.enumConstants.joinToString(separator = "|") { it.name.toLowerCase() } + ")"
}

private inline fun <reified T : Enum<T>> getEnumVariant(name: String): T? {
val enumified = name.enumify()
return enumValues<T>().firstOrNull { it.name == enumified }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import network.warzone.mars.admin.AdminService
import network.warzone.mars.api.ApiClient
import network.warzone.mars.api.socket.models.SimplePlayer
import network.warzone.mars.feature.NamedCachedFeature
import network.warzone.mars.leaderboard.LeaderboardCommands
import network.warzone.mars.player.PlayerManager
import network.warzone.mars.player.commands.ChatCommands
import network.warzone.mars.player.commands.MiscCommands
Expand Down Expand Up @@ -250,7 +251,7 @@ object PlayerFeature : NamedCachedFeature<PlayerProfile>(), Listener {
}

override fun getCommands(): List<Any> {
return listOf(ModCommands(), MiscCommands(), StatCommands(), PerkCommands(), AdminCommands())
return listOf(ModCommands(), MiscCommands(), StatCommands(), PerkCommands(), AdminCommands(), LeaderboardCommands())
}

override fun getSubcommands(): Map<List<String>, Any> {
Expand Down

0 comments on commit 087be9c

Please sign in to comment.