From c3ab40efcff3b2667c7422331ffd4d30590dd74a Mon Sep 17 00:00:00 2001 From: Ellet <73608287+ellet0@users.noreply.github.com> Date: Fri, 5 Jul 2024 06:21:58 +0300 Subject: [PATCH] feat: server list sync (#17) * chore: remove duplicated code in function Path.createFileWithParentDirectoriesOrTerminate() in file FilePathUtils.kt * chore: add Path.createFileOrTerminate() in FilePathUtils.kt * fix(proguard): the BuildMinimizedJarTask will suppress the output if '-i' is not passed * fix: fix proguard warning related to Okio * chore(admin): use java.io.IOException for IOException in PrismLauncherDataSource * chore(common): add Path.isFileEmpty() in SharedFilePathUtils.kt * chore: remove println() in finalize() function * docs(readme): update server list syncing status in README.md * docs(readme): update the development status for syncing Mods, Resource Packs, Shader packs * chore(proguard): use when block in BuildMinimizedJarTask.execute() to check for project.gradle.startParameter.logLevel --- README.md | 12 +- .../prismLauncher/PrismLauncherDataSource.kt | 2 +- .../src/main/kotlin/BuildMinimizedJarTask.kt | 14 +- .../main/kotlin/utils/SharedFilePathUtils.kt | 3 + gradle/libs.versions.toml | 4 + sync-script/build.gradle.kts | 3 + sync-script/src/main/kotlin/Main.kt | 4 +- .../kotlin/syncService/ServersSyncService.kt | 144 +++++++++++++++++- .../src/main/kotlin/utils/FilePathUtils.kt | 14 +- sync-script/src/main/kotlin/utils/Utils.kt | 3 + 10 files changed, 176 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index b9b8a63..7117272 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ instance**. The script will sync the following: -1. 🛠ī¸ Mods (experimental) -2. 🎨 Resource packs (highly experimental) -3. ✨ Shader-packs (not implemented yet) -4. 🌐 Server List (in-game) (not implemented yet) -5. 🧩 Mods configurations (not implemented yet): These may be synced initially, since each player can have their own +1. 🛠ī¸ Mods (Experimental): In the experimental phase and subject to changes. +2. 🎨 Resource Packs (Highly Experimental): In the highly experimental phase and subject to changes. +3. ✨ Shader Packs (Not Implemented Yet): This feature is not available yet. Future updates may implement it +4. 🌐 In-Game Server List (Alpha): It is subject to potential changes or removal in future updates. +5. 🧩 Mods configurations (Not Implemented Yet): These may be synced initially, since each player can have their own configurations. We have not yet found a solution, except by enforcing the same mod settings for all players. -6. ⌨ī¸ Keybindings (not implemented yet): These may be synced initially since each player can have their own +6. ⌨ī¸ Keybindings (Not Implemented Yet): These may be synced initially since each player can have their own keybindings. We have not yet found a solution for this, except by enforcing the same keybindings for all players diff --git a/admin/src/main/kotlin/launchers/prismLauncher/PrismLauncherDataSource.kt b/admin/src/main/kotlin/launchers/prismLauncher/PrismLauncherDataSource.kt index 34d492b..0984279 100644 --- a/admin/src/main/kotlin/launchers/prismLauncher/PrismLauncherDataSource.kt +++ b/admin/src/main/kotlin/launchers/prismLauncher/PrismLauncherDataSource.kt @@ -5,10 +5,10 @@ import constants.DotMinecraftFileNames import curseForgeDataSource import launchers.Instance import launchers.LauncherDataSource -import okio.IOException import syncInfo.models.mod.Mod import utils.SystemFileProvider import utils.listFilteredPaths +import java.io.IOException import java.nio.file.Path import kotlin.io.path.absolutePathString import kotlin.io.path.exists diff --git a/buildSrc/src/main/kotlin/BuildMinimizedJarTask.kt b/buildSrc/src/main/kotlin/BuildMinimizedJarTask.kt index 762de32..13a077f 100644 --- a/buildSrc/src/main/kotlin/BuildMinimizedJarTask.kt +++ b/buildSrc/src/main/kotlin/BuildMinimizedJarTask.kt @@ -152,12 +152,13 @@ open class BuildMinimizedJarTask : DefaultTask() { // A workaround for executing ProGuard without getting the notes by disabling the logging // when the `-i` or `--info` is not set - if (project.gradle.startParameter.logLevel != LogLevel.INFO) { - suppressOutputAndExecute { - proguardTask.actions.forEach { it.execute(proguardTask) } + when (project.gradle.startParameter.logLevel) { + LogLevel.INFO -> proguardTask.actions.forEach { it.execute(proguardTask) } + else -> { + suppressOutputAndExecute { + proguardTask.actions.forEach { it.execute(proguardTask) } + } } - } else { - proguardTask.actions.forEach { it.execute(proguardTask) } } logResultMessage(isObfuscatedEnabled = isObfuscatedEnabled) @@ -173,7 +174,8 @@ open class BuildMinimizedJarTask : DefaultTask() { val formattedPercentageDifference = String.format("%.2f%%", kotlinMathAbs(percentageDifference)) logger.lifecycle( - "đŸ“Ļ The size of the Proguard ${if (isObfuscatedEnabled) "obfuscated" else "minimized"} JAR file (${minimizedJarFile.name}) is $minimizedFileSizeInMegabytes MB." + + "đŸ“Ļ The size of the Proguard ${if (isObfuscatedEnabled) "obfuscated" else "minimized"} JAR file " + + "(${minimizedJarFile.name}) is $minimizedFileSizeInMegabytes MB." + " The size has been reduced \uD83D\uDCC9 by $formattedPercentageDifference. Location: ${minimizedJarFile.path}", ) } diff --git a/common/src/main/kotlin/utils/SharedFilePathUtils.kt b/common/src/main/kotlin/utils/SharedFilePathUtils.kt index 7f0fca2..a206561 100644 --- a/common/src/main/kotlin/utils/SharedFilePathUtils.kt +++ b/common/src/main/kotlin/utils/SharedFilePathUtils.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.withContext import java.nio.file.FileSystemException import java.nio.file.Files import java.nio.file.Path +import kotlin.io.path.fileSize import kotlin.io.path.name import kotlin.streams.toList @@ -31,3 +32,5 @@ suspend fun Path.listFilteredPaths(filter: (path: Path) -> Boolean): Result = + buildNbtList { + serversSyncService.servers.map { + val serverCompound = + buildNbtCompound { + put("ip", it.address) + put("name", it.name) + } + add(serverCompound) + } + } + + val newRootCompound = + currentRootCompound.let { + val mutableRootMap = it.toMutableMap() + + val mutableMainMap = it[SERVER_NBT_MAIN_COMPOUND_KEY]?.nbtCompound?.toMutableMap() ?: mutableMapOf() + mutableMainMap["servers"] = newServerListCompound + + mutableRootMap[SERVER_NBT_MAIN_COMPOUND_KEY] = NbtCompound(mutableMainMap) + + NbtCompound(mutableRootMap) + } + + updateServersDatFile(newRootCompound = newRootCompound).getOrElse { + showErrorMessageAndTerminate( + title = "🚨 File Update Error", + message = "⚠ī¸ Unable to update the '${serversDatFilePath.name}' file: $it", + ) + return + } + + println("\uD83D\uDD52 Finished syncing the server list in ${executionTimer.getRunningUntilNowDuration().inWholeMilliseconds}ms.") } + + private fun loadServersDatFile(): Result { + return try { + if (serversDatFilePath.exists()) { + if (!serversDatFilePath.isRegularFile()) { + showErrorMessageAndTerminate( + title = "❌ Invalid '${serversDatFilePath.name}' File", + message = + "\uD83D\uDEE0 '${serversDatFilePath.name}' must be a file \uD83D\uDCC2, a directory/folder was found instead.", + ) + // This will never reach due to the previous statement stopping the application + exitProcess(1) + } + if (serversDatFilePath.isFileEmpty()) { + println("ℹī¸ The file '${serversDatFilePath.name}' exists and is currently empty.") + return Result.success(createEmptyServerCompound()) + } + return Result.success( + serversDatFilePath + .inputStream() + .use { inputStream -> + nbt.decodeFromStream(inputStream) + } as NbtCompound, + ) + } + Result.success(createEmptyServerCompound()) + } catch (e: Exception) { + Result.failure(e) + } + } + + private fun updateServersDatFile(newRootCompound: NbtCompound): Result = + runCatching { + serversDatFilePath + .outputStream() + .use { outputStream -> + nbt.encodeToStream(newRootCompound, outputStream) + } + } + + private fun createEmptyServerCompound(): NbtCompound = NbtCompound(mapOf(SERVER_NBT_MAIN_COMPOUND_KEY to NbtCompound(emptyMap()))) } diff --git a/sync-script/src/main/kotlin/utils/FilePathUtils.kt b/sync-script/src/main/kotlin/utils/FilePathUtils.kt index f7872a0..7af21d2 100644 --- a/sync-script/src/main/kotlin/utils/FilePathUtils.kt +++ b/sync-script/src/main/kotlin/utils/FilePathUtils.kt @@ -70,16 +70,10 @@ fun Path.createParentDirectoriesIfDoesNotExist() { fun Path.createFileWithParentDirectoriesOrTerminate() { createParentDirectoriesIfDoesNotExist() - if (!parent.exists()) { - try { - createParentDirectories() - } catch (e: Exception) { - showErrorMessageAndTerminate( - title = "File Creation Error ⚠\uFE0F", - message = "❌ Failed to create the directory '${this.name}': $e", - ) - } - } + createFileOrTerminate() +} + +fun Path.createFileOrTerminate() { try { createFile() } catch (e: Exception) { diff --git a/sync-script/src/main/kotlin/utils/Utils.kt b/sync-script/src/main/kotlin/utils/Utils.kt index 9cc98e2..0ad8a7b 100644 --- a/sync-script/src/main/kotlin/utils/Utils.kt +++ b/sync-script/src/main/kotlin/utils/Utils.kt @@ -19,6 +19,9 @@ import kotlin.system.exitProcess * we might call [showErrorMessageAndTerminate] when having error while loading the [ScriptConfig] * (for example), which is why we can't [ScriptConfig.getInstanceOrThrow] because we will get not initialized error * while showing an error message + * + * TODO: Some usages pass HTML message to [message], which will cause unreadable message in CLI mode, we need to + * either provide a different message parameter for the CLI or convert the HTML to plain text * */ fun showErrorMessageAndTerminate( title: String,