Skip to content

Commit 39bcb49

Browse files
committed
Backup controlling task implemented.
Signed-off-by: Pavel Erokhin (MairwunNx) <MairwunNx@gmail.com>
1 parent a8198a2 commit 39bcb49

4 files changed

Lines changed: 208 additions & 0 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package com.mairwunnx.projectessentials.backup
2+
3+
import com.mairwunnx.projectessentials.backup.configuration.BackupConfigurationController
4+
import kotlinx.coroutines.*
5+
import net.minecraftforge.api.distmarker.Dist
6+
import net.minecraftforge.fml.DistExecutor
7+
import org.zeroturnaround.zip.ZipUtil
8+
import java.io.File
9+
import java.text.SimpleDateFormat
10+
import java.util.*
11+
12+
object BackupController {
13+
private lateinit var job: Job
14+
var isFirstLaunch = true
15+
16+
fun launchBackupLoop() {
17+
job = GlobalScope.launch {
18+
while (isActive) {
19+
backupLoop()
20+
}
21+
}
22+
job.start()
23+
}
24+
25+
fun abortBackupLoop() = job.cancel()
26+
27+
private suspend fun backupLoop() {
28+
val configuration = BackupConfigurationController.get()
29+
30+
if (configuration.backupEnabled) {
31+
if (configuration.firstLaunchDelay) {
32+
if (isFirstLaunch) {
33+
delay(configuration.backupCreationDelay)
34+
isFirstLaunch = false
35+
}
36+
}
37+
38+
val backupDirectory = File(configuration.backupDirectoryPath)
39+
backupDirectory.mkdirs()
40+
41+
val fileList = backupDirectory.listFiles() ?: emptyArray()
42+
removeExtraFiles(fileList)
43+
44+
if (fileList.count() >= configuration.maxBackupFiles) {
45+
if (configuration.rollingBackupFilesEnabled) {
46+
doRollingFiles(fileList)
47+
} else {
48+
fileList.forEach {
49+
if (it.isFile && !it.isDirectory) {
50+
it.delete()
51+
}
52+
}
53+
}
54+
}
55+
56+
DistExecutor.runWhenOn(Dist.CLIENT) {
57+
Runnable {
58+
makeBackup("saves${File.separator}${EntryPoint.serverInstance.worldName}")
59+
}
60+
}
61+
62+
DistExecutor.runWhenOn(Dist.DEDICATED_SERVER) {
63+
Runnable {
64+
makeBackup(EntryPoint.serverInstance.worldName)
65+
}
66+
}
67+
68+
delay(configuration.backupCreationDelay)
69+
} else {
70+
abortBackupLoop()
71+
}
72+
}
73+
74+
private fun removeExtraFiles(files: Array<File>) {
75+
if (!BackupConfigurationController.get().removeExtraFiles) return
76+
77+
files.forEach {
78+
if (it.isDirectory || it.extension != "zip") {
79+
it.delete()
80+
}
81+
}
82+
}
83+
84+
private fun doRollingFiles(fileList: Array<File>) {
85+
val lastModifiedDates = mutableListOf<Long>()
86+
fileList.forEach {
87+
if (it.isFile && !it.isDirectory) {
88+
lastModifiedDates.add(it.lastModified())
89+
}
90+
}
91+
92+
val lastModified = lastModifiedDates.min()
93+
if (lastModified != null) {
94+
fileList.forEach {
95+
if (it.isFile && !it.isDirectory) {
96+
if (it.lastModified() == lastModified) {
97+
it.delete()
98+
}
99+
}
100+
}
101+
}
102+
}
103+
104+
private fun makeBackup(path: String) {
105+
val configuration = BackupConfigurationController.get()
106+
107+
ZipUtil.pack(
108+
File(path),
109+
File(buildFilePathName(path)),
110+
configuration.backupCompressionLevel
111+
)
112+
}
113+
114+
private fun buildFilePathName(path: String): String {
115+
val configuration = BackupConfigurationController.get()
116+
val dateFormat = SimpleDateFormat(configuration.backupDateFormat)
117+
val extension = ".zip"
118+
119+
val backupDirectory = configuration.backupDirectoryPath
120+
val currentDateTime = dateFormat.format(Date())
121+
122+
return if (path.contains(File.separator)) {
123+
val worldName = path.split(File.separator)[1]
124+
backupDirectory + File.separator + worldName + currentDateTime + extension
125+
} else {
126+
backupDirectory + File.separator + path + currentDateTime + extension
127+
}
128+
}
129+
}

