133 lines
4.5 KiB
Kotlin
133 lines
4.5 KiB
Kotlin
package nl.voidcorp.backupplugin
|
|
|
|
import com.github.luben.zstd.ZstdOutputStream
|
|
import nl.voidcorp.mainplugin.CommandHandler
|
|
import nl.voidcorp.mainplugin.VoidPluginBase
|
|
import nl.voidcorp.mainplugin.adapter
|
|
import nl.voidcorp.mainplugin.commands.VoidCommand
|
|
import nl.voidcorp.mainplugin.messaging.Message
|
|
import nl.voidcorp.mainplugin.messaging.MessageType
|
|
import nl.voidcorp.mainplugin.moshi
|
|
import org.bukkit.Bukkit
|
|
import org.bukkit.command.Command
|
|
import org.bukkit.command.CommandSender
|
|
import org.bukkit.scheduler.BukkitRunnable
|
|
import org.joda.time.DateTime
|
|
import org.joda.time.format.ISODateTimeFormat
|
|
import org.kamranzafar.jtar.TarEntry
|
|
import org.kamranzafar.jtar.TarOutputStream
|
|
import java.io.File
|
|
import java.nio.file.Files
|
|
import java.util.*
|
|
import kotlin.concurrent.thread
|
|
import kotlin.math.ln
|
|
import kotlin.math.pow
|
|
import kotlin.streams.toList
|
|
|
|
class VoidBackup(override val comment: String = "Make Backups!") : VoidPluginBase() {
|
|
|
|
private val backupDir = File(server.worldContainer, "backups").apply {
|
|
if (!exists()) mkdirs()
|
|
}
|
|
|
|
private val backupTask = BackupTask(this)
|
|
lateinit var conf: Config
|
|
|
|
|
|
override fun enable() {
|
|
|
|
send("VoidPlugin", MessageType.GET_CONFIG)
|
|
send("VoidPlugin", MessageType.POST_CONFIG, moshi.adapter<Config>().toJson(conf))
|
|
|
|
backupTask.runTaskTimer(this, 20, conf.timeoutMinutes * 60 * 20)
|
|
CommandHandler(this).registerCommand("force-backup", ForceBackupCommand(this))
|
|
|
|
}
|
|
|
|
override fun disable() {
|
|
backupTask.cancel()
|
|
}
|
|
|
|
override fun recieve(message: Message) {
|
|
if (message.messageType == MessageType.GET_CONFIG) {
|
|
conf = moshi.adapter<Config>().fromJson(message.content)!!
|
|
}
|
|
}
|
|
|
|
fun backup() {
|
|
Bukkit.broadcastMessage("Backing up. Server may lag for a bit...")
|
|
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "save-off")
|
|
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "save-all")
|
|
val done = mutableListOf<UUID>()
|
|
server.worlds.forEach { world ->
|
|
val dir = world.worldFolder
|
|
|
|
val whereTo = createTempFile("yeet")
|
|
val whereToZ = File(
|
|
backupDir,
|
|
"${world.name}-${DateTime.now().toString(ISODateTimeFormat.basicDateTimeNoMillis())}.tar.zst"
|
|
)
|
|
|
|
thread {
|
|
val files = Files.walk(dir.toPath()).map { it.toFile() }.filter { !it.isDirectory }.toList()
|
|
|
|
val tar = TarOutputStream(whereTo)
|
|
tar.use { tarOS ->
|
|
for (f in files) {
|
|
tarOS.putNextEntry(TarEntry(f, f.path.removePrefix(".${File.separatorChar}")))
|
|
f.inputStream().use {
|
|
it.copyTo(tarOS)
|
|
}
|
|
}
|
|
}
|
|
val zstOutputStream = ZstdOutputStream(whereToZ.outputStream())
|
|
val inp = whereTo.inputStream()
|
|
zstOutputStream.use { zst ->
|
|
inp.use { tr ->
|
|
tr.copyTo(zst)
|
|
}
|
|
}
|
|
Bukkit.broadcastMessage("World ${world.name} has been saved, it is ${humanReadableByteCount(whereToZ.length())}")
|
|
done.add(world.uid)
|
|
if (server.worlds.size == done.size) {
|
|
Bukkit.getScheduler().runTask(this, EnableSave)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun humanReadableByteCount(bytes: Long, si: Boolean = false): String {
|
|
val unit = if (si) 1000 else 1024
|
|
if (bytes < unit) return "$bytes B"
|
|
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
|
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
|
|
return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
|
|
}
|
|
|
|
object EnableSave : Runnable {
|
|
override fun run() {
|
|
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "save-on")
|
|
}
|
|
}
|
|
|
|
class BackupTask(private val pl: VoidBackup) : BukkitRunnable() {
|
|
override fun run() {
|
|
pl.backup()
|
|
}
|
|
}
|
|
|
|
data class Config(val timeoutMinutes: Long = 30)
|
|
|
|
class ForceBackupCommand(private val pl: VoidBackup) : VoidCommand() {
|
|
override fun onCommand(
|
|
sender: CommandSender,
|
|
command: Command,
|
|
label: String,
|
|
args: Array<out String>
|
|
): Boolean {
|
|
pl.backup()
|
|
return true
|
|
}
|
|
}
|
|
|
|
} |