Skip to content

Commit 48fb02c

Browse files
committed
BackupManager.kt implemented.
Signed-off-by: Pavel Erokhin (MairwunNx) <MairwunNx@gmail.com>
1 parent 05b0699 commit 48fb02c

File tree

1 file changed

+117
-1
lines changed

1 file changed

+117
-1
lines changed
Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,127 @@
11
package com.mairwunnx.projectessentials.backup.managers
22

3+
import com.mairwunnx.projectessentials.backup.configurations.BackupConfiguration
4+
import com.mairwunnx.projectessentials.core.api.v1.configuration.ConfigurationAPI.getConfigurationByName
5+
import com.mairwunnx.projectessentials.core.api.v1.extensions.empty
6+
import kotlinx.coroutines.*
7+
import net.minecraftforge.api.distmarker.Dist
8+
import net.minecraftforge.fml.DistExecutor
9+
import net.minecraftforge.fml.server.ServerLifecycleHooks.getCurrentServer
10+
import org.apache.logging.log4j.LogManager
11+
import org.apache.logging.log4j.MarkerManager
12+
import org.zeroturnaround.zip.ZipUtil
13+
import java.io.File
14+
import java.text.SimpleDateFormat
15+
import java.util.*
16+
import kotlin.system.measureTimeMillis
17+
318
object BackupManager {
19+
private val logger = LogManager.getLogger()
20+
private val marker = MarkerManager.getMarker("BACKUP")
21+
private var firstLaunch = true
22+
private lateinit var job: Job
23+
24+
private val backupConfiguration by lazy {
25+
getConfigurationByName<BackupConfiguration>("backup").take()
26+
}
27+
428
fun initialize() {
29+
if (backupConfiguration.backupEnabled) {
30+
logger.debug(marker, "Initializing backup loop")
31+
job = CoroutineScope(Dispatchers.Default).launch {
32+
while (isActive) loop()
33+
}.also { it.start() }
34+
}
35+
}
36+
37+
fun terminate() = logger.debug(marker, "Terminating backup loop").also {
38+
job.cancel().also { firstLaunch = true }
39+
}
40+
41+
private suspend fun loop() {
42+
if (backupConfiguration.backupEnabled) {
43+
if (backupConfiguration.firstLaunchDelay) {
44+
if (firstLaunch) {
45+
logger.debug(marker, "Backup loop do first launch delay")
46+
delay(backupConfiguration.backupCreationDelay * 1000L)
47+
firstLaunch = false
48+
}
49+
}
550

51+
File(backupConfiguration.backupDirectoryPath).also { it.mkdirs() }.let {
52+
purge(it).run { rotate(it) }.run { compile(it) }
53+
}
54+
} else {
55+
logger.debug(marker, "Backup loop was aborted with configuration").also { terminate() }
56+
}
657
}
758

8-
fun terminate() {
59+
private fun purge(out: File) {
60+
if (backupConfiguration.purgeBackupOutDirectory) {
61+
logger.debug(marker, "Purging backup out directory")
62+
// @formatter:off
63+
out.listFiles()!!.asSequence().filter {
64+
it.extension !in backupConfiguration.purgeExtensionsExceptions &&
65+
it.nameWithoutExtension !in backupConfiguration.purgeNamesExceptions &&
66+
it.extension != "zip"
67+
}.forEach { it.deleteRecursively() }
68+
// @formatter:on
69+
} else logger.debug(marker, "Purging backup out directory skipped")
70+
}
71+
72+
private fun rotate(out: File) {
73+
logger.debug(marker, "Starting rolling old backup files")
74+
out.listFiles()!!.asSequence().filter {
75+
it.extension == "zip"
76+
}.also { files ->
77+
if (files.count() > backupConfiguration.maxBackupFiles) {
78+
if (backupConfiguration.rollingBackupFilesEnabled) {
79+
files.map { it.lastModified() }.sorted().take(
80+
files.count() - backupConfiguration.maxBackupFiles
81+
).forEach { date ->
82+
files.filter { it.lastModified() == date }.forEach { it.delete() }
83+
}
84+
} else files.forEach { it.delete() }
85+
}
86+
}
87+
}
88+
89+
private fun compile(file: File) {
90+
runBlocking {
91+
launch(Dispatchers.Default) {
92+
try {
93+
getCurrentServer().save(false, true, true)
94+
} catch (ex: ConcurrentModificationException) {
95+
logger.error("Saving server world failed, backup will pack probably outdated data!")
96+
}
97+
}.also {
98+
it.invokeOnCompletion {
99+
val path = outPath(file).also { path -> logger.debug("Saving backup to $path") }
100+
val inPath = inPath()
101+
measureTimeMillis {
102+
ZipUtil.pack(
103+
File(inPath), File(path), backupConfiguration.backupCompressionLevel
104+
)
105+
}.also { time -> logger.debug("Backup saved to $path for ${time / 1000} seconds") }
106+
}
107+
}.start()
108+
}
109+
}
110+
111+
private fun inPath(): String {
112+
var path = String.empty
113+
DistExecutor.runWhenOn(Dist.CLIENT) {
114+
Runnable { path = "saves${File.separator}${getCurrentServer().folderName}" }
115+
}
116+
DistExecutor.runWhenOn(Dist.DEDICATED_SERVER) {
117+
Runnable { path = getCurrentServer().folderName }
118+
}
119+
return if (path.isEmpty()) error("Backup `in` path was empty") else path
120+
}
9121

122+
private fun outPath(file: File): String {
123+
val ext = ".zip"
124+
val dateTime = SimpleDateFormat(backupConfiguration.backupDateFormat).format(Date())
125+
return file.absolutePath + File.separator + getCurrentServer().folderName + "-" + dateTime + ext
10126
}
11127
}

0 commit comments

Comments
 (0)