Skip to content
Open
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
- **`/unfreeze <Player>`**
☀️ Unfreeze a player.
- Permission: `surf.moderation.tools.command.unfreeze`

- **`/freezelist`**
📋 View a list of currently frozen players.
- Permission: `surf.moderation.tools.command.freeze`

- **`/pingPlayer <Player> <WithMessage>`**
📍 Ping a player with an auditory indicator and an optional message.
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
kotlin.code.style=official
kotlin.stdlib.default.dependency=false
org.gradle.parallel=true
version=2.1.3
version=2.1.4
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ object PaperCommandManager {
unfreezeCommand()
stopInteractionCommand()
pingPlayerCommand()
freezeListCommand()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ fun freezeCommand() = commandAPICommand("freeze") {
return@anyExecutorSuspend
}

FreezeService.freeze(targetUuid, duration)
val frozenBy = (sender as? Player)?.uniqueId
FreezeService.freeze(targetUuid, duration, frozenBy, sender.name)

withContext(plugin.entityDispatcher(targetPlayer)) {
val location = targetPlayer.location
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package dev.slne.surf.moderation.tools.commands

import com.github.shynixn.mccoroutine.folia.launch
import dev.jorel.commandapi.kotlindsl.anyExecutor
import dev.jorel.commandapi.kotlindsl.commandTree
import dev.jorel.commandapi.kotlindsl.getValue
import dev.jorel.commandapi.kotlindsl.integerArgument
import dev.slne.surf.api.core.font.toSmallCaps
import dev.slne.surf.api.core.messages.CommonComponents
import dev.slne.surf.api.core.messages.adventure.buildText
import dev.slne.surf.api.core.messages.adventure.clickRunsCommand
import dev.slne.surf.api.core.messages.adventure.sendText
import dev.slne.surf.api.core.messages.pagination.Pagination
import dev.slne.surf.moderation.tools.plugin
import dev.slne.surf.moderation.tools.service.FreezeService
import dev.slne.surf.moderation.tools.util.PermissionRegistry
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.Bukkit
import java.time.Duration
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.time.toKotlinDuration

private val FREEZE_TIME_FORMATTER: DateTimeFormatter =
DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss").withZone(ZoneId.systemDefault())

fun freezeListCommand() = commandTree("freezelist") {
withPermission(PermissionRegistry.COMMAND_FREEZE)

integerArgument("page", optional = true) {
anyExecutor { executor, args ->
val page: Int? by args

plugin.launch {
val frozenPlayers = FreezeService.getFrozenEntries()
.entries
.sortedBy { it.value.expiresAt }

if (frozenPlayers.isEmpty()) {
executor.sendText {
appendErrorPrefix()
error("Es sind aktuell keine Spieler eingefroren.")
}
return@launch
}

val pagination =
Pagination<Map.Entry<UUID, FreezeService.FreezeData>> {
title {
primary("Eingefrorene Spieler".toSmallCaps(), TextDecoration.BOLD)
spacer(" (${frozenPlayers.size})")
}
rowRenderer { entry, _ ->
val uuid = entry.key
val data = entry.value
val targetName = Bukkit.getOfflinePlayer(uuid).name ?: uuid.toString()
val remaining = Duration.between(Instant.now(), data.expiresAt)
.coerceAtLeast(Duration.ZERO)

listOf(
buildText {
append(CommonComponents.EM_DASH)
appendSpace()
append {
variableValue(targetName)
hoverEvent(buildText {
info("Eingefroren von: ")
variableValue(data.frozenByName)
appendNewline()
info("Eingefroren am: ")
variableValue(FREEZE_TIME_FORMATTER.format(data.frozenAt))
appendNewline()
info("Läuft ab am: ")
variableValue(FREEZE_TIME_FORMATTER.format(data.expiresAt))
appendNewline()
info("Verbleibend: ")
appendTime(remaining.toKotlinDuration())
})
}
appendSpace()
append {
spacer("[")
error("Entfrieren")
spacer("]")
hoverEvent(buildText {
info("Klicke, um ")
variableValue(targetName)
info(" aufzutauen.")
})
clickRunsCommand("/unfreeze $targetName")
}
}
)
}
}

executor.sendText {
append(pagination.renderComponent(frozenPlayers, page ?: 1))
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@ package dev.slne.surf.moderation.tools.service
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Expiry
import java.time.Duration
import java.time.Instant
import java.util.*

object FreezeService {

data class FreezeData(
val frozenBy: UUID?,
val frozenByName: String,
val frozenAt: Instant,
val expiresAt: Instant,
)

private val frozenPlayers = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfter(Expiry.writing<UUID, Long> { _, durationMs -> Duration.ofMillis(durationMs) })
.build<UUID, Long>()
.expireAfter(Expiry.writing<UUID, FreezeData> { _, data ->
Duration.between(data.frozenAt, data.expiresAt).coerceAtLeast(Duration.ZERO)
})
.build<UUID, FreezeData>()

fun freeze(uuid: UUID, durationMs: Long) {
frozenPlayers.put(uuid, durationMs)
fun freeze(uuid: UUID, durationMs: Long, frozenBy: UUID?, frozenByName: String) {
val now = Instant.now()
frozenPlayers.put(
uuid,
FreezeData(frozenBy, frozenByName, now, now.plusMillis(durationMs))
)
}

fun unfreeze(uuid: UUID) {
Expand All @@ -22,4 +37,6 @@ object FreezeService {
fun isFrozen(uuid: UUID): Boolean {
return frozenPlayers.getIfPresent(uuid) != null
}
}

fun getFrozenEntries(): Map<UUID, FreezeData> = frozenPlayers.asMap().toMap()
}