diff --git a/build.gradle.kts b/build.gradle.kts index 89aac7f..65729b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("dev.kikugie.stonecutter") - alias(libs.plugins.fabric.loom) + alias(libs.plugins.fabric.loom.remap) id("maven-publish") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.mod.publish.plugin) @@ -19,11 +19,6 @@ base { } repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. maven { url = uri("https://maven.shedaniel.me") } maven { url = uri("https://maven.isxander.dev/releases") } maven { url = uri("https://maven.terraformersmc.com/releases") } @@ -161,9 +156,10 @@ tasks { processResources { inputs.property("version", project.version) inputs.property("minecraft_version", mcVersion) + inputs.property("java_version", 21) filesMatching("fabric.mod.json") { - expand(mapOf("version" to inputs.properties["version"], "minecraft_version" to inputs.properties["minecraft_version"])) + expand(mapOf("version" to inputs.properties["version"], "minecraft_version" to inputs.properties["minecraft_version"], "java_version" to inputs.properties["java_version"])) } } jar { @@ -207,9 +203,6 @@ kotlin { } java { - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. withSourcesJar() sourceCompatibility = JavaVersion.VERSION_21 @@ -248,26 +241,18 @@ publishing { } } - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. } } // Auto-start Xvfb for headless client test execution (Wayland / headless environments) -// Shared state for Xvfb process between run task and cleanup task val xvfbState = objects.property() val xvfbShutdownHook = objects.property() fun needsXvfb(): Boolean { val display = System.getenv("DISPLAY") if (display.isNullOrBlank()) return true - // For remote displays (e.g., SSH X11 forwarding like localhost:10.0), trust the env if (display.contains(":") && !display.startsWith(":")) return false - // For local displays, check if the X11 socket file exists (simple heuristic; does not probe connection) val displayNum = display.removePrefix(":").takeWhile { it.isDigit() } val socket = File("/tmp/.X11-unix/X$displayNum") return !socket.exists() @@ -286,7 +271,6 @@ fun findXvfb(): String? { } fun startXvfb(xvfb: String): Pair { - // Try multiple display numbers to handle concurrent usage for (displayNum in 99..199) { val display = ":$displayNum" if (File("/tmp/.X11-unix/X$displayNum").exists()) continue @@ -295,7 +279,6 @@ fun startXvfb(xvfb: String): Pair { .redirectErrorStream(true) .start() - // Poll for X11 socket to appear (readiness check) val socketFile = File("/tmp/.X11-unix/X$displayNum") val deadline = System.currentTimeMillis() + 5_000 while (System.currentTimeMillis() < deadline) { @@ -304,7 +287,6 @@ fun startXvfb(xvfb: String): Pair { Thread.sleep(100) } - // This display didn't work, clean up and try next if (process.isAlive) process.destroyForcibly() } error("Failed to start Xvfb: no available display number in :99..:199") @@ -321,7 +303,6 @@ val cleanupXvfbTask = tasks.register("cleanupXvfb") { if (process.isAlive) process.destroyForcibly() } } - // Remove shutdown hook after process cleanup (keeps hook as safety net until stop completes) xvfbShutdownHook.orNull?.let { hook -> runCatching { Runtime.getRuntime().removeShutdownHook(hook) } } @@ -342,7 +323,6 @@ tasks.named("runClientGameTest") { val (process, display) = startXvfb(xvfb) xvfbState.set(process) - // Last-resort cleanup for JVM crash (daemon shutdown) val shutdownHook = Thread { if (process.isAlive) process.destroyForcibly() } Runtime.getRuntime().addShutdownHook(shutdownHook) xvfbShutdownHook.set(shutdownHook) diff --git a/build.unobfuscated.gradle.kts b/build.unobfuscated.gradle.kts new file mode 100644 index 0000000..0f86b2b --- /dev/null +++ b/build.unobfuscated.gradle.kts @@ -0,0 +1,285 @@ +import java.util.concurrent.TimeUnit +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("dev.kikugie.stonecutter") + alias(libs.plugins.fabric.loom) + id("maven-publish") + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.mod.publish.plugin) +} + +val mcVersion = stonecutter.current.version + +version = providers.environmentVariable("MOD_VERSION").orElse("dev").get() + "+mc$mcVersion" +group = project.property("maven_group") as String + +base { + archivesName.set(project.property("archives_base_name") as String) +} + +repositories { + maven { url = uri("https://maven.isxander.dev/releases") } + maven { url = uri("https://maven.terraformersmc.com/releases") } + + exclusiveContent { + forRepository { + maven { + name = "Modrinth" + url = uri("https://api.modrinth.com/maven") + } + } + filter { + includeGroup("maven.modrinth") + } + } +} + +loom { + splitEnvironmentSourceSets() + + mods { + register("connectedtank") { + sourceSet(sourceSets.main.get()) + sourceSet(sourceSets.getByName("client")) + } + } +} + +fabricApi { + configureDataGeneration { + client = true + } + @Suppress("UnstableApiUsage") + configureTests { + createSourceSet = true + modId = "connectedtank-test" + enableGameTests = true + enableClientGameTests = false + eula = true + } +} + +sourceSets.named("gametest") { + kotlin.srcDir("src/gametest/kotlin") +} + +val clientGametestSourceSet = sourceSets.create("clientGametest") { + compileClasspath += sourceSets.main.get().output + runtimeClasspath += sourceSets.main.get().output + compileClasspath += sourceSets.getByName("client").output + runtimeClasspath += sourceSets.getByName("client").output + kotlin.srcDir("src/clientGametest/kotlin") +} + +configurations.named(clientGametestSourceSet.compileClasspathConfigurationName) { + extendsFrom(configurations[sourceSets.main.get().compileClasspathConfigurationName]) + extendsFrom(configurations[sourceSets.getByName("client").compileClasspathConfigurationName]) +} +configurations.named(clientGametestSourceSet.runtimeClasspathConfigurationName) { + extendsFrom(configurations[sourceSets.main.get().runtimeClasspathConfigurationName]) + extendsFrom(configurations[sourceSets.getByName("client").runtimeClasspathConfigurationName]) +} + +loom { + mods { + register("connectedtank-client-test") { + sourceSet(clientGametestSourceSet) + } + } + + runs { + register("clientGameTest") { + inherit(runs.getByName("client")) + source(clientGametestSourceSet) + property("fabric.client.gametest") + property( + "fabric.client.gametest.testModResourcesPath", + file("src/clientGametest/resources").absolutePath, + ) + runDir("build/run/clientGameTest") + } + } +} + +dependencies { + val fabricApiVersion = "0.148.0+26.1.2" + val yaclVersion = "3.9.3+26.1-fabric" + val modmenuVersion = "18.0.0-alpha.8" + val jeiVersion = "29.5.0.28" + val jadeVersion = "26.1.0+fabric" + + minecraft("com.mojang:minecraft:$mcVersion") + implementation(libs.fabric.loader) + + implementation("net.fabricmc.fabric-api:fabric-api:$fabricApiVersion") + implementation(libs.fabric.language.kotlin) + + compileOnly("dev.isxander:yet-another-config-lib:$yaclVersion") + runtimeOnly("dev.isxander:yet-another-config-lib:$yaclVersion") + compileOnly("com.terraformersmc:modmenu:$modmenuVersion") + runtimeOnly("com.terraformersmc:modmenu:$modmenuVersion") + + runtimeOnly("maven.modrinth:jei:$jeiVersion") + compileOnly("maven.modrinth:jade:$jadeVersion") + runtimeOnly("maven.modrinth:jade:$jadeVersion") +} + +tasks { + processResources { + inputs.property("version", project.version) + inputs.property("minecraft_version", mcVersion) + inputs.property("java_version", 25) + + filesMatching("fabric.mod.json") { + expand(mapOf("version" to inputs.properties["version"], "minecraft_version" to inputs.properties["minecraft_version"], "java_version" to inputs.properties["java_version"])) + } + } + jar { + inputs.property("archivesName", base.archivesName) + + from("LICENSE") { + rename { "${it}_${inputs.properties["archivesName"]}" } + } + } + withType().configureEach { + options.release.set(25) + } +} + +kotlin { + compilerOptions { + jvmTarget = JvmTarget.fromTarget("25") + } + jvmToolchain(25) +} + +java { + withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 +} + +publishMods { + file.set(tasks.jar.flatMap { it.archiveFile }) + additionalFiles.from(tasks.named("sourcesJar").map { (it as Jar).archiveFile }) + changelog.set(providers.environmentVariable("CHANGELOG").orElse("")) + type.set(STABLE) + modLoaders.add("fabric") + + modrinth { + projectId.set(providers.environmentVariable("MODRINTH_ID")) + accessToken.set(providers.environmentVariable("MODRINTH_TOKEN")) + minecraftVersions.add(mcVersion) + requires("fabric-api") + requires("fabric-language-kotlin") + } + curseforge { + projectId.set(providers.environmentVariable("CURSEFORGE_ID")) + accessToken.set(providers.environmentVariable("CURSEFORGE_TOKEN")) + minecraftVersions.add(mcVersion) + requires("fabric-api") + requires("fabric-language-kotlin") + } +} + +// configure the maven publication +publishing { + publications { + create("mavenJava") { + artifactId = project.property("archives_base_name") as String + from(components["java"]) + } + } + + repositories { + } +} + +// Auto-start Xvfb for headless client test execution (Wayland / headless environments) +val xvfbState = objects.property() +val xvfbShutdownHook = objects.property() + +fun needsXvfb(): Boolean { + val display = System.getenv("DISPLAY") + if (display.isNullOrBlank()) return true + if (display.contains(":") && !display.startsWith(":")) return false + val displayNum = display.removePrefix(":").takeWhile { it.isDigit() } + val socket = File("/tmp/.X11-unix/X$displayNum") + return !socket.exists() +} + +fun findXvfb(): String? { + val candidates = listOf("Xvfb", "/usr/bin/Xvfb") + return candidates.firstOrNull { name -> + runCatching { + ProcessBuilder("which", name) + .redirectErrorStream(true) + .start() + .waitFor() == 0 + }.getOrDefault(false) + } +} + +fun startXvfb(xvfb: String): Pair { + for (displayNum in 99..199) { + val display = ":$displayNum" + if (File("/tmp/.X11-unix/X$displayNum").exists()) continue + + val process = ProcessBuilder(xvfb, display, "-screen", "0", "1280x1024x24", "-nolisten", "tcp") + .redirectErrorStream(true) + .start() + + val socketFile = File("/tmp/.X11-unix/X$displayNum") + val deadline = System.currentTimeMillis() + 5_000 + while (System.currentTimeMillis() < deadline) { + if (!process.isAlive) break + if (socketFile.exists()) return process to display + Thread.sleep(100) + } + + if (process.isAlive) process.destroyForcibly() + } + error("Failed to start Xvfb: no available display number in :99..:199") +} + +val cleanupXvfbTask = tasks.register("cleanupXvfb") { + notCompatibleWithConfigurationCache("Manages Xvfb process lifecycle at execution time") + doLast { + xvfbState.orNull?.let { process -> + if (process.isAlive) { + logger.lifecycle("Stopping Xvfb (pid: ${process.pid()})") + process.destroy() + process.waitFor(5, TimeUnit.SECONDS) + if (process.isAlive) process.destroyForcibly() + } + } + xvfbShutdownHook.orNull?.let { hook -> + runCatching { Runtime.getRuntime().removeShutdownHook(hook) } + } + } +} + +tasks.named("runClientGameTest") { + notCompatibleWithConfigurationCache("Manages Xvfb process lifecycle at execution time") + finalizedBy(cleanupXvfbTask) + + doFirst { + if (!needsXvfb()) return@doFirst + + val xvfb = findXvfb() ?: error( + "No usable DISPLAY found and Xvfb is not installed. " + + "Install Xvfb or run with a display server (e.g., xvfb-run ./gradlew runClientGameTest)", + ) + + val (process, display) = startXvfb(xvfb) + xvfbState.set(process) + val shutdownHook = Thread { if (process.isAlive) process.destroyForcibly() } + Runtime.getRuntime().addShutdownHook(shutdownHook) + xvfbShutdownHook.set(shutdownHook) + + logger.lifecycle("Started Xvfb on display $display (pid: ${process.pid()})") + environment("DISPLAY", display) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4f73810..99eedc5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -loader = "0.17.2" -loom = "1.16-SNAPSHOT" -fabric-kotlin = "1.13.5+kotlin.2.2.10" -kotlin = "2.2.10" +loader = "0.19.2" +loom = "1.15.5" +fabric-kotlin = "1.13.11+kotlin.2.3.21" +kotlin = "2.3.21" spotless = "7.2.1" mod-publish-plugin = "0.8.4" @@ -11,7 +11,8 @@ fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "loader" fabric-language-kotlin = { module = "net.fabricmc:fabric-language-kotlin", version.ref = "fabric-kotlin" } [plugins] -fabric-loom = { id = "fabric-loom", version.ref = "loom" } +fabric-loom = { id = "net.fabricmc.fabric-loom", version.ref = "loom" } +fabric-loom-remap = { id = "net.fabricmc.fabric-loom-remap", version.ref = "loom" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } mod-publish-plugin = { id = "me.modmuss50.mod-publish-plugin", version.ref = "mod-publish-plugin" } diff --git a/settings.gradle.kts b/settings.gradle.kts index f093fd5..d48ac6f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ plugins { stonecutter { create(rootProject) { versions("1.21.8", "1.21.11") + version("26.1").buildscript("build.unobfuscated.gradle.kts") vcsVersion = "1.21.8" } } diff --git a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt index bd81067..d5f1ad0 100644 --- a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt +++ b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt @@ -3,8 +3,10 @@ package net.turtton.connectedtank import net.fabricmc.api.ClientModInitializer import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking +//? if <26.1 { import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap import net.minecraft.client.renderer.chunk.ChunkSectionLayer +//?} import net.minecraft.client.renderer.blockentity.BlockEntityRenderers import net.turtton.connectedtank.block.CTBlockEntityTypes import net.turtton.connectedtank.block.CTBlocks @@ -21,7 +23,9 @@ object ConnectedTankClient : ClientModInitializer { override fun onInitializeClient() { CTClientConfig.load() + //? if <26.1 { CTBlocks.ALL_TANKS.forEach { BlockRenderLayerMap.putBlock(it, ChunkSectionLayer.CUTOUT) } + //?} //? if >=1.21.11 { /*BlockEntityRenderers.register<_, ConnectedTankRenderState>( CTBlockEntityTypes.CONNECTED_TANK, diff --git a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt index d4f9cb5..c5c2108 100644 --- a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt +++ b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt @@ -6,10 +6,18 @@ import java.util.concurrent.CompletableFuture import net.fabricmc.fabric.api.client.datagen.v1.provider.FabricModelProvider import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator +//? if >=26.1 { +/*import net.fabricmc.fabric.api.datagen.v1.FabricPackOutput*/ +//?} else { import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput +//?} import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider +//? if >=26.1 { +/*import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagsProvider*/ +//?} else { import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider +//?} import net.minecraft.advancements.AdvancementRequirements import net.minecraft.advancements.AdvancementRewards //? if >=1.21.11 { @@ -23,13 +31,24 @@ import net.minecraft.client.data.models.ItemModelGenerators import net.minecraft.client.data.models.model.ItemModelUtils import net.minecraft.client.data.models.model.ModelInstance import net.minecraft.client.data.models.blockstates.MultiPartGenerator +//? if >=26.1 { +/*import net.minecraft.client.renderer.block.dispatch.Variant*/ +//?} else { import net.minecraft.client.renderer.block.model.Variant +//?} import net.minecraft.client.data.models.blockstates.ConditionBuilder import net.minecraft.client.data.models.MultiVariant import net.minecraft.data.recipes.RecipeOutput import net.minecraft.data.recipes.SmithingTransformRecipeBuilder import net.minecraft.world.item.Item +//? if <26.1 { import net.minecraft.world.item.ItemStack +//?} +//? if >=26.1 { +/*import net.minecraft.world.item.ItemStackTemplate +import net.minecraft.world.item.crafting.CraftingRecipe +import net.minecraft.world.item.crafting.Recipe*/ +//?} import net.minecraft.world.item.Items import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.ShapedRecipePattern @@ -64,7 +83,11 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { pack.addProvider(::JapaneseLanguageProvider) } + //? if >=26.1 { + /*private class ModelProvider(output: FabricPackOutput) : FabricModelProvider(output) {*/ + //?} else { private class ModelProvider(output: FabricDataOutput) : FabricModelProvider(output) { + //?} override fun generateBlockStateModels(generator: BlockModelGenerators) { generateBorderTemplateModels(generator) @@ -347,10 +370,17 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { } } + //? if >=26.1 { + /*private class BlockTagProvider( + output: FabricPackOutput, + registriesFuture: CompletableFuture, + ) : FabricTagsProvider.BlockTagsProvider(output, registriesFuture) {*/ + //?} else { private class BlockTagProvider( output: FabricDataOutput, registriesFuture: CompletableFuture, ) : FabricTagProvider.BlockTagProvider(output, registriesFuture) { + //?} override fun addTags(wrapperLookup: HolderLookup.Provider) { val pickaxeMineable = valueLookupBuilder(BlockTags.MINEABLE_WITH_PICKAXE) for (block in CTBlocks.ALL_TANKS) { @@ -359,10 +389,17 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { } } + //? if >=26.1 { + /*private class CTRecipeProvider( + output: FabricPackOutput, + registriesFuture: CompletableFuture, + ) : FabricRecipeProvider(output, registriesFuture) {*/ + //?} else { private class CTRecipeProvider( output: FabricDataOutput, registriesFuture: CompletableFuture, ) : FabricRecipeProvider(output, registriesFuture) { + //?} override fun getName(): String = "ConnectedTank Recipes" override fun createRecipeProvider( @@ -441,12 +478,21 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { "MMM", ) val recipeKey = ResourceKey.create(Registries.RECIPE, ModIdentifier((output as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id)) + //? if >=26.1 { + /*val shaped = ShapedRecipe( + Recipe.CommonInfo(true), + CraftingRecipe.CraftingBookInfo(CraftingBookCategory.MISC, ""), + raw, + ItemStackTemplate(output.asItem()), + )*/ + //?} else { val shaped = ShapedRecipe( "", CraftingBookCategory.MISC, raw, ItemStack(output), ) + //?} val recipe = TankUpgradeRecipe(shaped) val advancement = exporter.advancement() .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeKey)) @@ -480,12 +526,21 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { "MMM", ) val recipeKey = ResourceKey.create(Registries.RECIPE, ModIdentifier((output as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id)) + //? if >=26.1 { + /*val shaped = ShapedRecipe( + Recipe.CommonInfo(true), + CraftingRecipe.CraftingBookInfo(CraftingBookCategory.MISC, ""), + raw, + ItemStackTemplate(output.asItem()), + )*/ + //?} else { val shaped = ShapedRecipe( "", CraftingBookCategory.MISC, raw, ItemStack(output), ) + //?} val recipe = TankUpgradeRecipe(shaped) val advancement = exporter.advancement() .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeKey)) @@ -505,10 +560,17 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { } } + //? if >=26.1 { + /*private class EnglishLanguageProvider( + output: FabricPackOutput, + registriesFuture: CompletableFuture, + ) : FabricLanguageProvider(output, registriesFuture) {*/ + //?} else { private class EnglishLanguageProvider( output: FabricDataOutput, registriesFuture: CompletableFuture, ) : FabricLanguageProvider(output, registriesFuture) { + //?} override fun generateTranslations( registryLookup: HolderLookup.Provider, builder: TranslationBuilder, @@ -537,10 +599,17 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { } } + //? if >=26.1 { + /*private class JapaneseLanguageProvider( + output: FabricPackOutput, + registriesFuture: CompletableFuture, + ) : FabricLanguageProvider(output, "ja_jp", registriesFuture) {*/ + //?} else { private class JapaneseLanguageProvider( output: FabricDataOutput, registriesFuture: CompletableFuture, ) : FabricLanguageProvider(output, "ja_jp", registriesFuture) { + //?} override fun generateTranslations( registryLookup: HolderLookup.Provider, builder: TranslationBuilder, diff --git a/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt b/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt index 9991ffb..d606331 100644 --- a/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt +++ b/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt @@ -9,7 +9,12 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState import net.minecraft.client.renderer.feature.ModelFeatureRenderer import net.minecraft.client.renderer.SubmitNodeCollector +//? if >=26.1 { +import net.minecraft.client.renderer.state.level.CameraRenderState +import net.minecraft.client.Minecraft +//?} else { import net.minecraft.client.renderer.state.CameraRenderState +//?} import net.minecraft.client.renderer.texture.TextureAtlasSprite import com.mojang.blaze3d.vertex.PoseStack import net.minecraft.core.BlockPos @@ -52,7 +57,12 @@ class ConnectedTankBlockEntityRenderer( return } + //? if >=26.1 { + val fluidModels = Minecraft.getInstance().modelManager.fluidStateModelSet + state.sprite = fluidModels.get(entity.fluidVariant.fluid.defaultFluidState()).stillMaterial().sprite() + //?} else { state.sprite = FluidVariantRendering.getSprite(entity.fluidVariant) + //?} val color = FluidVariantRendering.getColor(entity.fluidVariant) state.argb = (0xFF shl 24) or (color and 0x00FFFFFF) diff --git a/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt b/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt index 1b20423..154323b 100644 --- a/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt +++ b/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt @@ -11,7 +11,9 @@ import net.minecraft.client.renderer.special.SpecialModelRenderer import net.minecraft.client.renderer.special.SpecialModelRenderers import com.mojang.blaze3d.vertex.PoseStack import net.minecraft.world.item.BlockItem +//? if <26.1 { import net.minecraft.world.item.ItemDisplayContext +//?} import net.minecraft.world.item.ItemStack import net.minecraft.resources.Identifier import net.turtton.connectedtank.block.ConnectedTankBlock @@ -23,10 +25,24 @@ import net.turtton.connectedtank.config.SyncedServerConfig import net.turtton.connectedtank.render.FluidRenderHelper import net.turtton.connectedtank.render.WaveParams import org.joml.Vector3fc +//? if >=26.1 { +import net.minecraft.client.Minecraft +//?} class ConnectedTankItemRenderer : SpecialModelRenderer { override fun extractArgument(stack: ItemStack): ItemStack? = stack + //? if >=26.1 { + override fun submit( + data: ItemStack?, + matrices: PoseStack, + queue: SubmitNodeCollector, + light: Int, + overlay: Int, + glint: Boolean, + seed: Int, + ) { + //?} else { override fun submit( data: ItemStack?, displayContext: ItemDisplayContext, @@ -37,11 +53,17 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { glint: Boolean, seed: Int, ) { + //?} data ?: return val fluidData = data.get(CTDataComponentTypes.TANK_FLUID) ?: return if (fluidData.variant.isBlank || fluidData.amount <= 0L) return + //? if >=26.1 { + val fluidModels = Minecraft.getInstance().modelManager.fluidStateModelSet + val sprite = fluidModels.get(fluidData.variant.fluid.defaultFluidState()).stillMaterial().sprite() + //?} else { val sprite = FluidVariantRendering.getSprite(fluidData.variant) ?: return + //?} val color = FluidVariantRendering.getColor(fluidData.variant) val argb = (0xFF shl 24) or (color and 0x00FFFFFF) @@ -63,7 +85,11 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { argb, fillLevel, WaveParams(animTime = 0f, gridSize = gridSize), + //? if >=26.1 { + renderLayer = RenderTypes.entityTranslucentCullItemTarget(sprite.atlasLocation()), + //?} else { renderLayer = RenderTypes.itemEntityTranslucentCull(sprite.atlasLocation()), + //?} ) } finally { matrices.popPose() @@ -73,6 +99,18 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { override fun getExtents(vertices: Consumer) { } + //? if >=26.1 { + class Unbaked : SpecialModelRenderer.Unbaked { + override fun bake(context: SpecialModelRenderer.BakingContext): SpecialModelRenderer = + ConnectedTankItemRenderer() + + override fun type(): MapCodec> = CODEC + + companion object { + val CODEC: MapCodec = MapCodec.unit(Unbaked()) + } + } + //?} else { class Unbaked : SpecialModelRenderer.Unbaked { override fun bake(context: SpecialModelRenderer.BakingContext): SpecialModelRenderer<*> = ConnectedTankItemRenderer() @@ -83,6 +121,7 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { val CODEC: MapCodec = MapCodec.unit(Unbaked()) } } + //?} companion object { val ID: Identifier = Identifier.fromNamespaceAndPath("connectedtank", "tank_fluid") diff --git a/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt b/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt index dc7b087..9d68f62 100644 --- a/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt +++ b/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt @@ -1,7 +1,11 @@ package net.turtton.connectedtank.render import kotlin.math.sin +//? if >=26.1 { +/*import net.minecraft.util.LightCoordsUtil*/ +//?} else { import net.minecraft.client.renderer.LightTexture +//?} import net.minecraft.client.renderer.texture.OverlayTexture import com.mojang.blaze3d.vertex.VertexConsumer //? if >=1.21.11 { @@ -125,7 +129,11 @@ object FluidRenderHelper { maxZ: Float, neighbors: NeighborMask, ) { + //? if >=26.1 { + /*val fullLight = LightCoordsUtil.FULL_BRIGHT*/ + //?} else { val fullLight = LightTexture.FULL_BRIGHT + //?} val ov = OverlayTexture.NO_OVERLAY val u0 = sprite.u0 val u1 = sprite.u1 diff --git a/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt b/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt index ff4edc8..45bc370 100644 --- a/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt +++ b/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt @@ -4,6 +4,8 @@ import net.fabricmc.fabric.api.gametest.v1.GameTest import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction +import net.minecraft.world.entity.EntityType +import net.minecraft.world.entity.item.ItemEntity import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.material.Fluids import net.minecraft.world.item.ItemStack @@ -12,6 +14,7 @@ import net.minecraft.network.chat.Component import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.world.level.GameType +import net.minecraft.world.phys.AABB import net.turtton.connectedtank.block.CTBlocks import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.block.TankFluidStorage @@ -987,4 +990,201 @@ object ConnectedTankGameTest { ) context.succeed() } + + // === ブロック破壊時の液体保持テスト === + + private fun GameTestHelper.findDroppedTankItem(relativePos: BlockPos): ItemStack? { + val absPos = absolutePos(relativePos) + val entities = level.getEntities(EntityType.ITEM, AABB(absPos).inflate(1.0)) { it.isAlive } + for (entity in entities) { + val itemEntity = entity as ItemEntity + val stack = itemEntity.item + if (stack.item in CTItems.ALL_TANK_ITEMS) return stack + } + return null + } + + @GameTest + fun breakSingleTankRetainsFluidInDrop(context: GameTestHelper) { + val tankPos = BlockPos(0, 2, 0) + context.placeTank(tankPos) + + val state = context.getFluidState() + val water = FluidVariant.of(Fluids.WATER) + val storage = state.getStorage(context.absolutePos(tankPos))!! + Transaction.openOuter().use { tx -> + storage.insert(water, FluidConstants.BUCKET * 5, tx) + tx.commit() + } + + // ブロック破壊 (ドロップ生成あり) + context.level.destroyBlock(context.absolutePos(tankPos), true) + + // ドロップされたアイテムを検索 + val droppedStack = context.findDroppedTankItem(tankPos) + context.assertTrue(droppedStack != null, Component.literal("Dropped tank item should exist")) + + val fluidData = droppedStack!!.get(CTDataComponentTypes.TANK_FLUID) + context.assertTrue(fluidData != null, Component.literal("Dropped item should have fluid data")) + context.assertTrue(fluidData!!.variant == water, Component.literal("Fluid variant should be water")) + context.assertTrue( + fluidData.amount == FluidConstants.BUCKET * 5, + Component.literal("Fluid amount should be 5 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), + ) + context.succeed() + } + + @GameTest + fun breakTankAndReplaceRestoresFluid(context: GameTestHelper) { + val tankPos = BlockPos(0, 2, 0) + context.placeTank(tankPos) + + val state = context.getFluidState() + val water = FluidVariant.of(Fluids.WATER) + val storage = state.getStorage(context.absolutePos(tankPos))!! + Transaction.openOuter().use { tx -> + storage.insert(water, FluidConstants.BUCKET * 7, tx) + tx.commit() + } + + // ブロック破壊 + context.level.destroyBlock(context.absolutePos(tankPos), true) + + // ストレージが削除されたことを確認 + val removedStorage = state.getStorage(context.absolutePos(tankPos)) + context.assertTrue(removedStorage == null, Component.literal("Storage should be removed after breaking")) + + // ドロップされたアイテムから液体データを取得 + val droppedStack = context.findDroppedTankItem(tankPos) + context.assertTrue(droppedStack != null, Component.literal("Dropped tank item should exist")) + val fluidData = droppedStack!!.get(CTDataComponentTypes.TANK_FLUID) + context.assertTrue(fluidData != null, Component.literal("Dropped item should have fluid data")) + + // 液体入りタンクを再設置 (setPlacedBy をシミュレート) + val block = CTBlocks.CONNECTED_TANK as ConnectedTankBlock + context.setBlock(tankPos, block.defaultBlockState()) + val capacity = block.tier.bucketCapacity + val tankStorage = TankFluidStorage(capacity, fluidData) + state.addStorage(context.absolutePos(tankPos), tankStorage) + + // 液体が復元されたことを確認 + val restored = state.getStorage(context.absolutePos(tankPos)) + context.assertTrue(restored != null, Component.literal("Restored storage should exist")) + context.assertTrue(restored!!.variant == water, Component.literal("Restored variant should be water")) + context.assertTrue( + restored.amount == FluidConstants.BUCKET * 7, + Component.literal("Restored amount should be 7 buckets but was ${restored.amount / FluidConstants.BUCKET}"), + ) + context.succeed() + } + + @GameTest + fun breakMiddleTankRetainsShareInDrop(context: GameTestHelper) { + // 3 連結タンク (各 32 バケツ容量) に 30 バケツ注入 → 中央を破壊 → 中央のシェア (10 バケツ) がドロップ + val posL = BlockPos(0, 2, 0) + val posM = BlockPos(1, 2, 0) + val posR = BlockPos(2, 2, 0) + context.placeTank(posL) + context.placeTank(posM) + context.placeTank(posR) + + val state = context.getFluidState() + val water = FluidVariant.of(Fluids.WATER) + val storage = state.getStorage(context.absolutePos(posL))!! + Transaction.openOuter().use { tx -> + storage.insert(water, FluidConstants.BUCKET * 30, tx) + tx.commit() + } + + // 中央タンクを破壊 + context.level.destroyBlock(context.absolutePos(posM), true) + + // ドロップされたアイテムのシェアを確認 + val droppedStack = context.findDroppedTankItem(posM) + context.assertTrue(droppedStack != null, Component.literal("Dropped tank item should exist")) + + val fluidData = droppedStack!!.get(CTDataComponentTypes.TANK_FLUID) + context.assertTrue(fluidData != null, Component.literal("Dropped item should have fluid data")) + context.assertTrue( + fluidData!!.amount == FluidConstants.BUCKET * 10, + Component.literal("Middle share should be 10 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), + ) + + // 残りのタンクは分断されてそれぞれ 10 バケツ + val sL = state.getStorage(context.absolutePos(posL)) + val sR = state.getStorage(context.absolutePos(posR)) + context.assertTrue(sL !== sR, Component.literal("Left and right should be separate groups")) + context.assertTrue( + sL!!.amount == FluidConstants.BUCKET * 10, + Component.literal("Left should have 10 buckets but was ${sL.amount / FluidConstants.BUCKET}"), + ) + context.assertTrue( + sR!!.amount == FluidConstants.BUCKET * 10, + Component.literal("Right should have 10 buckets but was ${sR.amount / FluidConstants.BUCKET}"), + ) + context.succeed() + } + + @GameTest + fun destroyBlockDoesNotLeakStaleFluidData(context: GameTestHelper) { + val tankPos = BlockPos(0, 2, 0) + context.placeTank(tankPos) + + val state = context.getFluidState() + val water = FluidVariant.of(Fluids.WATER) + val storage = state.getStorage(context.absolutePos(tankPos))!! + Transaction.openOuter().use { tx -> + storage.insert(water, FluidConstants.BUCKET * 5, tx) + tx.commit() + } + + context.level.destroyBlock(context.absolutePos(tankPos), true) + + val absPos = context.absolutePos(tankPos) + context.level.getEntities(EntityType.ITEM, AABB(absPos).inflate(1.0)) { true } + .forEach { it.discard() } + + context.placeTank(tankPos) + context.level.destroyBlock(context.absolutePos(tankPos), true) + + val droppedStack = context.findDroppedTankItem(tankPos) + context.assertTrue(droppedStack != null, Component.literal("Empty tank should drop")) + + val fluidData = droppedStack!!.get(CTDataComponentTypes.TANK_FLUID) + context.assertTrue( + fluidData == null, + Component.literal("Empty tank should not have stale fluid data"), + ) + context.succeed() + } + + @GameTest + fun survivalMiningRetainsFluid(context: GameTestHelper) { + val tankPos = BlockPos(0, 2, 0) + context.placeTank(tankPos) + + val state = context.getFluidState() + val water = FluidVariant.of(Fluids.WATER) + val storage = state.getStorage(context.absolutePos(tankPos))!! + Transaction.openOuter().use { tx -> + storage.insert(water, FluidConstants.BUCKET * 5, tx) + tx.commit() + } + + val player = context.makeMockServerPlayerInLevel() + player.gameMode.changeGameModeForPlayer(GameType.SURVIVAL) + player.gameMode.destroyBlock(context.absolutePos(tankPos)) + + val droppedStack = context.findDroppedTankItem(tankPos) + context.assertTrue(droppedStack != null, Component.literal("Dropped tank item should exist")) + + val fluidData = droppedStack!!.get(CTDataComponentTypes.TANK_FLUID) + context.assertTrue(fluidData != null, Component.literal("Survival mining should retain fluid data")) + context.assertTrue(fluidData!!.variant == water, Component.literal("Fluid variant should be water")) + context.assertTrue( + fluidData.amount == FluidConstants.BUCKET * 5, + Component.literal("Fluid amount should be 5 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), + ) + context.succeed() + } } diff --git a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt index aee6fce..734dd19 100644 --- a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt +++ b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt @@ -124,10 +124,6 @@ class ConnectedTankBlock(val tier: TankTier, settings: Properties) : CTBlocks.syncGroupBlockEntities(world, neighborPos, persistentState) } - // クリエイティブモード等で getDroppedStacks が呼ばれないパスのクリーンアップ - val immutablePos = pos.immutable() - world.server?.execute { pendingDropData.remove(immutablePos) } - super.affectNeighborsAfterRemoval(state, world, pos, moved) } @@ -195,14 +191,22 @@ class ConnectedTankBlock(val tier: TankTier, settings: Properties) : val storage = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val tankStorage = storage.getStorage(pos) if (tankStorage == null) { + //? if >=26.1 { + /*player.sendOverlayMessage(Component.literal("No storage"))*/ + //?} else { player.displayClientMessage(Component.literal("No storage"), true) + //?} return InteractionResult.SUCCESS } val fluidName = if (tankStorage.isResourceBlank) "Empty" else FluidVariantAttributes.getName(tankStorage.variant).string val buckets = tankStorage.amount.toDouble() / FluidConstants.BUCKET val capacity = tankStorage.bucketCapacity + //? if >=26.1 { + /*player.sendOverlayMessage(Component.literal("$fluidName: %.2f / %d buckets".format(buckets, capacity)))*/ + //?} else { player.displayClientMessage(Component.literal("$fluidName: %.2f / %d buckets".format(buckets, capacity)), true) + //?} return InteractionResult.SUCCESS } diff --git a/src/main/kotlin/net/turtton/connectedtank/compat/jade/ConnectedTankJadePlugin.kt b/src/main/kotlin/net/turtton/connectedtank/compat/jade/ConnectedTankJadePlugin.kt index 0026440..14b527f 100644 --- a/src/main/kotlin/net/turtton/connectedtank/compat/jade/ConnectedTankJadePlugin.kt +++ b/src/main/kotlin/net/turtton/connectedtank/compat/jade/ConnectedTankJadePlugin.kt @@ -49,7 +49,11 @@ object TankFluidProvider : if (storage.isResourceBlank || storage.amount <= 0) return null val variant = storage.variant + //? if >=26.1 { + /*val fluidObject = JadeFluidObject.of(variant.fluid, storage.amount, variant.componentsPatch)*/ + //?} else { val fluidObject = JadeFluidObject.of(variant.fluid, storage.amount, variant.components) + //?} val capacity = storage.bucketCapacity.toLong() * FluidConstants.BUCKET val data = FluidView.Data(fluidObject, capacity) return listOf(ViewGroup(listOf(data))) diff --git a/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt b/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt index 7700ab6..71d1fac 100644 --- a/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt +++ b/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt @@ -1,6 +1,10 @@ package net.turtton.connectedtank.item +//? if >=26.1 { +/*import net.fabricmc.fabric.api.creativetab.v1.FabricCreativeModeTab*/ +//?} else { import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup +//?} import net.minecraft.world.item.BlockItem import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack @@ -61,7 +65,11 @@ object CTItems { Registry.register( BuiltInRegistries.CREATIVE_MODE_TAB, ITEM_GROUP_KEY, + //? if >=26.1 { + /*FabricCreativeModeTab.builder()*/ + //?} else { FabricItemGroup.builder() + //?} .title(Component.translatable("itemGroup.connectedtank.item_group")) .icon { ItemStack(CONNECTED_TANK) } .displayItems { _, entries -> diff --git a/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt b/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt index edd58f3..cb5c1c5 100644 --- a/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt +++ b/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt @@ -52,7 +52,11 @@ data class ConfigSyncPayload( ) fun registerServer() { + //? if >=26.1 { + /*PayloadTypeRegistry.clientboundPlay().register(ID, CODEC)*/ + //?} else { PayloadTypeRegistry.playS2C().register(ID, CODEC) + //?} ServerPlayConnectionEvents.JOIN.register { handler, _, _ -> val config = CTServerConfig.instance val payload = ConfigSyncPayload(config.tankBucketCapacity, config.tierMultipliers) diff --git a/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt b/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt index 1a2cef9..c4823f9 100644 --- a/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt +++ b/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt @@ -9,7 +9,11 @@ object CTRecipeSerializers { val TANK_UPGRADE: RecipeSerializer = Registry.register( BuiltInRegistries.RECIPE_SERIALIZER, ModIdentifier("tank_upgrade"), + //? if >=26.1 { + /*RecipeSerializer(TankUpgradeRecipe.MAP_CODEC, TankUpgradeRecipe.STREAM_CODEC),*/ + //?} else { TankUpgradeRecipe.Serializer(), + //?} ) fun init() {} diff --git a/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt b/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt index dfb2457..21f0aa0 100644 --- a/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt +++ b/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt @@ -11,15 +11,23 @@ import net.minecraft.world.item.crafting.ShapedRecipe import net.minecraft.world.item.crafting.CraftingBookCategory import net.minecraft.world.item.crafting.display.RecipeDisplay import net.minecraft.world.item.crafting.CraftingInput +//? if <26.1 { import net.minecraft.core.HolderLookup +//?} import net.minecraft.world.level.Level import net.turtton.connectedtank.component.CTDataComponentTypes class TankUpgradeRecipe(private val shaped: ShapedRecipe) : CraftingRecipe { override fun matches(input: CraftingInput, world: Level): Boolean = shaped.matches(input, world) + //? if >=26.1 { + /*override fun assemble(input: CraftingInput): ItemStack { + val result = shaped.assemble(input) + */ + //?} else { override fun assemble(input: CraftingInput, lookup: HolderLookup.Provider): ItemStack { val result = shaped.assemble(input, lookup) + //?} for (stack in input.items()) { val fluidData = stack.get(CTDataComponentTypes.TANK_FLUID) if (fluidData != null) { @@ -42,10 +50,19 @@ class TankUpgradeRecipe(private val shaped: ShapedRecipe) : CraftingRecipe { override fun display(): List = shaped.display() + companion object { + //? if >=26.1 { + /*val MAP_CODEC: MapCodec = ShapedRecipe.MAP_CODEC.xmap(::TankUpgradeRecipe) { it.shaped } + val STREAM_CODEC: StreamCodec = ShapedRecipe.STREAM_CODEC.map(::TankUpgradeRecipe) { it.shaped }*/ + //?} + } + + //? if <26.1 { class Serializer : RecipeSerializer { override fun codec(): MapCodec = ShapedRecipe.Serializer.CODEC.xmap(::TankUpgradeRecipe) { it.shaped } @Deprecated("Recipe is no longer synced to clients") override fun streamCodec(): StreamCodec = ShapedRecipe.Serializer.STREAM_CODEC.map(::TankUpgradeRecipe) { it.shaped } } + //?} } diff --git a/src/main/kotlin/net/turtton/connectedtank/world/FluidStoragePersistentState.kt b/src/main/kotlin/net/turtton/connectedtank/world/FluidStoragePersistentState.kt index f166196..2195eeb 100644 --- a/src/main/kotlin/net/turtton/connectedtank/world/FluidStoragePersistentState.kt +++ b/src/main/kotlin/net/turtton/connectedtank/world/FluidStoragePersistentState.kt @@ -13,6 +13,9 @@ import net.turtton.connectedtank.MOD_ID import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.block.TankFluidStorage import net.turtton.connectedtank.config.CTServerConfig +//? if >=26.1 { +/*import net.turtton.connectedtank.extension.ModIdentifier*/ +//?} class FluidStoragePersistentState( positionalStorageMap: Map = mapOf(), @@ -394,7 +397,11 @@ class FluidStoragePersistentState( ).apply(it, ::FluidStoragePersistentState) } val TYPE: SavedDataType = SavedDataType( + //? if >=26.1 { + /*ModIdentifier("fluid_storage"),*/ + //?} else { "${MOD_ID}_fluid_storage", + //?} ::FluidStoragePersistentState, CODEC, net.minecraft.util.datafix.DataFixTypes.LEVEL, diff --git a/src/main/resources/assets/connectedtank/icon.png b/src/main/resources/assets/connectedtank/icon.png index 047b91f..6aef406 100644 Binary files a/src/main/resources/assets/connectedtank/icon.png and b/src/main/resources/assets/connectedtank/icon.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 2d91747..7f79011 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -53,9 +53,9 @@ } ], "depends": { - "fabricloader": ">=0.17.2", + "fabricloader": ">=0.19.2", "minecraft": "~${minecraft_version}", - "java": ">=21", + "java": ">=${java_version}", "fabric-api": "*", "fabric-language-kotlin": "*" }, diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index f617bb0..9a2b08e 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -25,6 +25,6 @@ spotless { } java { target("src/**/*.java") - palantirJavaFormat() + palantirJavaFormat("2.90.0") } }