diff --git a/api/src/main/java/com/github/zly2006/xbackup/api/XBackupApi.java b/api/src/main/java/com/github/zly2006/xbackup/api/XBackupApi.java index 57895a8..bbad4cb 100644 --- a/api/src/main/java/com/github/zly2006/xbackup/api/XBackupApi.java +++ b/api/src/main/java/com/github/zly2006/xbackup/api/XBackupApi.java @@ -20,12 +20,14 @@ static XBackupApi getInstance() { static XBackupApi setInstance(XBackupApi instance) { if (Instance.instance != null && instance != null) { - throw new IllegalStateException("Instance already set"); + Instance.instance.close(); } Instance.instance = instance; return instance; } + void close(); + @NotNull Path getBlobFile(@NotNull String hash); @Nullable IBackup getBackup(int id); diff --git a/common/src/main/kotlin/com/github/zly2006/xbackup/BackupDatabaseService.kt b/common/src/main/kotlin/com/github/zly2006/xbackup/BackupDatabaseService.kt index bce32f5..e8ba8b0 100644 --- a/common/src/main/kotlin/com/github/zly2006/xbackup/BackupDatabaseService.kt +++ b/common/src/main/kotlin/com/github/zly2006/xbackup/BackupDatabaseService.kt @@ -717,7 +717,7 @@ class BackupDatabaseService( BackupTable.selectAll().count().toInt() } - fun close() { + override fun close() { syncExecutor.close() TransactionManager.closeAndUnregister(database) } diff --git a/common/src/main/resources/assets/x-backup/lang/en_us.json b/common/src/main/resources/assets/x-backup/lang/en_us.json index 7db130d..5830665 100644 --- a/common/src/main/resources/assets/x-backup/lang/en_us.json +++ b/common/src/main/resources/assets/x-backup/lang/en_us.json @@ -48,5 +48,10 @@ "xb.gui.backups.restore_check_failed": "This backup is corrupted, cannot restore it!", "xb.gui.backups.restored": "Backup restored successfully!", "xb.gui.backups.title": "World Backups", - "xb.gui.no_polylib": "PolyLib is not installed, please install it to use the GUI." + "xb.gui.no_polylib": "PolyLib is not installed, please install it to use the GUI.", + "xb.gui.restore.title": "Restore Completed", + "xb.gui.restore.id": "Backup #%s", + "xb.gui.restore.comment": "Comment: %s", + "xb.gui.restore.close": "Back to Title", + "xb.gui.restore.reopen": "Reopen Save" } diff --git a/common/src/main/resources/assets/x-backup/lang/zh_cn.json b/common/src/main/resources/assets/x-backup/lang/zh_cn.json index 2d0d05b..8b1b0d4 100644 --- a/common/src/main/resources/assets/x-backup/lang/zh_cn.json +++ b/common/src/main/resources/assets/x-backup/lang/zh_cn.json @@ -48,5 +48,10 @@ "xb.gui.backups.restore_check_failed": "此备份已损坏,无法回档!", "xb.gui.backups.restored": "成功还原备份", "xb.gui.backups.title": "备份列表", - "xb.gui.no_polylib": "未安装 PolyLib,请安装后使用 GUI 功能。" + "xb.gui.no_polylib": "未安装 PolyLib,请安装后使用 GUI 功能。", + "xb.gui.restore.title": "回档完成", + "xb.gui.restore.id": "备份 #%s", + "xb.gui.restore.comment": "备注: %s", + "xb.gui.restore.close": "返回标题界面", + "xb.gui.restore.reopen": "重新打开存档" } diff --git a/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt b/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt index 72628da..88fc94d 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt @@ -6,6 +6,7 @@ import com.github.zly2006.xbackup.Utils.save import com.github.zly2006.xbackup.Utils.send import com.github.zly2006.xbackup.Utils.setAutoSaving import com.github.zly2006.xbackup.api.IBackup +import com.github.zly2006.xbackup.gui.RestoreInfoScreen import com.github.zly2006.xbackup.ktdsl.register import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.arguments.IntegerArgumentType @@ -739,6 +740,8 @@ object Commands { XBackup.isBusy = false XBackup.restoring = false it.finishRestore() + } else if (!forceStop) { + RestoreInfoScreen.open(backup, path) } } } diff --git a/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt b/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt index 3c68d1a..16cf64b 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt @@ -42,9 +42,9 @@ object XBackup : ModInitializer { lateinit var config: Config private val configPath = FabricLoader.getInstance().configDir.resolve("x-backup.config.json") val log = LoggerFactory.getLogger("XBackup")!! - const val MOD_VERSION = /*$ mod_version*/ "0.3.11" - const val GIT_COMMIT = /*$ git_commit*/ "f1f683f" - const val COMMIT_DATE = /*$ commit_date*/ "2025-04-16T14:12:41+08:00" + const val MOD_VERSION = /*$ mod_version*/ "0.3.12" + const val GIT_COMMIT = /*$ git_commit*/ "7736a28" + const val COMMIT_DATE = /*$ commit_date*/ "2025-06-15T23:39:54+08:00" var _service: BackupDatabaseService? = null val service get() = _service!! var server: MinecraftServer? = null diff --git a/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt b/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt new file mode 100644 index 0000000..29fee6d --- /dev/null +++ b/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt @@ -0,0 +1,82 @@ +package com.github.zly2006.xbackup.gui + +import com.github.zly2006.xbackup.XBackup +import com.github.zly2006.xbackup.api.IBackup +import net.minecraft.client.MinecraftClient +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.widget.ButtonWidget +import net.minecraft.text.Text +import java.nio.file.Path +import kotlin.io.path.name + +class RestoreInfoScreen(private val backup: IBackup, private val worldRoot: Path) : Screen(Text.translatable("xb.gui.restore.title")) { + private lateinit var reopenButton: ButtonWidget + + override fun init() { + reopenButton = addDrawableChild( + ButtonWidget.builder(Text.translatable("xb.gui.restore.reopen")) { + reopenWorld() + }.dimensions(width / 2 - 75, height - 52, 150, 20).build() + ) + addDrawableChild( + ButtonWidget.builder(Text.translatable("xb.gui.restore.close")) { + client?.setScreen(null) + }.dimensions(width / 2 - 75, height - 28, 150, 20).build() + ) + } + + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + //? if >= 1.20.4 { + renderBackground(context, mouseX, mouseY, delta) + //?} else { + /*renderBackground(context) + *///?} + super.render(context, mouseX, mouseY, delta) + context.drawCenteredTextWithShadow(textRenderer, title, width / 2, 20, 0xFFFFFF) + var y = height / 2 - 20 + val idText = Text.translatable("xb.gui.restore.id", backup.id) + context.drawCenteredTextWithShadow(textRenderer, idText, width / 2, y, 0xFFFFFF) + y += 12 + if (backup.comment.isNotEmpty()) { + val comment = Text.translatable("xb.gui.restore.comment", backup.comment) + context.drawCenteredTextWithShadow(textRenderer, comment, width / 2, y, 0xFFFFFF) + } + + val progress = XBackup.service.activeTaskProgress + if (progress in 0..100) { + val barWidth = 150 + val barHeight = 8 + val x = (width - barWidth) / 2 + val yBar = height / 2 + 20 + context.fill(x, yBar, x + barWidth, yBar + barHeight, 0xFF555555.toInt()) + val w = (barWidth * progress) / 100 + if (w > 0) { + context.fill(x + 1, yBar + 1, x + w - 1, yBar + barHeight - 1, 0xFF00FF00.toInt()) + } + context.drawCenteredTextWithShadow(textRenderer, Text.literal("$progress%"), width / 2, yBar - 10, 0xFFFFFF) + } + } + + companion object { + fun open(backup: IBackup, worldRoot: Path) { + val client = MinecraftClient.getInstance() + client.execute { client.setScreen(RestoreInfoScreen(backup, worldRoot)) } + } + } + + private fun reopenWorld() { + val client = MinecraftClient.getInstance() + client.setScreen(null) + runCatching { + val loader = client.createIntegratedServerLoader() + //? if >= 1.20.4 { + loader.start(worldRoot.normalize().name) { + this.close() + } + //?} else { + /*loader.start(this, worldRoot.normalize().name) + *///?} + }.onFailure { XBackup.log.error("Failed to reopen world", it) } + } +}