From 95fd93d4e32a9eba5b7265b0e7512251c99bc5b6 Mon Sep 17 00:00:00 2001 From: lnminh Date: Fri, 29 May 2026 07:58:12 +0700 Subject: [PATCH 1/2] Updated to 26.1.2 --- CHANGELOG.md | 4 + api/build.gradle.kts | 5 + build.gradle.kts | 108 ++- cli/build.gradle.kts | 15 +- common/build.gradle.kts | 13 +- .../resources/assets/x-backup/lang/en_us.json | 7 +- .../resources/assets/x-backup/lang/zh_cn.json | 7 +- compat-fake-source/build.gradle.kts | 9 + .../api/permissions/v0/Permissions.java | 8 +- .../{command => commands}/CommandSource.java | 2 +- .../commands/SharedSuggestionProvider.java | 4 + gradle.properties | 28 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradle_error.txt | 286 ++++++++ mc_26_1_2_naming_port.md | 626 ++++++++++++++++++ project_map.md | 145 ++++ settings.gradle.kts | 31 +- .../github/zly2006/xbackup/gui/BMStyle.java | 18 +- .../zly2006/xbackup/gui/OptionDialog.java | 28 +- .../xbackup/mixin/MixinSelectWorldScreen.java | 74 +-- .../zly2006/xbackup/mixin/MixinServer.java | 6 +- .../disable/MixinDedicatedServerWatchdog.java | 12 +- .../mixin/disable/MixinStorageIoWorker.java | 4 +- .../com/github/zly2006/xbackup/Commands.kt | 186 +++--- .../kotlin/com/github/zly2006/xbackup/Task.kt | 4 +- .../com/github/zly2006/xbackup/Utils.kt | 40 +- .../com/github/zly2006/xbackup/XBackup.kt | 60 +- .../zly2006/xbackup/cloud/OnedriveSupport.kt | 19 +- .../github/zly2006/xbackup/gui/BackupsGui.kt | 58 +- .../zly2006/xbackup/gui/RestoreInfoScreen.kt | 66 +- src/main/resources/xb.shared.accesswidener | 7 +- stonecutter.gradle.kts | 51 -- versions/1.20.1/gradle.properties | 8 - versions/1.20.4/gradle.properties | 8 - versions/1.20.6/gradle.properties | 8 - versions/1.21.1/gradle.properties | 8 - versions/1.21.11/gradle.properties | 8 - versions/1.21.3/gradle.properties | 8 - versions/1.21.4/gradle.properties | 8 - versions/1.21.5/gradle.properties | 8 - versions/1.21.6/gradle.properties | 8 - versions/1.21.9/gradle.properties | 8 - 42 files changed, 1423 insertions(+), 590 deletions(-) rename compat-fake-source/src/main/java/net/minecraft/{command => commands}/CommandSource.java (71%) create mode 100644 compat-fake-source/src/main/java/net/minecraft/commands/SharedSuggestionProvider.java create mode 100644 gradle_error.txt create mode 100644 mc_26_1_2_naming_port.md create mode 100644 project_map.md delete mode 100644 stonecutter.gradle.kts delete mode 100644 versions/1.20.1/gradle.properties delete mode 100644 versions/1.20.4/gradle.properties delete mode 100644 versions/1.20.6/gradle.properties delete mode 100644 versions/1.21.1/gradle.properties delete mode 100644 versions/1.21.11/gradle.properties delete mode 100644 versions/1.21.3/gradle.properties delete mode 100644 versions/1.21.4/gradle.properties delete mode 100644 versions/1.21.5/gradle.properties delete mode 100644 versions/1.21.6/gradle.properties delete mode 100644 versions/1.21.9/gradle.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index e3c5248..10c55e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,7 @@ Refactor restart logic. Auto restart is disabled by default now because of plenty of mod incompatibility. + +## 0.1.0 + +Updated to 26.1.2. No new features yet. \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 562b1b1..5e7bba5 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + `java-library` + kotlin("jvm") version "2.3.21" +} + group = "io.github.zly2006" version = rootProject.version diff --git a/build.gradle.kts b/build.gradle.kts index 26b36b4..e76a6b1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,12 +1,14 @@ plugins { `maven-publish` - id("fabric-loom") - kotlin("jvm") - kotlin("plugin.serialization") - id("io.github.goooler.shadow") version "8.1.7" - id("me.modmuss50.mod-publish-plugin") + id("net.fabricmc.fabric-loom") version "1.16-SNAPSHOT" + kotlin("jvm") version "2.3.21" + kotlin("plugin.serialization") version "2.3.21" + id("com.gradleup.shadow") version "9.0.0" + id("me.modmuss50.mod-publish-plugin") version "0.5.1" } +import org.gradle.api.tasks.bundling.AbstractArchiveTask + class ModData { val id = property("mod.id").toString() val name = property("mod.name").toString() @@ -20,9 +22,12 @@ class ModDependencies { val mod = ModData() val deps = ModDependencies() -val mcVersion = stonecutter.current.version +val mcVersion = "26.1.2" val mcDep = property("mod.mc_dep").toString() +// MC 26.1.2 is unobfuscated — no remapping needed, use shadowJar directly +val jarTaskProvider = tasks.named("shadowJar") + version = "${mod.version}+$mcVersion" group = mod.group base { archivesName.set(mod.id) } @@ -31,6 +36,19 @@ loom { accessWidenerPath = rootProject.file("src/main/resources/xb.shared.accesswidener") } +allprojects { + repositories { + mavenCentral() + } + configurations.all { + resolutionStrategy { + force("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") + force("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2") + force("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.10.2") + } + } +} + repositories { fun strictMaven(url: String, alias: String, vararg groups: String) = exclusiveContent { forRepository { maven(url) { name = alias } } @@ -43,28 +61,25 @@ repositories { } dependencies { + println("CONFIGS: " + configurations.map { it.name }) fun fapi(vararg modules: String) = modules.forEach { - modImplementation(fabricApi.module(it, deps["fabric_api"])) + implementation(fabricApi.module(it, deps["fabric_api"])) } testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.6.10") minecraft("com.mojang:minecraft:$mcVersion") - mappings("net.fabricmc:yarn:$mcVersion+build.${deps["yarn_build"]}:v2") - modImplementation("net.fabricmc:fabric-loader:${deps["fabric_loader"]}") - modImplementation("net.fabricmc:fabric-language-kotlin:${deps["kotlin_loader_version"]}") + // MC 26.1.2 is unobfuscated — no Yarn mappings needed + implementation("net.fabricmc:fabric-loader:${deps["fabric_loader"]}") + implementation("net.fabricmc:fabric-language-kotlin:${deps["kotlin_loader_version"]}") fapi( - // Add modules from https://github.com/FabricMC/fabric "fabric-lifecycle-events-v1", - "fabric-resource-loader-v0" + "fabric-resource-loader-v0", + "fabric-command-api-v2" ) - if (stonecutter.eval(stonecutter.current.version, ">=1.20")) { - fapi("fabric-command-api-v2") - } - if (deps["poly_lib"].isNotEmpty()) { - modCompileOnly("net.creeperhost:polylib-fabric:${deps["poly_lib"]}") { + compileOnly("maven.modrinth:polylib:2.0.6") { exclude(group = "net.fabricmc.fabric-api") exclude(group = "dev.architectury") exclude(group = "teamreborn") @@ -72,17 +87,16 @@ dependencies { } api(project(":common")) - configurations.create("compileLib") { - defaultDependencies { - add(project(":common", configuration = "shadow")) - } - } + configurations.create("compileLib") + add("compileLib", project(":common")) + add("compileLib", project(":api")) + add("compileLib", project(":common", configuration = "shadow")) compileOnly(project(":compat-fake-source")) } loom { decompilers { - get("vineflower").apply { // Adds names to lambdas - useful for mixins + get("vineflower").apply { options.put("mark-corresponding-synthetics", "1") } } @@ -94,9 +108,8 @@ loom { } } -val javaVersion = - if (stonecutter.eval(mcVersion, ">=1.20.6")) 21 - else 17 +// MC 26.1.2 requires Java 25 +val javaVersion = 25 java { withSourcesJar() @@ -126,7 +139,6 @@ tasks.processResources { dependsOn(project(":common").tasks.processResources) outputs.upToDateWhen { false } doLast { - // copying this is for dev only, int here is a shadowJar task copy { from(project(":common").tasks.processResources.get().outputs.files) into(outputs.files.first()) @@ -136,7 +148,7 @@ tasks.processResources { tasks.register("buildAndCollect") { group = "build" - from(tasks.remapJar.get().archiveFile) + from(jarTaskProvider.flatMap { it.archiveFile }) into(rootProject.layout.buildDirectory.file("libs/${mod.version}")) dependsOn("build") } @@ -149,7 +161,7 @@ tasks { project.configurations.shadow.get(), project.configurations["compileLib"] ) - archiveClassifier.set("dev-all") + archiveClassifier.set("") exclude("kotlin/**", "kotlinx/**", "javax/**") exclude("org/checkerframework/**", "org/intellij/**", "org/jetbrains/annotations/**") @@ -179,48 +191,30 @@ tasks { relocate(it, relocatePath + it) } } - - remapJar { - dependsOn(shadowJar) - inputFile.set(shadowJar.get().archiveFile) - } } - publishMods { - file = tasks.remapJar.get().archiveFile + file = jarTaskProvider.flatMap { it.archiveFile } displayName = "${mod.name} ${mod.version} for $mcVersion" version = "${mod.version}+$mcVersion" changelog = rootProject.file("CHANGELOG.md").readText() type = STABLE modLoaders.add("fabric") -// dryRun = providers.environmentVariable("MODRINTH_TOKEN") -// .getOrNull() == null || providers.environmentVariable("CURSEFORGE_TOKEN").getOrNull() == null - modrinth { projectId = property("publish.modrinth").toString() accessToken = providers.environmentVariable("MODRINTH_TOKEN") - minecraftVersions.addAll( - property("mod.mc_targets").toString().split(" ") - .filter { it.isNotBlank() } - .plus(mcVersion) - .distinct() - ) + minecraftVersions.add(mcVersion) requires("fabric-api", "fabric-language-kotlin") optional("polylib") } +} -// curseforge { -// projectId = property("publish.curseforge").toString() -// accessToken = providers.environmentVariable("CURSEFORGE_TOKEN") -// minecraftVersions.addAll( -// property("mod.mc_targets").toString().split(" ") -// .filter { it.isNotBlank() } -// .plus(mcVersion) -// .distinct() -// ) -// requires("fabric-api", "fabric-language-kotlin") -// optional("polylib") -// } +tasks.jar { + enabled = false } + +tasks.assemble { + dependsOn(tasks.shadowJar) +} + diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index d637b4e..4dcf9b4 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -1,20 +1,11 @@ import java.util.jar.Attributes plugins { - id("io.github.goooler.shadow") version "8.1.7" + java + kotlin("jvm") version "2.3.21" + id("com.gradleup.shadow") version "9.0.0" } -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath("com.guardsquare:proguard-gradle:7.6.0") - } -} - -apply(plugin = "java") - java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 175da5a..645a780 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,7 +1,10 @@ @file:Suppress("PropertyName") plugins { - id("io.github.goooler.shadow") version "8.1.7" + `java-library` + kotlin("jvm") version "2.3.21" + kotlin("plugin.serialization") version "2.3.21" + id("com.gradleup.shadow") version "9.0.0" } val exposed_version = property("deps.exposed_version") as String @@ -37,10 +40,10 @@ dependencies { sharedLib("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktorVersion") sharedLib(project(":api")) // kotlin - api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21") - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0") - api("org.jetbrains.kotlinx:atomicfu:0.26.0") + api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.21") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") + api("org.jetbrains.kotlinx:atomicfu:0.27.0") } tasks { 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 5830665..2835ab6 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 @@ -32,7 +32,12 @@ "command.xb.statistics": "Total backups: %s, Blob disk usage: %s, Actual disk usage (only count valid backups): %s", "command.xb.status": "X Backup status: %s", "command.xb.uploading_backup": "Uploading backup %s...", - "command.xb.version": "X Backup %s https://github.com/zly2006/x-backup", + "command.xb.version": "X Backup %s by lnminh https://github.com/lnminh1411/x-backup", + "command.xb.config_reloaded": "Configuration reloaded", + "command.xb.exporting_backup": "Exporting backup %s...", + "command.xb.backup_exported": "Backup %s exported successfully", + "command.xb.zipping_backup": "Zipping backup %s...", + "command.xb.backup_zipped": "Backup %s zipped successfully", "message.xb.error.free_plan_limit": "You have reached the free plan limit (1 GB per backup, 3 backups in total), please upgrade to premium plan at https://redenmc.com/x-backup/plans", "message.xb.prune_finished": "Prune finished, %s backups pruned", "message.xb.pruning_backup": "Pruning backup %s", 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 8b1b0d4..84f34f3 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 @@ -32,7 +32,12 @@ "command.xb.statistics": "总备份数: %s, 总磁盘使用量: %s, 实际磁盘使用量 (仅计算有效备份): %s", "command.xb.status": "X Backup 状态: %s", "command.xb.uploading_backup": "正在上传备份 %s...", - "command.xb.version": "X Backup %s https://github.com/zly2006/x-backup", + "command.xb.version": "X Backup %s by lnminh https://github.com/lnminh1411/x-backup", + "command.xb.config_reloaded": "配置已重新加载", + "command.xb.exporting_backup": "正在导出备份 %s...", + "command.xb.backup_exported": "备份 %s 导出成功", + "command.xb.zipping_backup": "正在打包备份 %s...", + "command.xb.backup_zipped": "备份 %s 打包成功", "message.xb.error.free_plan_limit": "你已达到免费限额 (每个备份最多1GB, 总共 3 个备份),请升级到高级计划 https://redenmc.com/x-backup/plans", "message.xb.prune_finished": "清理完成,已删除 %s 个备份", "message.xb.pruning_backup": "正在清理旧备份 %s", diff --git a/compat-fake-source/build.gradle.kts b/compat-fake-source/build.gradle.kts index 3b89c81..c22c208 100644 --- a/compat-fake-source/build.gradle.kts +++ b/compat-fake-source/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + java + kotlin("jvm") version "2.3.21" +} + java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -10,3 +15,7 @@ tasks.withType().configureEach { kotlin { jvmToolchain(17) } + +tasks.jar { + exclude("net/minecraft/**") +} diff --git a/compat-fake-source/src/main/java/me/lucko/fabric/api/permissions/v0/Permissions.java b/compat-fake-source/src/main/java/me/lucko/fabric/api/permissions/v0/Permissions.java index 2d5d374..3ac74bc 100644 --- a/compat-fake-source/src/main/java/me/lucko/fabric/api/permissions/v0/Permissions.java +++ b/compat-fake-source/src/main/java/me/lucko/fabric/api/permissions/v0/Permissions.java @@ -1,19 +1,19 @@ package me.lucko.fabric.api.permissions.v0; -import net.minecraft.command.CommandSource; +import net.minecraft.commands.SharedSuggestionProvider; import org.jetbrains.annotations.NotNull; @SuppressWarnings("unused") public interface Permissions { - static boolean check(@NotNull CommandSource source, @NotNull String permission, boolean defaultValue) { + static boolean check(@NotNull SharedSuggestionProvider source, @NotNull String permission, boolean defaultValue) { throw new AssertionError("Stub!"); } - static boolean check(@NotNull CommandSource source, @NotNull String permission, int defaultRequiredLevel) { + static boolean check(@NotNull SharedSuggestionProvider source, @NotNull String permission, int defaultRequiredLevel) { throw new AssertionError("Stub!"); } - static boolean check(@NotNull CommandSource source, @NotNull String permission) { + static boolean check(@NotNull SharedSuggestionProvider source, @NotNull String permission) { throw new AssertionError("Stub!"); } } diff --git a/compat-fake-source/src/main/java/net/minecraft/command/CommandSource.java b/compat-fake-source/src/main/java/net/minecraft/commands/CommandSource.java similarity index 71% rename from compat-fake-source/src/main/java/net/minecraft/command/CommandSource.java rename to compat-fake-source/src/main/java/net/minecraft/commands/CommandSource.java index 3701548..1f0d60b 100644 --- a/compat-fake-source/src/main/java/net/minecraft/command/CommandSource.java +++ b/compat-fake-source/src/main/java/net/minecraft/commands/CommandSource.java @@ -1,4 +1,4 @@ -package net.minecraft.command; +package net.minecraft.commands; public interface CommandSource { boolean hasPermissionLevel(int level); diff --git a/compat-fake-source/src/main/java/net/minecraft/commands/SharedSuggestionProvider.java b/compat-fake-source/src/main/java/net/minecraft/commands/SharedSuggestionProvider.java new file mode 100644 index 0000000..fd84b23 --- /dev/null +++ b/compat-fake-source/src/main/java/net/minecraft/commands/SharedSuggestionProvider.java @@ -0,0 +1,4 @@ +package net.minecraft.commands; + +public interface SharedSuggestionProvider { +} diff --git a/gradle.properties b/gradle.properties index d50db4b..c1dff3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,28 +6,26 @@ org.gradle.caching.debug=false org.gradle.configureondemand=true # Mod properties -mod.version=0.3.16 +mod.version=0.4.0 mod.group=com.github.zly2006 mod.id=x-backup mod.name=X Backup -# for fabric.mod.json -mod.mc_dep=[VERSIONED] -# for release title -mod.mc_title=[VERSIONED] -# for release metadata -mod.mc_targets=[VERSIONED] +# Minecraft version +mod.mc_dep=26.1.2 +mod.mc_title=26.1.2 +mod.mc_targets=26.1.2 # Global dependencies -deps.kotlin_loader_version=1.12.3+kotlin.2.0.21 -deps.exposed_version=0.57.0 -deps.fabric_loader=0.18.1 -deps.ktor_version=2.3.12 +deps.kotlin_loader_version=1.13.11+kotlin.2.3.21 +deps.exposed_version=0.61.0 +deps.fabric_loader=0.19.2 +deps.ktor_version=3.5.0 -# Versioned dependencies -deps.yarn_build=[VERSIONED] -deps.fabric_api=[VERSIONED] -deps.poly_lib=[VERSIONED] +# Versioned dependencies (pinned to 26.1.2) +deps.yarn_build=0 +deps.fabric_api=0.150.0+26.1.2 +deps.poly_lib=26.1.2-2.0.6 # Publishing publish.modrinth=zJ4gyF5J diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da..5dd3c01 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradle_error.txt b/gradle_error.txt new file mode 100644 index 0000000..c9230a1 --- /dev/null +++ b/gradle_error.txt @@ -0,0 +1,286 @@ +WARNING: A restricted method in java.lang.System has been called +WARNING: java.lang.System::load has been called by net.rubygrapefruit.platform.internal.NativeLibraryLoader in an unnamed module (file:/C:/Users/lnminh1411/.gradle/wrapper/dists/gradle-9.5.1-bin/iq79hdu3mqx29lgffhp8bfmx/gradle-9.5.1/lib/native-platform-0.22-milestone-29.jar) +WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module +WARNING: Restricted methods will be blocked in a future release unless native access is enabled + +Starting a Gradle Daemon, 1 incompatible and 4 stopped Daemons could not be reused, use --status for details +Configuration on demand is an incubating feature. +Running Stonecutter 0.7.7-beta.2 + +> Configure project :1.20.1 +Fabric Loom: 1.15.5 +Previous process has disowned the lock due to abrupt termination. +Found existing cache lock file (ACQUIRED_PREVIOUS_OWNER_DISOWNED), rebuilding loom cache. This may have been caused by a failed or canceled build. + +[Incubating] Problems report is available at: file:///E:/x-backup/build/reports/problems/problems-report.html + +FAILURE: Build failed with an exception. + +* What went wrong: +A problem occurred configuring project ':1.20.1'. +> Failed to setup Minecraft, java.lang.RuntimeException: Failed to remap 5 mods + +* Try: +> Run with --info or --debug option to get more log output. +> Run with --scan to get full insights from a Build Scan (powered by Develocity). +> Get more help at https://help.gradle.org. + +* Exception is: +org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':1.20.1'. + at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:84) + at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:77) + at org.gradle.configuration.project.LifecycleProjectEvaluator.access$500(LifecycleProjectEvaluator.java:55) + at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:255) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48) + at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.lambda$run$0(LifecycleProjectEvaluator.java:114) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:443) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$fromMutableState$1(DefaultProjectStateRegistry.java:450) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.runWithModelLock(DefaultProjectStateRegistry.java:466) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:450) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:442) + at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:100) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48) + at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:72) + at org.gradle.api.internal.project.DefaultProject.evaluateUnchecked(DefaultProject.java:796) + at org.gradle.api.internal.project.ProjectLifecycleController.lambda$ensureSelfConfigured$2(ProjectLifecycleController.java:93) + at org.gradle.internal.model.StateTransitionController.lambda$doTransition$15(StateTransitionController.java:293) + at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:304) + at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:292) + at org.gradle.internal.model.StateTransitionController.lambda$maybeTransitionIfNotCurrentlyTransitioning$11(StateTransitionController.java:237) + at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:35) + at org.gradle.internal.model.StateTransitionController.maybeTransitionIfNotCurrentlyTransitioning(StateTransitionController.java:233) + at org.gradle.api.internal.project.ProjectLifecycleController.ensureSelfConfigured(ProjectLifecycleController.java:93) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.ensureConfigured(DefaultProjectStateRegistry.java:408) + at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:71) + at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:87) + at org.gradle.execution.DefaultTaskSelector.getSelection(DefaultTaskSelector.java:74) + at org.gradle.execution.selection.DefaultBuildTaskSelector.resolveTaskName(DefaultBuildTaskSelector.java:109) + at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:49) + at org.gradle.execution.TaskNameResolvingBuildTaskScheduler.scheduleRequestedTasks(TaskNameResolvingBuildTaskScheduler.java:66) + at org.gradle.execution.DefaultTasksBuildTaskScheduler.scheduleRequestedTasks(DefaultTasksBuildTaskScheduler.java:72) + at org.gradle.initialization.DefaultTaskExecutionPreparer.lambda$scheduleRequestedTasks$0(DefaultTaskExecutionPreparer.java:47) + at org.gradle.internal.Factories$1.create(Factories.java:30) + at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$runAndReleaseLocks$0(DefaultWorkerLeaseService.java:300) + at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:43) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAndReleaseLocks(DefaultWorkerLeaseService.java:298) + at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:294) + at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withReplacedLocks$4(DefaultWorkerLeaseService.java:450) + at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:372) + at org.gradle.internal.work.DefaultWorkerLeaseService.withReplacedLocks(DefaultWorkerLeaseService.java:449) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:254) + at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:247) + at org.gradle.initialization.DefaultTaskExecutionPreparer.scheduleRequestedTasks(DefaultTaskExecutionPreparer.java:46) + at org.gradle.initialization.VintageBuildModelController.lambda$scheduleRequestedTasks$0(VintageBuildModelController.java:75) + at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:119) + at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:134) + at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:45) + at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:130) + at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:118) + at org.gradle.initialization.VintageBuildModelController.scheduleRequestedTasks(VintageBuildModelController.java:75) + at org.gradle.internal.build.DefaultBuildLifecycleController$DefaultWorkGraphBuilder.addRequestedTasks(DefaultBuildLifecycleController.java:414) + at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$0(DefaultBuildTreeWorkPreparer.java:40) + at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$8(DefaultBuildLifecycleController.java:199) + at org.gradle.internal.build.DefaultBuildWorkPreparer.populateWorkGraph(DefaultBuildWorkPreparer.java:42) + at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.populateTaskGraph(BuildOperationFiringBuildWorkPreparer.java:106) + at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.run(BuildOperationFiringBuildWorkPreparer.java:92) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48) + at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer.populateWorkGraph(BuildOperationFiringBuildWorkPreparer.java:67) + at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$9(DefaultBuildLifecycleController.java:199) + at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:119) + at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:134) + at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:45) + at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:130) + at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:118) + at org.gradle.internal.build.DefaultBuildLifecycleController.populateWorkGraph(DefaultBuildLifecycleController.java:199) + at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.populateWorkGraph(DefaultBuildWorkGraphController.java:169) + at org.gradle.composite.internal.DefaultBuildController.populateWorkGraph(DefaultBuildController.java:76) + at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraphBuilder.withWorkGraph(DefaultIncludedBuildTaskGraph.java:156) + at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$1(DefaultBuildTreeWorkPreparer.java:40) + at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1.run(DefaultIncludedBuildTaskGraph.java:212) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48) + at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph.scheduleWork(DefaultIncludedBuildTaskGraph.java:207) + at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.scheduleRequestedTasks(DefaultBuildTreeWorkPreparer.java:36) + at org.gradle.internal.cc.impl.barrier.BarrierAwareBuildTreeWorkPreparer.scheduleRequestedTasks$lambda$0(BarrierAwareBuildTreeWorkPreparer.kt:34) + at org.gradle.internal.cc.impl.barrier.VintageConfigurationTimeActionRunner.runConfigurationTimeAction(VintageConfigurationTimeActionRunner.kt:48) + at org.gradle.internal.cc.impl.barrier.BarrierAwareBuildTreeWorkPreparer.scheduleRequestedTasks(BarrierAwareBuildTreeWorkPreparer.kt:33) + at org.gradle.internal.cc.impl.VintageBuildTreeWorkController$scheduleAndRunRequestedTasks$1$finalizedGraph$1.call(VintageBuildTreeWorkController.kt:37) + at org.gradle.internal.cc.impl.VintageBuildTreeWorkController$scheduleAndRunRequestedTasks$1$finalizedGraph$1.call(VintageBuildTreeWorkController.kt:37) + at org.gradle.internal.Try.ofFailable(Try.java:46) + at org.gradle.internal.cc.impl.VintageBuildTreeWorkController$scheduleAndRunRequestedTasks$1.apply(VintageBuildTreeWorkController.kt:37) + at org.gradle.internal.cc.impl.VintageBuildTreeWorkController$scheduleAndRunRequestedTasks$1.apply(VintageBuildTreeWorkController.kt:36) + at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph.withNewWorkGraph(DefaultIncludedBuildTaskGraph.java:115) + at org.gradle.internal.cc.impl.VintageBuildTreeWorkController.scheduleAndRunRequestedTasks(VintageBuildTreeWorkController.kt:36) + at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$scheduleAndRunTasks$1(DefaultBuildTreeLifecycleController.java:76) + at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$runBuild$5(DefaultBuildTreeLifecycleController.java:121) + at org.gradle.internal.model.StateTransitionController.lambda$transition$7(StateTransitionController.java:207) + at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:304) + at org.gradle.internal.model.StateTransitionController.lambda$transition$8(StateTransitionController.java:207) + at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:45) + at org.gradle.internal.model.StateTransitionController.transition(StateTransitionController.java:207) + at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.runBuild(DefaultBuildTreeLifecycleController.java:118) + at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:76) + at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:71) + at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:31) + at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) + at org.gradle.internal.buildtree.ProblemReportingBuildActionRunner.run(ProblemReportingBuildActionRunner.java:54) + at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:83) + at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:135) + at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:65) + at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.lambda$execute$0(RootBuildLifecycleBuildActionExecutor.java:101) + at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:126) + at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.execute(RootBuildLifecycleBuildActionExecutor.java:101) + at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.runRootBuildAction(BuildTreeLifecycleBuildActionExecutor.java:104) + at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.execute(BuildTreeLifecycleBuildActionExecutor.java:74) + at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$2.call(RunAsBuildOperationBuildActionExecutor.java:65) + at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$2.call(RunAsBuildOperationBuildActionExecutor.java:61) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54) + at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor.execute(RunAsBuildOperationBuildActionExecutor.java:61) + at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.lambda$execute$0(RunAsWorkerThreadBuildActionExecutor.java:36) + at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$runAndReleaseLocks$0(DefaultWorkerLeaseService.java:300) + at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:43) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAndReleaseLocks(DefaultWorkerLeaseService.java:298) + at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:294) + at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:286) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:130) + at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.execute(RunAsWorkerThreadBuildActionExecutor.java:36) + at org.gradle.tooling.internal.provider.continuous.ContinuousBuildActionExecutor.execute(ContinuousBuildActionExecutor.java:110) + at org.gradle.tooling.internal.provider.SubscribableBuildActionExecutor.execute(SubscribableBuildActionExecutor.java:64) + at org.gradle.internal.session.DefaultBuildSessionContext.execute(DefaultBuildSessionContext.java:46) + at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor$ActionImpl.apply(BuildSessionLifecycleBuildActionExecutor.java:106) + at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor$ActionImpl.apply(BuildSessionLifecycleBuildActionExecutor.java:94) + at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:73) + at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor.execute(BuildSessionLifecycleBuildActionExecutor.java:67) + at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor.execute(BuildSessionLifecycleBuildActionExecutor.java:45) + at org.gradle.internal.buildprocess.execution.StartParamsValidatingActionExecutor.execute(StartParamsValidatingActionExecutor.java:57) + at org.gradle.internal.buildprocess.execution.StartParamsValidatingActionExecutor.execute(StartParamsValidatingActionExecutor.java:32) + at org.gradle.internal.buildprocess.execution.SessionFailureReportingActionExecutor.execute(SessionFailureReportingActionExecutor.java:51) + at org.gradle.internal.buildprocess.execution.SessionFailureReportingActionExecutor.execute(SessionFailureReportingActionExecutor.java:39) + at org.gradle.internal.buildprocess.execution.SetupLoggingActionExecutor.execute(SetupLoggingActionExecutor.java:47) + at org.gradle.internal.buildprocess.execution.SetupLoggingActionExecutor.execute(SetupLoggingActionExecutor.java:31) + at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:70) + at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.ForwardClientInput.lambda$execute$0(ForwardClientInput.java:40) + at org.gradle.internal.daemon.clientinput.ClientInputForwarder.forwardInput(ClientInputForwarder.java:80) + at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:37) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:64) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63) + at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84) + at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) + at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) + at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52) + at org.gradle.launcher.daemon.server.DaemonStateCoordinator.lambda$runCommand$0(DaemonStateCoordinator.java:321) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47) +Caused by: java.lang.RuntimeException: Failed to setup Minecraft, java.lang.RuntimeException: Failed to remap 5 mods + at net.fabricmc.loom.util.ExceptionUtil.createDescriptiveWrapper(ExceptionUtil.java:59) + at net.fabricmc.loom.configuration.CompileConfiguration.lambda$run$1(CompileConfiguration.java:135) + at net.fabricmc.loom.configuration.CompileConfiguration.lambda$afterEvaluationWithService$7(CompileConfiguration.java:487) + at net.fabricmc.loom.util.gradle.GradleUtils.lambda$afterSuccessfulEvaluation$0(GradleUtils.java:51) + at org.gradle.internal.code.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:124) + at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1.run(DefaultListenerBuildOperationDecorator.java:173) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30) + at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48) + at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction.execute(DefaultListenerBuildOperationDecorator.java:170) + at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:101) + at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:89) + at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:44) + at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:270) + at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:172) + at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:84) + at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:70) + at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:382) + at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:274) + at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:160) + at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37) + at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88) + at jdk.proxy1/jdk.proxy1.$Proxy64.afterEvaluate(Unknown Source) + at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:247) + at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:244) + at org.gradle.api.internal.project.DefaultProject.stepEvaluationListener(DefaultProject.java:1510) + at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:253) + ... 181 more +Caused by: java.lang.RuntimeException: Failed to remap 5 mods + at net.fabricmc.loom.configuration.mods.ModProcessor.processMods(ModProcessor.java:95) + at net.fabricmc.loom.configuration.mods.ModConfigurationRemapper.lambda$supplyModConfigurations$6(ModConfigurationRemapper.java:217) + at net.fabricmc.loom.configuration.mods.ModConfigurationRemapper.supplyModConfigurations(ModConfigurationRemapper.java:199) + at net.fabricmc.loom.configuration.LoomDependencyManager.handleRemapDependencies(LoomDependencyManager.java:49) + at net.fabricmc.loom.configuration.LoomDependencyManager.handleDependencies(LoomDependencyManager.java:39) + at net.fabricmc.loom.configuration.CompileConfiguration.lambda$run$1(CompileConfiguration.java:131) + ... 210 more +Caused by: java.lang.RuntimeException: Failed to remap: ModDependency{group='net.fabricmc', name='fabric-language-kotlin', version='1.13.11+kotlin.2.3.21', classifier='null'} + at net.fabricmc.loom.configuration.mods.ModProcessor.remapJars(ModProcessor.java:230) + at net.fabricmc.loom.configuration.mods.ModProcessor.processMods(ModProcessor.java:93) + ... 215 more +Caused by: java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: kotlinx-metadata-jvm cannot write metadata for future compiler versions. Requested to write version 2.3.0, but highest known version is 2.1.0 + at net.fabricmc.tinyremapper.TinyRemapper.waitForAll(TinyRemapper.java:1294) + at net.fabricmc.tinyremapper.TinyRemapper.apply(TinyRemapper.java:969) + at net.fabricmc.loom.configuration.mods.ModProcessor.remapJars(ModProcessor.java:228) + ... 216 more +Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: kotlinx-metadata-jvm cannot write metadata for future compiler versions. Requested to write version 2.3.0, but highest known version is 2.1.0 + at net.fabricmc.tinyremapper.TinyRemapper.waitForAll(TinyRemapper.java:1291) + ... 218 more +Caused by: java.lang.IllegalArgumentException: kotlinx-metadata-jvm cannot write metadata for future compiler versions. Requested to write version 2.3.0, but highest known version is 2.1.0 + at kotlin.metadata.jvm.KotlinClassMetadata$Companion.checkMetadataVersionForWrite(KotlinClassMetadata.kt:430) + at kotlin.metadata.jvm.KotlinClassMetadata$Companion.access$checkMetadataVersionForWrite(KotlinClassMetadata.kt:364) + at kotlin.metadata.jvm.KotlinClassMetadata$FileFacade.write(KotlinClassMetadata.kt:149) + at net.fabricmc.loom.kotlin.remapping.KotlinClassMetadataRemappingAnnotationVisitor.visitEnd(KotlinClassMetadataRemappingAnnotationVisitor.kt:94) + at org.objectweb.asm.ClassReader.readElementValues(ClassReader.java:3016) + at org.objectweb.asm.ClassReader.accept(ClassReader.java:609) + at org.objectweb.asm.ClassReader.accept(ClassReader.java:425) + at net.fabricmc.tinyremapper.TinyRemapper.apply(TinyRemapper.java:1134) + at net.fabricmc.tinyremapper.TinyRemapper.lambda$apply$5(TinyRemapper.java:965) + + +Deprecated Gradle features were used in this build, making it incompatible with Gradle 10. + +You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins. + +For more on this, please refer to https://docs.gradle.org/9.5.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation. + +BUILD FAILED in 13s diff --git a/mc_26_1_2_naming_port.md b/mc_26_1_2_naming_port.md new file mode 100644 index 0000000..b0223ac --- /dev/null +++ b/mc_26_1_2_naming_port.md @@ -0,0 +1,626 @@ +# MC 26.1.2 Naming Port Reference + +> **Context**: Minecraft 26.1.2 ships unobfuscated with Mojang official class names. +> Yarn mappings **do not exist** for this version (`net.fabricmc:yarn:26.1.2` → empty `[]`). +> The intermediary is `0.0.0` (placeholder). Fabric Loom performs **no remapping**. +> All source code must use Mojang official package/class names directly. + +--- + +## 1. Core Class Mapping Table + +| Old Name (Yarn / 1.21.x) | New Name (Mojang Official / 26.1.2) | Notes | +|---|---|---| +| `net.minecraft.text.Text` | `net.minecraft.network.chat.Component` | Factory methods changed (see §3) | +| `net.minecraft.text.MutableText` | `net.minecraft.network.chat.MutableComponent` | | +| `net.minecraft.text.ClickEvent` | `net.minecraft.network.chat.ClickEvent` | Same package prefix now `network.chat` | +| `net.minecraft.text.HoverEvent` | `net.minecraft.network.chat.HoverEvent` | Same | +| `net.minecraft.text.Style` | `net.minecraft.network.chat.Style` | Same | +| `net.minecraft.util.Formatting` | `net.minecraft.ChatFormatting` | Moved to root package | +| `net.minecraft.util.WorldSavePath` | `net.minecraft.world.level.storage.LevelResource` | | +| `net.minecraft.util.Identifier` | `net.minecraft.resources.ResourceLocation` | **Confirmed: no `ResourceLocation` in jar** – use `net.minecraft.resources.ResourceLocation` | +| `net.minecraft.util.Util` | **Does not exist** in jar root | `Util.getOperatingSystem()` → use `System.getProperty("os.name")` or platform detection | +| `net.minecraft.util.Util.OperatingSystem` | **Removed** | See §4 | +| `net.minecraft.server.command.ServerCommandSource` | `net.minecraft.commands.CommandSourceStack` | | +| `net.minecraft.server.MinecraftServer` | `net.minecraft.server.MinecraftServer` | **Same class name**, same package ✓ | +| `net.minecraft.server.world.ServerWorld` | `net.minecraft.server.level.ServerLevel` | | +| `net.minecraft.server.dedicated.DedicatedServerWatchdog` | `net.minecraft.server.dedicated.ServerWatchdog` | Class renamed | +| `net.minecraft.world.dimension.DimensionType` | `net.minecraft.world.level.dimension.DimensionType` | Package changed | +| `net.minecraft.world.storage.StorageIoWorker` | `net.minecraft.world.level.chunk.storage.RegionFileStorage` (approx) | Functionality moved; verify | +| `net.minecraft.screen.ScreenTexts` | `net.minecraft.network.chat.CommonComponents` | | +| `net.minecraft.client.MinecraftClient` | `net.minecraft.client.Minecraft` | Class renamed | +| `net.minecraft.client.gui.DrawContext` | `net.minecraft.client.gui.GuiGraphics` | Class renamed | +| `net.minecraft.client.gui.screen.Screen` | `net.minecraft.client.gui.screens.Screen` | Package changed (`screen` → `screens`) | +| `net.minecraft.client.gui.screen.world.SelectWorldScreen` | `net.minecraft.client.gui.screens.worldselection.SelectWorldScreen` | Package changed | +| `net.minecraft.client.gui.screen.world.WorldListWidget` | `net.minecraft.client.gui.screens.worldselection.WorldSelectionList` | Class renamed | +| `net.minecraft.client.gui.widget.ButtonWidget` | `net.minecraft.client.gui.components.Button` | Package changed (`widget` → `components`) | +| `net.minecraft.client.texture.NativeImage` | `com.mojang.blaze3d.platform.NativeImage` | Moved to blaze3d! | +| `net.minecraft.client.texture.NativeImageBackedTexture` | `net.minecraft.client.renderer.texture.DynamicTexture` | Class renamed | +| `net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket` | `net.minecraft.network.protocol.game.ClientboundTabListPacket` | Class renamed | +| `net.minecraft.command.argument.ColumnPosArgumentType` | `net.minecraft.commands.arguments.coordinates.ColumnPosArgument` | Package + class renamed | +| `net.minecraft.server.PlayerManager` | `net.minecraft.server.players.PlayerList` | Package changed | + +--- + +## 2. Package-Level Changes Summary + +| Old Package (Yarn) | New Package (Mojang) | +|---|---| +| `net.minecraft.text.*` | `net.minecraft.network.chat.*` | +| `net.minecraft.util.Formatting` | `net.minecraft` (root) → `net.minecraft.ChatFormatting` | +| `net.minecraft.util.WorldSavePath` | `net.minecraft.world.level.storage.LevelResource` | +| `net.minecraft.util.Identifier` | `net.minecraft.resources.ResourceLocation` | +| `net.minecraft.server.command.*` | `net.minecraft.commands.*` | +| `net.minecraft.server.world.*` | `net.minecraft.server.level.*` | +| `net.minecraft.server.dedicated.DedicatedServerWatchdog` | `net.minecraft.server.dedicated.ServerWatchdog` | +| `net.minecraft.world.dimension.*` | `net.minecraft.world.level.dimension.*` | +| `net.minecraft.world.storage.StorageIoWorker` | `net.minecraft.world.level.chunk.storage.RegionFileStorage` | +| `net.minecraft.screen.ScreenTexts` | `net.minecraft.network.chat.CommonComponents` | +| `net.minecraft.client.MinecraftClient` | `net.minecraft.client.Minecraft` | +| `net.minecraft.client.gui.DrawContext` | `net.minecraft.client.gui.GuiGraphics` | +| `net.minecraft.client.gui.screen.*` | `net.minecraft.client.gui.screens.*` | +| `net.minecraft.client.gui.screen.world.*` | `net.minecraft.client.gui.screens.worldselection.*` | +| `net.minecraft.client.gui.widget.*` | `net.minecraft.client.gui.components.*` | +| `net.minecraft.client.texture.*` | `net.minecraft.client.renderer.texture.*` + `com.mojang.blaze3d.platform.*` | +| `net.minecraft.network.packet.s2c.play.*` | `net.minecraft.network.protocol.game.Clientbound*` | +| `net.minecraft.command.argument.*` | `net.minecraft.commands.arguments.*` | + +--- + +## 3. API / Method Changes + +### Text / Component + +| Old (1.21.x Yarn) | New (26.1.2 Official) | +|---|---| +| `Text.literal(str)` | `Component.literal(str)` | +| `Text.translatable(key)` | `Component.translatable(key)` | +| `Text.translatable(key, args...)` | `Component.translatable(key, args...)` | +| `Text.translatableWithFallback(key, fallback, args...)` | `Component.translatableWithFallback(key, fallback, args...)` *(check if exists)* | +| `Text.empty()` | `Component.empty()` | +| `Text.of(str)` | `Component.literal(str)` | +| `text.formatted(Formatting.RED)` | `component.withStyle(ChatFormatting.RED)` | +| `text.styled { it.withColor(...) }` | `component.withStyle { it.withColor(...) }` | +| `text.withHoverEvent(HoverEvent(...))` | `component.withStyle(Style.EMPTY.withHoverEvent(...))` | +| `text.withClickEvent(ClickEvent(...))` | `component.withStyle(Style.EMPTY.withClickEvent(...))` | +| `mutableText.append(other)` | `mutableComponent.append(other)` ✓ same | +| `mutableText.copy()` | `mutableComponent.copy()` ✓ same | + +### ClickEvent + +| Old (Yarn) | New (Official) | +|---|---| +| `ClickEvent(ClickEvent.Action.RUN_COMMAND, str)` | Constructor now sealed — use `new ClickEvent.RunCommand(str)` | +| `ClickEvent.Action.RUN_COMMAND` | `ClickEvent.RunCommand` (record type) | +| `ClickEvent.Action.SUGGEST_COMMAND` | `ClickEvent.SuggestCommand` (record type) | +| `ClickEvent.Action.OPEN_URL` | `ClickEvent.OpenUrl` (record type) | +| `ClickEvent.Action.COPY_TO_CLIPBOARD` | `ClickEvent.CopyToClipboard` (record type) | + +> **Note**: In MC 26.x, `ClickEvent` was refactored to use sealed subclasses/records instead of an `Action` enum. Each action is now its own class (e.g. `ClickEvent.RunCommand`, `ClickEvent.OpenUrl`). + +### HoverEvent + +| Old (Yarn) | New (Official) | +|---|---| +| `HoverEvent(HoverEvent.Action.SHOW_TEXT, text)` | `new HoverEvent.ShowText(component)` | +| `HoverEvent.Action.SHOW_TEXT` | `HoverEvent.ShowText` (record) | +| `HoverEvent.Action.SHOW_ITEM` | `HoverEvent.ShowItem` (record) | +| `HoverEvent.Action.SHOW_ENTITY` | `HoverEvent.ShowEntity` (record) | + +### Formatting / ChatFormatting + +| Old | New | +|---|---| +| `import net.minecraft.util.Formatting` | `import net.minecraft.ChatFormatting` | +| `Formatting.RED` | `ChatFormatting.RED` | +| `Formatting.GREEN` | `ChatFormatting.GREEN` | +| `Formatting.GOLD` | `ChatFormatting.GOLD` | +| `Formatting.GRAY` | `ChatFormatting.GRAY` | +| `Formatting.BOLD` | `ChatFormatting.BOLD` | +| `Formatting.ITALIC` | `ChatFormatting.ITALIC` | +| `text.formatted(Formatting.X)` | `component.withStyle(ChatFormatting.X)` | + +### WorldSavePath / LevelResource + +| Old | New | +|---|---| +| `import net.minecraft.util.WorldSavePath` | `import net.minecraft.world.level.storage.LevelResource` | +| `WorldSavePath.ROOT` | `LevelResource.ROOT` | +| `WorldSavePath.PLAYERS` | `LevelResource.PLAYER_DATA_DIR` *(verify)* | +| `server.getSavePath(WorldSavePath.ROOT)` | `server.getWorldPath(LevelResource.ROOT)` *(verify method name)* | + +### MinecraftServer methods + +| Old (Yarn) | New (Official) | +|---|---| +| `server.getSavePath(WorldSavePath.ROOT)` | `server.getWorldPath(LevelResource.ROOT)` | +| `server.getPlayerManager()` | `server.getPlayerList()` | +| `server.playerManager` (Kotlin) | `server.playerList` | +| `server.getOverworld()` | `server.overworld()` | +| `server.worlds` (iterable) | `server.getAllLevels()` | +| `server.commandManager.parseAndExecute(src, cmd)` | `server.getCommands().performPrefixedCommand(src, cmd)` *(verify)* | +| `server.commandManager.executeWithPrefix(src, cmd)` | `server.getCommands().performPrefixedCommand(src, cmd)` | +| `server.asCoroutineDispatcher()` | Same (Kotlin coroutine extension from fabric-language-kotlin) | +| `server.running` | `server.isRunning()` | +| `server.stopped` | `server.isStopped()` | + +### ServerWorld / ServerLevel + +| Old | New | +|---|---| +| `import net.minecraft.server.world.ServerWorld` | `import net.minecraft.server.level.ServerLevel` | +| `world.savingDisabled` | `world.noSave` | +| `world.registryKey` | `world.dimension()` → returns `ResourceKey` | +| `world.server.getSavePath(WorldSavePath.ROOT)` | `world.getServer().getWorldPath(LevelResource.ROOT)` | +| `world.server` | `world.getServer()` | + +### DimensionType + +| Old | New | +|---|---| +| `import net.minecraft.world.dimension.DimensionType` | `import net.minecraft.world.level.dimension.DimensionType` | +| `DimensionType.getSaveDirectory(registryKey, rootPath)` | Need to compute manually using `Level.dimension()` key name — see §5 | + +### Util / OperatingSystem + +`net.minecraft.util.Util` does **not exist** in MC 26.1.2 jar. Use: +- `Util.getOperatingSystem()` → Replace with Java's `System.getProperty("os.name")` and compare +- `Util.OperatingSystem.OSX` → `"mac"` in os.name +- `Util.OperatingSystem.LINUX` → `"linux"` in os.name + +### PlayerManager / PlayerList + +| Old | New | +|---|---| +| `import net.minecraft.server.PlayerManager` | `import net.minecraft.server.players.PlayerList` | +| `playerManager.broadcast(text, false)` | `playerList.broadcastSystemMessage(component, false)` | +| `playerManager.sendToAll(packet)` | `playerList.broadcastAll(packet)` | + +### Network Packets + +| Old | New | +|---|---| +| `import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket` | `import net.minecraft.network.protocol.game.ClientboundTabListPacket` | +| `PlayerListHeaderS2CPacket(header, footer)` | `new ClientboundTabListPacket(header, footer)` | + +### Client API + +| Old | New | +|---|---| +| `MinecraftClient.getInstance()` | `Minecraft.getInstance()` | +| `client.options.language` | `client.options.languageCode` *(verify field name)* | +| `client.setScreen(screen)` | Same ✓ | +| `client.execute { ... }` | Same ✓ | +| `client.createIntegratedServerLoader()` | `client.createWorldOpenFlows()` *(verify)* | +| `loader.start(worldName) { ... }` | Verify new API | + +### GUI Components + +| Old | New | +|---|---| +| `ButtonWidget.builder(text, action).dimensions(...).build()` | `Button.builder(component, action).bounds(x,y,w,h).build()` | +| `import net.minecraft.client.gui.widget.ButtonWidget` | `import net.minecraft.client.gui.components.Button` | +| `DrawContext` | `GuiGraphics` | +| `context.drawCenteredTextWithShadow(renderer, text, x, y, color)` | `graphics.drawCenteredString(font, component, x, y, color)` | +| `context.fill(x1, y1, x2, y2, color)` | `graphics.fill(x1, y1, x2, y2, color)` ✓ same | +| `WorldListWidget` | `WorldSelectionList` | +| `SelectWorldScreen` | `SelectWorldScreen` ✓ same name, different package | +| `Screen(title)` constructor | `Screen(Component title)` ✓ same pattern | +| `screen.addDrawableChild(widget)` | `screen.addRenderableWidget(widget)` | +| `NativeImage` | `com.mojang.blaze3d.platform.NativeImage` | +| `NativeImageBackedTexture` | `net.minecraft.client.renderer.texture.DynamicTexture` | +| `ScreenTexts.OK` | `CommonComponents.GUI_OK` | + +### Mixin Target Descriptors + +Mixin `@At` `target` strings use internal class names that **also changed**: + +| Old Descriptor | New Descriptor | +|---|---| +| `Lnet/minecraft/server/dedicated/DedicatedServerWatchdog;maxTickTime:J` | `Lnet/minecraft/server/dedicated/ServerWatchdog;maxTickTime:J` | +| `Lnet/minecraft/world/storage/StorageIoWorker;` | `Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;` | +| `Lnet/minecraft/server/MinecraftServer;` | `Lnet/minecraft/server/MinecraftServer;` ✓ same | + +--- + +## 4. Removed / Unavailable APIs + +| Removed | Replacement Strategy | +|---|---| +| `net.minecraft.util.Util` (entire class) | No direct equivalent. `getOperatingSystem()` → use Java `System.getProperty("os.name")` | +| `Util.OperatingSystem` enum | Use String comparison on `System.getProperty("os.name").toLowerCase()` | +| `net.minecraft.util.Identifier` | Use `net.minecraft.resources.ResourceLocation` (may need `ResourceLocation.fromNamespaceAndPath(ns, path)`) | +| `Text.translatableWithFallback()` | `Component.translatableWithFallback()` — verify if present in 26.1.2 `Component` class | +| `StorageIoWorker` (Mixin target) | Target `RegionFileStorage` instead, or remove mixin if no longer needed | + +--- + +## 5. File-by-File Port Guide + +### `src/main/kotlin/.../XBackup.kt` + +**Imports to change:** +``` +// OLD → NEW +import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket + → import net.minecraft.network.protocol.game.ClientboundTabListPacket + +import net.minecraft.server.command.ServerCommandSource + → import net.minecraft.commands.CommandSourceStack + +import net.minecraft.text.Text + → import net.minecraft.network.chat.Component + +import net.minecraft.util.Util + → REMOVE (use Java OS detection) + +import net.minecraft.util.WorldSavePath + → import net.minecraft.world.level.storage.LevelResource +``` + +**Code changes:** +```kotlin +// Line 127-136: Util.getOperatingSystem() +// OLD: +when (Util.getOperatingSystem()) { + Util.OperatingSystem.OSX, Util.OperatingSystem.LINUX -> { ... } + else -> error(...) +} +// NEW: +val os = System.getProperty("os.name", "").lowercase() +when { + os.contains("mac") || os.contains("nix") || os.contains("nux") -> { ... } + else -> error(...) +} + +// Line 163: server.getSavePath(WorldSavePath.ROOT) +// → server.getWorldPath(LevelResource.ROOT) + +// Line 234-240: PlayerListHeaderS2CPacket / Text.empty() +// → ClientboundTabListPacket(Component.empty(), Component.literal(...)) +// → server.playerList.broadcastAll(packet) + +// Line 236-237: Text.empty(), Text.literal(...) +// → Component.empty(), Component.literal(...) + +// Line 299: server.getSavePath(WorldSavePath.ROOT) +// → server.getWorldPath(LevelResource.ROOT) + +// Line 396: ServerCommandSource param type +// → CommandSourceStack + +// Line 401, 410: Text.of(...), source.sendError(Text.of(...)) +// → Component.literal(...), source.sendFailure(Component.literal(...)) +``` + +--- + +### `src/main/kotlin/.../Utils.kt` + +**Imports to change:** +``` +import net.minecraft.server.command.ServerCommandSource → import net.minecraft.commands.CommandSourceStack +import net.minecraft.server.world.ServerWorld → import net.minecraft.server.level.ServerLevel +import net.minecraft.text.MutableText → import net.minecraft.network.chat.MutableComponent +import net.minecraft.text.Text → import net.minecraft.network.chat.Component +import net.minecraft.util.WorldSavePath → import net.minecraft.world.level.storage.LevelResource +import net.minecraft.world.dimension.DimensionType → import net.minecraft.world.level.dimension.DimensionType +``` + +**Code changes:** +```kotlin +// translate(): Text.translatableWithFallback → Component.translatableWithFallback +// Return type: MutableText → MutableComponent + +// ServerCommandSource.send(): sendMessage(text) → sendSystemMessage(component) + +// MinecraftServer.setAutoSaving: worlds → getAllLevels() / allLevels +// world.savingDisabled = !value → world.noSave = !value + +// MinecraftServer.save(): saveAll() signature may differ + +// MinecraftServer.finishRestore(): +// running = true → use server.setRunning(true) if setter exists +// runServer() → verify method name + +// MinecraftServer.broadcast: playerManager.broadcast → playerList.broadcastSystemMessage + +// isFileInWorld(): +// DimensionType.getSaveDirectory(world.registryKey, ...) → compute from dimension().location() +// world.registryKey → world.dimension() (returns ResourceKey) +// world.server → world.getServer() +// WorldSavePath.ROOT → LevelResource.ROOT +``` + +--- + +### `src/main/kotlin/.../Commands.kt` + +**Imports to change:** +``` +import net.minecraft.command.argument.ColumnPosArgumentType + → import net.minecraft.commands.arguments.coordinates.ColumnPosArgument + +import net.minecraft.server.command.ServerCommandSource + → import net.minecraft.commands.CommandSourceStack + +import net.minecraft.text.ClickEvent → import net.minecraft.network.chat.ClickEvent +import net.minecraft.text.HoverEvent → import net.minecraft.network.chat.HoverEvent +import net.minecraft.text.MutableText → import net.minecraft.network.chat.MutableComponent +import net.minecraft.text.Text → import net.minecraft.network.chat.Component + +import net.minecraft.util.Formatting → import net.minecraft.ChatFormatting +import net.minecraft.util.Util → REMOVE +import net.minecraft.util.WorldSavePath → import net.minecraft.world.level.storage.LevelResource +``` + +**Code changes:** +```kotlin +// All Text.literal() → Component.literal() +// All Text.translatable() → Component.translatable() +// All Text.empty() → Component.empty() +// MutableText → MutableComponent +// .formatted(Formatting.X) → .withStyle(ChatFormatting.X) +// ClickEvent(ClickEvent.Action.RUN_COMMAND, str) → ClickEvent.RunCommand(str) +// ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, str) → ClickEvent.SuggestCommand(str) +// HoverEvent(HoverEvent.Action.SHOW_TEXT, text) → HoverEvent.ShowText(component) +// .styled { ... } / .withHoverEvent() / .withClickEvent() +// → .withStyle(Style.EMPTY.withHoverEvent(...)) etc. +// ServerCommandSource → CommandSourceStack +// ColumnPosArgumentType → ColumnPosArgument +// WorldSavePath.ROOT → LevelResource.ROOT +``` + +--- + +### `src/main/kotlin/.../Task.kt` + +``` +import net.minecraft.text.Text → import net.minecraft.network.chat.Component +// val displayName: Text → val displayName: Component +``` + +--- + +### `src/main/kotlin/.../cloud/OnedriveSupport.kt` + +``` +import net.minecraft.text.ClickEvent → import net.minecraft.network.chat.ClickEvent +// Any ClickEvent usage → new sealed class API (ClickEvent.RunCommand, etc.) +``` + +--- + +### `src/main/kotlin/.../gui/BackupsGui.kt` + +``` +import net.minecraft.client.MinecraftClient → import net.minecraft.client.Minecraft +import net.minecraft.client.texture.NativeImage → import com.mojang.blaze3d.platform.NativeImage +import net.minecraft.client.texture.NativeImageBackedTexture → import net.minecraft.client.renderer.texture.DynamicTexture +import net.minecraft.text.Text → import net.minecraft.network.chat.Component +import net.minecraft.util.Formatting → import net.minecraft.ChatFormatting +import net.minecraft.util.Identifier → import net.minecraft.resources.ResourceLocation +``` + +--- + +### `src/main/kotlin/.../gui/RestoreInfoScreen.kt` + +``` +import net.minecraft.client.MinecraftClient → import net.minecraft.client.Minecraft +import net.minecraft.client.gui.DrawContext → import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.screen.Screen → import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.widget.ButtonWidget → import net.minecraft.client.gui.components.Button +import net.minecraft.text.Text → import net.minecraft.network.chat.Component +``` + +**Code changes:** +```kotlin +// Screen(Text.translatable(...)) → Screen(Component.translatable(...)) +// ButtonWidget.builder(text, action).dimensions(...).build() +// → Button.builder(component, action).bounds(x,y,w,h).build() +// context.drawCenteredTextWithShadow(textRenderer, text, x, y, color) +// → guiGraphics.drawCenteredString(font, component, x, y, color) +// MinecraftClient.getInstance() → Minecraft.getInstance() +// client.createIntegratedServerLoader() → verify new API +// addDrawableChild(widget) → addRenderableWidget(widget) +``` + +--- + +### `src/main/java/.../mixin/MixinSelectWorldScreen.java` + +```java +// Imports: +import net.minecraft.client.gui.DrawContext → import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.screen.Screen → import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screen.world.SelectWorldScreen → import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen +import net.minecraft.client.gui.screen.world.WorldListWidget → import net.minecraft.client.gui.screens.worldselection.WorldSelectionList +import net.minecraft.client.gui.widget.ButtonWidget → import net.minecraft.client.gui.components.Button +import net.minecraft.text.Text → import net.minecraft.network.chat.Component +import net.minecraft.util.Formatting → import net.minecraft.ChatFormatting +``` + +**Code changes:** +```java +// ButtonWidget → Button (same builder pattern) +// Text.literal("回") → Component.literal("回") +// Text.translatable(...) → Component.translatable(...) +// .formatted(Formatting.RED) → .withStyle(ChatFormatting.RED) +// WorldListWidget → WorldSelectionList (field type) +// levelList.getSelectedAsOptional().get().level.getName() +// → verify API for WorldSelectionList entry +// Screen(Text) constructor → Screen(Component) +// this.addDrawableChild(widget) → this.addRenderableWidget(widget) +// context.drawCenteredTextWithShadow → graphics.drawCenteredString +``` + +**Mixin @Mixin targets — no change** (SelectWorldScreen class name preserved). + +--- + +### `src/main/java/.../mixin/disable/MixinDedicatedServerWatchdog.java` + +```java +// OLD: +import net.minecraft.server.dedicated.DedicatedServerWatchdog; +@Mixin(DedicatedServerWatchdog.class) +// Field target: "Lnet/minecraft/server/dedicated/DedicatedServerWatchdog;maxTickTime:J" + +// NEW: +import net.minecraft.server.dedicated.ServerWatchdog; +@Mixin(ServerWatchdog.class) +// Field target: "Lnet/minecraft/server/dedicated/ServerWatchdog;maxTickTime:J" +``` + +--- + +### `src/main/java/.../mixin/disable/MixinStorageIoWorker.java` + +```java +// OLD: @Mixin(StorageIoWorker.class) +// StorageIoWorker moved/renamed. +// Target: net.minecraft.world.level.chunk.storage.RegionFileStorage (closest equivalent) +// This mixin is likely a stub (empty body) — may just need the import updated +// or the mixin can be removed if no longer needed. + +// NEW (if needed): +import net.minecraft.world.level.chunk.storage.RegionFileStorage; +@Mixin(RegionFileStorage.class) +``` + +--- + +### `src/main/java/.../mixin/MixinServer.java` + +```java +// MinecraftServer class is SAME → no import change needed +// Method names to verify: +// "save" method → verify still exists with same signature +// "saveAll" method → verify +// "shutdown" method → verify +// These may have changed signatures in 26.1.2 +``` + +--- + +### `src/main/java/.../gui/OptionDialog.java` + +```java +// import net.minecraft.screen.ScreenTexts → import net.minecraft.network.chat.CommonComponents +// import net.minecraft.text.Text → import net.minecraft.network.chat.Component +// import static net.minecraft.util.Formatting.GREEN → import net.minecraft.ChatFormatting (static import) + +// ScreenTexts.OK.copy().formatted(GREEN) +// → CommonComponents.GUI_OK.copy().withStyle(ChatFormatting.GREEN) +// OR: Component.translatable("gui.ok").withStyle(ChatFormatting.GREEN) + +// Text params in method signatures → Component +``` + +--- + +## 6. Fabric API Changes for 26.1.2 + +These Fabric API modules remain available but their callback signatures now use Mojang official types: + +| Fabric API | Old Type | New Type | +|---|---|---| +| `ServerLifecycleEvents.SERVER_STARTED` callback | `MinecraftServer` | `MinecraftServer` ✓ same | +| `CommandRegistrationCallback` dispatcher param | `CommandDispatcher` | `CommandDispatcher` | +| `ServerWorldEvents` world param | `ServerWorld` | `ServerLevel` | + +> **Note**: `CommandRegistrationCallback { dispatcher, _, _ -> ... }` — the dispatcher type parameter changes to `CommandSourceStack`. + +--- + +## 7. Mixin Configuration + +The mixin JSON file (`src/main/resources/x_backup.mixins.json`) references class names for `@Mixin` targets. Since Mixin resolves targets at **runtime** by class name (not by Java package), the `@Mixin(TargetClass.class)` annotation in source drives resolution — the JSON config lists the mixin *class* names, not target names, so the JSON itself may not need changes. However, `@At(target=...)` string descriptors **must** be updated as described above. + +--- + +## 8. Stonecutter Strategy + +The project uses **Stonecutter** for multi-version support. For 26.1.2-specific code, use: + +```kotlin +//? if >= 26.1.2 { +// Mojang official API code here +//?} else { +/* Yarn-mapped code here */ +//?} +``` + +For imports that differ, use type aliases at the top of each file: + +```kotlin +//? if >= 26.1.2 { +import net.minecraft.network.chat.Component as Text +import net.minecraft.network.chat.MutableComponent as MutableText +import net.minecraft.ChatFormatting as Formatting +//?} else { +/* +import net.minecraft.text.Text +import net.minecraft.text.MutableText +import net.minecraft.util.Formatting +*/ +//?} +``` + +> **Caution**: Type alias approach only works if APIs are identical beyond the name. For methods that changed (e.g. `.formatted()` → `.withStyle()`), full code blocks must be conditioned. + +--- + +## 9. Verified Mappings & Port Findings + +Through direct inspection of `minecraft-merged.jar` for Minecraft 26.1.2 and resolution of compiler errors, the following mappings are confirmed: + +### 9.1 Core Classes & Packages +* **`net.minecraft.util.Identifier`** (Yarn) → **`net.minecraft.resources.Identifier`** (MojMap). + * Note: It is NOT named `ResourceLocation`. The class name is `Identifier` and it has moved packages. + * Static creators: `Identifier.parse(String)`, `Identifier.fromNamespaceAndPath(String, String)`. +* **`net.minecraft.client.gui.DrawContext`** (Yarn) → **`net.minecraft.client.gui.GuiGraphicsExtractor`** (MojMap). + * Render methods on screens override `extractRenderState(GuiGraphicsExtractor, int, int, float)`. + * Background rendering uses `extractBackground(GuiGraphicsExtractor, int, int, float)`. + * Text rendering on `GuiGraphicsExtractor` uses `text(...)` and `centeredText(...)` instead of `drawString` or `drawCenteredString`. +* **`net.minecraft.client.gui.screen.world.WorldListWidget`** (Yarn) → **`net.minecraft.client.gui.screens.worldselection.WorldSelectionList`** (MojMap). + * Entry class is `WorldSelectionList.Entry`. + * Get selected entry: `levelList.getSelectedOpt()` (returns `Optional`). + * Get level summary from entry: `entry.getLevelSummary()` (returns `LevelSummary`). +* **`net.minecraft.world.level.storage.LevelSummary`**: + * Folder name / ID of world: `getLevelId()`. + * Display name of world: `getLevelName()`. +* **`net.minecraft.client.gui.components.Tooltip`**: + * Tooltip creation: `Tooltip.create(Component)`. +* **`net.minecraft.client.gui.widget.ButtonWidget`** (Yarn) → **`net.minecraft.client.gui.components.Button`** (MojMap). + * Added to screen via `addRenderableWidget(...)` (formerly `addDrawableChild`). + * Bounds set in builder: `Button.builder(...).bounds(x, y, width, height).build()`. + +### 9.2 Server Lifecycle & Threading +* **`MinecraftServer.isOnThread`** (Yarn) → **`MinecraftServer.isSameThread()`** (MojMap). +* **`MinecraftServer.networkIo`** (Yarn) → **`MinecraftServer.connection`** (field) / **`getConnection()`** (method) returning `ServerConnectionListener`. + * Listing connections: `connection.getConnections()`. +* **`MinecraftServer.thread`** (Yarn) → **`MinecraftServer.serverThread`** (field) / **`getRunningThread()`** (method). +* **`MinecraftServer.save()`** (Yarn) → **`MinecraftServer.saveEverything(suppressLog, flush, force)`** (MojMap). +* **`MinecraftServer.shutdown()`** (Yarn) → **`MinecraftServer.stopServer()`** (MojMap). +* **`Connection.disconnected`** (Yarn) → **`Connection.disconnectionHandled`** (field). + * Setting `connection.disconnectionHandled = true` prevents save-on-disconnect during restore. + * Disconnecting connection: `connection.disconnect(Component)`. +* **`DimensionType.getSaveDirectory()`** (Yarn) → **`DimensionType.getStorageFolder(ResourceKey, Path)`** (MojMap). + +### 9.3 Permissions & Command Sources +* **`Permissions.check(source, perm, defaultLevel)`**: + * Under MojMap 26.1.2, `CommandSourceStack` does NOT implement `net.minecraft.commands.CommandSource`. + * To check permissions using the stub, pass `source.source` (which is `CommandSource`). + * Stub classes package: `net.minecraft.commands.CommandSource` instead of `net.minecraft.command.CommandSource`. +* **`CommandSourceStack.permissions`**: + * The `permissions` field is private. Use the public method `permissions()` returning `PermissionSet`. + * Checking a permission level: `source.permissions().hasPermission(new Permission.HasCommandLevel(permissionLevel))`. +* **`CommandSourceStack.name`** (Yarn) → **`CommandSourceStack.textName`** (field) / **`getTextName()`** (getter). +* **`CommandSourceStack.world`** (Yarn) → **`CommandSourceStack.level`** (field) / **`getLevel()`** (getter). +* **`CommandSourceStack.sendError(Text)`** (Yarn) → **`CommandSourceStack.sendFailure(Component)`** (MojMap). + +--- + +*This document was updated by directly inspecting `minecraft-merged.jar` for MC 26.1.2 and analyzing compilation failures.* diff --git a/project_map.md b/project_map.md new file mode 100644 index 0000000..d19b2ab --- /dev/null +++ b/project_map.md @@ -0,0 +1,145 @@ +# X Backup Architectural Map + +This document maps out the architecture, module layout, file dependencies, and execution flow of the **X Backup** Minecraft mod (designed for Minecraft 1.21.11, with cross-version support managed via Stonecutter). + +--- + +## 1. Project Organization (Gradle Modules) + +The project is structured as a multi-module Gradle build: + +```mermaid +graph TD + api[":api - Public API & Interfaces"] + common[":common - Shared Core Backend Logic"] + compat[":compat-fake-source - Compile-only Stubs"] + cli[":cli - Command-line Interface"] + mod[":src - Minecraft Fabric Mod Implementation"] + + common --> api + mod --> common + mod -.->|compileOnly| compat + cli --> common +``` + +* **`:api`**: Defines interface boundaries. It holds no external dependencies other than Kotlin/Java standards, allowing other mods to interact with X Backup without importing Exposed or Ktor. +* **`:common`**: The functional heart. Handles file walking, hashing, deduplication database (SQLite), and configuration settings. +* **`:compat-fake-source`**: A compilation-helper module containing empty stubs of third-party libraries (like LuckPerms, fabric-permissions-api, and net.minecraft classes). This permits conditional compiles without declaring heavy transitive dependencies. +* **`:cli`**: A standalone terminal tool. Admins can run this inside a world directory to list, back up, or restore worlds entirely offline. +* **`:src` (Main Mod)**: Extends `:common` to tie into the Fabric/Minecraft lifecycle, adding game commands, user interfaces, mixins, and network integrations (OneDrive). + +--- + +## 2. File-by-File Breakdown & Interactions + +### A. The API Module (`:api`) +* [XBackupApi.java](file:///e:/x-backup/api/src/main/java/com/github/zly2006/xbackup/api/XBackupApi.java): Serves as the static entry point hook (`getInstance`/`setInstance`). Exposes high-level methods to request backups, check integrity, delete backups, or restore targets. +* [IBackup.kt](file:///e:/x-backup/api/src/main/java/com/github/zly2006/xbackup/api/IBackup.kt) & [IBackupEntry.kt](file:///e:/x-backup/api/src/main/java/com/github/zly2006/xbackup/api/IBackupEntry.kt): Immutable data contracts representing a completed backup metadata envelope and the individual files indexed within it. +* [CloudStorageProvider.kt](file:///e:/x-backup/api/src/main/java/com/github/zly2006/xbackup/api/CloudStorageProvider.kt): Interface defining asynchronous cloud upload triggers and speed telemetry hooks. +* [XBackupKotlinAsyncApi.kt](file:///e:/x-backup/api/src/main/java/com/github/zly2006/xbackup/api/XBackupKotlinAsyncApi.kt): Provides Kotlin coroutine extensions (like `suspend fun restore`) and raw SQLite database transaction hooks (`dbQuery`). + +### B. The Common Backend Module (`:common`) +* [BackupDatabaseService.kt](file:///e:/x-backup/common/src/main/kotlin/com/github/zly2006/xbackup/BackupDatabaseService.kt): + * **Responsibility**: Implements `XBackupKotlinAsyncApi`. Connects to the SQLite database via JetBrains Exposed. + * **Key Logic**: + * *Content-Addressable Storage (CAS)*: Walks files, computes MD5 hashes, and compresses newly encountered files into the GZIP/ZIP blob store. Relies on `BackupEntryTable`, `BackupTable`, and `BackupEntryBackupTable` to achieve perfect file-level deduplication. + * *Restoration*: Compares target directory state with database indexes, deletes un-indexed files, and streams blobs back to disk while checking MD5 integrity. + * *GC/Packing*: Bundles files smaller than 50MB into joint Zip files to keep file system inode counts low, and garbage-collects orphaned blobs (`deleteUnusedBlobs`). +* [Config.kt](file:///e:/x-backup/common/src/main/kotlin/com/github/zly2006/xbackup/Config.kt): Configures backup intervals, cloud API tokens, and lists file exclusions. Implements a custom cron-like Grandfather-Father-Son (GFS) pruning evaluator to keep older backups progressively sparser. +* [I18n.kt](file:///e:/x-backup/common/src/main/kotlin/com/github/zly2006/xbackup/I18n.kt): Resolves translation JSON resources (`en_us.json`, `zh_cn.json`) for chat prompts and command errors. + +### C. The Main Mod Module (`:src`) +* [XBackup.kt](file:///e:/x-backup/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt): + * **Responsibility**: Main Fabric `ModInitializer`. + * **Interactions**: Hooks into server startup (`SERVER_STARTED`) to initialize `BackupDatabaseService` pointing to the world's database, spins up the background auto-backup crontab coroutine thread, and registers `/xb` command handlers. +* [Commands.kt](file:///e:/x-backup/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt): + * **Responsibility**: Registers `/xb` command dispatch trees (create, list, info, restore, prune, debug). + * **Interactions**: Interacts with the `BackupDatabaseService` to trigger backups, and controls Minecraft servers (e.g., saving worlds, stopping watchdogs, and launching restores). Supports regional restores by checking coordinates inside chunk region files (`.mca`/`.mcc`). +* [RestartUtils.kt](file:///e:/x-backup/src/main/kotlin/com/github/zly2006/xbackup/RestartUtils.kt): Evaluates Java Runtime Management parameters to generate a native command to hot-restart the JVM (on Linux/macOS/Windows) when restoring worlds. +* [cloud/OnedriveSupport.kt](file:///e:/x-backup/src/main/kotlin/com/github/zly2006/xbackup/cloud/OnedriveSupport.kt): Implements chunk-based uploading (10MB slices) to OneDrive using Ktor and RedenMC API proxies. Writes upload state to `.tmp/xb.upload.json` so interrupted transfers can resume seamlessly. +* [gui/BackupsGui.kt](file:///e:/x-backup/src/main/kotlin/com/github/zly2006/xbackup/gui/BackupsGui.kt): Extends PolyLib's modular screen system. Extracts `icon.png` from backup archives and uploads them dynamically as textures to list backups visually in the Singleplayer Select World menu. + +--- + +## 3. Core Execution Flow Diagrams + +### A. Backup Creation Process +```mermaid +sequenceDiagram + autonumber + actor Admin as Admin/Crontab + participant Cmd as Commands/Scheduler + participant MS as MinecraftServer + participant DB as BackupDatabaseService + participant Disk as File System / Blobs + + Admin->>Cmd: Trigger Backup + Cmd->>MS: save() (Flush world to disk) + Cmd->>MS: setAutoSaving(false) (Lock files) + Cmd->>DB: createBackup(worldPath) + activate DB + DB->>Disk: Walk world files + loop Each File + DB->>DB: Check if ignored + DB->>DB: Check if file matches existing DB entry (path, size, modify time) + alt Cache Miss + DB->>Disk: Compute MD5 & GZIP compress + DB->>Disk: Save to blob store /blob/xx/xxxxxxxx + end + DB->>DB: Reference in database + end + DB-->>Cmd: Return Backup Summary + deactivate DB + Cmd->>MS: setAutoSaving(true) (Unlock files) + Cmd->>Admin: Broadcast Completion Message +``` + +### B. Restore Process (Safely Off-thread) +Because you cannot overwrite active Minecraft region files while the game is running, X Backup intercepts the shutdown loop to perform restorations. + +```mermaid +sequenceDiagram + autonumber + actor Admin as Admin/Client + participant Cmd as Commands/GUI + participant MS as MinecraftServer + participant Mixin as MixinServer (Shutdown Injector) + participant DB as BackupDatabaseService + participant RU as RestartUtils + + Admin->>Cmd: Trigger Restore Backup #ID + Cmd->>MS: Kick players & stop(save=false) + MS->>MS: Run clean shutdown sequence... + MS->>Mixin: onShutdown() hook + activate Mixin + Mixin->>DB: restore(id, targetDir) + activate DB + DB->>DB: Scan files & delete un-indexed assets + loop Each File in Backup + DB->>DB: Retrieve stream from blob + DB->>DB: Decompress GZIP/ZIP & write to world folder + DB->>DB: Validate checksum matches expected MD5 + end + DB-->>Mixin: Restoration Completed + deactivate DB + Mixin->>RU: generateRestartCommand() + Mixin->>RU: Launch new Java process & Halt old process + deactivate Mixin +``` + +--- + +## 4. Third-Party Library & Build Dependencies + +Dependencies are declared globally in `gradle.properties` and resolved contextually inside `build.gradle.kts`: + +| Dependency | Purpose | Scope | Notes | +| :--- | :--- | :--- | :--- | +| **Fabric Loom** | Compilation environment and Loom mappings | Build system | Handles Yarn mappings and decompilers | +| **Stonecutter** | Multi-version compilation framework | Build system | Evaluates conditional statements in code | +| **Fabric Language Kotlin** | Kotlin standard libraries loading in MC | Runtime & compile | Direct loader dependency | +| **JetBrains Exposed** (`exposed-version`) | SQL ORM library | Shadowed & compiled | Handles connection pooling and queries | +| **SQLite-JDBC** | Database driver for SQLite | Shadowed & compiled | Drives local `x_backup.db` storage | +| **Ktor Client** (`ktor_version`) | HTTP requests & Serialization | Shadowed & compiled | Drives OneDrive upload communications | +| **PolyLib** (`deps.poly_lib`) | Client Modular GUI controls | CompileOnly/Optional | Enables the "回" backup button in Singleplayer | +| **LuckPerms / Perms API** | Permission validation | CompileOnly | Extracted from `compat-fake-source` | diff --git a/settings.gradle.kts b/settings.gradle.kts index 29fb757..cdb58b7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,43 +3,16 @@ pluginManagement { mavenCentral() gradlePluginPortal() maven("https://maven.fabricmc.net/") - maven("https://maven.kikugie.dev/releases/") - maven { - name = "Kikugie Snapshot" - url = uri("https://maven.kikugie.dev/snapshots") - } } } plugins { - id("dev.kikugie.stonecutter") version "0.7.7-beta.2" - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } rootProject.name = "X Backup" include("common") - -stonecutter { - kotlinController = true - centralScript = "build.gradle.kts" - - shared { - versions( - "1.20.1", - "1.20.4", - "1.20.6", - "1.21.1", - "1.21.3", - "1.21.4", - "1.21.5", - "1.21.6", - "1.21.9", - "1.21.11", - ) - } - create(rootProject) -} - include("compat-fake-source") include("cli") include("api") + diff --git a/src/main/java/com/github/zly2006/xbackup/gui/BMStyle.java b/src/main/java/com/github/zly2006/xbackup/gui/BMStyle.java index e95a797..95bc2ac 100644 --- a/src/main/java/com/github/zly2006/xbackup/gui/BMStyle.java +++ b/src/main/java/com/github/zly2006/xbackup/gui/BMStyle.java @@ -1,12 +1,12 @@ package com.github.zly2006.xbackup.gui; -//? if poly_lib { + import net.creeperhost.polylib.client.PolyPalette; import net.creeperhost.polylib.client.modulargui.elements.*; import net.creeperhost.polylib.client.modulargui.lib.Assembly; import net.creeperhost.polylib.client.modulargui.lib.geometry.Axis; import net.creeperhost.polylib.client.modulargui.lib.geometry.GuiParent; -import net.minecraft.text.Text; +import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; import java.util.function.Supplier; @@ -23,27 +23,27 @@ public static GuiElement contentArea(GuiElement parent) { return PolyPalette.Flat.contentArea(parent); } - public static GuiButton button(GuiElement parent, Text label) { + public static GuiButton button(GuiElement parent, Component label) { return PolyPalette.Flat.button(parent, label); } - public static GuiButton button(GuiElement parent, @Nullable Supplier label) { + public static GuiButton button(GuiElement parent, @Nullable Supplier label) { return PolyPalette.Flat.button(parent, label); } - public static GuiButton buttonCaution(GuiElement parent, Text label) { + public static GuiButton buttonCaution(GuiElement parent, Component label) { return PolyPalette.Flat.buttonCaution(parent, label); } - public static GuiButton buttonCaution(GuiElement parent, @Nullable Supplier label) { + public static GuiButton buttonCaution(GuiElement parent, @Nullable Supplier label) { return PolyPalette.Flat.buttonCaution(parent, label); } - public static GuiButton buttonPrimary(GuiElement parent, Text label) { + public static GuiButton buttonPrimary(GuiElement parent, Component label) { return PolyPalette.Flat.buttonPrimary(parent, label); } - public static GuiButton buttonPrimary(GuiElement parent, @Nullable Supplier label) { + public static GuiButton buttonPrimary(GuiElement parent, @Nullable Supplier label) { return PolyPalette.Flat.buttonPrimary(parent, label); } @@ -62,4 +62,4 @@ public static int listEntryBackground(boolean hoveredOrSelected) { } } } -//?} + diff --git a/src/main/java/com/github/zly2006/xbackup/gui/OptionDialog.java b/src/main/java/com/github/zly2006/xbackup/gui/OptionDialog.java index 9e342ad..aadce93 100644 --- a/src/main/java/com/github/zly2006/xbackup/gui/OptionDialog.java +++ b/src/main/java/com/github/zly2006/xbackup/gui/OptionDialog.java @@ -1,6 +1,6 @@ package com.github.zly2006.xbackup.gui; -//? if poly_lib { + import net.creeperhost.polylib.client.PolyPalette; import net.creeperhost.polylib.client.modulargui.ModularGui; import net.creeperhost.polylib.client.modulargui.elements.GuiButton; @@ -12,12 +12,12 @@ import net.creeperhost.polylib.client.modulargui.lib.GuiRender; import net.creeperhost.polylib.client.modulargui.lib.geometry.Constraint; import net.creeperhost.polylib.client.modulargui.lib.geometry.GuiParent; -import net.minecraft.screen.ScreenTexts; -import net.minecraft.text.Text; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; import static net.creeperhost.polylib.client.modulargui.lib.geometry.Constraint.*; import static net.creeperhost.polylib.client.modulargui.lib.geometry.GeoParam.*; -import static net.minecraft.util.Formatting.GREEN; +import static net.minecraft.ChatFormatting.GREEN; /** * Created by brandon3055 on 14/10/2023 @@ -27,7 +27,7 @@ public class OptionDialog extends GuiElement implements Background private final GuiButton[] buttons; - public OptionDialog(@NotNull GuiParent parent, Text title, Text... buttonLabels) { + public OptionDialog(@NotNull GuiParent parent, Component title, Component... buttonLabels) { super(parent); this.setOpaque(true); int buttonCount = buttonLabels.length; @@ -63,7 +63,7 @@ public OptionDialog(@NotNull GuiParent parent, Text title, Text... buttonLabe constrain(HEIGHT, literal(60)); } - public static GuiButton button(GuiElement parent, Text label) { + public static GuiButton button(GuiElement parent, Component label) { GuiButton button = new GuiButton(parent); GuiRectangle background = new GuiRectangle(button) .fill(() -> button.isDisabled() ? 0x88202020 : (button.isMouseOver() || button.toggleState() || button.isPressed() ? 0xFF909090 : 0xFF505050)); @@ -87,9 +87,7 @@ public void close() { getParent().removeChild(this); } - //? if < 1.21.9 { - @Override - //?} + public boolean keyPressed(int key, int scancode, int modifiers) { return true; } @@ -97,7 +95,7 @@ public boolean keyPressed(int key, int scancode, int modifiers) { @Override public void renderBehind(GuiRender render, double mouseX, double mouseY, float partialTicks) { render.toolTipBackground(xMin(), yMin(), xSize(), ySize(), 0xFF100010, 0xFF5000FF, 0xFF28007f); - } + } /** * Opens a simple dialog that can be used to display information with an "Ok" button that will close the dialog. @@ -105,7 +103,7 @@ public void renderBehind(GuiRender render, double mouseX, double mouseY, float p * @param parent Can be any gui element (Will just be used to get the root element) * @param dialogText */ - public static OptionDialog simpleInfoDialog(@NotNull GuiParent parent, Text dialogText) { + public static OptionDialog simpleInfoDialog(@NotNull GuiParent parent, Component dialogText) { return simpleInfoDialog(parent, dialogText, () ->{}); } @@ -114,16 +112,16 @@ public static OptionDialog simpleInfoDialog(@NotNull GuiParent parent, Text d * * @param parent Can be any gui element (Will just be used to get the root element) */ - public static OptionDialog simpleInfoDialog(@NotNull GuiParent parent, Text dialogText, Runnable onAccepted) { + public static OptionDialog simpleInfoDialog(@NotNull GuiParent parent, Component dialogText, Runnable onAccepted) { OptionDialog dialog = new OptionDialog(parent.getModularGui().getRoot(), dialogText, - ScreenTexts.OK.copy().formatted(GREEN)) + CommonComponents.GUI_OK.copy().withStyle(GREEN)) .onButtonPress(0, onAccepted); - int height = parent.font().getWrappedLinesHeight(dialogText, 190); + int height = parent.font().wordWrapHeight(dialogText, 190); dialog.constrain(HEIGHT, literal(45 + height)); return dialog; } } -//?} + diff --git a/src/main/java/com/github/zly2006/xbackup/mixin/MixinSelectWorldScreen.java b/src/main/java/com/github/zly2006/xbackup/mixin/MixinSelectWorldScreen.java index 83af7a0..dee8f9a 100644 --- a/src/main/java/com/github/zly2006/xbackup/mixin/MixinSelectWorldScreen.java +++ b/src/main/java/com/github/zly2006/xbackup/mixin/MixinSelectWorldScreen.java @@ -2,20 +2,16 @@ import com.github.zly2006.xbackup.BackupDatabaseService; import com.github.zly2006.xbackup.XBackup; -//? if poly_lib { import com.github.zly2006.xbackup.gui.BackupsGui; -//?} import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.world.SelectWorldScreen; -import net.minecraft.client.gui.screen.world.WorldListWidget; -//? if >= 1.21.6 { -/*import net.minecraft.client.gui.tooltip.Tooltip; -*///?} -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen; +import net.minecraft.client.gui.screens.worldselection.WorldSelectionList; +import net.minecraft.world.level.storage.LevelSummary; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import net.minecraft.ChatFormatting; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -27,37 +23,34 @@ @Mixin(SelectWorldScreen.class) public class MixinSelectWorldScreen extends Screen { - protected MixinSelectWorldScreen(Text title) { + protected MixinSelectWorldScreen(Component title) { super(title); } - //? if poly_lib { @Unique - ButtonWidget buttonWidget; + Button buttonWidget; @Shadow - private WorldListWidget levelList; + private WorldSelectionList list; @Inject( method = "init", at = @At("RETURN") ) private void postInit(CallbackInfo ci) { - //? if >=1.21.6 { - /*Tooltip backupTooltip; + Tooltip backupTooltip; if (FabricLoader.getInstance().isModLoaded("polylib")) { - backupTooltip = Tooltip.of(Text.translatable("xb.button.backups")); + backupTooltip = Tooltip.create(Component.translatable("xb.button.backups")); } else { - backupTooltip = Tooltip.of(Text.translatable("xb.gui.no_polylib").formatted(Formatting.RED)); + backupTooltip = Tooltip.create(Component.translatable("xb.gui.no_polylib").withStyle(ChatFormatting.RED)); } - *///?} - buttonWidget = ButtonWidget.builder(Text.literal("回"), + buttonWidget = Button.builder(Component.literal("回"), (button) -> { if (!FabricLoader.getInstance().isModLoaded("polylib")) { return; } - if (levelList.getSelectedAsOptional().isPresent()) { - String name = levelList.getSelectedAsOptional().get().level.getName(); + if (list.getSelectedOpt().isPresent()) { + String name = list.getSelectedOpt().get().getLevelSummary().getLevelId(); BackupDatabaseService service = new BackupDatabaseService( Path.of("saves").toAbsolutePath().normalize(), XBackup.INSTANCE.getDatabaseFromWorld(Path.of("saves", name)), @@ -66,39 +59,20 @@ private void postInit(CallbackInfo ci) { ); BackupsGui.Companion.open(service, Path.of("saves", name)); } - }).dimensions(this.width / 2 + 160, this.height - 28, 20, 20) - //? if >= 1.21.6 { - /*.tooltip(backupTooltip) - *///?} + }).bounds(this.width / 2 + 160, this.height - 28, 20, 20) + .tooltip(backupTooltip) .build(); - buttonWidget.active = levelList.getSelectedAsOptional().isPresent(); - this.addDrawableChild(buttonWidget); + buttonWidget.active = list.getSelectedOpt().isPresent(); + this.addRenderableWidget(buttonWidget); } @Inject( - method = "worldSelected", + method = "updateButtonStatus", at = @At("RETURN") ) - private void worldSelected(CallbackInfo ci) { + private void onUpdateButtonStatus(LevelSummary summary, CallbackInfo ci) { if (buttonWidget != null) { - buttonWidget.active = levelList.getSelectedAsOptional().isPresent(); + buttonWidget.active = list.getSelectedOpt().isPresent(); } } - - //? if < 1.21.6 { - @Inject( - method = "render", - at = @At("RETURN") - ) - private void render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { - if (buttonWidget != null && buttonWidget.isHovered()) { - if (FabricLoader.getInstance().isModLoaded("polylib")) { - setTooltip(Text.translatable("xb.button.backups")); - } else { - setTooltip(Text.translatable("xb.gui.no_polylib").formatted(Formatting.RED)); - } - } - } - //?} - //?} } diff --git a/src/main/java/com/github/zly2006/xbackup/mixin/MixinServer.java b/src/main/java/com/github/zly2006/xbackup/mixin/MixinServer.java index 13cefb3..74b8469 100644 --- a/src/main/java/com/github/zly2006/xbackup/mixin/MixinServer.java +++ b/src/main/java/com/github/zly2006/xbackup/mixin/MixinServer.java @@ -11,7 +11,7 @@ @Mixin(value = MinecraftServer.class, priority = 1001) public class MixinServer { @Inject( - method = "save", + method = "saveAllChunks", at = @At("HEAD"), cancellable = true ) @@ -22,7 +22,7 @@ private void disableSave(CallbackInfoReturnable cir) { } @Inject( - method = "saveAll", + method = "saveEverything", at = @At("HEAD"), cancellable = true ) @@ -33,7 +33,7 @@ private void disableSaveAll(CallbackInfoReturnable cir) { } @Inject( - method = "shutdown", + method = "stopServer", at = @At("TAIL") ) private void onShutdown(CallbackInfo ci) { diff --git a/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinDedicatedServerWatchdog.java b/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinDedicatedServerWatchdog.java index 499e4cd..4ad6784 100644 --- a/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinDedicatedServerWatchdog.java +++ b/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinDedicatedServerWatchdog.java @@ -1,28 +1,28 @@ package com.github.zly2006.xbackup.mixin.disable; import com.github.zly2006.xbackup.XBackup; -import net.minecraft.server.dedicated.DedicatedServerWatchdog; +import net.minecraft.server.dedicated.ServerWatchdog; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -@Mixin(DedicatedServerWatchdog.class) +@Mixin(ServerWatchdog.class) public class MixinDedicatedServerWatchdog { - @Shadow @Final private long maxTickTime; + @Shadow @Final private long maxTickTimeNanos; @Redirect( method = "run", at = @At( value = "FIELD", - target = "Lnet/minecraft/server/dedicated/DedicatedServerWatchdog;maxTickTime:J", + target = "Lnet/minecraft/server/dedicated/ServerWatchdog;maxTickTimeNanos:J", ordinal = 0 ) ) - private long redirectMaxTickTime(DedicatedServerWatchdog instance) { + private long redirectMaxTickTime(ServerWatchdog instance) { if (XBackup.INSTANCE.getDisableWatchdog()) { return Long.MAX_VALUE; - } else return maxTickTime; + } else return maxTickTimeNanos; } } diff --git a/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinStorageIoWorker.java b/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinStorageIoWorker.java index 6e02b17..7ebd4bb 100644 --- a/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinStorageIoWorker.java +++ b/src/main/java/com/github/zly2006/xbackup/mixin/disable/MixinStorageIoWorker.java @@ -1,9 +1,9 @@ package com.github.zly2006.xbackup.mixin.disable; -import net.minecraft.world.storage.StorageIoWorker; +import net.minecraft.world.level.chunk.storage.RegionFileStorage; import org.spongepowered.asm.mixin.*; -@Mixin(StorageIoWorker.class) +@Mixin(RegionFileStorage.class) public abstract class MixinStorageIoWorker { } diff --git a/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt b/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt index 7ec5753..3ec2935 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/Commands.kt @@ -20,18 +20,17 @@ import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.put import me.lucko.fabric.api.permissions.v0.Permissions -//? if >=1.21.11 { -/*import net.minecraft.command.DefaultPermissions -*///?} -import net.minecraft.command.argument.ColumnPosArgumentType -import net.minecraft.server.command.ServerCommandSource -import net.minecraft.text.ClickEvent -import net.minecraft.text.HoverEvent -import net.minecraft.text.MutableText -import net.minecraft.text.Text -import net.minecraft.util.Formatting -import net.minecraft.util.Util -import net.minecraft.util.WorldSavePath +import net.minecraft.server.permissions.PermissionLevel +import net.minecraft.commands.arguments.coordinates.ColumnPosArgument +import net.minecraft.commands.CommandSourceStack +import net.minecraft.network.chat.ClickEvent +import net.minecraft.network.chat.HoverEvent +import net.minecraft.network.chat.MutableComponent +import net.minecraft.network.chat.Component +import net.minecraft.ChatFormatting + +import net.minecraft.world.level.storage.LevelResource +import net.minecraft.server.permissions.Permission import java.net.URI import java.nio.file.Path import java.text.SimpleDateFormat @@ -42,7 +41,7 @@ import kotlin.math.max import kotlin.math.min import kotlin.system.exitProcess -fun shortDateTimeText(time: Long): MutableText { +fun shortDateTimeText(time: Long): MutableComponent { val text = if (System.currentTimeMillis() - time < 24 * 3600 * 1000) { SimpleDateFormat("HH:mm").apply { timeZone = TimeZone.getDefault() @@ -53,15 +52,15 @@ fun shortDateTimeText(time: Long): MutableText { timeZone = TimeZone.getDefault() }.format(time) } - return Text.literal(text).apply { - hover(Text.literal(SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").apply { + return Component.literal(text).apply { + hover(Component.literal(SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").apply { timeZone = TimeZone.getDefault() }.format(time))) - formatted(Formatting.GOLD)!! + withStyle(ChatFormatting.GOLD)!! } } -fun backupIdText(id: Int) = Text.literal("#$id").formatted(Formatting.AQUA)!! +fun backupIdText(id: Int) = Component.literal("#$id").withStyle(ChatFormatting.AQUA)!! fun sizeToString(bytes: Long): String { val kb = bytes / 1024.0 @@ -78,45 +77,31 @@ fun sizeToString(bytes: Long): String { } } -fun sizeText(bytes: Long) = Text.literal(sizeToString(bytes)).formatted(Formatting.GREEN)!! +fun sizeText(bytes: Long) = Component.literal(sizeToString(bytes)).withStyle(ChatFormatting.GREEN)!! -fun MutableText.hover(literalText: MutableText) { - styled { +fun MutableComponent.hover(literalText: MutableComponent) { + withStyle { it.withHoverEvent( - //? if >=1.21.5 { HoverEvent.ShowText(literalText) - //?} else { - /*HoverEvent( - HoverEvent.Action.SHOW_TEXT, - literalText - ) - *///?} ) } } -fun MutableText.clickRun(cmd: String) { - styled { +fun MutableComponent.clickRun(cmd: String) { + withStyle { it.withClickEvent( - //? if >=1.21.5 { ClickEvent.RunCommand(cmd) - //?} else { - /*ClickEvent( - ClickEvent.Action.RUN_COMMAND, - cmd - ) - *///?} ) } } object Commands { - fun networkStatsText(): MutableText { + fun networkStatsText(): MutableComponent { val cloudStorage = XBackup.service.cloudStorageProvider - return Text.empty().apply { - append(Text.literal("⏶" + sizeToString(cloudStorage.bytesSentLastSecond) + "/s")) + return Component.empty().apply { + append(Component.literal("⏶" + sizeToString(cloudStorage.bytesSentLastSecond) + "/s")) append(" ") - append(Text.literal("⏷" + sizeToString(cloudStorage.bytesReceivedLastSecond) + "/s")) + append(Component.literal("⏷" + sizeToString(cloudStorage.bytesReceivedLastSecond) + "/s")) } } @@ -126,14 +111,14 @@ object Commands { ).create() } - fun register(dispatcher: CommandDispatcher) { + fun register(dispatcher: CommandDispatcher) { dispatcher.register { literal("xb") { literal("status") { - fun basicStatus(source: ServerCommandSource) { + fun basicStatus(source: CommandSourceStack) { source.send(Utils.translate("command.xb.status", if (XBackup.isBusy) "Busy" else "OK")) if (XBackup.config.mirrorMode) { - source.send(Text.literal("X Backup is in mirror mode").formatted(Formatting.GOLD)) + source.send(Component.literal("X Backup is in mirror mode").withStyle(ChatFormatting.GOLD)) } source.send( Utils.translate( @@ -142,7 +127,7 @@ object Commands { ) ) if (XBackup.service.activeTaskProgress != -1) { - source.send(Text.literal("云备份任务:${XBackup.service.activeTask} ${XBackup.service.activeTaskProgress}%")) + source.send(Component.literal("云备份任务:${XBackup.service.activeTask} ${XBackup.service.activeTaskProgress}%")) source.send(networkStatsText()) } } @@ -214,7 +199,7 @@ object Commands { } if (XBackup.service.backupCount() > offset + 6) { it.source.send(Utils.translate("command.xb.more_backups").apply { - formatted(Formatting.GRAY) + withStyle(ChatFormatting.GRAY) hover(Utils.translate("command.xb.click_view_more")) clickRun("/xb list ${offset + 6}") }) @@ -228,16 +213,9 @@ object Commands { executes { it.source.send( Utils.translate("command.xb.version", XBackup.MOD_VERSION + "(" + XBackup.GIT_COMMIT + ")") - .styled { + .withStyle { it.withClickEvent( - //? if >=1.21.5 { - ClickEvent.OpenUrl(URI("https://github.com/zly2006/x-backup")) - //?} else { - /*ClickEvent( - ClickEvent.Action.OPEN_URL, - "https://github.com/zly2006/x-backup" - ) - *///?} + ClickEvent.OpenUrl(URI("https://github.com/lnminh1411/x-backup")) ) } ) @@ -254,18 +232,18 @@ object Commands { "command.xb.backup_info", backupIdText(id), backup.comment, sizeText(backup.size), sizeText(backup.zippedSize), - Text.literal(SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").apply { + Component.literal(SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").apply { timeZone = TimeZone.getDefault() }.format(backup.created)).apply { - formatted(Formatting.GOLD)!! + withStyle(ChatFormatting.GOLD)!! } ).apply { append("\n") append( Utils.translate("command.xb.delete").apply { - hover(Utils.translate("command.xb.click_delete").formatted(Formatting.RED)) + hover(Utils.translate("command.xb.click_delete").withStyle(ChatFormatting.RED)) clickRun("/xb delete $id") - formatted(Formatting.RED) + withStyle(ChatFormatting.RED) } ) append(Utils.translate("command.xb.space")) @@ -273,7 +251,7 @@ object Commands { Utils.translate("command.xb.restore").apply { hover(Utils.translate("command.xb.click_restore")) clickRun("/xb restore $id") - formatted(Formatting.DARK_GREEN) + withStyle(ChatFormatting.DARK_GREEN) } ) } @@ -292,10 +270,10 @@ object Commands { } } - private fun registerMirrorMode(dispatcher: CommandDispatcher) { + private fun registerMirrorMode(dispatcher: CommandDispatcher) { dispatcher.register { literal("mirror") { - fun CommandContext.backup(): IBackup { + fun CommandContext.backup(): IBackup { val id = try { IntegerArgumentType.getInteger(this, "id") } catch (_: IllegalArgumentException) { @@ -308,12 +286,12 @@ object Commands { optional(argument("id", IntegerArgumentType.integer(1))) { requires = checkPermission("x_backup.mirror", 0) executes { - val path = it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath() + val path = it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath() doRestore(it.backup(), it, path, forceStop = true) 1 } literal("--restart").executes { - val path = it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath() + val path = it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath() doRestore(it.backup(), it, path, forceStop = false) 1 } @@ -323,14 +301,14 @@ object Commands { } @OptIn(ExperimentalSerializationApi::class) - private fun registerBackupMode(dispatcher: CommandDispatcher) { + private fun registerBackupMode(dispatcher: CommandDispatcher) { dispatcher.register { literal("xb") { literal("create") { requires = checkPermission("x_backup.create", 0) optional(argument("comment", StringArgumentType.greedyString())) { executes { - val path = it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath().normalize() + val path = it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize() val comment = try { StringArgumentType.getString(it, "comment") } catch (_: IllegalArgumentException) { @@ -338,24 +316,24 @@ object Commands { } XBackup.ensureNotBusy { it.source.server.broadcast( - Utils.translate("command.xb.creating_backup", it.source.name) + Utils.translate("command.xb.creating_backup", it.source.textName) ) it.source.server.save() it.source.server.setAutoSaving(false) XBackup.disableSaving = true val result = XBackup.service.createBackup( path, - "$comment by ${it.source.name}", + "$comment by ${it.source.textName}", temporary = false, buildJsonObject { put("mod_ver", XBackup.MOD_VERSION) - put("source", it.source.name) + put("source", it.source.textName) } ) it.source.server.broadcast( Utils.translate( "command.xb.backup_finished", - backupIdText(result.backId), it.source.name, sizeText(result.totalSize), + backupIdText(result.backId), it.source.textName, sizeText(result.totalSize), sizeText(result.compressedSize), sizeText(result.addedSize), result.millis ) ) @@ -402,14 +380,14 @@ object Commands { argument("id", IntegerArgumentType.integer(1)) { literal("--chunk") { requires = checkPermission("x_backup.restore.regional", 4) - argument("from", ColumnPosArgumentType.columnPos()) { - argument("to", ColumnPosArgumentType.columnPos()).executes { + argument("from", ColumnPosArgument.columnPos()) { + argument("to", ColumnPosArgument.columnPos()).executes { val id = IntegerArgumentType.getInteger(it, "id") - val from = ColumnPosArgumentType.getColumnPos(it, "from") - val to = ColumnPosArgumentType.getColumnPos(it, "to") + val from = ColumnPosArgument.getColumnPos(it, "from") + val to = ColumnPosArgument.getColumnPos(it, "to") val path = - it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath().normalize() - val world = it.source.world + it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize() + val world = it.source.level val backup = getBackup(id) val minX = min(from.x, to.x) val maxX = max(from.x, to.x) @@ -452,7 +430,7 @@ object Commands { } literal("--restart").executes { val id = IntegerArgumentType.getInteger(it, "id") - val path = it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath() + val path = it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath() val backup = getBackup(id) doRestore(backup, it, path, forceStop = false) 1 @@ -461,7 +439,7 @@ object Commands { requires = checkPermission("x_backup.restore.force", 4) executes { val id = IntegerArgumentType.getInteger(it, "id") - val path = it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath() + val path = it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath() val backup = getBackup(id) doRestore(backup, it, path, recheck = false, forceStop = true) 1 @@ -469,7 +447,7 @@ object Commands { } }.executes { val id = IntegerArgumentType.getInteger(it, "id") - val path = it.source.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath() + val path = it.source.server.getWorldPath(LevelResource.ROOT).toAbsolutePath() val backup = getBackup(id) doRestore(backup, it, path, forceStop = true) 1 @@ -495,9 +473,10 @@ object Commands { .toAbsolutePath() .createParentDirectories() file.outputStream().use { - Json.encodeToStream(backup, it) + @OptIn(ExperimentalSerializationApi::class) + Json.encodeToStream(BackupDatabaseService.Backup.serializer(), backup as BackupDatabaseService.Backup, it) } - it.source.send(Text.literal("Saved backup details to $file")) + it.source.send(Component.literal("Saved backup details to $file")) } 1 } @@ -506,7 +485,7 @@ object Commands { executes { XBackup.ensureNotBusy { val result = XBackup.service.deleteUnusedBlobs() - it.source.send(Text.literal("Deleted $result unused blobs")) + it.source.send(Component.literal("Deleted $result unused blobs")) } 1 } @@ -544,7 +523,7 @@ object Commands { downloaded++ XBackup.service.activeTaskProgress = 100 * downloaded / total } - it.source.send(Text.keybind("Debug: Downloaded backup $id")) + it.source.send(Component.keybind("Debug: Downloaded backup $id")) 1 } 1 @@ -568,16 +547,17 @@ object Commands { literal("restart") { executes { Thread { - when (Util.getOperatingSystem()) { - Util.OperatingSystem.WINDOWS -> { - it.source.server.stop(true) + val os = System.getProperty("os.name", "").lowercase() + when { + os.contains("win") -> { + it.source.server.halt(true) ProcessBuilder( RestartUtils.generateWindowsRestartCommand() ).start() } - Util.OperatingSystem.LINUX, Util.OperatingSystem.OSX -> { - it.source.server.stop(true) + os.contains("mac") || os.contains("nix") || os.contains("nux") || os.contains("aix") -> { + it.source.server.halt(true) ProcessBuilder( RestartUtils.generateUnixRestartCommand() ).start() @@ -614,7 +594,7 @@ object Commands { runBlocking { XBackup.crontabJob?.cancelAndJoin() } - it.source.send(Text.literal("Stopped crontab job")) + it.source.send(Component.literal("Stopped crontab job")) 1 } literal("zip") { @@ -673,7 +653,7 @@ object Commands { private fun doRestore( backup: IBackup, - it: CommandContext, + it: CommandContext, path: Path, forceStop: Boolean = false, recheck: Boolean = true, @@ -682,7 +662,7 @@ object Commands { val service = XBackup.service // Note: on server thread if (recheck && !service.check(backup)) { - it.source.sendError(Utils.translate("command.xb.backup_corrupted", backupIdText(backup.id)).apply { + it.source.sendFailure(Utils.translate("command.xb.backup_corrupted", backupIdText(backup.id)).apply { hover(Utils.translate("command.xb.backup_corrupted.force", backupIdText(backup.id))) }) return @@ -731,34 +711,30 @@ object Commands { } } } - it.source.server.stop(false) - it.source.server.networkIo?.connections?.forEach { - it.disconnected = true + it.source.server.halt(false) + it.source.server.connection?.connections?.forEach { + it.disconnectionHandled = true // prevent the game from saving player data again } XBackup.log.info("[X Backup] Waiting for server to stop...") - it.source.server.thread.join() + it.source.server.runningThread.join() } } - private fun checkPermission(perm: String, defaultLevel: Int = 2): (ServerCommandSource) -> Boolean = { source -> + private fun checkPermission(perm: String, defaultLevel: Int = 2): (CommandSourceStack) -> Boolean = { source -> try { // Call fabric-permissions API, but it might not be available Permissions.check(source, perm, defaultLevel) } catch (e: NoClassDefFoundError) { // If the API is not available, just return true - //? if >=1.21.11 { - /*val permission = when { + val permission = when { defaultLevel <= 0 -> null - defaultLevel <= 1 -> DefaultPermissions.MODERATORS - defaultLevel <= 2 -> DefaultPermissions.GAMEMASTERS - defaultLevel <= 3 -> DefaultPermissions.ADMINS - else -> DefaultPermissions.OWNERS + defaultLevel <= 1 -> PermissionLevel.MODERATORS + defaultLevel <= 2 -> PermissionLevel.GAMEMASTERS + defaultLevel <= 3 -> PermissionLevel.ADMINS + else -> PermissionLevel.OWNERS } - permission == null || source.permissions.hasPermission(permission) - *///?} else { - source.hasPermissionLevel(defaultLevel) - //?} + permission == null || source.permissions().hasPermission(Permission.HasCommandLevel(permission)) } } } diff --git a/src/main/kotlin/com/github/zly2006/xbackup/Task.kt b/src/main/kotlin/com/github/zly2006/xbackup/Task.kt index 3b9e730..ca9c6f1 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/Task.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/Task.kt @@ -1,10 +1,10 @@ package com.github.zly2006.xbackup -import net.minecraft.text.Text +import net.minecraft.network.chat.Component interface Task { val name: String - val displayName: Text + val displayName: Component val startedAt: Long val finishedAt: Long val status: Status diff --git a/src/main/kotlin/com/github/zly2006/xbackup/Utils.kt b/src/main/kotlin/com/github/zly2006/xbackup/Utils.kt index 0d2aac8..f2c5606 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/Utils.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/Utils.kt @@ -1,37 +1,35 @@ package com.github.zly2006.xbackup import net.minecraft.server.MinecraftServer -import net.minecraft.server.command.ServerCommandSource -import net.minecraft.server.world.ServerWorld -import net.minecraft.text.MutableText -import net.minecraft.text.Text -import net.minecraft.util.WorldSavePath -import net.minecraft.world.dimension.DimensionType +import net.minecraft.commands.CommandSourceStack +import net.minecraft.server.level.ServerLevel +import net.minecraft.network.chat.MutableComponent +import net.minecraft.network.chat.Component +import net.minecraft.world.level.storage.LevelResource +import net.minecraft.world.level.dimension.DimensionType import java.nio.file.Path @Suppress("NOTHING_TO_INLINE") object Utils { - inline fun translate(key: String, vararg args: Any): MutableText { - return Text.translatableWithFallback( + inline fun translate(key: String, vararg args: Any): MutableComponent { + return Component.translatableWithFallback( key, I18n[key], *args ) } - inline fun ServerCommandSource.send(text: Text) { - sendMessage(text) + inline fun CommandSourceStack.send(text: Component) { + sendSystemMessage(text) } inline fun MinecraftServer.setAutoSaving(value: Boolean) { - worlds.forEach { it.savingDisabled = !value } + allLevels.forEach { it.noSave = !value } } inline fun MinecraftServer.save() { - saveAll(false, false, true) - //? if >=1.21.11 { - /*syncChunkWrites() - *///?} + saveEverything(false, false, true) + forceSynchronousWrites() } inline fun MinecraftServer.finishRestore() { @@ -45,14 +43,14 @@ object Utils { runServer() } - inline fun MinecraftServer.broadcast(text: Text) { - playerManager.broadcast(text, false) + inline fun MinecraftServer.broadcast(text: Component) { + playerList.broadcastSystemMessage(text, false) } - fun isFileInWorld(world: ServerWorld, p: Path): Boolean { - val path = DimensionType.getSaveDirectory( - world.registryKey, - world.server.getSavePath(WorldSavePath.ROOT).toAbsolutePath() + fun isFileInWorld(world: ServerLevel, p: Path): Boolean { + val path = DimensionType.getStorageFolder( + world.dimension(), + world.server.getWorldPath(LevelResource.ROOT).toAbsolutePath() ).normalize() return p.normalize().startsWith(path) } diff --git a/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt b/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt index 4b88be9..015f384 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/XBackup.kt @@ -17,13 +17,12 @@ import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents import net.fabricmc.loader.api.FabricLoader -import net.minecraft.client.MinecraftClient -import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket +import net.minecraft.client.Minecraft +import net.minecraft.network.protocol.game.ClientboundTabListPacket import net.minecraft.server.MinecraftServer -import net.minecraft.server.command.ServerCommandSource -import net.minecraft.text.Text -import net.minecraft.util.Util -import net.minecraft.util.WorldSavePath +import net.minecraft.commands.CommandSourceStack +import net.minecraft.network.chat.Component +import net.minecraft.world.level.storage.LevelResource import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.transactions.TransactionManager import org.slf4j.LoggerFactory @@ -42,9 +41,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.16" - const val GIT_COMMIT = /*$ git_commit*/ "72cc36c" - const val COMMIT_DATE = /*$ commit_date*/ "2026-01-12T11:45:52+08:00" + const val MOD_VERSION = "0.4.0" + const val GIT_COMMIT = "72cc36c" + const val COMMIT_DATE = "2026-01-12T11:45:52+08:00" var _service: BackupDatabaseService? = null val service get() = _service!! var server: MinecraftServer? = null @@ -124,15 +123,12 @@ object XBackup : ModInitializer { } } if (System.getProperty("xb.restart") == "true") { - when (Util.getOperatingSystem()) { - Util.OperatingSystem.OSX, Util.OperatingSystem.LINUX -> { - ProcessBuilder(RestartUtils.generateUnixRestartCommand()) - .start() - } - - else -> { - error("Unsupported operating system") - } + val os = System.getProperty("os.name", "").lowercase() + if (os.contains("mac") || os.contains("nix") || os.contains("nux") || os.contains("aix")) { + ProcessBuilder(RestartUtils.generateUnixRestartCommand()) + .start() + } else { + error("Unsupported operating system") } log.info("Restarting...") @@ -147,20 +143,16 @@ object XBackup : ModInitializer { ServerLifecycleEvents.SERVER_STARTED.register { server -> this.server = server - //? if >= 1.21.11 { - /*server.commandManager.parseAndExecute(XBackup.server!!.commandSource, "1") - *///?} else { - server.commandManager.executeWithPrefix(XBackup.server!!.commandSource, "1") - //?} + server.commands.performPrefixedCommand(XBackup.server!!.createCommandSourceStack(), "1") kotlin.runCatching { // sync client language to the integrated server - config.language = I18n.setLanguage(MinecraftClient.getInstance().options.language) + config.language = I18n.setLanguage(Minecraft.getInstance().options.languageCode) } val worldPath = if (config.mirrorMode) { File(config.mirrorFrom!!).toPath().resolve("world") } else { - server.getSavePath(WorldSavePath.ROOT) + server.getWorldPath(LevelResource.ROOT) }.toAbsolutePath().normalize() val database = getDatabaseFromWorld(worldPath) if (config.mirrorMode) { @@ -231,10 +223,10 @@ object XBackup : ModInitializer { (cs.bytesSentLastSecond > 0 || cs.bytesReceivedLastSecond > 0) ) { runCatching { - server.playerManager.sendToAll( - PlayerListHeaderS2CPacket( - Text.empty(), - Text.literal("X Backup Network Stat\n") + server.playerList.broadcastAll( + ClientboundTabListPacket( + Component.empty(), + Component.literal("X Backup Network Stat\n") .append(Commands.networkStatsText()) ) ) @@ -296,7 +288,7 @@ object XBackup : ModInitializer { isBusy = true server.broadcast(Utils.translate("message.xb.running_scheduled_backup")) val (_, _, backId, totalSize, compressedSize, addedSize, millis) = service.createBackup( - server.getSavePath(WorldSavePath.ROOT).toAbsolutePath(), + server.getWorldPath(LevelResource.ROOT).toAbsolutePath(), I18n["message.xb.scheduled_backup"], metadata = buildJsonObject { put("scheduled", true) @@ -393,12 +385,12 @@ object XBackup : ModInitializer { fun ensureNotBusy( context: CoroutineContext = server!!.asCoroutineDispatcher(), - source: ServerCommandSource? = null, + source: CommandSourceStack? = null, block: suspend () -> Unit ) { - require(server!!.isOnThread) + require(server!!.isSameThread) if (isBusy) { - throw SimpleCommandExceptionType(Text.of("Backup is already running")).create() + throw SimpleCommandExceptionType(Component.literal("Backup is already running")).create() } isBusy = true service.launch(context) { @@ -407,7 +399,7 @@ object XBackup : ModInitializer { } catch (e: Throwable) { log.error("Error running X Backup task", e) - source?.sendError(Text.of("Error running X Backup task: ${e.message}")) + source?.sendFailure(Component.literal("Error running X Backup task: ${e.message}")) } finally { isBusy = false diff --git a/src/main/kotlin/com/github/zly2006/xbackup/cloud/OnedriveSupport.kt b/src/main/kotlin/com/github/zly2006/xbackup/cloud/OnedriveSupport.kt index 1933969..0e345c2 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/cloud/OnedriveSupport.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/cloud/OnedriveSupport.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonPrimitive -import net.minecraft.text.ClickEvent +import net.minecraft.network.chat.ClickEvent import org.jetbrains.exposed.sql.update import org.slf4j.LoggerFactory import java.net.URI @@ -129,18 +129,11 @@ class OnedriveSupport( if (err?.startsWith("freePlan:") == true) { service.activeTaskProgress = -1 service.activeTask = "Failed to get upload session: Free plan limit" - XBackup.server?.broadcast(Utils.translate("message.xb.error.free_plan_limit").styled { - it.withClickEvent( - //? if >=1.21.5 { - ClickEvent.OpenUrl(URI("https://redenmc.com/x-backup/plans")) - //?} else { - /*ClickEvent( - ClickEvent.Action.OPEN_URL, - "https://redenmc.com/x-backup/plans" - ) - *///?} - ) - }) + XBackup.server?.broadcast(Utils.translate("message.xb.error.free_plan_limit").withStyle { + it.withClickEvent( + ClickEvent.OpenUrl(URI("https://redenmc.com/x-backup/plans")) + ) + }) throw DontRetryException(IllegalStateException("Free plan limit")) } } diff --git a/src/main/kotlin/com/github/zly2006/xbackup/gui/BackupsGui.kt b/src/main/kotlin/com/github/zly2006/xbackup/gui/BackupsGui.kt index 3a01c8b..3989ade 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/gui/BackupsGui.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/gui/BackupsGui.kt @@ -1,6 +1,5 @@ package com.github.zly2006.xbackup.gui -//? if poly_lib { import com.github.zly2006.xbackup.BackupDatabaseService import kotlinx.coroutines.runBlocking import net.creeperhost.polylib.client.modulargui.ModularGui @@ -12,12 +11,12 @@ import net.creeperhost.polylib.client.modulargui.lib.GuiProvider import net.creeperhost.polylib.client.modulargui.lib.GuiRender import net.creeperhost.polylib.client.modulargui.lib.geometry.* import net.creeperhost.polylib.client.modulargui.sprite.Material -import net.minecraft.client.MinecraftClient -import net.minecraft.client.texture.NativeImage -import net.minecraft.client.texture.NativeImageBackedTexture -import net.minecraft.text.Text -import net.minecraft.util.Formatting -import net.minecraft.util.Identifier +import net.minecraft.client.Minecraft +import com.mojang.blaze3d.platform.NativeImage +import net.minecraft.client.renderer.texture.DynamicTexture +import net.minecraft.network.chat.Component +import net.minecraft.ChatFormatting +import net.minecraft.resources.Identifier import java.nio.file.Path import java.text.SimpleDateFormat @@ -32,7 +31,7 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path override fun buildGui(gui: ModularGui) { gui.renderScreenBackground(false) gui.initFullscreenGui() - gui.guiTitle = Text.translatable("xb.gui.backups.title") + gui.guiTitle = Component.translatable("xb.gui.backups.title") val root = gui.root @@ -49,14 +48,14 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path .constrain(GeoParam.TOP, Constraint.relative(root[GeoParam.TOP], 20.0)) .constrain(GeoParam.BOTTOM, Constraint.relative(root[GeoParam.BOTTOM], -24.0)) - val back = BMStyle.Flat.button(root, Text.translatable("xb.button.back_arrow")) + val back = BMStyle.Flat.button(root, Component.translatable("xb.button.back_arrow")) .onPress { gui.mc().setScreen(gui.parentScreen) } .constrain(GeoParam.BOTTOM, Constraint.relative(listBackground!![GeoParam.TOP], -4.0)) .constrain(GeoParam.LEFT, Constraint.match(listBackground[GeoParam.LEFT])) .constrain(GeoParam.WIDTH, Constraint.literal(50.0)) .constrain(GeoParam.HEIGHT, Constraint.literal(12.0)) - val restore = BMStyle.Flat.buttonPrimary(root, Text.translatable("xb.button.restore_backup")) + val restore = BMStyle.Flat.buttonPrimary(root, Component.translatable("xb.button.restore_backup")) .setDisabled { selected == null } .onPress { restoreSelected(gui) } .constrain(GeoParam.TOP, Constraint.relative(listBackground[GeoParam.BOTTOM], 5.0)) @@ -64,7 +63,7 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path .constrain(GeoParam.WIDTH, Constraint.literal(150.0)) .constrain(GeoParam.HEIGHT, Constraint.literal(14.0)) - val delete = BMStyle.Flat.buttonCaution(root, Text.translatable("xb.button.delete_backup")) + val delete = BMStyle.Flat.buttonCaution(root, Component.translatable("xb.button.delete_backup")) .onPress { deleteSelected(gui) } .setDisabled { selected == null } .constrain(GeoParam.TOP, Constraint.relative(listBackground[GeoParam.BOTTOM], 5.0)) @@ -123,8 +122,8 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path if (!service.check(selected!!)) { OptionDialog.simpleInfoDialog( gui, - Text.translatable("xb.gui.backups.restore_check_failed") - .formatted(Formatting.RED) + Component.translatable("xb.gui.backups.restore_check_failed") + .withStyle(ChatFormatting.RED) ) return } @@ -134,8 +133,8 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path selected = null OptionDialog.simpleInfoDialog( gui, - Text.translatable("xb.gui.backups.restored") - .formatted(Formatting.GREEN) + Component.translatable("xb.gui.backups.restored") + .withStyle(ChatFormatting.GREEN) ) } @@ -162,14 +161,10 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path } if (stream != null) { leftOffset = ySize().toInt() - 2 - val resourceLocation = Identifier.of("xbackup", "tmp/${backup.id}/icon.png") - //? if < 1.21.5 { - /*val texture = NativeImageBackedTexture(NativeImage.read(stream.readBytes())) - *///?} else { - val texture = NativeImageBackedTexture({ "xb_temp" } ,NativeImage.read(stream.readBytes())) - //?} + val resourceLocation = Identifier.fromNamespaceAndPath("xbackup", "tmp/${backup.id}/icon.png") + val texture = DynamicTexture({ "xb_temp" } ,NativeImage.read(stream.readBytes())) texture.upload() - mc().textureManager.registerTexture(resourceLocation, texture) + mc().textureManager.register(resourceLocation, texture) GuiTexture(this) { Material.fromRawTexture(resourceLocation) } .constrain(GeoParam.TOP, Constraint.relative(this[GeoParam.TOP], 1.0)) @@ -179,7 +174,7 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path leftOffset += 3 } - val name = GuiText(this, Text.literal("#" + backup.id).formatted(Formatting.AQUA)) + val name = GuiText(this, Component.literal("#" + backup.id).withStyle(ChatFormatting.AQUA)) .setShadow(false) .setAlignment(Align.LEFT) .constrain(GeoParam.TOP, Constraint.relative(get(GeoParam.TOP), 3.0)) @@ -189,7 +184,7 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path val created = GuiText( this, - Text.literal(DATE_TIME_FORMAT.format(backup.created)).formatted(Formatting.GRAY) + Component.literal(DATE_TIME_FORMAT.format(backup.created)).withStyle(ChatFormatting.GRAY) ) .setShadow(false) .setAlignment(Align.LEFT) @@ -198,24 +193,24 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path .constrain(GeoParam.RIGHT, Constraint.relative(get(GeoParam.RIGHT), -2.0)) .constrain(GeoParam.HEIGHT, Constraint.literal(8.0)) - val provText: Text = Text.literal("Backup") + val provText: Component = Component.literal("Backup") val provider = GuiText(this, provText) - .setTooltip(Text.literal("Backup")) + .setTooltip(Component.literal("Backup")) .setShadow(false) .setAlignment(Align.RIGHT) .constrain(GeoParam.TOP, Constraint.relative(get(GeoParam.TOP), 3.0)) - .constrain(GeoParam.WIDTH, Constraint.literal(font().getWidth(provText).toDouble())) + .constrain(GeoParam.WIDTH, Constraint.literal(font().width(provText).toDouble())) .constrain(GeoParam.RIGHT, Constraint.relative(get(GeoParam.RIGHT), -2.0)) .constrain(GeoParam.HEIGHT, Constraint.literal(8.0)) - val info = GuiTextList(this, listOf(Text.literal(backup.comment))) + val info = GuiTextList(this, listOf(Component.literal(backup.comment))) .setHorizontalAlign(Align.MIN) .constrain(GeoParam.LEFT, Constraint.relative(get(GeoParam.LEFT), leftOffset.toDouble())) .constrain(GeoParam.RIGHT, Constraint.relative(get(GeoParam.RIGHT), -2.0)) .constrain(GeoParam.BOTTOM, Constraint.relative(get(GeoParam.BOTTOM), -2.0)) .autoHeight() - setTooltip(Text.literal(backup.comment)) + setTooltip(Component.literal(backup.comment)) } override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { @@ -240,10 +235,7 @@ class BackupsGui(private val service: BackupDatabaseService, val worldRoot: Path companion object { private val DATE_TIME_FORMAT = SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss") fun open(service: BackupDatabaseService, worldRoot: Path) { - MinecraftClient.getInstance().setScreen(ModularGuiScreen(BackupsGui(service, worldRoot))) + Minecraft.getInstance().setScreen(ModularGuiScreen(BackupsGui(service, worldRoot))) } } } -//?} else { -/*class BackupsGui -*///?} diff --git a/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt b/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt index 29fee6d..21ad024 100644 --- a/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt +++ b/src/main/kotlin/com/github/zly2006/xbackup/gui/RestoreInfoScreen.kt @@ -2,45 +2,41 @@ 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 net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiGraphicsExtractor +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.components.Button +import net.minecraft.network.chat.Component 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 + +class RestoreInfoScreen(private val backup: IBackup, private val worldRoot: Path) : Screen(Component.translatable("xb.gui.restore.title")) { + private lateinit var reopenButton: Button override fun init() { - reopenButton = addDrawableChild( - ButtonWidget.builder(Text.translatable("xb.gui.restore.reopen")) { + reopenButton = addRenderableWidget( + Button.builder(Component.translatable("xb.gui.restore.reopen")) { reopenWorld() - }.dimensions(width / 2 - 75, height - 52, 150, 20).build() + }.bounds(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() + addRenderableWidget( + Button.builder(Component.translatable("xb.gui.restore.close")) { + minecraft?.setScreen(null) + }.bounds(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) + override fun extractRenderState(context: GuiGraphicsExtractor, mouseX: Int, mouseY: Int, delta: Float) { + extractBackground(context, mouseX, mouseY, delta) + super.extractRenderState(context, mouseX, mouseY, delta) + context.centeredText(font, 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) + val idText = Component.translatable("xb.gui.restore.id", backup.id) + context.centeredText(font, 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 comment = Component.translatable("xb.gui.restore.comment", backup.comment) + context.centeredText(font, comment, width / 2, y, 0xFFFFFF) } val progress = XBackup.service.activeTaskProgress @@ -54,29 +50,25 @@ class RestoreInfoScreen(private val backup: IBackup, private val worldRoot: Path 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) + context.centeredText(font, Component.literal("$progress%"), width / 2, yBar - 10, 0xFFFFFF) } } companion object { fun open(backup: IBackup, worldRoot: Path) { - val client = MinecraftClient.getInstance() + val client = Minecraft.getInstance() client.execute { client.setScreen(RestoreInfoScreen(backup, worldRoot)) } } } private fun reopenWorld() { - val client = MinecraftClient.getInstance() + val client = Minecraft.getInstance() client.setScreen(null) runCatching { - val loader = client.createIntegratedServerLoader() - //? if >= 1.20.4 { - loader.start(worldRoot.normalize().name) { - this.close() + val loader = client.createWorldOpenFlows() + loader.openWorld(worldRoot.normalize().fileName.toString()) { + // Callback (can be empty) } - //?} else { - /*loader.start(this, worldRoot.normalize().name) - *///?} }.onFailure { XBackup.log.error("Failed to reopen world", it) } } } diff --git a/src/main/resources/xb.shared.accesswidener b/src/main/resources/xb.shared.accesswidener index 17855b1..29df352 100644 --- a/src/main/resources/xb.shared.accesswidener +++ b/src/main/resources/xb.shared.accesswidener @@ -1,6 +1,7 @@ -accessWidener v1 named +accessWidener v1 official accessible field net/minecraft/server/MinecraftServer running Z accessible field net/minecraft/server/MinecraftServer stopped Z accessible method net/minecraft/server/MinecraftServer runServer ()V -accessible field net/minecraft/network/ClientConnection disconnected Z -accessible field net/minecraft/client/gui/screen/world/WorldListWidget$WorldEntry level Lnet/minecraft/world/level/storage/LevelSummary; +accessible field net/minecraft/network/Connection disconnectionHandled Z +accessible field net/minecraft/client/gui/screens/worldselection/WorldSelectionList$WorldListEntry summary Lnet/minecraft/world/level/storage/LevelSummary; +accessible field net/minecraft/commands/CommandSourceStack source Lnet/minecraft/commands/CommandSource; diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts deleted file mode 100644 index 8b2fc10..0000000 --- a/stonecutter.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id("dev.kikugie.stonecutter") - id("fabric-loom") version "1.13.3" apply false - - kotlin("jvm") version "2.0.21" apply false - kotlin("plugin.serialization") version "2.0.0" apply false - id("io.github.goooler.shadow") version "8.1.7" apply false - base - id("me.modmuss50.mod-publish-plugin") version "0.8.4" apply false - id("org.ajoberstar.grgit") version "5.0.0-rc.3" -} -stonecutter active "1.21.5" /* [SC] DO NOT EDIT */ - -subprojects { - apply(plugin = "org.jetbrains.kotlin.jvm") - apply(plugin = "org.jetbrains.kotlin.plugin.serialization") - - repositories { - mavenLocal() - mavenCentral() - maven { - url = uri("https://maven.shedaniel.me/") - } - maven { - url = uri("https://jitpack.io/") - } - maven { - url = uri("https://masa.dy.fi/maven") - } - } - - base { - archivesName = property("mod.id") as String + "-" + name - } -} - - -/* -// Publishes every version -stonecutter registerChiseled tasks.register("chiseledPublishMods", stonecutter.chiseled) { - group = "project" - ofTask("publishMods") -} -*/ - -stonecutter parameters { - swap("mod_version", "\"${node.project.property("mod.version")}\"") - swap("git_commit", "\"${grgit.head().abbreviatedId}\"") - swap("commit_date", "\"${grgit.head().dateTime.toString().substringBefore("[")}\"") - const("poly_lib", node.project.property("deps.poly_lib").toString().isNotEmpty()) -} diff --git a/versions/1.20.1/gradle.properties b/versions/1.20.1/gradle.properties deleted file mode 100644 index ed21f95..0000000 --- a/versions/1.20.1/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=10 -deps.fabric_api=0.92.2+1.20.1 - -mod.mc_dep=>=1.20 <=1.20.1 -mod.mc_title=1.20.1 -mod.mc_targets=1.20 1.20.1 -deps.poly_lib=2000.0.3-build.143 diff --git a/versions/1.20.4/gradle.properties b/versions/1.20.4/gradle.properties deleted file mode 100644 index 03ecec3..0000000 --- a/versions/1.20.4/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=3 -deps.fabric_api=0.97.2+1.20.4 - -mod.mc_dep=>=1.20.3 <=1.20.4 -mod.mc_title=1.20.4 -mod.mc_targets=1.20.3 1.20.4 -deps.poly_lib=2004.0.3-build.155 diff --git a/versions/1.20.6/gradle.properties b/versions/1.20.6/gradle.properties deleted file mode 100644 index e3e6da3..0000000 --- a/versions/1.20.6/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=3 -deps.fabric_api=0.100.8+1.20.6 - -mod.mc_dep=>=1.20.5 <=1.20.6 -mod.mc_title=1.20.6 -mod.mc_targets=1.20.5 1.20.6 -deps.poly_lib= diff --git a/versions/1.21.1/gradle.properties b/versions/1.21.1/gradle.properties deleted file mode 100644 index 0bc7ecd..0000000 --- a/versions/1.21.1/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=3 -deps.fabric_api=0.110.0+1.21.1 - -mod.mc_dep=>=1.21 <=1.21.1 -mod.mc_title=1.21.1 -mod.mc_targets=1.21 1.21.1 -deps.poly_lib=2100.0.3-build.159 diff --git a/versions/1.21.11/gradle.properties b/versions/1.21.11/gradle.properties deleted file mode 100644 index 7ef0d5c..0000000 --- a/versions/1.21.11/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=4 -deps.fabric_api=0.141.1+1.21.11 - -mod.mc_dep=>=1.21.11 <=1.21.11 -mod.mc_title=1.21.11 -mod.mc_targets=1.21.11 -deps.poly_lib=2111.1.1 diff --git a/versions/1.21.3/gradle.properties b/versions/1.21.3/gradle.properties deleted file mode 100644 index 8d72f4e..0000000 --- a/versions/1.21.3/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=2 -deps.fabric_api=0.112.1+1.21.3 - -mod.mc_dep=>=1.21.2 <=1.21.3 -mod.mc_title=1.21.3 -mod.mc_targets=1.21.2 1.21.3 -deps.poly_lib=2103.0.4-build.161 diff --git a/versions/1.21.4/gradle.properties b/versions/1.21.4/gradle.properties deleted file mode 100644 index 8e513f7..0000000 --- a/versions/1.21.4/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=2 -deps.fabric_api=0.112.1+1.21.4 - -mod.mc_dep=>=1.21.4 <=1.21.4 -mod.mc_title=1.21.4 -mod.mc_targets=1.21.4 -deps.poly_lib=2104.0.4-build.163 diff --git a/versions/1.21.5/gradle.properties b/versions/1.21.5/gradle.properties deleted file mode 100644 index 43b3480..0000000 --- a/versions/1.21.5/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=1 -deps.fabric_api=0.120.0+1.21.5 - -mod.mc_dep=1.21.5 -mod.mc_title=1.21.5 -mod.mc_targets=1.21.5 -deps.poly_lib=2105.0.4.174 diff --git a/versions/1.21.6/gradle.properties b/versions/1.21.6/gradle.properties deleted file mode 100644 index edfcea6..0000000 --- a/versions/1.21.6/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=1 -deps.fabric_api=0.127.0+1.21.6 - -mod.mc_dep=>=1.21.6 <=1.21.8 -mod.mc_title=1.21.6 -mod.mc_targets=1.21.6 1.21.7 1.21.8 -deps.poly_lib=2106.1.0.182 diff --git a/versions/1.21.9/gradle.properties b/versions/1.21.9/gradle.properties deleted file mode 100644 index 0af0ae7..0000000 --- a/versions/1.21.9/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -# https://fabricmc.net/develop -deps.yarn_build=1 -deps.fabric_api=0.134.0+1.21.9 - -mod.mc_dep=>=1.21.9 <=1.21.10 -mod.mc_title=1.21.9 -mod.mc_targets=1.21.9 1.21.10 -deps.poly_lib=2109.1.0.184 From aa6fd00badfc69b953e86538e3fc4656cb286254 Mon Sep 17 00:00:00 2001 From: lnminh Date: Fri, 29 May 2026 08:03:38 +0700 Subject: [PATCH 2/2] Bump version --- CHANGELOG.md | 2 +- mc_26_1_2_naming_port.md | 626 --------------------------------------- 2 files changed, 1 insertion(+), 627 deletions(-) delete mode 100644 mc_26_1_2_naming_port.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c55e3..c58c96c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,6 @@ Refactor restart logic. Auto restart is disabled by default now because of plenty of mod incompatibility. -## 0.1.0 +## 0.4.0 Updated to 26.1.2. No new features yet. \ No newline at end of file diff --git a/mc_26_1_2_naming_port.md b/mc_26_1_2_naming_port.md deleted file mode 100644 index b0223ac..0000000 --- a/mc_26_1_2_naming_port.md +++ /dev/null @@ -1,626 +0,0 @@ -# MC 26.1.2 Naming Port Reference - -> **Context**: Minecraft 26.1.2 ships unobfuscated with Mojang official class names. -> Yarn mappings **do not exist** for this version (`net.fabricmc:yarn:26.1.2` → empty `[]`). -> The intermediary is `0.0.0` (placeholder). Fabric Loom performs **no remapping**. -> All source code must use Mojang official package/class names directly. - ---- - -## 1. Core Class Mapping Table - -| Old Name (Yarn / 1.21.x) | New Name (Mojang Official / 26.1.2) | Notes | -|---|---|---| -| `net.minecraft.text.Text` | `net.minecraft.network.chat.Component` | Factory methods changed (see §3) | -| `net.minecraft.text.MutableText` | `net.minecraft.network.chat.MutableComponent` | | -| `net.minecraft.text.ClickEvent` | `net.minecraft.network.chat.ClickEvent` | Same package prefix now `network.chat` | -| `net.minecraft.text.HoverEvent` | `net.minecraft.network.chat.HoverEvent` | Same | -| `net.minecraft.text.Style` | `net.minecraft.network.chat.Style` | Same | -| `net.minecraft.util.Formatting` | `net.minecraft.ChatFormatting` | Moved to root package | -| `net.minecraft.util.WorldSavePath` | `net.minecraft.world.level.storage.LevelResource` | | -| `net.minecraft.util.Identifier` | `net.minecraft.resources.ResourceLocation` | **Confirmed: no `ResourceLocation` in jar** – use `net.minecraft.resources.ResourceLocation` | -| `net.minecraft.util.Util` | **Does not exist** in jar root | `Util.getOperatingSystem()` → use `System.getProperty("os.name")` or platform detection | -| `net.minecraft.util.Util.OperatingSystem` | **Removed** | See §4 | -| `net.minecraft.server.command.ServerCommandSource` | `net.minecraft.commands.CommandSourceStack` | | -| `net.minecraft.server.MinecraftServer` | `net.minecraft.server.MinecraftServer` | **Same class name**, same package ✓ | -| `net.minecraft.server.world.ServerWorld` | `net.minecraft.server.level.ServerLevel` | | -| `net.minecraft.server.dedicated.DedicatedServerWatchdog` | `net.minecraft.server.dedicated.ServerWatchdog` | Class renamed | -| `net.minecraft.world.dimension.DimensionType` | `net.minecraft.world.level.dimension.DimensionType` | Package changed | -| `net.minecraft.world.storage.StorageIoWorker` | `net.minecraft.world.level.chunk.storage.RegionFileStorage` (approx) | Functionality moved; verify | -| `net.minecraft.screen.ScreenTexts` | `net.minecraft.network.chat.CommonComponents` | | -| `net.minecraft.client.MinecraftClient` | `net.minecraft.client.Minecraft` | Class renamed | -| `net.minecraft.client.gui.DrawContext` | `net.minecraft.client.gui.GuiGraphics` | Class renamed | -| `net.minecraft.client.gui.screen.Screen` | `net.minecraft.client.gui.screens.Screen` | Package changed (`screen` → `screens`) | -| `net.minecraft.client.gui.screen.world.SelectWorldScreen` | `net.minecraft.client.gui.screens.worldselection.SelectWorldScreen` | Package changed | -| `net.minecraft.client.gui.screen.world.WorldListWidget` | `net.minecraft.client.gui.screens.worldselection.WorldSelectionList` | Class renamed | -| `net.minecraft.client.gui.widget.ButtonWidget` | `net.minecraft.client.gui.components.Button` | Package changed (`widget` → `components`) | -| `net.minecraft.client.texture.NativeImage` | `com.mojang.blaze3d.platform.NativeImage` | Moved to blaze3d! | -| `net.minecraft.client.texture.NativeImageBackedTexture` | `net.minecraft.client.renderer.texture.DynamicTexture` | Class renamed | -| `net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket` | `net.minecraft.network.protocol.game.ClientboundTabListPacket` | Class renamed | -| `net.minecraft.command.argument.ColumnPosArgumentType` | `net.minecraft.commands.arguments.coordinates.ColumnPosArgument` | Package + class renamed | -| `net.minecraft.server.PlayerManager` | `net.minecraft.server.players.PlayerList` | Package changed | - ---- - -## 2. Package-Level Changes Summary - -| Old Package (Yarn) | New Package (Mojang) | -|---|---| -| `net.minecraft.text.*` | `net.minecraft.network.chat.*` | -| `net.minecraft.util.Formatting` | `net.minecraft` (root) → `net.minecraft.ChatFormatting` | -| `net.minecraft.util.WorldSavePath` | `net.minecraft.world.level.storage.LevelResource` | -| `net.minecraft.util.Identifier` | `net.minecraft.resources.ResourceLocation` | -| `net.minecraft.server.command.*` | `net.minecraft.commands.*` | -| `net.minecraft.server.world.*` | `net.minecraft.server.level.*` | -| `net.minecraft.server.dedicated.DedicatedServerWatchdog` | `net.minecraft.server.dedicated.ServerWatchdog` | -| `net.minecraft.world.dimension.*` | `net.minecraft.world.level.dimension.*` | -| `net.minecraft.world.storage.StorageIoWorker` | `net.minecraft.world.level.chunk.storage.RegionFileStorage` | -| `net.minecraft.screen.ScreenTexts` | `net.minecraft.network.chat.CommonComponents` | -| `net.minecraft.client.MinecraftClient` | `net.minecraft.client.Minecraft` | -| `net.minecraft.client.gui.DrawContext` | `net.minecraft.client.gui.GuiGraphics` | -| `net.minecraft.client.gui.screen.*` | `net.minecraft.client.gui.screens.*` | -| `net.minecraft.client.gui.screen.world.*` | `net.minecraft.client.gui.screens.worldselection.*` | -| `net.minecraft.client.gui.widget.*` | `net.minecraft.client.gui.components.*` | -| `net.minecraft.client.texture.*` | `net.minecraft.client.renderer.texture.*` + `com.mojang.blaze3d.platform.*` | -| `net.minecraft.network.packet.s2c.play.*` | `net.minecraft.network.protocol.game.Clientbound*` | -| `net.minecraft.command.argument.*` | `net.minecraft.commands.arguments.*` | - ---- - -## 3. API / Method Changes - -### Text / Component - -| Old (1.21.x Yarn) | New (26.1.2 Official) | -|---|---| -| `Text.literal(str)` | `Component.literal(str)` | -| `Text.translatable(key)` | `Component.translatable(key)` | -| `Text.translatable(key, args...)` | `Component.translatable(key, args...)` | -| `Text.translatableWithFallback(key, fallback, args...)` | `Component.translatableWithFallback(key, fallback, args...)` *(check if exists)* | -| `Text.empty()` | `Component.empty()` | -| `Text.of(str)` | `Component.literal(str)` | -| `text.formatted(Formatting.RED)` | `component.withStyle(ChatFormatting.RED)` | -| `text.styled { it.withColor(...) }` | `component.withStyle { it.withColor(...) }` | -| `text.withHoverEvent(HoverEvent(...))` | `component.withStyle(Style.EMPTY.withHoverEvent(...))` | -| `text.withClickEvent(ClickEvent(...))` | `component.withStyle(Style.EMPTY.withClickEvent(...))` | -| `mutableText.append(other)` | `mutableComponent.append(other)` ✓ same | -| `mutableText.copy()` | `mutableComponent.copy()` ✓ same | - -### ClickEvent - -| Old (Yarn) | New (Official) | -|---|---| -| `ClickEvent(ClickEvent.Action.RUN_COMMAND, str)` | Constructor now sealed — use `new ClickEvent.RunCommand(str)` | -| `ClickEvent.Action.RUN_COMMAND` | `ClickEvent.RunCommand` (record type) | -| `ClickEvent.Action.SUGGEST_COMMAND` | `ClickEvent.SuggestCommand` (record type) | -| `ClickEvent.Action.OPEN_URL` | `ClickEvent.OpenUrl` (record type) | -| `ClickEvent.Action.COPY_TO_CLIPBOARD` | `ClickEvent.CopyToClipboard` (record type) | - -> **Note**: In MC 26.x, `ClickEvent` was refactored to use sealed subclasses/records instead of an `Action` enum. Each action is now its own class (e.g. `ClickEvent.RunCommand`, `ClickEvent.OpenUrl`). - -### HoverEvent - -| Old (Yarn) | New (Official) | -|---|---| -| `HoverEvent(HoverEvent.Action.SHOW_TEXT, text)` | `new HoverEvent.ShowText(component)` | -| `HoverEvent.Action.SHOW_TEXT` | `HoverEvent.ShowText` (record) | -| `HoverEvent.Action.SHOW_ITEM` | `HoverEvent.ShowItem` (record) | -| `HoverEvent.Action.SHOW_ENTITY` | `HoverEvent.ShowEntity` (record) | - -### Formatting / ChatFormatting - -| Old | New | -|---|---| -| `import net.minecraft.util.Formatting` | `import net.minecraft.ChatFormatting` | -| `Formatting.RED` | `ChatFormatting.RED` | -| `Formatting.GREEN` | `ChatFormatting.GREEN` | -| `Formatting.GOLD` | `ChatFormatting.GOLD` | -| `Formatting.GRAY` | `ChatFormatting.GRAY` | -| `Formatting.BOLD` | `ChatFormatting.BOLD` | -| `Formatting.ITALIC` | `ChatFormatting.ITALIC` | -| `text.formatted(Formatting.X)` | `component.withStyle(ChatFormatting.X)` | - -### WorldSavePath / LevelResource - -| Old | New | -|---|---| -| `import net.minecraft.util.WorldSavePath` | `import net.minecraft.world.level.storage.LevelResource` | -| `WorldSavePath.ROOT` | `LevelResource.ROOT` | -| `WorldSavePath.PLAYERS` | `LevelResource.PLAYER_DATA_DIR` *(verify)* | -| `server.getSavePath(WorldSavePath.ROOT)` | `server.getWorldPath(LevelResource.ROOT)` *(verify method name)* | - -### MinecraftServer methods - -| Old (Yarn) | New (Official) | -|---|---| -| `server.getSavePath(WorldSavePath.ROOT)` | `server.getWorldPath(LevelResource.ROOT)` | -| `server.getPlayerManager()` | `server.getPlayerList()` | -| `server.playerManager` (Kotlin) | `server.playerList` | -| `server.getOverworld()` | `server.overworld()` | -| `server.worlds` (iterable) | `server.getAllLevels()` | -| `server.commandManager.parseAndExecute(src, cmd)` | `server.getCommands().performPrefixedCommand(src, cmd)` *(verify)* | -| `server.commandManager.executeWithPrefix(src, cmd)` | `server.getCommands().performPrefixedCommand(src, cmd)` | -| `server.asCoroutineDispatcher()` | Same (Kotlin coroutine extension from fabric-language-kotlin) | -| `server.running` | `server.isRunning()` | -| `server.stopped` | `server.isStopped()` | - -### ServerWorld / ServerLevel - -| Old | New | -|---|---| -| `import net.minecraft.server.world.ServerWorld` | `import net.minecraft.server.level.ServerLevel` | -| `world.savingDisabled` | `world.noSave` | -| `world.registryKey` | `world.dimension()` → returns `ResourceKey` | -| `world.server.getSavePath(WorldSavePath.ROOT)` | `world.getServer().getWorldPath(LevelResource.ROOT)` | -| `world.server` | `world.getServer()` | - -### DimensionType - -| Old | New | -|---|---| -| `import net.minecraft.world.dimension.DimensionType` | `import net.minecraft.world.level.dimension.DimensionType` | -| `DimensionType.getSaveDirectory(registryKey, rootPath)` | Need to compute manually using `Level.dimension()` key name — see §5 | - -### Util / OperatingSystem - -`net.minecraft.util.Util` does **not exist** in MC 26.1.2 jar. Use: -- `Util.getOperatingSystem()` → Replace with Java's `System.getProperty("os.name")` and compare -- `Util.OperatingSystem.OSX` → `"mac"` in os.name -- `Util.OperatingSystem.LINUX` → `"linux"` in os.name - -### PlayerManager / PlayerList - -| Old | New | -|---|---| -| `import net.minecraft.server.PlayerManager` | `import net.minecraft.server.players.PlayerList` | -| `playerManager.broadcast(text, false)` | `playerList.broadcastSystemMessage(component, false)` | -| `playerManager.sendToAll(packet)` | `playerList.broadcastAll(packet)` | - -### Network Packets - -| Old | New | -|---|---| -| `import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket` | `import net.minecraft.network.protocol.game.ClientboundTabListPacket` | -| `PlayerListHeaderS2CPacket(header, footer)` | `new ClientboundTabListPacket(header, footer)` | - -### Client API - -| Old | New | -|---|---| -| `MinecraftClient.getInstance()` | `Minecraft.getInstance()` | -| `client.options.language` | `client.options.languageCode` *(verify field name)* | -| `client.setScreen(screen)` | Same ✓ | -| `client.execute { ... }` | Same ✓ | -| `client.createIntegratedServerLoader()` | `client.createWorldOpenFlows()` *(verify)* | -| `loader.start(worldName) { ... }` | Verify new API | - -### GUI Components - -| Old | New | -|---|---| -| `ButtonWidget.builder(text, action).dimensions(...).build()` | `Button.builder(component, action).bounds(x,y,w,h).build()` | -| `import net.minecraft.client.gui.widget.ButtonWidget` | `import net.minecraft.client.gui.components.Button` | -| `DrawContext` | `GuiGraphics` | -| `context.drawCenteredTextWithShadow(renderer, text, x, y, color)` | `graphics.drawCenteredString(font, component, x, y, color)` | -| `context.fill(x1, y1, x2, y2, color)` | `graphics.fill(x1, y1, x2, y2, color)` ✓ same | -| `WorldListWidget` | `WorldSelectionList` | -| `SelectWorldScreen` | `SelectWorldScreen` ✓ same name, different package | -| `Screen(title)` constructor | `Screen(Component title)` ✓ same pattern | -| `screen.addDrawableChild(widget)` | `screen.addRenderableWidget(widget)` | -| `NativeImage` | `com.mojang.blaze3d.platform.NativeImage` | -| `NativeImageBackedTexture` | `net.minecraft.client.renderer.texture.DynamicTexture` | -| `ScreenTexts.OK` | `CommonComponents.GUI_OK` | - -### Mixin Target Descriptors - -Mixin `@At` `target` strings use internal class names that **also changed**: - -| Old Descriptor | New Descriptor | -|---|---| -| `Lnet/minecraft/server/dedicated/DedicatedServerWatchdog;maxTickTime:J` | `Lnet/minecraft/server/dedicated/ServerWatchdog;maxTickTime:J` | -| `Lnet/minecraft/world/storage/StorageIoWorker;` | `Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;` | -| `Lnet/minecraft/server/MinecraftServer;` | `Lnet/minecraft/server/MinecraftServer;` ✓ same | - ---- - -## 4. Removed / Unavailable APIs - -| Removed | Replacement Strategy | -|---|---| -| `net.minecraft.util.Util` (entire class) | No direct equivalent. `getOperatingSystem()` → use Java `System.getProperty("os.name")` | -| `Util.OperatingSystem` enum | Use String comparison on `System.getProperty("os.name").toLowerCase()` | -| `net.minecraft.util.Identifier` | Use `net.minecraft.resources.ResourceLocation` (may need `ResourceLocation.fromNamespaceAndPath(ns, path)`) | -| `Text.translatableWithFallback()` | `Component.translatableWithFallback()` — verify if present in 26.1.2 `Component` class | -| `StorageIoWorker` (Mixin target) | Target `RegionFileStorage` instead, or remove mixin if no longer needed | - ---- - -## 5. File-by-File Port Guide - -### `src/main/kotlin/.../XBackup.kt` - -**Imports to change:** -``` -// OLD → NEW -import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket - → import net.minecraft.network.protocol.game.ClientboundTabListPacket - -import net.minecraft.server.command.ServerCommandSource - → import net.minecraft.commands.CommandSourceStack - -import net.minecraft.text.Text - → import net.minecraft.network.chat.Component - -import net.minecraft.util.Util - → REMOVE (use Java OS detection) - -import net.minecraft.util.WorldSavePath - → import net.minecraft.world.level.storage.LevelResource -``` - -**Code changes:** -```kotlin -// Line 127-136: Util.getOperatingSystem() -// OLD: -when (Util.getOperatingSystem()) { - Util.OperatingSystem.OSX, Util.OperatingSystem.LINUX -> { ... } - else -> error(...) -} -// NEW: -val os = System.getProperty("os.name", "").lowercase() -when { - os.contains("mac") || os.contains("nix") || os.contains("nux") -> { ... } - else -> error(...) -} - -// Line 163: server.getSavePath(WorldSavePath.ROOT) -// → server.getWorldPath(LevelResource.ROOT) - -// Line 234-240: PlayerListHeaderS2CPacket / Text.empty() -// → ClientboundTabListPacket(Component.empty(), Component.literal(...)) -// → server.playerList.broadcastAll(packet) - -// Line 236-237: Text.empty(), Text.literal(...) -// → Component.empty(), Component.literal(...) - -// Line 299: server.getSavePath(WorldSavePath.ROOT) -// → server.getWorldPath(LevelResource.ROOT) - -// Line 396: ServerCommandSource param type -// → CommandSourceStack - -// Line 401, 410: Text.of(...), source.sendError(Text.of(...)) -// → Component.literal(...), source.sendFailure(Component.literal(...)) -``` - ---- - -### `src/main/kotlin/.../Utils.kt` - -**Imports to change:** -``` -import net.minecraft.server.command.ServerCommandSource → import net.minecraft.commands.CommandSourceStack -import net.minecraft.server.world.ServerWorld → import net.minecraft.server.level.ServerLevel -import net.minecraft.text.MutableText → import net.minecraft.network.chat.MutableComponent -import net.minecraft.text.Text → import net.minecraft.network.chat.Component -import net.minecraft.util.WorldSavePath → import net.minecraft.world.level.storage.LevelResource -import net.minecraft.world.dimension.DimensionType → import net.minecraft.world.level.dimension.DimensionType -``` - -**Code changes:** -```kotlin -// translate(): Text.translatableWithFallback → Component.translatableWithFallback -// Return type: MutableText → MutableComponent - -// ServerCommandSource.send(): sendMessage(text) → sendSystemMessage(component) - -// MinecraftServer.setAutoSaving: worlds → getAllLevels() / allLevels -// world.savingDisabled = !value → world.noSave = !value - -// MinecraftServer.save(): saveAll() signature may differ - -// MinecraftServer.finishRestore(): -// running = true → use server.setRunning(true) if setter exists -// runServer() → verify method name - -// MinecraftServer.broadcast: playerManager.broadcast → playerList.broadcastSystemMessage - -// isFileInWorld(): -// DimensionType.getSaveDirectory(world.registryKey, ...) → compute from dimension().location() -// world.registryKey → world.dimension() (returns ResourceKey) -// world.server → world.getServer() -// WorldSavePath.ROOT → LevelResource.ROOT -``` - ---- - -### `src/main/kotlin/.../Commands.kt` - -**Imports to change:** -``` -import net.minecraft.command.argument.ColumnPosArgumentType - → import net.minecraft.commands.arguments.coordinates.ColumnPosArgument - -import net.minecraft.server.command.ServerCommandSource - → import net.minecraft.commands.CommandSourceStack - -import net.minecraft.text.ClickEvent → import net.minecraft.network.chat.ClickEvent -import net.minecraft.text.HoverEvent → import net.minecraft.network.chat.HoverEvent -import net.minecraft.text.MutableText → import net.minecraft.network.chat.MutableComponent -import net.minecraft.text.Text → import net.minecraft.network.chat.Component - -import net.minecraft.util.Formatting → import net.minecraft.ChatFormatting -import net.minecraft.util.Util → REMOVE -import net.minecraft.util.WorldSavePath → import net.minecraft.world.level.storage.LevelResource -``` - -**Code changes:** -```kotlin -// All Text.literal() → Component.literal() -// All Text.translatable() → Component.translatable() -// All Text.empty() → Component.empty() -// MutableText → MutableComponent -// .formatted(Formatting.X) → .withStyle(ChatFormatting.X) -// ClickEvent(ClickEvent.Action.RUN_COMMAND, str) → ClickEvent.RunCommand(str) -// ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, str) → ClickEvent.SuggestCommand(str) -// HoverEvent(HoverEvent.Action.SHOW_TEXT, text) → HoverEvent.ShowText(component) -// .styled { ... } / .withHoverEvent() / .withClickEvent() -// → .withStyle(Style.EMPTY.withHoverEvent(...)) etc. -// ServerCommandSource → CommandSourceStack -// ColumnPosArgumentType → ColumnPosArgument -// WorldSavePath.ROOT → LevelResource.ROOT -``` - ---- - -### `src/main/kotlin/.../Task.kt` - -``` -import net.minecraft.text.Text → import net.minecraft.network.chat.Component -// val displayName: Text → val displayName: Component -``` - ---- - -### `src/main/kotlin/.../cloud/OnedriveSupport.kt` - -``` -import net.minecraft.text.ClickEvent → import net.minecraft.network.chat.ClickEvent -// Any ClickEvent usage → new sealed class API (ClickEvent.RunCommand, etc.) -``` - ---- - -### `src/main/kotlin/.../gui/BackupsGui.kt` - -``` -import net.minecraft.client.MinecraftClient → import net.minecraft.client.Minecraft -import net.minecraft.client.texture.NativeImage → import com.mojang.blaze3d.platform.NativeImage -import net.minecraft.client.texture.NativeImageBackedTexture → import net.minecraft.client.renderer.texture.DynamicTexture -import net.minecraft.text.Text → import net.minecraft.network.chat.Component -import net.minecraft.util.Formatting → import net.minecraft.ChatFormatting -import net.minecraft.util.Identifier → import net.minecraft.resources.ResourceLocation -``` - ---- - -### `src/main/kotlin/.../gui/RestoreInfoScreen.kt` - -``` -import net.minecraft.client.MinecraftClient → import net.minecraft.client.Minecraft -import net.minecraft.client.gui.DrawContext → import net.minecraft.client.gui.GuiGraphics -import net.minecraft.client.gui.screen.Screen → import net.minecraft.client.gui.screens.Screen -import net.minecraft.client.gui.widget.ButtonWidget → import net.minecraft.client.gui.components.Button -import net.minecraft.text.Text → import net.minecraft.network.chat.Component -``` - -**Code changes:** -```kotlin -// Screen(Text.translatable(...)) → Screen(Component.translatable(...)) -// ButtonWidget.builder(text, action).dimensions(...).build() -// → Button.builder(component, action).bounds(x,y,w,h).build() -// context.drawCenteredTextWithShadow(textRenderer, text, x, y, color) -// → guiGraphics.drawCenteredString(font, component, x, y, color) -// MinecraftClient.getInstance() → Minecraft.getInstance() -// client.createIntegratedServerLoader() → verify new API -// addDrawableChild(widget) → addRenderableWidget(widget) -``` - ---- - -### `src/main/java/.../mixin/MixinSelectWorldScreen.java` - -```java -// Imports: -import net.minecraft.client.gui.DrawContext → import net.minecraft.client.gui.GuiGraphics -import net.minecraft.client.gui.screen.Screen → import net.minecraft.client.gui.screens.Screen -import net.minecraft.client.gui.screen.world.SelectWorldScreen → import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen -import net.minecraft.client.gui.screen.world.WorldListWidget → import net.minecraft.client.gui.screens.worldselection.WorldSelectionList -import net.minecraft.client.gui.widget.ButtonWidget → import net.minecraft.client.gui.components.Button -import net.minecraft.text.Text → import net.minecraft.network.chat.Component -import net.minecraft.util.Formatting → import net.minecraft.ChatFormatting -``` - -**Code changes:** -```java -// ButtonWidget → Button (same builder pattern) -// Text.literal("回") → Component.literal("回") -// Text.translatable(...) → Component.translatable(...) -// .formatted(Formatting.RED) → .withStyle(ChatFormatting.RED) -// WorldListWidget → WorldSelectionList (field type) -// levelList.getSelectedAsOptional().get().level.getName() -// → verify API for WorldSelectionList entry -// Screen(Text) constructor → Screen(Component) -// this.addDrawableChild(widget) → this.addRenderableWidget(widget) -// context.drawCenteredTextWithShadow → graphics.drawCenteredString -``` - -**Mixin @Mixin targets — no change** (SelectWorldScreen class name preserved). - ---- - -### `src/main/java/.../mixin/disable/MixinDedicatedServerWatchdog.java` - -```java -// OLD: -import net.minecraft.server.dedicated.DedicatedServerWatchdog; -@Mixin(DedicatedServerWatchdog.class) -// Field target: "Lnet/minecraft/server/dedicated/DedicatedServerWatchdog;maxTickTime:J" - -// NEW: -import net.minecraft.server.dedicated.ServerWatchdog; -@Mixin(ServerWatchdog.class) -// Field target: "Lnet/minecraft/server/dedicated/ServerWatchdog;maxTickTime:J" -``` - ---- - -### `src/main/java/.../mixin/disable/MixinStorageIoWorker.java` - -```java -// OLD: @Mixin(StorageIoWorker.class) -// StorageIoWorker moved/renamed. -// Target: net.minecraft.world.level.chunk.storage.RegionFileStorage (closest equivalent) -// This mixin is likely a stub (empty body) — may just need the import updated -// or the mixin can be removed if no longer needed. - -// NEW (if needed): -import net.minecraft.world.level.chunk.storage.RegionFileStorage; -@Mixin(RegionFileStorage.class) -``` - ---- - -### `src/main/java/.../mixin/MixinServer.java` - -```java -// MinecraftServer class is SAME → no import change needed -// Method names to verify: -// "save" method → verify still exists with same signature -// "saveAll" method → verify -// "shutdown" method → verify -// These may have changed signatures in 26.1.2 -``` - ---- - -### `src/main/java/.../gui/OptionDialog.java` - -```java -// import net.minecraft.screen.ScreenTexts → import net.minecraft.network.chat.CommonComponents -// import net.minecraft.text.Text → import net.minecraft.network.chat.Component -// import static net.minecraft.util.Formatting.GREEN → import net.minecraft.ChatFormatting (static import) - -// ScreenTexts.OK.copy().formatted(GREEN) -// → CommonComponents.GUI_OK.copy().withStyle(ChatFormatting.GREEN) -// OR: Component.translatable("gui.ok").withStyle(ChatFormatting.GREEN) - -// Text params in method signatures → Component -``` - ---- - -## 6. Fabric API Changes for 26.1.2 - -These Fabric API modules remain available but their callback signatures now use Mojang official types: - -| Fabric API | Old Type | New Type | -|---|---|---| -| `ServerLifecycleEvents.SERVER_STARTED` callback | `MinecraftServer` | `MinecraftServer` ✓ same | -| `CommandRegistrationCallback` dispatcher param | `CommandDispatcher` | `CommandDispatcher` | -| `ServerWorldEvents` world param | `ServerWorld` | `ServerLevel` | - -> **Note**: `CommandRegistrationCallback { dispatcher, _, _ -> ... }` — the dispatcher type parameter changes to `CommandSourceStack`. - ---- - -## 7. Mixin Configuration - -The mixin JSON file (`src/main/resources/x_backup.mixins.json`) references class names for `@Mixin` targets. Since Mixin resolves targets at **runtime** by class name (not by Java package), the `@Mixin(TargetClass.class)` annotation in source drives resolution — the JSON config lists the mixin *class* names, not target names, so the JSON itself may not need changes. However, `@At(target=...)` string descriptors **must** be updated as described above. - ---- - -## 8. Stonecutter Strategy - -The project uses **Stonecutter** for multi-version support. For 26.1.2-specific code, use: - -```kotlin -//? if >= 26.1.2 { -// Mojang official API code here -//?} else { -/* Yarn-mapped code here */ -//?} -``` - -For imports that differ, use type aliases at the top of each file: - -```kotlin -//? if >= 26.1.2 { -import net.minecraft.network.chat.Component as Text -import net.minecraft.network.chat.MutableComponent as MutableText -import net.minecraft.ChatFormatting as Formatting -//?} else { -/* -import net.minecraft.text.Text -import net.minecraft.text.MutableText -import net.minecraft.util.Formatting -*/ -//?} -``` - -> **Caution**: Type alias approach only works if APIs are identical beyond the name. For methods that changed (e.g. `.formatted()` → `.withStyle()`), full code blocks must be conditioned. - ---- - -## 9. Verified Mappings & Port Findings - -Through direct inspection of `minecraft-merged.jar` for Minecraft 26.1.2 and resolution of compiler errors, the following mappings are confirmed: - -### 9.1 Core Classes & Packages -* **`net.minecraft.util.Identifier`** (Yarn) → **`net.minecraft.resources.Identifier`** (MojMap). - * Note: It is NOT named `ResourceLocation`. The class name is `Identifier` and it has moved packages. - * Static creators: `Identifier.parse(String)`, `Identifier.fromNamespaceAndPath(String, String)`. -* **`net.minecraft.client.gui.DrawContext`** (Yarn) → **`net.minecraft.client.gui.GuiGraphicsExtractor`** (MojMap). - * Render methods on screens override `extractRenderState(GuiGraphicsExtractor, int, int, float)`. - * Background rendering uses `extractBackground(GuiGraphicsExtractor, int, int, float)`. - * Text rendering on `GuiGraphicsExtractor` uses `text(...)` and `centeredText(...)` instead of `drawString` or `drawCenteredString`. -* **`net.minecraft.client.gui.screen.world.WorldListWidget`** (Yarn) → **`net.minecraft.client.gui.screens.worldselection.WorldSelectionList`** (MojMap). - * Entry class is `WorldSelectionList.Entry`. - * Get selected entry: `levelList.getSelectedOpt()` (returns `Optional`). - * Get level summary from entry: `entry.getLevelSummary()` (returns `LevelSummary`). -* **`net.minecraft.world.level.storage.LevelSummary`**: - * Folder name / ID of world: `getLevelId()`. - * Display name of world: `getLevelName()`. -* **`net.minecraft.client.gui.components.Tooltip`**: - * Tooltip creation: `Tooltip.create(Component)`. -* **`net.minecraft.client.gui.widget.ButtonWidget`** (Yarn) → **`net.minecraft.client.gui.components.Button`** (MojMap). - * Added to screen via `addRenderableWidget(...)` (formerly `addDrawableChild`). - * Bounds set in builder: `Button.builder(...).bounds(x, y, width, height).build()`. - -### 9.2 Server Lifecycle & Threading -* **`MinecraftServer.isOnThread`** (Yarn) → **`MinecraftServer.isSameThread()`** (MojMap). -* **`MinecraftServer.networkIo`** (Yarn) → **`MinecraftServer.connection`** (field) / **`getConnection()`** (method) returning `ServerConnectionListener`. - * Listing connections: `connection.getConnections()`. -* **`MinecraftServer.thread`** (Yarn) → **`MinecraftServer.serverThread`** (field) / **`getRunningThread()`** (method). -* **`MinecraftServer.save()`** (Yarn) → **`MinecraftServer.saveEverything(suppressLog, flush, force)`** (MojMap). -* **`MinecraftServer.shutdown()`** (Yarn) → **`MinecraftServer.stopServer()`** (MojMap). -* **`Connection.disconnected`** (Yarn) → **`Connection.disconnectionHandled`** (field). - * Setting `connection.disconnectionHandled = true` prevents save-on-disconnect during restore. - * Disconnecting connection: `connection.disconnect(Component)`. -* **`DimensionType.getSaveDirectory()`** (Yarn) → **`DimensionType.getStorageFolder(ResourceKey, Path)`** (MojMap). - -### 9.3 Permissions & Command Sources -* **`Permissions.check(source, perm, defaultLevel)`**: - * Under MojMap 26.1.2, `CommandSourceStack` does NOT implement `net.minecraft.commands.CommandSource`. - * To check permissions using the stub, pass `source.source` (which is `CommandSource`). - * Stub classes package: `net.minecraft.commands.CommandSource` instead of `net.minecraft.command.CommandSource`. -* **`CommandSourceStack.permissions`**: - * The `permissions` field is private. Use the public method `permissions()` returning `PermissionSet`. - * Checking a permission level: `source.permissions().hasPermission(new Permission.HasCommandLevel(permissionLevel))`. -* **`CommandSourceStack.name`** (Yarn) → **`CommandSourceStack.textName`** (field) / **`getTextName()`** (getter). -* **`CommandSourceStack.world`** (Yarn) → **`CommandSourceStack.level`** (field) / **`getLevel()`** (getter). -* **`CommandSourceStack.sendError(Text)`** (Yarn) → **`CommandSourceStack.sendFailure(Component)`** (MojMap). - ---- - -*This document was updated by directly inspecting `minecraft-merged.jar` for MC 26.1.2 and analyzing compilation failures.*