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
@@ -1,3 +1,3 @@
group=com.mineinabyss
version=0.12
idofrontVersion=1.0.3
idofrontVersion=1.0.5
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
gearyPaper = "0.33.2"
gearyPaper = "0.33.14"
chatty = "0.9.1"

[libraries]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.mineinabyss.extracommands

import com.mineinabyss.extracommands.dailyrestarts.RestartManager
import com.mineinabyss.extracommands.listeners.AfkListener
import com.mineinabyss.extracommands.listeners.GodListener
import com.mineinabyss.extracommands.listeners.HuskHomesListener
import com.mineinabyss.extracommands.listeners.SeenListener
import com.mineinabyss.extracommands.listeners.VanishListener
import com.mineinabyss.idofront.config.config
import com.mineinabyss.idofront.di.DI
import com.mineinabyss.idofront.plugin.Plugins
import com.mineinabyss.idofront.plugin.listeners
Expand All @@ -16,6 +18,8 @@ class ExtraCommands : JavaPlugin() {
createExtraCommandsContext()
ExtraBrigadierCommands.registerCommands()

extraCommands.restartManager.scheduleDailyRestartIfEnabled()

listeners(
AfkListener(),
GodListener(),
Expand All @@ -32,7 +36,8 @@ class ExtraCommands : JavaPlugin() {
DI.remove<ExtraCommandContext>()
DI.add<ExtraCommandContext>(object : ExtraCommandContext {
override val plugin = this@ExtraCommands
override val config = ExtraConfig()
override val config by config("config", dataFolder.toPath(), ExtraConfig())
override val restartManager: RestartManager = RestartManager(config.dailyRestarts)
})
}
}
4 changes: 3 additions & 1 deletion src/main/kotlin/com/mineinabyss/extracommands/ExtraConfig.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.mineinabyss.extracommands

import com.mineinabyss.extracommands.dailyrestarts.DailyRestartsConfig
import com.mineinabyss.idofront.serialization.DurationSerializer
import kotlinx.serialization.Serializable
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

@Serializable
data class ExtraConfig(
val afk: AfkConfig = AfkConfig()
val afk: AfkConfig = AfkConfig(),
val dailyRestarts: DailyRestartsConfig = DailyRestartsConfig(),
) {
@Serializable
data class AfkConfig(
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/mineinabyss/extracommands/ExtraContext.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.mineinabyss.extracommands

import com.mineinabyss.extracommands.dailyrestarts.RestartManager
import com.mineinabyss.idofront.di.DI

val extraCommands by DI.observe<ExtraCommandContext>()
interface ExtraCommandContext {
val plugin: ExtraCommands
val config: ExtraConfig
val restartManager: RestartManager
}
Original file line number Diff line number Diff line change
@@ -1,113 +1,43 @@
package com.mineinabyss.extracommands.commands

import com.github.shynixn.mccoroutine.bukkit.launch
import com.mineinabyss.extracommands.extraCommands
import com.mineinabyss.geary.papermc.datastore.encodeComponentsTo
import com.mineinabyss.geary.papermc.tracking.entities.toGearyOrNull
import com.mineinabyss.extracommands.dailyrestarts.RestartManager
import com.mineinabyss.idofront.commands.brigadier.RootIdoCommands
import com.mineinabyss.idofront.commands.brigadier.arguments.DurationTypeArgument
import com.mineinabyss.idofront.commands.brigadier.executes
import com.mineinabyss.idofront.plugin.Plugins
import com.mineinabyss.idofront.textcomponents.miniMsg
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import net.kyori.adventure.title.Title
import org.bukkit.Bukkit
import java.time.Duration.ofSeconds
import kotlin.time.Duration
import com.mineinabyss.idofront.messaging.error
import com.mineinabyss.idofront.messaging.success
import kotlin.time.Duration.Companion.seconds

//val maintenance by lazy { runCatching { MaintenanceProvider.get() as MaintenanceProxy }.getOrNull() }
fun RootIdoCommands.scheduleRestartCommand() {
var currentJob: Job? = null
fun RootIdoCommands.scheduleRestartCommand(
service: RestartManager = extraCommands.restartManager,
) {
"schedulestop" {
executes(DurationTypeArgument(10.seconds)) { duration ->
fun showTitle(time: Duration, fade: Boolean) {
Bukkit.getServer().showTitle(
Title.title(
"<red><bold>Server Stopping".miniMsg(),
"Server will stop in ${time.toComponents { d, h, m, s, _ -> "${d}d ${h}h ${m}m ${s}s" }.replace("0[a-zA-Z]\\s? ".toRegex(), "")}.".miniMsg(),
if (fade) Title.Times.times(ofSeconds(1), ofSeconds(5), ofSeconds(1))
else Title.Times.times(ofSeconds(0), ofSeconds(2), ofSeconds(0))
)
)
}
currentJob = extraCommands.plugin.launch {
showTitle(duration, fade = true)

(duration - 10.seconds).takeIf(Duration::isPositive)?.let { delay(it) }

repeat(10) {
showTitle((10 - it).seconds, fade = false)
delay(1.seconds)
}

//val server = maintenance?.getServer("survival")
//val isMaintenance = maintenance?.isMaintenance(server) ?: false
//if (!isMaintenance) maintenance?.setMaintenanceToServer(server, true)
Bukkit.savePlayers()
Bukkit.getWorlds().forEach { world ->
if (Plugins.isEnabled("Geary")) world.entities.forEach { e ->
e.toGearyOrNull()?.encodeComponentsTo(e.persistentDataContainer)
}
world.save()
}
//if (!isMaintenance) maintenance?.setMaintenanceToServer(server, false)

Bukkit.shutdown()
}
service.scheduleStop(showTitleAtStart = true, duration)
}
}
"schedulerestart" {
executes(DurationTypeArgument(10.seconds)) { duration ->
fun showTitle(time: Duration, fade: Boolean) {
Bukkit.getServer().showTitle(
Title.title(
"<red><bold>Server Restarting".miniMsg(),
"Server will restart in ${time.toComponents { d, h, m, s, _ -> "${d}d ${h}h ${m}m ${s}s" }.replace("0[a-zA-Z]\\s? ".toRegex(), "")}.".miniMsg(),
if (fade) Title.Times.times(ofSeconds(1), ofSeconds(5), ofSeconds(1))
else Title.Times.times(ofSeconds(0), ofSeconds(2), ofSeconds(0))
)
)
}
currentJob = extraCommands.plugin.launch {
showTitle(duration, fade = true)

(duration - 10.seconds).takeIf(Duration::isPositive)?.let { delay(it) }

repeat(10) {
showTitle((10 - it).seconds, fade = false)
delay(1.seconds)
}

//val server = maintenance?.getServer("survival")
//val isMaintenance = maintenance?.isMaintenance(server) ?: false
//if (!isMaintenance) maintenance?.setMaintenanceToServer(server, true)
Bukkit.savePlayers()
Bukkit.getWorlds().forEach { world ->
if (Plugins.isEnabled("Geary")) world.entities.forEach { e ->
e.toGearyOrNull()?.encodeComponentsTo(e.persistentDataContainer)
}
world.save()
}
//if (!isMaintenance) maintenance?.setMaintenanceToServer(server, false)

Bukkit.spigot().restart()
}
service.scheduleRestart(showTitleAtStart = true, duration)
}
}
"cancelrestart" {
requiresPermission("extracommands.cancelrestart")

"daily" {
requiresPermission("extracommands.cancelrestart.daily")
executes {
if (service.cancelDailyJob())
sender.success("Cancelled daily restart.")
else sender.error("No daily restart scheduled, nothing to cancel.")
}
}

executes {
currentJob?.cancel() ?: return@executes
Bukkit.getServer().clearTitle()
Bukkit.getServer().showTitle(
Title.title(
"<green><bold>Server Restart Cancelled!".miniMsg(),
"Server will no longer restart...".miniMsg(),
Title.Times.times(ofSeconds(1), ofSeconds(5), ofSeconds(1))
)
)
if (service.cancelJob())
sender.success("Cancelled scheduled restart.")
else sender.error("No restart scheduled, nothing to cancel.")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.mineinabyss.extracommands.dailyrestarts

import com.mineinabyss.idofront.serialization.DurationSerializer
import kotlinx.serialization.Serializable
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours

@Serializable
data class DailyRestartsConfig(
val enabled: Boolean = false,
val skipIfUptimeLessThan: @Serializable(with = DurationSerializer::class) Duration = 4.hours,
val hour: Int = 0,
val minute: Int = 0,
val timeZone: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package com.mineinabyss.extracommands.dailyrestarts

import com.github.shynixn.mccoroutine.bukkit.launch
import com.mineinabyss.extracommands.extraCommands
import com.mineinabyss.geary.papermc.datastore.encodeComponentsTo
import com.mineinabyss.geary.papermc.tracking.entities.toGearyOrNull
import com.mineinabyss.idofront.plugin.Plugins
import com.mineinabyss.idofront.textcomponents.miniMsg
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import net.kyori.adventure.title.Title
import org.bukkit.Bukkit
import java.time.Duration
import java.time.ZoneId
import java.time.ZonedDateTime
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toKotlinDuration

class RestartManager(
val config: DailyRestartsConfig,
) {
var dailyRestartJob: Job? = null
var currentJob: Job? = null

fun scheduleDailyRestartIfEnabled() {
if (!config.enabled) return

val now = ZonedDateTime.now(runCatching { ZoneId.of(config.timeZone) }.getOrDefault(ZoneId.systemDefault()))
val nextRun = now
.withHour(config.hour)
.withMinute(config.minute)
.withSecond(0)
.let { if (now > it) it.plusDays(1) else it }
.let { nextTime ->
val timeUntilNextRun = Duration.between(now, nextTime)
if (timeUntilNextRun.toKotlinDuration() < config.skipIfUptimeLessThan)
nextTime.plusDays(1)
else nextTime
}
val delay = Duration.between(now, nextRun).toKotlinDuration()
println("Scheduling daily restart at $nextRun (in $delay)")
dailyRestartJob?.cancel()
dailyRestartJob = restartJob(showTitleAtStart = false, delay)
}

fun showTitle(
fade: Boolean,
title: String,
subtitle: String,
) {
Bukkit.getServer().showTitle(
Title.title(
title.miniMsg(),
subtitle.miniMsg(),
if (fade) Title.Times.times(Duration.ofSeconds(1), Duration.ofSeconds(5), Duration.ofSeconds(1))
else Title.Times.times(Duration.ofSeconds(0), Duration.ofSeconds(2), Duration.ofSeconds(0))
)
)
}

fun delayToString(duration: kotlin.time.Duration) = duration
.toString()

fun scheduleStop(showTitleAtStart: Boolean, duration: kotlin.time.Duration) {
currentJob?.cancel()
currentJob = scheduleJob(
showTitleAtStart = showTitleAtStart,
duration = duration,
title = { "<red><bold>Server Stopping" },
subtitle = { "Server will stop in ${delayToString(it)}." },
earlyWarning = { "<red><bold>[Server] stopping in ${delayToString(it)}." },
run = { Bukkit.shutdown() }
)
}

private fun restartJob(showTitleAtStart: Boolean, duration: kotlin.time.Duration) = scheduleJob(
showTitleAtStart = showTitleAtStart,
duration = duration,
title = { "<red><bold>Server Restarting" },
subtitle = { "Server will restart in ${delayToString(it)}." },
earlyWarning = { "<red><bold>[Server] restarting in ${delayToString(it)}." },
run = { Bukkit.restart() }
)

fun scheduleRestart(showTitleAtStart: Boolean, duration: kotlin.time.Duration) {
currentJob?.cancel()
currentJob = restartJob(showTitleAtStart, duration)
}

fun cancelJob(): Boolean {
currentJob?.cancel()
val present = currentJob != null
currentJob = null
if (present) {
Bukkit.getServer().clearTitle()
Bukkit.getServer().showTitle(
Title.title(
"<green><bold>Server Restart Cancelled!".miniMsg(),
"Server will no longer restart...".miniMsg(),
Title.Times.times(Duration.ofSeconds(1), Duration.ofSeconds(5), Duration.ofSeconds(1))
)
)
}
return present
}

fun cancelDailyJob(): Boolean {
dailyRestartJob?.cancel()
val present = dailyRestartJob != null
dailyRestartJob = null
return present
}

private fun scheduleJob(
showTitleAtStart: Boolean,
duration: kotlin.time.Duration,
earlyWarning: (kotlin.time.Duration) -> String,
title: (kotlin.time.Duration) -> String,
subtitle: (kotlin.time.Duration) -> String,
run: suspend () -> Unit,
): Job = extraCommands.plugin.launch {
if (showTitleAtStart) showTitle(fade = true, title(duration), subtitle(duration))
var rem = duration

suspend fun mark(time: kotlin.time.Duration, exec: suspend () -> Unit) {
if (rem >= time) {
delay(rem - time)
rem = time
exec()
}
}

mark(30.minutes) {
Bukkit.getServer().sendMessage(earlyWarning(rem).miniMsg())
}

mark(10.minutes) {
Bukkit.getServer().sendMessage(earlyWarning(rem).miniMsg())
}

mark(1.minutes) {
Bukkit.getServer().sendMessage(earlyWarning(rem).miniMsg())
}

mark(30.seconds) {
Bukkit.getServer().sendMessage(earlyWarning(rem).miniMsg())
}


// Always show last 10 second countdown
(rem - 10.seconds).takeIf(kotlin.time.Duration::isPositive)?.let { delay(it) }
repeat(10) {
val left = (10 - it).seconds
showTitle(fade = false, title(left), subtitle(left))
delay(1.seconds)
}

Bukkit.savePlayers()
Bukkit.getWorlds().forEach { world ->
if (Plugins.isEnabled("Geary")) world.entities.forEach { e ->
e.toGearyOrNull()?.encodeComponentsTo(e.persistentDataContainer)
}
world.save()
}

run()
}
}