src/main/kotlin/com/mairwunnx/projectessentials/backup/EntryPoint.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package com.mairwunnx.projectessentials.backup
22

3+
import com.mairwunnx.projectessentials.backup.configuration.BackupConfigurationController
34
import com.mairwunnx.projectessentials.core.EssBase
5+
import net.minecraft.server.MinecraftServer
46
import net.minecraftforge.common.MinecraftForge
7+
import net.minecraftforge.eventbus.api.SubscribeEvent
58
import net.minecraftforge.fml.common.Mod
9+
import net.minecraftforge.fml.event.server.FMLServerStartingEvent
10+
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent
611
import org.apache.logging.log4j.LogManager
712

813
@Suppress("unused")
@@ -15,5 +20,25 @@ class EntryPoint : EssBase() {
1520
logBaseInfo()
1621
validateForgeVersion()
1722
MinecraftForge.EVENT_BUS.register(this)
23+
BackupConfigurationController.load()
24+
}
25+
26+
@SubscribeEvent
27+
@Suppress("UNUSED_PARAMETER")
28+
fun onServerStarting(event: FMLServerStartingEvent) {
29+
serverInstance = event.server
30+
BackupController.isFirstLaunch = true
31+
BackupController.launchBackupLoop()
32+
}
33+
34+
@SubscribeEvent
35+
@Suppress("UNUSED_PARAMETER")
36+
fun onServerStopping(it: FMLServerStoppingEvent) {
37+
BackupController.abortBackupLoop()
38+
BackupConfigurationController.save()
39+
}
40+
41+
companion object {
42+
lateinit var serverInstance: MinecraftServer
1843
}
1944
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.mairwunnx.projectessentials.backup.configuration
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class BackupConfiguration(
7+
var backupEnabled: Boolean = true,
8+
var firstLaunchDelay: Boolean = true,
9+
var backupCreationDelay: Long = 150_000,
10+
var backupCompressionLevel: Int = 3,
11+
var backupDirectoryPath: String = "backup",
12+
var backupDateFormat: String = "yyyy-MM-dd_HH.mm.ss",
13+
var maxBackupFiles: Int = 10,
14+
var rollingBackupFilesEnabled: Boolean = true,
15+
var removeExtraFiles: Boolean = true
16+
)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.mairwunnx.projectessentials.backup.configuration
2+
3+
import com.mairwunnx.projectessentials.core.helpers.MOD_CONFIG_FOLDER
4+
import com.mairwunnx.projectessentials.core.helpers.jsonInstance
5+
import org.apache.logging.log4j.LogManager
6+
import java.io.File
7+
8+
object BackupConfigurationController {
9+
private val backupConfigurationPath = MOD_CONFIG_FOLDER + File.separator + "backup.json"
10+
private var backupConfiguration = BackupConfiguration()
11+
private val logger = LogManager.getLogger()
12+
13+
fun load() {
14+
logger.info("Loading backup configuration")
15+
if (!File(backupConfigurationPath).exists()) {
16+
logger.warn("Backup config not exist! creating it now!")
17+
File(MOD_CONFIG_FOLDER).mkdirs()
18+
val defaultConfig = jsonInstance.stringify(
19+
BackupConfiguration.serializer(), backupConfiguration
20+
)
21+
File(backupConfigurationPath).writeText(defaultConfig)
22+
}
23+
backupConfiguration = jsonInstance.parse(
24+
BackupConfiguration.serializer(), File(backupConfigurationPath).readText()
25+
)
26+
}
27+
28+
fun save() {
29+
logger.info("Saving backup configuration")
30+
File(MOD_CONFIG_FOLDER).mkdirs()
31+
val backupConfigurationString = jsonInstance.stringify(
32+
BackupConfiguration.serializer(), backupConfiguration
33+
)
34+
File(backupConfigurationPath).writeText(backupConfigurationString)
35+
}
36+
37+
fun get() = backupConfiguration
38+
}

0 commit comments

Comments
 (0)