diff --git a/.gitignore b/.gitignore index e3913ab..72a77fc 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,7 @@ replay_*.log # git-hooks.nix .pre-commit-config.yaml + +# stonecutter + +versions/ diff --git a/build.gradle.kts b/build.gradle.kts index 77dc72d..89aac7f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -111,11 +111,6 @@ loom { } dependencies { - val yarnMappings = when (mcVersion) { - "1.21.8" -> "1.21.8+build.1" - "1.21.11" -> "1.21.11+build.5" - else -> error("Unsupported MC version: $mcVersion") - } val fabricApiVersion = when (mcVersion) { "1.21.8" -> "0.132.0+1.21.8" "1.21.11" -> "0.141.3+1.21.11" @@ -143,7 +138,7 @@ dependencies { } minecraft("com.mojang:minecraft:$mcVersion") - mappings("net.fabricmc:yarn:$yarnMappings:v2") + mappings(loom.officialMojangMappings()) modImplementation(libs.fabric.loader) modImplementation("net.fabricmc.fabric-api:fabric-api:$fabricApiVersion") diff --git a/src/client/java/net/turtton/connectedtank/mixin/client/ExampleClientMixin.java b/src/client/java/net/turtton/connectedtank/mixin/client/ExampleClientMixin.java index 93fb579..e671f7e 100644 --- a/src/client/java/net/turtton/connectedtank/mixin/client/ExampleClientMixin.java +++ b/src/client/java/net/turtton/connectedtank/mixin/client/ExampleClientMixin.java @@ -1,15 +1,15 @@ package net.turtton.connectedtank.mixin.client; -import net.minecraft.client.MinecraftClient; +import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(MinecraftClient.class) +@Mixin(Minecraft.class) public class ExampleClientMixin { @Inject(at = @At("HEAD"), method = "run") private void init(CallbackInfo info) { - // This code is injected into the start of MinecraftClient.run()V + // This code is injected into the start of Minecraft.run()V } } diff --git a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt index b2a929a..bd81067 100644 --- a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt +++ b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankClient.kt @@ -4,8 +4,8 @@ import net.fabricmc.api.ClientModInitializer import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap -import net.minecraft.client.render.BlockRenderLayer -import net.minecraft.client.render.block.entity.BlockEntityRendererFactories +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 import net.turtton.connectedtank.block.ConnectedTankBlockEntityRenderer @@ -21,13 +21,13 @@ object ConnectedTankClient : ClientModInitializer { override fun onInitializeClient() { CTClientConfig.load() - CTBlocks.ALL_TANKS.forEach { BlockRenderLayerMap.putBlock(it, BlockRenderLayer.CUTOUT) } + CTBlocks.ALL_TANKS.forEach { BlockRenderLayerMap.putBlock(it, ChunkSectionLayer.CUTOUT) } //? if >=1.21.11 { - /*BlockEntityRendererFactories.register<_, ConnectedTankRenderState>( + /*BlockEntityRenderers.register<_, ConnectedTankRenderState>( CTBlockEntityTypes.CONNECTED_TANK, ) { context -> ConnectedTankBlockEntityRenderer(context) }*/ //?} else { - BlockEntityRendererFactories.register( + BlockEntityRenderers.register( CTBlockEntityTypes.CONNECTED_TANK, ) { context -> ConnectedTankBlockEntityRenderer(context) } //?} diff --git a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt index b917eaf..d4f9cb5 100644 --- a/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt +++ b/src/client/kotlin/net/turtton/connectedtank/ConnectedTankDataGenerator.kt @@ -10,37 +10,44 @@ 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 import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider -import net.minecraft.advancement.AdvancementRequirements -import net.minecraft.advancement.AdvancementRewards -import net.minecraft.advancement.criterion.RecipeUnlockedCriterion -import net.minecraft.block.Block -import net.minecraft.client.data.BlockStateModelGenerator -import net.minecraft.client.data.ItemModelGenerator -import net.minecraft.client.data.ItemModels -import net.minecraft.client.data.ModelSupplier -import net.minecraft.client.data.MultipartBlockModelDefinitionCreator -import net.minecraft.client.render.model.json.ModelVariant -import net.minecraft.client.render.model.json.MultipartModelConditionBuilder -import net.minecraft.client.render.model.json.WeightedVariant -import net.minecraft.data.recipe.RecipeExporter -import net.minecraft.data.recipe.RecipeGenerator -import net.minecraft.data.recipe.SmithingTransformRecipeJsonBuilder -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.item.Items -import net.minecraft.recipe.Ingredient -import net.minecraft.recipe.RawShapedRecipe -import net.minecraft.recipe.ShapedRecipe -import net.minecraft.recipe.book.CraftingRecipeCategory -import net.minecraft.recipe.book.RecipeCategory -import net.minecraft.registry.RegistryKey -import net.minecraft.registry.RegistryKeys -import net.minecraft.registry.RegistryWrapper -import net.minecraft.registry.tag.BlockTags -import net.minecraft.registry.tag.ItemTags -import net.minecraft.registry.tag.TagKey -import net.minecraft.util.Identifier -import net.minecraft.util.collection.Pool +import net.minecraft.advancements.AdvancementRequirements +import net.minecraft.advancements.AdvancementRewards +//? if >=1.21.11 { +/*import net.minecraft.advancements.criterion.RecipeUnlockedTrigger*/ +//?} else { +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger +//?} +import net.minecraft.world.level.block.Block +import net.minecraft.client.data.models.BlockModelGenerators +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 +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 +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.ShapedRecipePattern +import net.minecraft.world.item.crafting.ShapedRecipe +import net.minecraft.world.item.crafting.CraftingBookCategory +import net.minecraft.data.recipes.RecipeCategory +import net.minecraft.resources.ResourceKey +import net.minecraft.core.registries.Registries +import net.minecraft.core.HolderLookup +import net.minecraft.tags.BlockTags +import net.minecraft.tags.ItemTags +import net.minecraft.tags.TagKey +//? if >=1.21.11 { +/*import net.minecraft.resources.Identifier as ResourceLocation*/ +//?} else { +import net.minecraft.resources.ResourceLocation +//?} +import net.minecraft.util.random.WeightedList import net.turtton.connectedtank.block.CTBlocks import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.extension.ModIdentifier @@ -51,14 +58,14 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) { val pack = fabricDataGenerator.createPack() pack.addProvider(::ModelProvider) - pack.addProvider(::RecipeProvider) + pack.addProvider(::CTRecipeProvider) pack.addProvider(::BlockTagProvider) pack.addProvider(::EnglishLanguageProvider) pack.addProvider(::JapaneseLanguageProvider) } private class ModelProvider(output: FabricDataOutput) : FabricModelProvider(output) { - override fun generateBlockStateModels(generator: BlockStateModelGenerator) { + override fun generateBlockStateModels(generator: BlockModelGenerators) { generateBorderTemplateModels(generator) for (block in CTBlocks.ALL_TANKS) { @@ -72,7 +79,7 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { } } - override fun generateItemModels(generator: ItemModelGenerator) {} + override fun generateItemModels(generator: ItemModelGenerators) {} private fun jsonArray(vararg values: Number): JsonArray = JsonArray().apply { values.forEach { add(it) } @@ -128,8 +135,8 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { createSidePanelElement(Triple(0, 0, 15.99), Triple(16, 16, 16), "south"), ) - private fun generateBaseModel(generator: BlockStateModelGenerator, tierId: String) { - val modelId = Identifier.of("connectedtank", "block/$tierId") + private fun generateBaseModel(generator: BlockModelGenerators, tierId: String) { + val modelId = ResourceLocation.fromNamespaceAndPath("connectedtank", "block/$tierId") val json = JsonObject().apply { add( "textures", @@ -143,13 +150,13 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { JsonArray().apply { SIDE_PANEL_ELEMENTS.forEach { add(it) } }, ) } - generator.modelCollector.accept(modelId, ModelSupplier { json }) + generator.modelOutput.accept(modelId, ModelInstance { json }) } - private fun generateBorderTemplateModels(generator: BlockStateModelGenerator) { + private fun generateBorderTemplateModels(generator: BlockModelGenerators) { for ((direction, stripMap) in BORDER_STRIP_ELEMENTS) { for ((stripDir, element) in stripMap) { - val modelId = Identifier.of( + val modelId = ResourceLocation.fromNamespaceAndPath( "connectedtank", "block/tank_border_${direction}_$stripDir", ) @@ -162,15 +169,15 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { ) add("elements", JsonArray().apply { add(element) }) } - generator.modelCollector.accept(modelId, ModelSupplier { json }) + generator.modelOutput.accept(modelId, ModelInstance { json }) } } } - private fun generateBorderChildModels(generator: BlockStateModelGenerator, tierId: String) { + private fun generateBorderChildModels(generator: BlockModelGenerators, tierId: String) { for ((direction, stripMap) in BORDER_STRIP_ELEMENTS) { for (stripDir in stripMap.keys) { - val modelId = Identifier.of( + val modelId = ResourceLocation.fromNamespaceAndPath( "connectedtank", "block/${tierId}_border_${direction}_$stripDir", ) @@ -186,21 +193,21 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { }, ) } - generator.modelCollector.accept(modelId, ModelSupplier { json }) + generator.modelOutput.accept(modelId, ModelInstance { json }) } } } private fun generateMultipartBlockState( - generator: BlockStateModelGenerator, + generator: BlockModelGenerators, block: Block, tierId: String, ) { - val supplier = MultipartBlockModelDefinitionCreator.create(block) + val supplier = MultiPartGenerator.multiPart(block) // Base model (always applied) - val baseModelId = Identifier.of("connectedtank", "block/$tierId") - supplier.with(WeightedVariant(Pool.of(ModelVariant(baseModelId)))) + val baseModelId = ResourceLocation.fromNamespaceAndPath("connectedtank", "block/$tierId") + supplier.with(MultiVariant(WeightedList.of(Variant(baseModelId)))) // Border overlays (applied when NOT connected in each direction) val directionProperties = mapOf( @@ -216,26 +223,26 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { val borderProperty = requireNotNull(directionProperties[dirName]) for (stripDir in stripMap.keys) { val stripProperty = requireNotNull(directionProperties[stripDir]) - val modelId = Identifier.of( + val modelId = ResourceLocation.fromNamespaceAndPath( "connectedtank", "block/${tierId}_border_${dirName}_$stripDir", ) supplier.with( - MultipartModelConditionBuilder() - .put(borderProperty, false) - .put(stripProperty, false), - WeightedVariant(Pool.of(ModelVariant(modelId))), + ConditionBuilder() + .term(borderProperty, false) + .term(stripProperty, false), + MultiVariant(WeightedList.of(Variant(modelId))), ) } } - generator.blockStateCollector.accept(supplier) + generator.blockStateOutput.accept(supplier) } // The item model always includes all border overlays for every direction, // because a standalone item is never connected to adjacent blocks. - private fun generateItemModel(generator: BlockStateModelGenerator, block: Block, tierId: String) { - val modelId = Identifier.of("connectedtank", "block/${tierId}_item") + private fun generateItemModel(generator: BlockModelGenerators, block: Block, tierId: String) { + val modelId = ResourceLocation.fromNamespaceAndPath("connectedtank", "block/${tierId}_item") val json = JsonObject().apply { addProperty("parent", "minecraft:block/block") add( @@ -258,12 +265,12 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { }, ) } - generator.modelCollector.accept(modelId, ModelSupplier { json }) + generator.modelOutput.accept(modelId, ModelInstance { json }) generator.itemModelOutput.accept( block.asItem(), - ItemModels.composite( - ItemModels.basic(modelId), - ItemModels.special(modelId, ConnectedTankItemRenderer.Unbaked()), + ItemModelUtils.composite( + ItemModelUtils.plainModel(modelId), + ItemModelUtils.specialModel(modelId, ConnectedTankItemRenderer.Unbaked()), ), ) } @@ -342,37 +349,37 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { private class BlockTagProvider( output: FabricDataOutput, - registriesFuture: CompletableFuture, + registriesFuture: CompletableFuture, ) : FabricTagProvider.BlockTagProvider(output, registriesFuture) { - override fun configure(wrapperLookup: RegistryWrapper.WrapperLookup) { - val pickaxeMineable = valueLookupBuilder(BlockTags.PICKAXE_MINEABLE) + override fun addTags(wrapperLookup: HolderLookup.Provider) { + val pickaxeMineable = valueLookupBuilder(BlockTags.MINEABLE_WITH_PICKAXE) for (block in CTBlocks.ALL_TANKS) { pickaxeMineable.add(block) } } } - private class RecipeProvider( + private class CTRecipeProvider( output: FabricDataOutput, - registriesFuture: CompletableFuture, + registriesFuture: CompletableFuture, ) : FabricRecipeProvider(output, registriesFuture) { override fun getName(): String = "ConnectedTank Recipes" - override fun getRecipeGenerator( - registryLookup: RegistryWrapper.WrapperLookup, - exporter: RecipeExporter, - ): RecipeGenerator = object : RecipeGenerator(registryLookup, exporter) { - override fun generate() { + override fun createRecipeProvider( + registryLookup: HolderLookup.Provider, + exporter: RecipeOutput, + ): net.minecraft.data.recipes.RecipeProvider = object : net.minecraft.data.recipes.RecipeProvider(registryLookup, exporter) { + override fun buildRecipes() { // Base tank recipe (normal shaped) - createShaped(RecipeCategory.DECORATIONS, CTBlocks.CONNECTED_TANK) + shaped(RecipeCategory.DECORATIONS, CTBlocks.CONNECTED_TANK) .pattern("PGP") .pattern("GPG") .pattern("PGP") - .input('P', ItemTags.PLANKS) - .input('G', Items.GLASS) - .criterion("has_planks", conditionsFromTag(ItemTags.PLANKS)) - .criterion(hasItem(Items.GLASS), conditionsFromItem(Items.GLASS)) - .offerTo(exporter) + .define('P', ItemTags.PLANKS) + .define('G', Items.GLASS) + .unlockedBy("has_planks", has(ItemTags.PLANKS)) + .unlockedBy(getHasName(Items.GLASS), has(Items.GLASS)) + .save(exporter) // Upgrade recipes offerTankUpgrade( @@ -380,7 +387,7 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { CTBlocks.STONE_CONNECTED_TANK, ItemTags.STONE_CRAFTING_MATERIALS, "has_stone", - conditionsFromTag(ItemTags.STONE_CRAFTING_MATERIALS), + has(ItemTags.STONE_CRAFTING_MATERIALS), ) offerTankUpgradeItem( CTBlocks.STONE_CONNECTED_TANK, @@ -403,21 +410,21 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { Items.DIAMOND, ) // Netherite upgrade uses smithing table - val netheriteRecipeKey = RegistryKey.of( - RegistryKeys.RECIPE, + val netheriteRecipeKey = ResourceKey.create( + Registries.RECIPE, ModIdentifier( (CTBlocks.NETHERITE_CONNECTED_TANK as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id, ), ) - SmithingTransformRecipeJsonBuilder.create( - Ingredient.ofItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE), - Ingredient.ofItem(CTBlocks.DIAMOND_CONNECTED_TANK), - ingredientFromTag(ItemTags.NETHERITE_TOOL_MATERIALS), + SmithingTransformRecipeBuilder.smithing( + Ingredient.of(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE), + Ingredient.of(CTBlocks.DIAMOND_CONNECTED_TANK), + tag(ItemTags.NETHERITE_TOOL_MATERIALS), RecipeCategory.DECORATIONS, CTBlocks.NETHERITE_CONNECTED_TANK.asItem(), ) - .criterion("has_netherite_ingot", conditionsFromTag(ItemTags.NETHERITE_TOOL_MATERIALS)) - .offerTo(exporter, netheriteRecipeKey) + .unlocks("has_netherite_ingot", has(ItemTags.NETHERITE_TOOL_MATERIALS)) + .save(exporter, netheriteRecipeKey) } private fun offerTankUpgradeItem( @@ -425,31 +432,35 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { output: Block, material: Item, ) { - val materialIngredient = net.minecraft.recipe.Ingredient.ofItem(material) - val inputIngredient = net.minecraft.recipe.Ingredient.ofItem(input) - val raw = RawShapedRecipe.create( + val materialIngredient = net.minecraft.world.item.crafting.Ingredient.of(material) + val inputIngredient = net.minecraft.world.item.crafting.Ingredient.of(input) + val raw = ShapedRecipePattern.of( mapOf('M' to materialIngredient, 'T' to inputIngredient), "MMM", "MTM", "MMM", ) - val recipeKey = RegistryKey.of(RegistryKeys.RECIPE, ModIdentifier((output as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id)) + val recipeKey = ResourceKey.create(Registries.RECIPE, ModIdentifier((output as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id)) val shaped = ShapedRecipe( "", - CraftingRecipeCategory.MISC, + CraftingBookCategory.MISC, raw, ItemStack(output), ) val recipe = TankUpgradeRecipe(shaped) - val advancement = exporter.advancementBuilder - .criterion("has_the_recipe", RecipeUnlockedCriterion.create(recipeKey)) + val advancement = exporter.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeKey)) .rewards(AdvancementRewards.Builder.recipe(recipeKey)) - .criteriaMerger(AdvancementRequirements.CriterionMerger.OR) - .criterion(hasItem(material), conditionsFromItem(material)) + .requirements(AdvancementRequirements.Strategy.OR) + .addCriterion(getHasName(material), has(material)) exporter.accept( recipeKey, recipe, - advancement.build(recipeKey.value.withPrefixedPath("recipes/${RecipeCategory.DECORATIONS.name.lowercase()}/")), + //? if >=1.21.11 { + /*advancement.build(recipeKey.identifier().withPrefix("recipes/${RecipeCategory.DECORATIONS.name.lowercase()}/")),*/ + //?} else { + advancement.build(recipeKey.location().withPrefix("recipes/${RecipeCategory.DECORATIONS.name.lowercase()}/")), + //?} ) } @@ -458,33 +469,37 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { output: Block, materialTag: TagKey, criterionName: String, - criterion: net.minecraft.advancement.AdvancementCriterion<*>, + criterion: net.minecraft.advancements.Criterion<*>, ) { - val materialIngredient = ingredientFromTag(materialTag) - val inputIngredient = net.minecraft.recipe.Ingredient.ofItem(input) - val raw = RawShapedRecipe.create( + val materialIngredient = tag(materialTag) + val inputIngredient = net.minecraft.world.item.crafting.Ingredient.of(input) + val raw = ShapedRecipePattern.of( mapOf('M' to materialIngredient, 'T' to inputIngredient), "MMM", "MTM", "MMM", ) - val recipeKey = RegistryKey.of(RegistryKeys.RECIPE, ModIdentifier((output as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id)) + val recipeKey = ResourceKey.create(Registries.RECIPE, ModIdentifier((output as net.turtton.connectedtank.block.ConnectedTankBlock).tier.id)) val shaped = ShapedRecipe( "", - CraftingRecipeCategory.MISC, + CraftingBookCategory.MISC, raw, ItemStack(output), ) val recipe = TankUpgradeRecipe(shaped) - val advancement = exporter.advancementBuilder - .criterion("has_the_recipe", RecipeUnlockedCriterion.create(recipeKey)) + val advancement = exporter.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeKey)) .rewards(AdvancementRewards.Builder.recipe(recipeKey)) - .criteriaMerger(AdvancementRequirements.CriterionMerger.OR) - .criterion(criterionName, criterion) + .requirements(AdvancementRequirements.Strategy.OR) + .addCriterion(criterionName, criterion) exporter.accept( recipeKey, recipe, - advancement.build(recipeKey.value.withPrefixedPath("recipes/${RecipeCategory.DECORATIONS.name.lowercase()}/")), + //? if >=1.21.11 { + /*advancement.build(recipeKey.identifier().withPrefix("recipes/${RecipeCategory.DECORATIONS.name.lowercase()}/")),*/ + //?} else { + advancement.build(recipeKey.location().withPrefix("recipes/${RecipeCategory.DECORATIONS.name.lowercase()}/")), + //?} ) } } @@ -492,10 +507,10 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { private class EnglishLanguageProvider( output: FabricDataOutput, - registriesFuture: CompletableFuture, + registriesFuture: CompletableFuture, ) : FabricLanguageProvider(output, registriesFuture) { override fun generateTranslations( - registryLookup: RegistryWrapper.WrapperLookup, + registryLookup: HolderLookup.Provider, builder: TranslationBuilder, ) { builder.add(CTBlocks.CONNECTED_TANK, "Tank") @@ -524,10 +539,10 @@ object ConnectedTankDataGenerator : DataGeneratorEntrypoint { private class JapaneseLanguageProvider( output: FabricDataOutput, - registriesFuture: CompletableFuture, + registriesFuture: CompletableFuture, ) : FabricLanguageProvider(output, "ja_jp", registriesFuture) { override fun generateTranslations( - registryLookup: RegistryWrapper.WrapperLookup, + registryLookup: HolderLookup.Provider, builder: TranslationBuilder, ) { builder.add(CTBlocks.CONNECTED_TANK, "タンク") diff --git a/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt b/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt index 5b0ead7..9991ffb 100644 --- a/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt +++ b/src/client/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntityRenderer.kt @@ -4,16 +4,16 @@ import kotlin.math.max import net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering //? if >=1.21.11 { /*import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant -import net.minecraft.client.render.block.entity.BlockEntityRenderer -import net.minecraft.client.render.block.entity.BlockEntityRendererFactory -import net.minecraft.client.render.block.entity.state.BlockEntityRenderState -import net.minecraft.client.render.command.ModelCommandRenderer.CrumblingOverlayCommand -import net.minecraft.client.render.command.OrderedRenderCommandQueue -import net.minecraft.client.render.state.CameraRenderState -import net.minecraft.client.texture.Sprite -import net.minecraft.client.util.math.MatrixStack -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer +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 +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 +import net.minecraft.world.phys.Vec3 import net.turtton.connectedtank.config.CTClientConfig import net.turtton.connectedtank.config.CTClientConfig.RenderQuality import net.turtton.connectedtank.render.FluidRenderHelper @@ -21,7 +21,7 @@ import net.turtton.connectedtank.render.NeighborMask import net.turtton.connectedtank.render.WaveParams class ConnectedTankRenderState : BlockEntityRenderState() { - var sprite: Sprite? = null + var sprite: TextureAtlasSprite? = null var argb: Int = 0 var localFillLevel: Float = 0f var wave: WaveParams = WaveParams(animTime = 0f, gridSize = 4) @@ -29,7 +29,7 @@ class ConnectedTankRenderState : BlockEntityRenderState() { } class ConnectedTankBlockEntityRenderer( - @Suppress("UNUSED_PARAMETER") context: BlockEntityRendererFactory.Context, + @Suppress("UNUSED_PARAMETER") context: BlockEntityRendererProvider.Context, ) : BlockEntityRenderer { companion object { private const val DECAY_TICKS = 60f @@ -37,14 +37,14 @@ class ConnectedTankBlockEntityRenderer( override fun createRenderState(): ConnectedTankRenderState = ConnectedTankRenderState() - override fun updateRenderState( + override fun extractRenderState( entity: ConnectedTankBlockEntity, state: ConnectedTankRenderState, tickDelta: Float, - cameraPos: Vec3d, - crumbling: CrumblingOverlayCommand?, + cameraPos: Vec3, + crumbling: ModelFeatureRenderer.CrumblingOverlay?, ) { - super.updateRenderState(entity, state, tickDelta, cameraPos, crumbling) + super.extractRenderState(entity, state, tickDelta, cameraPos, crumbling) state.localFillLevel = entity.localFillLevel if (entity.localFillLevel <= 0f || entity.fluidVariant.isBlank) { @@ -56,8 +56,8 @@ class ConnectedTankBlockEntityRenderer( val color = FluidVariantRendering.getColor(entity.fluidVariant) state.argb = (0xFF shl 24) or (color and 0x00FFFFFF) - val world = entity.world - val pos = entity.pos + val world = entity.level + val pos = entity.blockPos val myGroupId = entity.groupId fun sameGroupNeighbor(neighborPos: BlockPos): ConnectedTankBlockEntity? { @@ -66,8 +66,8 @@ class ConnectedTankBlockEntityRenderer( return if (neighbor.groupId == myGroupId) neighbor else null } - val neighborDown = sameGroupNeighbor(pos.down()) - val neighborUp = sameGroupNeighbor(pos.up()) + val neighborDown = sameGroupNeighbor(pos.below()) + val neighborUp = sameGroupNeighbor(pos.above()) // 垂直: 下タンクが満杯で同一グループのときのみ連続とみなす val hasDown = neighborDown != null && neighborDown.localFillLevel >= 1.0f @@ -89,7 +89,7 @@ class ConnectedTankBlockEntityRenderer( ) val quality = CTClientConfig.instance.renderQuality - val worldTime = world?.time?.toFloat() ?: 0f + val worldTime = world?.gameTime?.toFloat() ?: 0f val elapsedTicks = worldTime - entity.waveStartTick.toFloat() val decayFactor = if (quality == RenderQuality.LOW) 0f else max(0f, 1f - elapsedTicks / DECAY_TICKS) val animTime = worldTime + tickDelta @@ -109,16 +109,16 @@ class ConnectedTankBlockEntityRenderer( ) } - override fun render( + override fun submit( state: ConnectedTankRenderState, - matrices: MatrixStack, - queue: OrderedRenderCommandQueue, + matrices: PoseStack, + queue: SubmitNodeCollector, cameraState: CameraRenderState, ) { val sprite = state.sprite ?: return if (state.localFillLevel <= 0f) return - matrices.push() + matrices.pushPose() FluidRenderHelper.renderFluid( queue, matrices, @@ -128,17 +128,17 @@ class ConnectedTankBlockEntityRenderer( state.wave, state.neighbors, ) - matrices.pop() + matrices.popPose() } } */ //?} else { -import net.minecraft.client.render.VertexConsumerProvider -import net.minecraft.client.render.block.entity.BlockEntityRenderer -import net.minecraft.client.render.block.entity.BlockEntityRendererFactory -import net.minecraft.client.util.math.MatrixStack -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider +import com.mojang.blaze3d.vertex.PoseStack +import net.minecraft.core.BlockPos +import net.minecraft.world.phys.Vec3 import net.turtton.connectedtank.config.CTClientConfig import net.turtton.connectedtank.config.CTClientConfig.RenderQuality import net.turtton.connectedtank.render.FluidRenderHelper @@ -146,7 +146,7 @@ import net.turtton.connectedtank.render.NeighborMask import net.turtton.connectedtank.render.WaveParams class ConnectedTankBlockEntityRenderer( - @Suppress("UNUSED_PARAMETER") context: BlockEntityRendererFactory.Context, + @Suppress("UNUSED_PARAMETER") context: BlockEntityRendererProvider.Context, ) : BlockEntityRenderer { companion object { private const val DECAY_TICKS = 60f @@ -155,11 +155,11 @@ class ConnectedTankBlockEntityRenderer( override fun render( entity: ConnectedTankBlockEntity, tickDelta: Float, - matrices: MatrixStack, - vertexConsumers: VertexConsumerProvider, + matrices: PoseStack, + vertexConsumers: MultiBufferSource, light: Int, overlay: Int, - cameraPos: Vec3d, + cameraPos: Vec3, ) { if (entity.localFillLevel <= 0f || entity.fluidVariant.isBlank) return @@ -167,8 +167,8 @@ class ConnectedTankBlockEntityRenderer( val color = FluidVariantRendering.getColor(entity.fluidVariant) val argb = (0xFF shl 24) or (color and 0x00FFFFFF) - val world = entity.world - val pos = entity.pos + val world = entity.level + val pos = entity.blockPos val myGroupId = entity.groupId fun sameGroupNeighbor(neighborPos: BlockPos): ConnectedTankBlockEntity? { @@ -177,8 +177,8 @@ class ConnectedTankBlockEntityRenderer( return if (neighbor.groupId == myGroupId) neighbor else null } - val neighborDown = sameGroupNeighbor(pos.down()) - val neighborUp = sameGroupNeighbor(pos.up()) + val neighborDown = sameGroupNeighbor(pos.below()) + val neighborUp = sameGroupNeighbor(pos.above()) // 垂直: 下タンクが満杯で同一グループのときのみ連続とみなす val hasDown = neighborDown != null && neighborDown.localFillLevel >= 1.0f @@ -190,10 +190,10 @@ class ConnectedTankBlockEntityRenderer( val hasWest = sameGroupNeighbor(pos.west())?.let { it.localFillLevel > 0f } == true val hasEast = sameGroupNeighbor(pos.east())?.let { it.localFillLevel > 0f } == true - matrices.push() + matrices.pushPose() val quality = CTClientConfig.instance.renderQuality - val worldTime = world?.time?.toFloat() ?: 0f + val worldTime = world?.gameTime?.toFloat() ?: 0f val elapsedTicks = worldTime - entity.waveStartTick.toFloat() val decayFactor = if (quality == RenderQuality.LOW) 0f else max(0f, 1f - elapsedTicks / DECAY_TICKS) @@ -228,7 +228,7 @@ class ConnectedTankBlockEntityRenderer( ), ) - matrices.pop() + matrices.popPose() } } //?} diff --git a/src/client/kotlin/net/turtton/connectedtank/config/CTConfigScreen.kt b/src/client/kotlin/net/turtton/connectedtank/config/CTConfigScreen.kt index e211e2b..9950a97 100644 --- a/src/client/kotlin/net/turtton/connectedtank/config/CTConfigScreen.kt +++ b/src/client/kotlin/net/turtton/connectedtank/config/CTConfigScreen.kt @@ -2,7 +2,7 @@ package net.turtton.connectedtank.config import com.terraformersmc.modmenu.api.ConfigScreenFactory import com.terraformersmc.modmenu.api.ModMenuApi -import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screens.Screen import net.turtton.connectedtank.ConnectedTank import net.turtton.connectedtank.block.TankTier import net.turtton.connectedtank.network.ConfigSyncPayload @@ -20,8 +20,8 @@ class CTConfigScreen : ModMenuApi { private object CTConfigScreenBuilder { fun createConfigScreen(parent: Screen?): Screen { - val client = net.minecraft.client.MinecraftClient.getInstance() - val isExternalServer = client.world != null && !client.isInSingleplayer + val client = net.minecraft.client.Minecraft.getInstance() + val isExternalServer = client.level != null && !client.isLocalServer val serverConfig = if (isExternalServer) { SyncedServerConfig.syncedConfig ?: CTServerConfig.instance @@ -30,10 +30,10 @@ private object CTConfigScreenBuilder { } val tankCapacityOption = dev.isxander.yacl3.api.Option.createBuilder() - .name(net.minecraft.text.Text.translatable("config.connectedtank.server.tankBucketCapacity")) + .name(net.minecraft.network.chat.Component.translatable("config.connectedtank.server.tankBucketCapacity")) .description( dev.isxander.yacl3.api.OptionDescription.of( - net.minecraft.text.Text.translatable("config.connectedtank.server.tankBucketCapacity.description"), + net.minecraft.network.chat.Component.translatable("config.connectedtank.server.tankBucketCapacity.description"), ), ) .binding( @@ -52,10 +52,10 @@ private object CTConfigScreenBuilder { val clientConfig = CTClientConfig.instance val renderQualityOption = dev.isxander.yacl3.api.Option.createBuilder() - .name(net.minecraft.text.Text.translatable("config.connectedtank.client.renderQuality")) + .name(net.minecraft.network.chat.Component.translatable("config.connectedtank.client.renderQuality")) .description( dev.isxander.yacl3.api.OptionDescription.of( - net.minecraft.text.Text.translatable("config.connectedtank.client.renderQuality.description"), + net.minecraft.network.chat.Component.translatable("config.connectedtank.client.renderQuality.description"), ), ) .binding( @@ -71,10 +71,10 @@ private object CTConfigScreenBuilder { val tierMultiplierOptions = TankTier.entries.map { tier -> dev.isxander.yacl3.api.Option.createBuilder() - .name(net.minecraft.text.Text.literal("${tier.name} multiplier")) + .name(net.minecraft.network.chat.Component.literal("${tier.name} multiplier")) .description( dev.isxander.yacl3.api.OptionDescription.of( - net.minecraft.text.Text.literal("Capacity multiplier for ${tier.name} tier (default: ${tier.defaultMultiplier})"), + net.minecraft.network.chat.Component.literal("Capacity multiplier for ${tier.name} tier (default: ${tier.defaultMultiplier})"), ), ) .binding( @@ -92,25 +92,25 @@ private object CTConfigScreenBuilder { } val serverCategoryBuilder = dev.isxander.yacl3.api.ConfigCategory.createBuilder() - .name(net.minecraft.text.Text.translatable("config.connectedtank.category.server")) + .name(net.minecraft.network.chat.Component.translatable("config.connectedtank.category.server")) .option(tankCapacityOption) for (option in tierMultiplierOptions) { serverCategoryBuilder.option(option) } return dev.isxander.yacl3.api.YetAnotherConfigLib.createBuilder() - .title(net.minecraft.text.Text.translatable("config.connectedtank.title")) + .title(net.minecraft.network.chat.Component.translatable("config.connectedtank.title")) .category(serverCategoryBuilder.build()) .category( dev.isxander.yacl3.api.ConfigCategory.createBuilder() - .name(net.minecraft.text.Text.translatable("config.connectedtank.category.client")) + .name(net.minecraft.network.chat.Component.translatable("config.connectedtank.category.client")) .option(renderQualityOption) .build(), ) .save { if (!isExternalServer) { CTServerConfig.instance.save() - client.server?.let { server -> + client.singleplayerServer?.let { server -> server.execute { ConfigSyncPayload.broadcastToAll(server) } } } diff --git a/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt b/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt index fe66e49..1b20423 100644 --- a/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt +++ b/src/client/kotlin/net/turtton/connectedtank/item/ConnectedTankItemRenderer.kt @@ -5,15 +5,15 @@ import net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants //? if >=1.21.11 { /*import java.util.function.Consumer -import net.minecraft.client.render.RenderLayers -import net.minecraft.client.render.command.OrderedRenderCommandQueue -import net.minecraft.client.render.item.model.special.SpecialModelRenderer -import net.minecraft.client.render.item.model.special.SpecialModelTypes -import net.minecraft.client.util.math.MatrixStack -import net.minecraft.item.BlockItem -import net.minecraft.item.ItemDisplayContext -import net.minecraft.item.ItemStack -import net.minecraft.util.Identifier +import net.minecraft.client.renderer.rendertype.RenderTypes +import net.minecraft.client.renderer.SubmitNodeCollector +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 +import net.minecraft.world.item.ItemDisplayContext +import net.minecraft.world.item.ItemStack +import net.minecraft.resources.Identifier import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.component.CTDataComponentTypes import net.turtton.connectedtank.config.CTClientConfig @@ -25,13 +25,13 @@ import net.turtton.connectedtank.render.WaveParams import org.joml.Vector3fc class ConnectedTankItemRenderer : SpecialModelRenderer { - override fun getData(stack: ItemStack): ItemStack? = stack + override fun extractArgument(stack: ItemStack): ItemStack? = stack - override fun render( + override fun submit( data: ItemStack?, displayContext: ItemDisplayContext, - matrices: MatrixStack, - queue: OrderedRenderCommandQueue, + matrices: PoseStack, + queue: SubmitNodeCollector, light: Int, overlay: Int, glint: Boolean, @@ -54,7 +54,7 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { val quality = CTClientConfig.instance.renderQuality val gridSize = if (quality == RenderQuality.HIGH) 8 else 4 - matrices.push() + matrices.pushPose() try { FluidRenderHelper.renderFluid( queue, @@ -63,21 +63,21 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { argb, fillLevel, WaveParams(animTime = 0f, gridSize = gridSize), - renderLayer = RenderLayers.itemEntityTranslucentCull(sprite.atlasId), + renderLayer = RenderTypes.itemEntityTranslucentCull(sprite.atlasLocation()), ) } finally { - matrices.pop() + matrices.popPose() } } - override fun collectVertices(vertices: Consumer) { + override fun getExtents(vertices: Consumer) { } class Unbaked : SpecialModelRenderer.Unbaked { - override fun bake(context: SpecialModelRenderer.BakeContext): SpecialModelRenderer<*> = + override fun bake(context: SpecialModelRenderer.BakingContext): SpecialModelRenderer<*> = ConnectedTankItemRenderer() - override fun getCodec(): MapCodec = CODEC + override fun type(): MapCodec = CODEC companion object { val CODEC: MapCodec = MapCodec.unit(Unbaked()) @@ -85,25 +85,29 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { } companion object { - val ID: Identifier = Identifier.of("connectedtank", "tank_fluid") + val ID: Identifier = Identifier.fromNamespaceAndPath("connectedtank", "tank_fluid") fun register() { - SpecialModelTypes.ID_MAPPER.put(ID, Unbaked.CODEC) + SpecialModelRenderers.ID_MAPPER.put(ID, Unbaked.CODEC) } } } */ //?} else { -import net.minecraft.client.render.RenderLayer -import net.minecraft.client.render.VertexConsumerProvider -import net.minecraft.client.render.entity.model.LoadedEntityModels -import net.minecraft.client.render.item.model.special.SpecialModelRenderer -import net.minecraft.client.render.item.model.special.SpecialModelTypes -import net.minecraft.client.util.math.MatrixStack -import net.minecraft.item.BlockItem -import net.minecraft.item.ItemDisplayContext -import net.minecraft.item.ItemStack -import net.minecraft.util.Identifier +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.model.geom.EntityModelSet +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 +import net.minecraft.world.item.ItemDisplayContext +import net.minecraft.world.item.ItemStack +//? if >=1.21.11 { +/*import net.minecraft.resources.Identifier as ResourceLocation*/ +//?} else { +import net.minecraft.resources.ResourceLocation +//?} import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.component.CTDataComponentTypes import net.turtton.connectedtank.config.CTClientConfig @@ -115,13 +119,13 @@ import net.turtton.connectedtank.render.WaveParams import org.joml.Vector3f class ConnectedTankItemRenderer : SpecialModelRenderer { - override fun getData(stack: ItemStack): ItemStack? = stack + override fun extractArgument(stack: ItemStack): ItemStack? = stack override fun render( data: ItemStack?, displayContext: ItemDisplayContext, - matrices: MatrixStack, - vertexConsumers: VertexConsumerProvider, + matrices: PoseStack, + vertexConsumers: MultiBufferSource, light: Int, overlay: Int, glint: Boolean, @@ -143,7 +147,7 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { val quality = CTClientConfig.instance.renderQuality val gridSize = if (quality == RenderQuality.HIGH) 8 else 4 - matrices.push() + matrices.pushPose() try { FluidRenderHelper.renderFluid( vertexConsumers, @@ -152,20 +156,20 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { argb, fillLevel, WaveParams(animTime = 0f, gridSize = gridSize), - renderLayer = RenderLayer.getItemEntityTranslucentCull(sprite.atlasId), + renderLayer = RenderType.itemEntityTranslucentCull(sprite.atlasLocation()), ) } finally { - matrices.pop() + matrices.popPose() } } - override fun collectVertices(vertices: MutableSet) { + override fun getExtents(vertices: MutableSet) { } class Unbaked : SpecialModelRenderer.Unbaked { - override fun bake(entityModels: LoadedEntityModels): SpecialModelRenderer<*> = ConnectedTankItemRenderer() + override fun bake(entityModels: EntityModelSet): SpecialModelRenderer<*> = ConnectedTankItemRenderer() - override fun getCodec(): MapCodec = CODEC + override fun type(): MapCodec = CODEC companion object { val CODEC: MapCodec = MapCodec.unit(Unbaked()) @@ -173,10 +177,10 @@ class ConnectedTankItemRenderer : SpecialModelRenderer { } companion object { - val ID: Identifier = Identifier.of("connectedtank", "tank_fluid") + val ID: ResourceLocation = ResourceLocation.fromNamespaceAndPath("connectedtank", "tank_fluid") fun register() { - SpecialModelTypes.ID_MAPPER.put(ID, Unbaked.CODEC) + SpecialModelRenderers.ID_MAPPER.put(ID, Unbaked.CODEC) } } } diff --git a/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt b/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt index e0cd69c..dc7b087 100644 --- a/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt +++ b/src/client/kotlin/net/turtton/connectedtank/render/FluidRenderHelper.kt @@ -1,19 +1,20 @@ package net.turtton.connectedtank.render import kotlin.math.sin -import net.minecraft.client.render.LightmapTextureManager -import net.minecraft.client.render.OverlayTexture -import net.minecraft.client.render.RenderLayer -import net.minecraft.client.render.VertexConsumer +import net.minecraft.client.renderer.LightTexture +import net.minecraft.client.renderer.texture.OverlayTexture +import com.mojang.blaze3d.vertex.VertexConsumer //? if >=1.21.11 { -/*import net.minecraft.client.render.RenderLayers -import net.minecraft.client.render.command.OrderedRenderCommandQueue +/*import net.minecraft.client.renderer.rendertype.RenderType +import net.minecraft.client.renderer.rendertype.RenderTypes +import net.minecraft.client.renderer.SubmitNodeCollector */ //?} else { -import net.minecraft.client.render.VertexConsumerProvider +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.MultiBufferSource //?} -import net.minecraft.client.texture.Sprite -import net.minecraft.client.util.math.MatrixStack +import net.minecraft.client.renderer.texture.TextureAtlasSprite +import com.mojang.blaze3d.vertex.PoseStack data class WaveParams( val animTime: Float, @@ -45,20 +46,20 @@ object FluidRenderHelper { fun renderFluid( //? if >=1.21.11 { - /*queue: OrderedRenderCommandQueue,*/ + /*queue: SubmitNodeCollector,*/ //?} else { - vertexConsumers: VertexConsumerProvider, + vertexConsumers: MultiBufferSource, //?} - matrices: MatrixStack, - sprite: Sprite, + matrices: PoseStack, + sprite: TextureAtlasSprite, argb: Int, fillLevel: Float, wave: WaveParams, neighbors: NeighborMask = NeighborMask(), //? if >=1.21.11 { - /*renderLayer: RenderLayer = RenderLayers.entityTranslucent(sprite.atlasId),*/ + /*renderLayer: RenderType = RenderTypes.entityTranslucent(sprite.atlasLocation()),*/ //?} else { - renderLayer: RenderLayer = RenderLayer.getEntityTranslucent(sprite.atlasId), + renderLayer: RenderType = RenderType.entityTranslucent(sprite.atlasLocation()), //?} ) { if (fillLevel <= 0f || wave.gridSize <= 0) return @@ -72,7 +73,7 @@ object FluidRenderHelper { val fluidTop = minY + (1f - INSET - minY) * fillLevel //? if >=1.21.11 { - /*queue.submitCustom(matrices, renderLayer) { entry, consumer -> + /*queue.submitCustomGeometry(matrices, renderLayer) { entry, consumer -> renderAnimatedFluid( consumer, entry, @@ -90,7 +91,7 @@ object FluidRenderHelper { }*/ //?} else { val consumer = vertexConsumers.getBuffer(renderLayer) - val entry = matrices.peek() + val entry = matrices.last() renderAnimatedFluid( consumer, @@ -112,8 +113,8 @@ object FluidRenderHelper { @Suppress("LongParameterList") private fun renderAnimatedFluid( consumer: VertexConsumer, - entry: MatrixStack.Entry, - sprite: Sprite, + entry: PoseStack.Pose, + sprite: TextureAtlasSprite, argb: Int, fluidTop: Float, wave: WaveParams, @@ -124,12 +125,12 @@ object FluidRenderHelper { maxZ: Float, neighbors: NeighborMask, ) { - val fullLight = LightmapTextureManager.MAX_LIGHT_COORDINATE - val ov = OverlayTexture.DEFAULT_UV - val u0 = sprite.minU - val u1 = sprite.maxU - val v0 = sprite.minV - val v1 = sprite.maxV + val fullLight = LightTexture.FULL_BRIGHT + val ov = OverlayTexture.NO_OVERLAY + val u0 = sprite.u0 + val u1 = sprite.u1 + val v0 = sprite.v0 + val v1 = sprite.v1 val maxY = 1f - INSET val gridSize = wave.gridSize @@ -402,7 +403,7 @@ object FluidRenderHelper { @Suppress("LongParameterList") private fun VertexConsumer.quadUV( - entry: MatrixStack.Entry, + entry: PoseStack.Pose, argb: Int, ov: Int, fullLight: Int, @@ -430,13 +431,13 @@ object FluidRenderHelper { ny: Float, nz: Float, ) { - vertex(entry, x1, y1, z1).color(argb).texture(su0, sv0).overlay(ov).light(fullLight) - .normal(entry, nx, ny, nz) - vertex(entry, x2, y2, z2).color(argb).texture(su1, sv1).overlay(ov).light(fullLight) - .normal(entry, nx, ny, nz) - vertex(entry, x3, y3, z3).color(argb).texture(su2, sv2).overlay(ov).light(fullLight) - .normal(entry, nx, ny, nz) - vertex(entry, x4, y4, z4).color(argb).texture(su3, sv3).overlay(ov).light(fullLight) - .normal(entry, nx, ny, nz) + addVertex(entry, x1, y1, z1).setColor(argb).setUv(su0, sv0).setOverlay(ov).setLight(fullLight) + .setNormal(entry, nx, ny, nz) + addVertex(entry, x2, y2, z2).setColor(argb).setUv(su1, sv1).setOverlay(ov).setLight(fullLight) + .setNormal(entry, nx, ny, nz) + addVertex(entry, x3, y3, z3).setColor(argb).setUv(su2, sv2).setOverlay(ov).setLight(fullLight) + .setNormal(entry, nx, ny, nz) + addVertex(entry, x4, y4, z4).setColor(argb).setUv(su3, sv3).setOverlay(ov).setLight(fullLight) + .setNormal(entry, nx, ny, nz) } } diff --git a/src/clientGametest/kotlin/net/turtton/connectedtank/test/ConnectedTankClientGameTest.kt b/src/clientGametest/kotlin/net/turtton/connectedtank/test/ConnectedTankClientGameTest.kt index 6bac831..0649585 100644 --- a/src/clientGametest/kotlin/net/turtton/connectedtank/test/ConnectedTankClientGameTest.kt +++ b/src/clientGametest/kotlin/net/turtton/connectedtank/test/ConnectedTankClientGameTest.kt @@ -6,12 +6,12 @@ import net.fabricmc.fabric.api.client.gametest.v1.context.TestServerContext 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.client.gui.screen.ingame.InventoryScreen -import net.minecraft.fluid.Fluids -import net.minecraft.item.ItemStack +import net.minecraft.client.gui.screens.inventory.InventoryScreen +import net.minecraft.world.level.material.Fluids +import net.minecraft.world.item.ItemStack import net.minecraft.server.MinecraftServer -import net.minecraft.util.math.BlockPos -import net.minecraft.world.World +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level import net.turtton.connectedtank.block.CTBlocks import net.turtton.connectedtank.block.TankFluidStorage import net.turtton.connectedtank.block.TankTier @@ -64,12 +64,12 @@ object ConnectedTankClientGameTest : FabricClientGameTest { private fun clearArea(server: TestServerContext, basePos: BlockPos, sizeX: Int, sizeY: Int, sizeZ: Int) { server.onServer { srv -> - val world = srv.getWorld(World.OVERWORLD)!! - val state = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val world = srv.getLevel(Level.OVERWORLD)!! + val state = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) for (x in 0 until sizeX) { for (y in 0 until sizeY) { for (z in 0 until sizeZ) { - val pos = basePos.add(x, y, z) + val pos = basePos.offset(x, y, z) if (state.getStorage(pos) != null) { state.removeStorage(pos) } @@ -86,9 +86,9 @@ object ConnectedTankClientGameTest : FabricClientGameTest { fluid: TankFluidStorage.ExistingData? = null, ) { server.onServer { srv -> - val world = srv.getWorld(World.OVERWORLD)!! - world.setBlockState(pos, CTBlocks.CONNECTED_TANK.defaultState) - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val world = srv.getLevel(Level.OVERWORLD)!! + world.setBlockAndUpdate(pos, CTBlocks.CONNECTED_TANK.defaultBlockState()) + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val storage = TankFluidStorage(fluid = fluid) persistentState.addStorage(pos, storage) CTBlocks.syncGroupBlockEntities(world, pos, persistentState) @@ -101,9 +101,9 @@ object ConnectedTankClientGameTest : FabricClientGameTest { fluid: TankFluidStorage.ExistingData, ) { server.onServer { srv -> - val world = srv.getWorld(World.OVERWORLD)!! - world.setBlockState(pos, CTBlocks.CONNECTED_TANK.defaultState) - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val world = srv.getLevel(Level.OVERWORLD)!! + world.setBlockAndUpdate(pos, CTBlocks.CONNECTED_TANK.defaultBlockState()) + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val storage = TankFluidStorage(fluid = fluid) persistentState.addIsolatedStorage(pos, storage) CTBlocks.syncGroupBlockEntities(world, pos, persistentState) @@ -117,8 +117,8 @@ object ConnectedTankClientGameTest : FabricClientGameTest { amount: Long, ) { server.onServer { srv -> - val world = srv.getWorld(World.OVERWORLD)!! - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val world = srv.getLevel(Level.OVERWORLD)!! + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val storage = persistentState.getStorage(pos) ?: error("Storage not found at $pos") Transaction.openOuter().use { tx -> storage.insert(variant, amount, tx) @@ -188,7 +188,7 @@ object ConnectedTankClientGameTest : FabricClientGameTest { private fun testVerticalConnectedTanks(context: ClientGameTestContext, server: TestServerContext) { clearArea(server, basePos, 3, 3, 3) val pos1 = basePos - val pos2 = basePos.up() + val pos2 = basePos.above() placeTank(server, pos1) placeTank(server, pos2) insertFluid(server, pos1, FluidVariant.of(Fluids.WATER), FluidConstants.BUCKET * TANK_CAPACITY * 2) @@ -201,7 +201,7 @@ object ConnectedTankClientGameTest : FabricClientGameTest { // 同一液体で下のタンクのみに液体がある場合、上面が正しく描画されることを確認 clearArea(server, basePos, 3, 3, 3) val pos1 = basePos - val pos2 = basePos.up() + val pos2 = basePos.above() placeTank(server, pos1) placeTank(server, pos2) // 下タンクの半分だけ液体を入れる(上タンクには液体なし) @@ -217,7 +217,7 @@ object ConnectedTankClientGameTest : FabricClientGameTest { // addIsolatedStorage で確実に別グループにする clearArea(server, basePos, 3, 3, 3) val pos1 = basePos - val pos2 = basePos.up() + val pos2 = basePos.above() placeTank(server, pos1) insertFluid(server, pos1, FluidVariant.of(Fluids.WATER), FluidConstants.BUCKET * TANK_CAPACITY) placeIsolatedTank( @@ -236,7 +236,7 @@ object ConnectedTankClientGameTest : FabricClientGameTest { // addIsolatedStorage でグループマージを回避して別グループを強制的に作る clearArea(server, basePos, 3, 3, 3) val pos1 = basePos - val pos2 = basePos.up() + val pos2 = basePos.above() placeTank(server, pos1) insertFluid(server, pos1, FluidVariant.of(Fluids.WATER), FluidConstants.BUCKET * TANK_CAPACITY) placeIsolatedTank( @@ -270,9 +270,9 @@ object ConnectedTankClientGameTest : FabricClientGameTest { context.waitTicks(5) server.onServer { srv -> - val player = srv.playerManager.playerList.firstOrNull() ?: return@onServer + val player = srv.playerList.players.firstOrNull() ?: return@onServer val inventory = player.inventory - inventory.clear() + inventory.clearContent() var slot = 0 val water = FluidVariant.of(Fluids.WATER) @@ -289,7 +289,7 @@ object ConnectedTankClientGameTest : FabricClientGameTest { TankFluidStorage.ExistingData(water, FluidConstants.BUCKET * tierCapacity / 2), ) } - if (slot < 36) inventory.setStack(slot++, halfStack) + if (slot < 36) inventory.setItem(slot++, halfStack) val fullStack = ItemStack(item).also { stack -> stack.set( @@ -297,7 +297,7 @@ object ConnectedTankClientGameTest : FabricClientGameTest { TankFluidStorage.ExistingData(water, FluidConstants.BUCKET * tierCapacity), ) } - if (slot < 36) inventory.setStack(slot++, fullStack) + if (slot < 36) inventory.setItem(slot++, fullStack) } } context.waitTicks(5) diff --git a/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt b/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt index d892eef..ff4edc8 100644 --- a/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt +++ b/src/gametest/kotlin/net/turtton/connectedtank/test/ConnectedTankGameTest.kt @@ -4,14 +4,14 @@ 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.block.Blocks -import net.minecraft.fluid.Fluids -import net.minecraft.item.ItemStack -import net.minecraft.test.TestContext -import net.minecraft.text.Text -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction -import net.minecraft.world.GameMode +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.material.Fluids +import net.minecraft.world.item.ItemStack +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.network.chat.Component +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.world.level.GameType import net.turtton.connectedtank.block.CTBlocks import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.block.TankFluidStorage @@ -22,7 +22,7 @@ import net.turtton.connectedtank.item.CTItems import net.turtton.connectedtank.world.FluidStoragePersistentState object ConnectedTankGameTest { - private fun TestContext.getFluidState(): FluidStoragePersistentState = getWorld().persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + private fun GameTestHelper.getFluidState(): FluidStoragePersistentState = level.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) /** * useStackOnBlock で指定位置にタンクを設置する。 @@ -30,136 +30,136 @@ object ConnectedTankGameTest { * @param tankPos タンクを置きたい相対座標 (y >= 2) * @param tier 設置するタンクのティア (デフォルト BASE) */ - private fun TestContext.placeTank(tankPos: BlockPos, tier: TankTier = TankTier.BASE) { - val basePos = tankPos.down() - setBlockState(basePos, Blocks.STONE) - val player = createMockPlayer(GameMode.SURVIVAL) + private fun GameTestHelper.placeTank(tankPos: BlockPos, tier: TankTier = TankTier.BASE) { + val basePos = tankPos.below() + setBlock(basePos, Blocks.STONE) + val player = makeMockPlayer(GameType.SURVIVAL) val item = CTItems.ALL_TANK_ITEMS.first { (CTBlocks.ALL_TANKS[CTItems.ALL_TANK_ITEMS.indexOf(it)] as? ConnectedTankBlock)?.tier == tier } val stack = ItemStack(item) - useStackOnBlock(player, stack, basePos.down(), Direction.UP) + placeAt(player, stack, basePos.below(), Direction.UP) } @GameTest - fun placeSingleTank(context: TestContext) { + fun placeSingleTank(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) context.placeTank(tankPos) val state = context.getFluidState() - val storage = state.getStorage(context.getAbsolutePos(tankPos)) - context.assertTrue(storage != null, Text.literal("Storage should exist after placing tank")) - context.assertTrue(storage!!.amount == 0L, Text.literal("New tank should be empty")) + val storage = state.getStorage(context.absolutePos(tankPos)) + context.assertTrue(storage != null, Component.literal("Storage should exist after placing tank")) + context.assertTrue(storage!!.amount == 0L, Component.literal("New tank should be empty")) context.assertTrue( storage.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY, - Text.literal("Single tank capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} buckets"), + Component.literal("Single tank capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} buckets"), ) - context.complete() + context.succeed() } @GameTest - fun placeAdjacentTanksShareStorage(context: TestContext) { + fun placeAdjacentTanksShareStorage(context: GameTestHelper) { val pos1 = BlockPos(0, 2, 0) val pos2 = BlockPos(1, 2, 0) context.placeTank(pos1) context.placeTank(pos2) val state = context.getFluidState() - val storage1 = state.getStorage(context.getAbsolutePos(pos1)) - val storage2 = state.getStorage(context.getAbsolutePos(pos2)) - context.assertTrue(storage1 != null, Text.literal("Storage1 should exist")) - context.assertTrue(storage2 != null, Text.literal("Storage2 should exist")) + val storage1 = state.getStorage(context.absolutePos(pos1)) + val storage2 = state.getStorage(context.absolutePos(pos2)) + context.assertTrue(storage1 != null, Component.literal("Storage1 should exist")) + context.assertTrue(storage2 != null, Component.literal("Storage2 should exist")) context.assertTrue( storage1 === storage2, - Text.literal("Adjacent tanks should share the same storage instance"), + Component.literal("Adjacent tanks should share the same storage instance"), ) context.assertTrue( storage1!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY * 2, - Text.literal("Combined tank capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 2} buckets"), + Component.literal("Combined tank capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 2} buckets"), ) - context.complete() + context.succeed() } @GameTest - fun removeTankFromCombinedReducesCapacity(context: TestContext) { + fun removeTankFromCombinedReducesCapacity(context: GameTestHelper) { val pos1 = BlockPos(0, 2, 0) val pos2 = BlockPos(1, 2, 0) context.placeTank(pos1) context.placeTank(pos2) val state = context.getFluidState() - state.removeStorage(context.getAbsolutePos(pos2), context.getWorld()) + state.removeStorage(context.absolutePos(pos2), context.level) - val remaining = state.getStorage(context.getAbsolutePos(pos1)) - context.assertTrue(remaining != null, Text.literal("Remaining storage should exist")) + val remaining = state.getStorage(context.absolutePos(pos1)) + context.assertTrue(remaining != null, Component.literal("Remaining storage should exist")) context.assertTrue( remaining!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY, - Text.literal("Capacity should be reduced to ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} buckets after removing one tank"), + Component.literal("Capacity should be reduced to ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} buckets after removing one tank"), ) - val removed = state.getStorage(context.getAbsolutePos(pos2)) - context.assertTrue(removed == null, Text.literal("Removed position should have no storage")) - context.complete() + val removed = state.getStorage(context.absolutePos(pos2)) + context.assertTrue(removed == null, Component.literal("Removed position should have no storage")) + context.succeed() } @GameTest - fun fluidInsertionPersists(context: TestContext) { + fun fluidInsertionPersists(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) context.placeTank(tankPos) val state = context.getFluidState() - val storage = state.getStorage(context.getAbsolutePos(tankPos))!! + val storage = state.getStorage(context.absolutePos(tankPos))!! val water = FluidVariant.of(Fluids.WATER) Transaction.openOuter().use { transaction -> val inserted = storage.insert(water, FluidConstants.BUCKET, transaction) context.assertTrue( inserted == FluidConstants.BUCKET, - Text.literal("Should insert exactly 1 bucket"), + Component.literal("Should insert exactly 1 bucket"), ) transaction.commit() } - context.assertTrue(storage.amount == FluidConstants.BUCKET, Text.literal("Storage should contain 1 bucket")) - context.assertTrue(storage.variant == water, Text.literal("Storage should contain water")) - context.complete() + context.assertTrue(storage.amount == FluidConstants.BUCKET, Component.literal("Storage should contain 1 bucket")) + context.assertTrue(storage.variant == water, Component.literal("Storage should contain water")) + context.succeed() } @GameTest - fun disconnectedTanksHaveSeparateStorage(context: TestContext) { + fun disconnectedTanksHaveSeparateStorage(context: GameTestHelper) { val pos1 = BlockPos(0, 2, 0) val pos2 = BlockPos(2, 2, 0) // 1 block gap context.placeTank(pos1) context.placeTank(pos2) val state = context.getFluidState() - val storage1 = state.getStorage(context.getAbsolutePos(pos1)) - val storage2 = state.getStorage(context.getAbsolutePos(pos2)) - context.assertTrue(storage1 != null, Text.literal("Storage1 should exist")) - context.assertTrue(storage2 != null, Text.literal("Storage2 should exist")) + val storage1 = state.getStorage(context.absolutePos(pos1)) + val storage2 = state.getStorage(context.absolutePos(pos2)) + context.assertTrue(storage1 != null, Component.literal("Storage1 should exist")) + context.assertTrue(storage2 != null, Component.literal("Storage2 should exist")) context.assertTrue( storage1 !== storage2, - Text.literal("Non-adjacent tanks should have separate storage"), + Component.literal("Non-adjacent tanks should have separate storage"), ) - context.complete() + context.succeed() } @GameTest - fun removeAllTanksRemovesStorage(context: TestContext) { + fun removeAllTanksRemovesStorage(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) context.placeTank(tankPos) val state = context.getFluidState() - val absPos = context.getAbsolutePos(tankPos) - context.assertTrue(state.getStorage(absPos) != null, Text.literal("Storage should exist")) + val absPos = context.absolutePos(tankPos) + context.assertTrue(state.getStorage(absPos) != null, Component.literal("Storage should exist")) - state.removeStorage(absPos, context.getWorld()) - context.assertTrue(state.getStorage(absPos) == null, Text.literal("Storage should be removed")) - context.complete() + state.removeStorage(absPos, context.level) + context.assertTrue(state.getStorage(absPos) == null, Component.literal("Storage should be removed")) + context.succeed() } @GameTest - fun placesBetweenTwoGroupsMergesThem(context: TestContext) { + fun placesBetweenTwoGroupsMergesThem(context: GameTestHelper) { // [Group A] [gap] [Group B] → [Group A] [New Tank] [Group B] → 1 group val posA = BlockPos(0, 2, 0) val posB = BlockPos(2, 2, 0) @@ -168,27 +168,27 @@ object ConnectedTankGameTest { context.placeTank(posB) val state = context.getFluidState() - val storageA = state.getStorage(context.getAbsolutePos(posA)) - val storageB = state.getStorage(context.getAbsolutePos(posB)) - context.assertTrue(storageA !== storageB, Text.literal("Groups should be separate before merge")) + val storageA = state.getStorage(context.absolutePos(posA)) + val storageB = state.getStorage(context.absolutePos(posB)) + context.assertTrue(storageA !== storageB, Component.literal("Groups should be separate before merge")) context.placeTank(posMid) - val sA = state.getStorage(context.getAbsolutePos(posA)) - val sMid = state.getStorage(context.getAbsolutePos(posMid)) - val sB = state.getStorage(context.getAbsolutePos(posB)) - context.assertTrue(sA != null, Text.literal("Storage A should exist")) - context.assertTrue(sA === sMid, Text.literal("A and Mid should share storage")) - context.assertTrue(sA === sB, Text.literal("A and B should share storage after merge")) + val sA = state.getStorage(context.absolutePos(posA)) + val sMid = state.getStorage(context.absolutePos(posMid)) + val sB = state.getStorage(context.absolutePos(posB)) + context.assertTrue(sA != null, Component.literal("Storage A should exist")) + context.assertTrue(sA === sMid, Component.literal("A and Mid should share storage")) + context.assertTrue(sA === sB, Component.literal("A and B should share storage after merge")) context.assertTrue( sA!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3, - Text.literal("Merged capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3} buckets but was ${sA.bucketCapacity}"), + Component.literal("Merged capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3} buckets but was ${sA.bucketCapacity}"), ) - context.complete() + context.succeed() } @GameTest - fun mergeGroupsPreservesFluidAmount(context: TestContext) { + fun mergeGroupsPreservesFluidAmount(context: GameTestHelper) { val posA = BlockPos(0, 2, 0) val posB = BlockPos(2, 2, 0) context.placeTank(posA) @@ -197,12 +197,12 @@ object ConnectedTankGameTest { val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storageA = state.getStorage(context.getAbsolutePos(posA))!! + val storageA = state.getStorage(context.absolutePos(posA))!! Transaction.openOuter().use { transaction -> storageA.insert(water, FluidConstants.BUCKET * 2, transaction) transaction.commit() } - val storageB = state.getStorage(context.getAbsolutePos(posB))!! + val storageB = state.getStorage(context.absolutePos(posB))!! Transaction.openOuter().use { transaction -> storageB.insert(water, FluidConstants.BUCKET * 3, transaction) transaction.commit() @@ -211,17 +211,17 @@ object ConnectedTankGameTest { val posMid = BlockPos(1, 2, 0) context.placeTank(posMid) - val merged = state.getStorage(context.getAbsolutePos(posA))!! + val merged = state.getStorage(context.absolutePos(posA))!! context.assertTrue( merged.amount == FluidConstants.BUCKET * 5, - Text.literal("Merged amount should be 5 buckets but was ${merged.amount / FluidConstants.BUCKET}"), + Component.literal("Merged amount should be 5 buckets but was ${merged.amount / FluidConstants.BUCKET}"), ) - context.assertTrue(merged.variant == water, Text.literal("Merged variant should be water")) - context.complete() + context.assertTrue(merged.variant == water, Component.literal("Merged variant should be water")) + context.succeed() } @GameTest - fun incompatibleGroupsConnectToPriority(context: TestContext) { + fun incompatibleGroupsConnectToPriority(context: GameTestHelper) { // 水タンクと溶岩タンクの間に空タンクを置くと、座標優先度で水側に接続 val posA = BlockPos(0, 2, 0) val posB = BlockPos(2, 2, 0) @@ -231,7 +231,7 @@ object ConnectedTankGameTest { val water = FluidVariant.of(Fluids.WATER) val lava = FluidVariant.of(Fluids.LAVA) - val storageA = state.getStorage(context.getAbsolutePos(posA))!! + val storageA = state.getStorage(context.absolutePos(posA))!! Transaction.openOuter().use { transaction -> storageA.insert(water, FluidConstants.BUCKET, transaction) transaction.commit() @@ -241,29 +241,29 @@ object ConnectedTankGameTest { CTServerConfig.DEFAULT_BUCKET_CAPACITY, TankFluidStorage.ExistingData(lava, FluidConstants.BUCKET), ) - state.addStorage(context.getAbsolutePos(posB), lavaStorage) + state.addStorage(context.absolutePos(posB), lavaStorage) val posMid = BlockPos(1, 2, 0) context.placeTank(posMid) - val sA = state.getStorage(context.getAbsolutePos(posA)) - val sMid = state.getStorage(context.getAbsolutePos(posMid)) - val sB = state.getStorage(context.getAbsolutePos(posB)) + val sA = state.getStorage(context.absolutePos(posA)) + val sMid = state.getStorage(context.absolutePos(posMid)) + val sB = state.getStorage(context.absolutePos(posB)) // 座標優先度: posA(0,2,0) < posB(2,2,0) → 空タンクは水グループに接続 - context.assertTrue(sA === sMid, Text.literal("Empty tank should connect to water group (higher priority)")) - context.assertTrue(sB !== sMid, Text.literal("Lava group should remain separate")) - context.assertTrue(sA!!.variant == water, Text.literal("A should still have water")) - context.assertTrue(sB!!.variant == lava, Text.literal("B should still have lava")) - context.complete() + context.assertTrue(sA === sMid, Component.literal("Empty tank should connect to water group (higher priority)")) + context.assertTrue(sB !== sMid, Component.literal("Lava group should remain separate")) + context.assertTrue(sA!!.variant == water, Component.literal("A should still have water")) + context.assertTrue(sB!!.variant == lava, Component.literal("B should still have lava")) + context.succeed() } @GameTest - fun differentFluidTanksDoNotMerge(context: TestContext) { + fun differentFluidTanksDoNotMerge(context: GameTestHelper) { val pos1 = BlockPos(0, 2, 0) context.placeTank(pos1) val state = context.getFluidState() - val storage1 = state.getStorage(context.getAbsolutePos(pos1))!! + val storage1 = state.getStorage(context.absolutePos(pos1))!! val water = FluidVariant.of(Fluids.WATER) Transaction.openOuter().use { transaction -> @@ -276,23 +276,23 @@ object ConnectedTankGameTest { CTServerConfig.DEFAULT_BUCKET_CAPACITY, TankFluidStorage.ExistingData(FluidVariant.of(Fluids.LAVA), FluidConstants.BUCKET), ) - state.addStorage(context.getAbsolutePos(pos2), lavaStorage) + state.addStorage(context.absolutePos(pos2), lavaStorage) - val s1 = state.getStorage(context.getAbsolutePos(pos1)) - val s2 = state.getStorage(context.getAbsolutePos(pos2)) - context.assertTrue(s1 !== s2, Text.literal("Tanks with different fluids should not merge")) - context.assertTrue(s1!!.variant == water, Text.literal("First tank should still have water")) + val s1 = state.getStorage(context.absolutePos(pos1)) + val s2 = state.getStorage(context.absolutePos(pos2)) + context.assertTrue(s1 !== s2, Component.literal("Tanks with different fluids should not merge")) + context.assertTrue(s1!!.variant == water, Component.literal("First tank should still have water")) context.assertTrue( s2!!.variant == FluidVariant.of(Fluids.LAVA), - Text.literal("Second tank should have lava"), + Component.literal("Second tank should have lava"), ) - context.complete() + context.succeed() } // === 座標優先度・interactedAt テスト === @GameTest - fun interactedAtConnectsToSpecifiedGroup(context: TestContext) { + fun interactedAtConnectsToSpecifiedGroup(context: GameTestHelper) { // 水グループと溶岩グループの間で、interactedAt で溶岩側を指定 val posA = BlockPos(0, 2, 0) val posB = BlockPos(2, 2, 0) @@ -302,7 +302,7 @@ object ConnectedTankGameTest { val water = FluidVariant.of(Fluids.WATER) val lava = FluidVariant.of(Fluids.LAVA) - val storageA = state.getStorage(context.getAbsolutePos(posA))!! + val storageA = state.getStorage(context.absolutePos(posA))!! Transaction.openOuter().use { transaction -> storageA.insert(water, FluidConstants.BUCKET, transaction) transaction.commit() @@ -312,25 +312,25 @@ object ConnectedTankGameTest { CTServerConfig.DEFAULT_BUCKET_CAPACITY, TankFluidStorage.ExistingData(lava, FluidConstants.BUCKET), ) - state.addStorage(context.getAbsolutePos(posB), lavaStorage) + state.addStorage(context.absolutePos(posB), lavaStorage) // interactedAt で溶岩側 (posB) を指定して addStorage val posMid = BlockPos(1, 2, 0) val midStorage = TankFluidStorage(CTServerConfig.DEFAULT_BUCKET_CAPACITY) - state.addStorage(context.getAbsolutePos(posMid), midStorage, context.getAbsolutePos(posB)) + state.addStorage(context.absolutePos(posMid), midStorage, context.absolutePos(posB)) - val sA = state.getStorage(context.getAbsolutePos(posA)) - val sMid = state.getStorage(context.getAbsolutePos(posMid)) - val sB = state.getStorage(context.getAbsolutePos(posB)) - context.assertTrue(sB === sMid, Text.literal("Middle should connect to lava group via interactedAt")) - context.assertTrue(sA !== sMid, Text.literal("Water group should remain separate")) - context.assertTrue(sA!!.variant == water, Text.literal("A should still have water")) - context.assertTrue(sB!!.variant == lava, Text.literal("B+Mid should have lava")) - context.complete() + val sA = state.getStorage(context.absolutePos(posA)) + val sMid = state.getStorage(context.absolutePos(posMid)) + val sB = state.getStorage(context.absolutePos(posB)) + context.assertTrue(sB === sMid, Component.literal("Middle should connect to lava group via interactedAt")) + context.assertTrue(sA !== sMid, Component.literal("Water group should remain separate")) + context.assertTrue(sA!!.variant == water, Component.literal("A should still have water")) + context.assertTrue(sB!!.variant == lava, Component.literal("B+Mid should have lava")) + context.succeed() } @GameTest - fun interactedAtDoesNotMergeOtherGroups(context: TestContext) { + fun interactedAtDoesNotMergeOtherGroups(context: GameTestHelper) { // interactedAt 指定時、他の互換グループはマージしない val posA = BlockPos(0, 2, 0) val posB = BlockPos(2, 2, 0) @@ -338,25 +338,25 @@ object ConnectedTankGameTest { context.placeTank(posB) val state = context.getFluidState() - val sA = state.getStorage(context.getAbsolutePos(posA)) - val sB = state.getStorage(context.getAbsolutePos(posB)) - context.assertTrue(sA !== sB, Text.literal("Groups should be separate before placement")) + val sA = state.getStorage(context.absolutePos(posA)) + val sB = state.getStorage(context.absolutePos(posB)) + context.assertTrue(sA !== sB, Component.literal("Groups should be separate before placement")) // interactedAt で posB を指定 → posA のグループとはマージしない val posMid = BlockPos(1, 2, 0) val midStorage = TankFluidStorage(CTServerConfig.DEFAULT_BUCKET_CAPACITY) - state.addStorage(context.getAbsolutePos(posMid), midStorage, context.getAbsolutePos(posB)) + state.addStorage(context.absolutePos(posMid), midStorage, context.absolutePos(posB)) - val sA2 = state.getStorage(context.getAbsolutePos(posA)) - val sMid = state.getStorage(context.getAbsolutePos(posMid)) - val sB2 = state.getStorage(context.getAbsolutePos(posB)) - context.assertTrue(sB2 === sMid, Text.literal("Mid should connect to B")) - context.assertTrue(sA2 !== sMid, Text.literal("A should remain separate from Mid")) - context.complete() + val sA2 = state.getStorage(context.absolutePos(posA)) + val sMid = state.getStorage(context.absolutePos(posMid)) + val sB2 = state.getStorage(context.absolutePos(posB)) + context.assertTrue(sB2 === sMid, Component.literal("Mid should connect to B")) + context.assertTrue(sA2 !== sMid, Component.literal("A should remain separate from Mid")) + context.succeed() } @GameTest - fun coordinatePrioritySelectsLowestCoordinate(context: TestContext) { + fun coordinatePrioritySelectsLowestCoordinate(context: GameTestHelper) { // Y が低い方が優先される val posBottom = BlockPos(1, 2, 0) val posTop = BlockPos(1, 4, 0) @@ -367,12 +367,12 @@ object ConnectedTankGameTest { val water = FluidVariant.of(Fluids.WATER) val lava = FluidVariant.of(Fluids.LAVA) - val storageBottom = state.getStorage(context.getAbsolutePos(posBottom))!! + val storageBottom = state.getStorage(context.absolutePos(posBottom))!! Transaction.openOuter().use { tx -> storageBottom.insert(water, FluidConstants.BUCKET, tx) tx.commit() } - val storageTop = state.getStorage(context.getAbsolutePos(posTop))!! + val storageTop = state.getStorage(context.absolutePos(posTop))!! Transaction.openOuter().use { tx -> storageTop.insert(lava, FluidConstants.BUCKET, tx) tx.commit() @@ -382,18 +382,18 @@ object ConnectedTankGameTest { val posMid = BlockPos(1, 3, 0) context.placeTank(posMid) - val sBottom = state.getStorage(context.getAbsolutePos(posBottom)) - val sMid = state.getStorage(context.getAbsolutePos(posMid)) - val sTop = state.getStorage(context.getAbsolutePos(posTop)) - context.assertTrue(sBottom === sMid, Text.literal("Mid should connect to bottom (lower Y)")) - context.assertTrue(sTop !== sMid, Text.literal("Top should remain separate")) - context.complete() + val sBottom = state.getStorage(context.absolutePos(posBottom)) + val sMid = state.getStorage(context.absolutePos(posMid)) + val sTop = state.getStorage(context.absolutePos(posTop)) + context.assertTrue(sBottom === sMid, Component.literal("Mid should connect to bottom (lower Y)")) + context.assertTrue(sTop !== sMid, Component.literal("Top should remain separate")) + context.succeed() } // === 分断検出テスト === @GameTest - fun breakMiddleOfThreeSplitsIntoTwoGroups(context: TestContext) { + fun breakMiddleOfThreeSplitsIntoTwoGroups(context: GameTestHelper) { val posL = BlockPos(0, 2, 0) val posM = BlockPos(1, 2, 0) val posR = BlockPos(2, 2, 0) @@ -402,32 +402,32 @@ object ConnectedTankGameTest { context.placeTank(posR) val state = context.getFluidState() - val sAll = state.getStorage(context.getAbsolutePos(posL)) + val sAll = state.getStorage(context.absolutePos(posL)) context.assertTrue( sAll!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3, - Text.literal("3 tanks should have ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3} bucket capacity"), + Component.literal("3 tanks should have ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3} bucket capacity"), ) - state.removeStorage(context.getAbsolutePos(posM), context.getWorld()) + state.removeStorage(context.absolutePos(posM), context.level) - val sL = state.getStorage(context.getAbsolutePos(posL)) - val sR = state.getStorage(context.getAbsolutePos(posR)) - context.assertTrue(sL != null, Text.literal("Left storage should exist")) - context.assertTrue(sR != null, Text.literal("Right storage should exist")) - context.assertTrue(sL !== sR, Text.literal("Left and right should be separate groups")) + val sL = state.getStorage(context.absolutePos(posL)) + val sR = state.getStorage(context.absolutePos(posR)) + context.assertTrue(sL != null, Component.literal("Left storage should exist")) + context.assertTrue(sR != null, Component.literal("Right storage should exist")) + context.assertTrue(sL !== sR, Component.literal("Left and right should be separate groups")) context.assertTrue( sL!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY, - Text.literal("Left capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} but was ${sL.bucketCapacity}"), + Component.literal("Left capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} but was ${sL.bucketCapacity}"), ) context.assertTrue( sR!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY, - Text.literal("Right capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} but was ${sR.bucketCapacity}"), + Component.literal("Right capacity should be ${CTServerConfig.DEFAULT_BUCKET_CAPACITY} but was ${sR.bucketCapacity}"), ) - context.complete() + context.succeed() } @GameTest - fun breakCornerOfLShapeSplitsIntoTwo(context: TestContext) { + fun breakCornerOfLShapeSplitsIntoTwo(context: GameTestHelper) { // L 字: (0,2,0) - (1,2,0) - (1,2,1) val posA = BlockPos(0, 2, 0) val posCorner = BlockPos(1, 2, 0) @@ -437,18 +437,18 @@ object ConnectedTankGameTest { context.placeTank(posB) val state = context.getFluidState() - state.removeStorage(context.getAbsolutePos(posCorner), context.getWorld()) + state.removeStorage(context.absolutePos(posCorner), context.level) - val sA = state.getStorage(context.getAbsolutePos(posA)) - val sB = state.getStorage(context.getAbsolutePos(posB)) - context.assertTrue(sA != null, Text.literal("A should exist")) - context.assertTrue(sB != null, Text.literal("B should exist")) - context.assertTrue(sA !== sB, Text.literal("A and B should be separate after corner break")) - context.complete() + val sA = state.getStorage(context.absolutePos(posA)) + val sB = state.getStorage(context.absolutePos(posB)) + context.assertTrue(sA != null, Component.literal("A should exist")) + context.assertTrue(sB != null, Component.literal("B should exist")) + context.assertTrue(sA !== sB, Component.literal("A and B should be separate after corner break")) + context.succeed() } @GameTest - fun breakOneFrom2x2KeepsGroupConnected(context: TestContext) { + fun breakOneFrom2x2KeepsGroupConnected(context: GameTestHelper) { // 2x2: (0,2,0) (1,2,0) (0,2,1) (1,2,1) → 1 つ破壊 → 残り 3 つは連結 val pos00 = BlockPos(0, 2, 0) val pos10 = BlockPos(1, 2, 0) @@ -460,25 +460,25 @@ object ConnectedTankGameTest { context.placeTank(pos11) val state = context.getFluidState() - state.removeStorage(context.getAbsolutePos(pos11), context.getWorld()) + state.removeStorage(context.absolutePos(pos11), context.level) - val s00 = state.getStorage(context.getAbsolutePos(pos00)) - val s10 = state.getStorage(context.getAbsolutePos(pos10)) - val s01 = state.getStorage(context.getAbsolutePos(pos01)) - context.assertTrue(s00 != null, Text.literal("00 should exist")) - context.assertTrue(s00 === s10, Text.literal("00 and 10 should share storage")) - context.assertTrue(s00 === s01, Text.literal("00 and 01 should share storage")) + val s00 = state.getStorage(context.absolutePos(pos00)) + val s10 = state.getStorage(context.absolutePos(pos10)) + val s01 = state.getStorage(context.absolutePos(pos01)) + context.assertTrue(s00 != null, Component.literal("00 should exist")) + context.assertTrue(s00 === s10, Component.literal("00 and 10 should share storage")) + context.assertTrue(s00 === s01, Component.literal("00 and 01 should share storage")) context.assertTrue( s00!!.bucketCapacity == CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3, - Text.literal("Remaining 3 tanks should have ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3} bucket capacity but was ${s00.bucketCapacity}"), + Component.literal("Remaining 3 tanks should have ${CTServerConfig.DEFAULT_BUCKET_CAPACITY * 3} bucket capacity but was ${s00.bucketCapacity}"), ) - context.complete() + context.succeed() } // === 液体均等分配テスト === @GameTest - fun splitEvenFluidDistribution(context: TestContext) { + fun splitEvenFluidDistribution(context: GameTestHelper) { // 30 バケツ / 3 タンク → 破壊タンク 10, 残り各 10 val posL = BlockPos(0, 2, 0) val posM = BlockPos(1, 2, 0) @@ -489,35 +489,35 @@ object ConnectedTankGameTest { val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage = state.getStorage(context.getAbsolutePos(posL))!! + val storage = state.getStorage(context.absolutePos(posL))!! Transaction.openOuter().use { tx -> storage.insert(water, FluidConstants.BUCKET * 30, tx) tx.commit() } - val removedData = state.removeStorage(context.getAbsolutePos(posM), context.getWorld()) + val removedData = state.removeStorage(context.absolutePos(posM), context.level) - context.assertTrue(removedData != null, Text.literal("Removed data should not be null")) + context.assertTrue(removedData != null, Component.literal("Removed data should not be null")) context.assertTrue( removedData!!.amount == FluidConstants.BUCKET * 10, - Text.literal("Removed share should be 10 buckets but was ${removedData.amount / FluidConstants.BUCKET}"), + Component.literal("Removed share should be 10 buckets but was ${removedData.amount / FluidConstants.BUCKET}"), ) - val sL = state.getStorage(context.getAbsolutePos(posL)) - val sR = state.getStorage(context.getAbsolutePos(posR)) + val sL = state.getStorage(context.absolutePos(posL)) + val sR = state.getStorage(context.absolutePos(posR)) context.assertTrue( sL!!.amount == FluidConstants.BUCKET * 10, - Text.literal("Left should have 10 buckets but was ${sL.amount / FluidConstants.BUCKET}"), + Component.literal("Left should have 10 buckets but was ${sL.amount / FluidConstants.BUCKET}"), ) context.assertTrue( sR!!.amount == FluidConstants.BUCKET * 10, - Text.literal("Right should have 10 buckets but was ${sR.amount / FluidConstants.BUCKET}"), + Component.literal("Right should have 10 buckets but was ${sR.amount / FluidConstants.BUCKET}"), ) - context.complete() + context.succeed() } @GameTest - fun splitUnevenFluidDistribution(context: TestContext) { + fun splitUnevenFluidDistribution(context: GameTestHelper) { // droplet 単位で端数が出るケース: (10 buckets + 2 droplets) / 3 tanks val posL = BlockPos(0, 2, 0) val posM = BlockPos(1, 2, 0) @@ -529,42 +529,42 @@ object ConnectedTankGameTest { val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) val totalAmount = FluidConstants.BUCKET * 10 + 2 // 810002 droplets - val storage = state.getStorage(context.getAbsolutePos(posL))!! + val storage = state.getStorage(context.absolutePos(posL))!! Transaction.openOuter().use { tx -> storage.insert(water, totalAmount, tx) tx.commit() } - val removedData = state.removeStorage(context.getAbsolutePos(posM), context.getWorld()) + val removedData = state.removeStorage(context.absolutePos(posM), context.level) // 位置ベース分配: 同一 Y レベル・同一ティアなので累積丸めで按分 // 810002 * 2/3 = 540001 (cumulative for posM) - 270000 (posL) = 270001 val expectedRemoved = 270001L - context.assertTrue(removedData != null, Text.literal("Removed data should not be null")) + context.assertTrue(removedData != null, Component.literal("Removed data should not be null")) context.assertTrue( removedData!!.amount == expectedRemoved, - Text.literal("Removed share should be $expectedRemoved but was ${removedData.amount}"), + Component.literal("Removed share should be $expectedRemoved but was ${removedData.amount}"), ) // remaining = 810002 - 270001 = 540001, 2 tanks (同一 Y レベル) // 累積丸め: posL = 270000, posR = 270001 (または逆) - val sL = state.getStorage(context.getAbsolutePos(posL)) - val sR = state.getStorage(context.getAbsolutePos(posR)) + val sL = state.getStorage(context.absolutePos(posL)) + val sR = state.getStorage(context.absolutePos(posR)) val leftAmt = sL!!.amount val rightAmt = sR!!.amount context.assertTrue( leftAmt + rightAmt == 540001L, - Text.literal("Total remaining should be 540001 but was ${leftAmt + rightAmt}"), + Component.literal("Total remaining should be 540001 but was ${leftAmt + rightAmt}"), ) context.assertTrue( (leftAmt == 270001L && rightAmt == 270000L) || (leftAmt == 270000L && rightAmt == 270001L), - Text.literal("Amounts should be 270001+270000 but were $leftAmt+$rightAmt"), + Component.literal("Amounts should be 270001+270000 but were $leftAmt+$rightAmt"), ) - context.complete() + context.succeed() } @GameTest - fun noSplitFluidReduction(context: TestContext) { + fun noSplitFluidReduction(context: GameTestHelper) { // 2 タンクから 1 つ破壊 (分断なし: 隣接なので分断にはならない) val pos1 = BlockPos(0, 2, 0) val pos2 = BlockPos(1, 2, 0) @@ -573,66 +573,66 @@ object ConnectedTankGameTest { val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage = state.getStorage(context.getAbsolutePos(pos1))!! + val storage = state.getStorage(context.absolutePos(pos1))!! Transaction.openOuter().use { tx -> storage.insert(water, FluidConstants.BUCKET * 20, tx) tx.commit() } - val removedData = state.removeStorage(context.getAbsolutePos(pos2), context.getWorld()) + val removedData = state.removeStorage(context.absolutePos(pos2), context.level) - context.assertTrue(removedData != null, Text.literal("Removed data should not be null")) + context.assertTrue(removedData != null, Component.literal("Removed data should not be null")) context.assertTrue( removedData!!.amount == FluidConstants.BUCKET * 10, - Text.literal("Removed share should be 10 buckets but was ${removedData.amount / FluidConstants.BUCKET}"), + Component.literal("Removed share should be 10 buckets but was ${removedData.amount / FluidConstants.BUCKET}"), ) - val remaining = state.getStorage(context.getAbsolutePos(pos1)) + val remaining = state.getStorage(context.absolutePos(pos1)) context.assertTrue( remaining!!.amount == FluidConstants.BUCKET * 10, - Text.literal("Remaining should have 10 buckets but was ${remaining.amount / FluidConstants.BUCKET}"), + Component.literal("Remaining should have 10 buckets but was ${remaining.amount / FluidConstants.BUCKET}"), ) - context.complete() + context.succeed() } // === DataComponent テスト === @GameTest - fun removeStorageReturnsFluidData(context: TestContext) { + fun removeStorageReturnsFluidData(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.getAbsolutePos(tankPos))!! + val storage = state.getStorage(context.absolutePos(tankPos))!! Transaction.openOuter().use { tx -> storage.insert(water, FluidConstants.BUCKET * 5, tx) tx.commit() } - val result = state.removeStorage(context.getAbsolutePos(tankPos), context.getWorld()) - context.assertTrue(result != null, Text.literal("Should return ExistingData")) - context.assertTrue(result!!.variant == water, Text.literal("Variant should be water")) + val result = state.removeStorage(context.absolutePos(tankPos), context.level) + context.assertTrue(result != null, Component.literal("Should return ExistingData")) + context.assertTrue(result!!.variant == water, Component.literal("Variant should be water")) context.assertTrue( result.amount == FluidConstants.BUCKET * 5, - Text.literal("Amount should be 5 buckets but was ${result.amount / FluidConstants.BUCKET}"), + Component.literal("Amount should be 5 buckets but was ${result.amount / FluidConstants.BUCKET}"), ) - context.complete() + context.succeed() } @GameTest - fun removeEmptyStorageReturnsNull(context: TestContext) { + fun removeEmptyStorageReturnsNull(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) context.placeTank(tankPos) val state = context.getFluidState() - val result = state.removeStorage(context.getAbsolutePos(tankPos), context.getWorld()) - context.assertTrue(result == null, Text.literal("Empty tank should return null")) - context.complete() + val result = state.removeStorage(context.absolutePos(tankPos), context.level) + context.assertTrue(result == null, Component.literal("Empty tank should return null")) + context.succeed() } @GameTest - fun placeFluidTankRestoresStorage(context: TestContext) { + fun placeFluidTankRestoresStorage(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) val water = FluidVariant.of(Fluids.WATER) val fluidData = TankFluidStorage.ExistingData(water, FluidConstants.BUCKET * 5) @@ -640,27 +640,27 @@ object ConnectedTankGameTest { // DataComponent 付きタンクを直接 addStorage で追加 val state = context.getFluidState() val tankStorage = TankFluidStorage(fluid = fluidData) - state.addStorage(context.getAbsolutePos(tankPos), tankStorage) + state.addStorage(context.absolutePos(tankPos), tankStorage) - val restored = state.getStorage(context.getAbsolutePos(tankPos)) - context.assertTrue(restored != null, Text.literal("Restored storage should exist")) - context.assertTrue(restored!!.variant == water, Text.literal("Variant should be water")) + val restored = state.getStorage(context.absolutePos(tankPos)) + context.assertTrue(restored != null, Component.literal("Restored storage should exist")) + context.assertTrue(restored!!.variant == water, Component.literal("Variant should be water")) context.assertTrue( restored.amount == FluidConstants.BUCKET * 5, - Text.literal("Amount should be 5 buckets but was ${restored.amount / FluidConstants.BUCKET}"), + Component.literal("Amount should be 5 buckets but was ${restored.amount / FluidConstants.BUCKET}"), ) - context.complete() + context.succeed() } @GameTest - fun placeFluidTankMergesWithAdjacent(context: TestContext) { + fun placeFluidTankMergesWithAdjacent(context: GameTestHelper) { // 隣に水タンクがある状態で、水入りタンクを設置 → 液体量がマージされる val pos1 = BlockPos(0, 2, 0) context.placeTank(pos1) val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage1 = state.getStorage(context.getAbsolutePos(pos1))!! + val storage1 = state.getStorage(context.absolutePos(pos1))!! Transaction.openOuter().use { tx -> storage1.insert(water, FluidConstants.BUCKET * 3, tx) tx.commit() @@ -670,29 +670,29 @@ object ConnectedTankGameTest { val pos2 = BlockPos(1, 2, 0) val fluidData = TankFluidStorage.ExistingData(water, FluidConstants.BUCKET * 2) val newTankStorage = TankFluidStorage(fluid = fluidData) - state.addStorage(context.getAbsolutePos(pos2), newTankStorage) + state.addStorage(context.absolutePos(pos2), newTankStorage) - val merged = state.getStorage(context.getAbsolutePos(pos1)) - context.assertTrue(merged != null, Text.literal("Merged storage should exist")) + val merged = state.getStorage(context.absolutePos(pos1)) + context.assertTrue(merged != null, Component.literal("Merged storage should exist")) context.assertTrue( merged!!.amount == FluidConstants.BUCKET * 5, - Text.literal("Merged amount should be 5 buckets but was ${merged.amount / FluidConstants.BUCKET}"), + Component.literal("Merged amount should be 5 buckets but was ${merged.amount / FluidConstants.BUCKET}"), ) - context.assertTrue(merged.variant == water, Text.literal("Variant should be water")) - context.complete() + context.assertTrue(merged.variant == water, Component.literal("Variant should be water")) + context.succeed() } // === 垂直スタック位置ベース分配テスト === - private fun TestContext.placeVerticalTanks(vararg yPositions: Int, x: Int = 0, z: Int = 0): List { + private fun GameTestHelper.placeVerticalTanks(vararg yPositions: Int, x: Int = 0, z: Int = 0): List { val positions = yPositions.map { BlockPos(x, it, z) } val state = getFluidState() for (pos in positions) { - setBlockState(pos, CTBlocks.CONNECTED_TANK.defaultState) + setBlock(pos, CTBlocks.CONNECTED_TANK.defaultBlockState()) } // 下から順に addStorage して接続 for (pos in positions.sortedBy { it.y }) { - val absPos = getAbsolutePos(pos) + val absPos = absolutePos(pos) val storage = TankFluidStorage(CTServerConfig.DEFAULT_BUCKET_CAPACITY) state.addStorage(absPos, storage) } @@ -700,13 +700,13 @@ object ConnectedTankGameTest { } @GameTest - fun verticalStackBottomGetsMoreFluid(context: TestContext) { + fun verticalStackBottomGetsMoreFluid(context: GameTestHelper) { // 3 段積み: 48 バケツ (50%) → 下=32, 中=16, 上=0 val (posBottom, posMid, posTop) = context.placeVerticalTanks(2, 3, 4) val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage = state.getStorage(context.getAbsolutePos(posBottom))!! + val storage = state.getStorage(context.absolutePos(posBottom))!! val bucketCap = CTServerConfig.DEFAULT_BUCKET_CAPACITY.toLong() val totalAmount = bucketCap * FluidConstants.BUCKET / 2 * 3 // 50% of total capacity Transaction.openOuter().use { tx -> @@ -715,40 +715,40 @@ object ConnectedTankGameTest { } // 中間タンクを破壊 → 位置ベースで分配 - val removedData = state.removeStorage(context.getAbsolutePos(posMid), context.getWorld()) + val removedData = state.removeStorage(context.absolutePos(posMid), context.level) val expectedMid = (bucketCap / 2) * FluidConstants.BUCKET context.assertTrue( removedData != null, - Text.literal("Removed data should not be null"), + Component.literal("Removed data should not be null"), ) context.assertTrue( removedData!!.amount == expectedMid, - Text.literal("Mid share should be $expectedMid but was ${removedData.amount}"), + Component.literal("Mid share should be $expectedMid but was ${removedData.amount}"), ) - val sBottom = state.getStorage(context.getAbsolutePos(posBottom)) - val sTop = state.getStorage(context.getAbsolutePos(posTop)) + val sBottom = state.getStorage(context.absolutePos(posBottom)) + val sTop = state.getStorage(context.absolutePos(posTop)) val expectedBottom = bucketCap * FluidConstants.BUCKET context.assertTrue( sBottom!!.amount == expectedBottom, - Text.literal("Bottom should have $expectedBottom but was ${sBottom.amount}"), + Component.literal("Bottom should have $expectedBottom but was ${sBottom.amount}"), ) context.assertTrue( sTop!!.amount == 0L, - Text.literal("Top should have 0 but was ${sTop.amount}"), + Component.literal("Top should have 0 but was ${sTop.amount}"), ) - context.complete() + context.succeed() } @GameTest - fun verticalStackBreakBottomRedistributes(context: TestContext) { + fun verticalStackBreakBottomRedistributes(context: GameTestHelper) { // 3 段積み: 48 バケツ → 下を破壊 // 下=32, 中=16, 上=0 → 下の 32 バケツがドロップ, 残り 16 バケツは中と上に再分配 val (posBottom, posMid, posTop) = context.placeVerticalTanks(2, 3, 4) val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage = state.getStorage(context.getAbsolutePos(posBottom))!! + val storage = state.getStorage(context.absolutePos(posBottom))!! val bucketCap = CTServerConfig.DEFAULT_BUCKET_CAPACITY.toLong() val totalAmount = bucketCap * FluidConstants.BUCKET / 2 * 3 Transaction.openOuter().use { tx -> @@ -756,40 +756,40 @@ object ConnectedTankGameTest { tx.commit() } - val removedData = state.removeStorage(context.getAbsolutePos(posBottom), context.getWorld()) + val removedData = state.removeStorage(context.absolutePos(posBottom), context.level) val expectedBottom = bucketCap * FluidConstants.BUCKET context.assertTrue( removedData != null, - Text.literal("Removed data should not be null"), + Component.literal("Removed data should not be null"), ) context.assertTrue( removedData!!.amount == expectedBottom, - Text.literal("Bottom share should be $expectedBottom but was ${removedData.amount}"), + Component.literal("Bottom share should be $expectedBottom but was ${removedData.amount}"), ) // 残り 16 バケツ: 中と上は同一グループで共有ストレージ - val sMid = state.getStorage(context.getAbsolutePos(posMid)) - val sTop = state.getStorage(context.getAbsolutePos(posTop)) + val sMid = state.getStorage(context.absolutePos(posMid)) + val sTop = state.getStorage(context.absolutePos(posTop)) context.assertTrue( sMid === sTop, - Text.literal("Mid and Top should share the same storage"), + Component.literal("Mid and Top should share the same storage"), ) val expectedRemaining = (bucketCap / 2) * FluidConstants.BUCKET context.assertTrue( sMid!!.amount == expectedRemaining, - Text.literal("Remaining group should have $expectedRemaining but was ${sMid.amount}"), + Component.literal("Remaining group should have $expectedRemaining but was ${sMid.amount}"), ) - context.complete() + context.succeed() } @GameTest - fun verticalStackEmptyTopGetsNothing(context: TestContext) { + fun verticalStackEmptyTopGetsNothing(context: GameTestHelper) { // 2 段積み: 容量の 30% → 下のみに入り、上を破壊しても液体なし val (posBottom, posTop) = context.placeVerticalTanks(2, 3) val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage = state.getStorage(context.getAbsolutePos(posBottom))!! + val storage = state.getStorage(context.absolutePos(posBottom))!! val bucketCap = CTServerConfig.DEFAULT_BUCKET_CAPACITY.toLong() val amount = bucketCap * FluidConstants.BUCKET * 30 / 100 // 30% of single tank Transaction.openOuter().use { tx -> @@ -797,136 +797,136 @@ object ConnectedTankGameTest { tx.commit() } - val removedData = state.removeStorage(context.getAbsolutePos(posTop), context.getWorld()) + val removedData = state.removeStorage(context.absolutePos(posTop), context.level) context.assertTrue( removedData == null, - Text.literal("Top tank should have no fluid to return"), + Component.literal("Top tank should have no fluid to return"), ) - val sBottom = state.getStorage(context.getAbsolutePos(posBottom)) + val sBottom = state.getStorage(context.absolutePos(posBottom)) context.assertTrue( sBottom!!.amount == amount, - Text.literal("Bottom should retain all $amount but was ${sBottom.amount}"), + Component.literal("Bottom should retain all $amount but was ${sBottom.amount}"), ) - context.complete() + context.succeed() } // === ティア別容量テスト === @GameTest - fun tierCapacityMatchesMultiplier(context: TestContext) { + fun tierCapacityMatchesMultiplier(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) context.placeTank(tankPos, TankTier.IRON) val state = context.getFluidState() - val storage = state.getStorage(context.getAbsolutePos(tankPos)) + val storage = state.getStorage(context.absolutePos(tankPos)) val expectedCapacity = CTServerConfig.instance.getTierCapacity(TankTier.IRON) - context.assertTrue(storage != null, Text.literal("Storage should exist")) + context.assertTrue(storage != null, Component.literal("Storage should exist")) context.assertTrue( storage!!.bucketCapacity == expectedCapacity, - Text.literal("Iron tank capacity should be $expectedCapacity but was ${storage.bucketCapacity}"), + Component.literal("Iron tank capacity should be $expectedCapacity but was ${storage.bucketCapacity}"), ) - context.complete() + context.succeed() } @GameTest - fun differentTiersConnect(context: TestContext) { + fun differentTiersConnect(context: GameTestHelper) { val pos1 = BlockPos(0, 2, 0) val pos2 = BlockPos(1, 2, 0) context.placeTank(pos1, TankTier.BASE) context.placeTank(pos2, TankTier.IRON) val state = context.getFluidState() - val storage1 = state.getStorage(context.getAbsolutePos(pos1)) - val storage2 = state.getStorage(context.getAbsolutePos(pos2)) - context.assertTrue(storage1 != null, Text.literal("Storage1 should exist")) - context.assertTrue(storage2 != null, Text.literal("Storage2 should exist")) + val storage1 = state.getStorage(context.absolutePos(pos1)) + val storage2 = state.getStorage(context.absolutePos(pos2)) + context.assertTrue(storage1 != null, Component.literal("Storage1 should exist")) + context.assertTrue(storage2 != null, Component.literal("Storage2 should exist")) context.assertTrue( storage1 === storage2, - Text.literal("Different tier tanks should share storage when adjacent"), + Component.literal("Different tier tanks should share storage when adjacent"), ) val expectedCapacity = CTServerConfig.instance.getTierCapacity(TankTier.BASE) + CTServerConfig.instance.getTierCapacity(TankTier.IRON) context.assertTrue( storage1!!.bucketCapacity == expectedCapacity, - Text.literal("Combined capacity should be $expectedCapacity but was ${storage1.bucketCapacity}"), + Component.literal("Combined capacity should be $expectedCapacity but was ${storage1.bucketCapacity}"), ) - context.complete() + context.succeed() } // === getPickStack テスト === @GameTest - fun pickStackWithIncludeDataContainsFluid(context: TestContext) { + fun pickStackWithIncludeDataContainsFluid(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.getAbsolutePos(tankPos))!! + val storage = state.getStorage(context.absolutePos(tankPos))!! Transaction.openOuter().use { tx -> storage.insert(water, FluidConstants.BUCKET * 5, tx) tx.commit() } - val world = context.getWorld() - val absPos = context.getAbsolutePos(tankPos) + val world = context.level + val absPos = context.absolutePos(tankPos) val blockState = world.getBlockState(absPos) val block = blockState.block as ConnectedTankBlock - val stack = block.getPickStack(world, absPos, blockState, true) + val stack = block.getCloneItemStack(world, absPos, blockState, true) val fluidData = stack.get(CTDataComponentTypes.TANK_FLUID) - context.assertTrue(fluidData != null, Text.literal("Pick stack should have fluid data")) - context.assertTrue(fluidData!!.variant == water, Text.literal("Variant should be water")) + context.assertTrue(fluidData != null, Component.literal("Pick stack should have fluid data")) + context.assertTrue(fluidData!!.variant == water, Component.literal("Variant should be water")) context.assertTrue( fluidData.amount == FluidConstants.BUCKET * 5, - Text.literal("Should have 5 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), + Component.literal("Should have 5 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), ) - context.complete() + context.succeed() } @GameTest - fun pickStackWithoutIncludeDataHasNoFluid(context: TestContext) { + fun pickStackWithoutIncludeDataHasNoFluid(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.getAbsolutePos(tankPos))!! + val storage = state.getStorage(context.absolutePos(tankPos))!! Transaction.openOuter().use { tx -> storage.insert(water, FluidConstants.BUCKET * 5, tx) tx.commit() } - val world = context.getWorld() - val absPos = context.getAbsolutePos(tankPos) + val world = context.level + val absPos = context.absolutePos(tankPos) val blockState = world.getBlockState(absPos) val block = blockState.block as ConnectedTankBlock - val stack = block.getPickStack(world, absPos, blockState, false) + val stack = block.getCloneItemStack(world, absPos, blockState, false) val fluidData = stack.get(CTDataComponentTypes.TANK_FLUID) - context.assertTrue(fluidData == null, Text.literal("Pick stack without includeData should have no fluid data")) - context.complete() + context.assertTrue(fluidData == null, Component.literal("Pick stack without includeData should have no fluid data")) + context.succeed() } @GameTest - fun pickStackFromEmptyTankHasNoFluidData(context: TestContext) { + fun pickStackFromEmptyTankHasNoFluidData(context: GameTestHelper) { val tankPos = BlockPos(0, 2, 0) context.placeTank(tankPos) - val world = context.getWorld() - val absPos = context.getAbsolutePos(tankPos) + val world = context.level + val absPos = context.absolutePos(tankPos) val blockState = world.getBlockState(absPos) val block = blockState.block as ConnectedTankBlock - val stack = block.getPickStack(world, absPos, blockState, true) + val stack = block.getCloneItemStack(world, absPos, blockState, true) val fluidData = stack.get(CTDataComponentTypes.TANK_FLUID) - context.assertTrue(fluidData == null, Text.literal("Empty tank pick stack should have no fluid data")) - context.complete() + context.assertTrue(fluidData == null, Component.literal("Empty tank pick stack should have no fluid data")) + context.succeed() } @GameTest - fun pickStackFromConnectedTanksCalculatesShare(context: TestContext) { + fun pickStackFromConnectedTanksCalculatesShare(context: GameTestHelper) { val pos1 = BlockPos(0, 2, 0) val pos2 = BlockPos(1, 2, 0) val pos3 = BlockPos(2, 2, 0) @@ -936,30 +936,30 @@ object ConnectedTankGameTest { val state = context.getFluidState() val water = FluidVariant.of(Fluids.WATER) - val storage = state.getStorage(context.getAbsolutePos(pos1))!! + val storage = state.getStorage(context.absolutePos(pos1))!! Transaction.openOuter().use { tx -> storage.insert(water, FluidConstants.BUCKET * 30, tx) tx.commit() } - val world = context.getWorld() - val absPos2 = context.getAbsolutePos(pos2) + val world = context.level + val absPos2 = context.absolutePos(pos2) val blockState = world.getBlockState(absPos2) val block = blockState.block as ConnectedTankBlock - val stack = block.getPickStack(world, absPos2, blockState, true) + val stack = block.getCloneItemStack(world, absPos2, blockState, true) val fluidData = stack.get(CTDataComponentTypes.TANK_FLUID) - context.assertTrue(fluidData != null, Text.literal("Pick stack should have fluid data")) - context.assertTrue(fluidData!!.variant == water, Text.literal("Variant should be water")) + context.assertTrue(fluidData != null, Component.literal("Pick stack should have fluid data")) + context.assertTrue(fluidData!!.variant == water, Component.literal("Variant should be water")) context.assertTrue( fluidData.amount == FluidConstants.BUCKET * 10, - Text.literal("Share should be 10 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), + Component.literal("Share should be 10 buckets but was ${fluidData.amount / FluidConstants.BUCKET}"), ) - context.complete() + context.succeed() } @GameTest - fun splitDifferentTiersRecalculatesCapacity(context: TestContext) { + fun splitDifferentTiersRecalculatesCapacity(context: GameTestHelper) { // BASE - IRON - BASE → IRON を破壊 → BASE 2 つに分断 val posL = BlockPos(0, 2, 0) val posM = BlockPos(1, 2, 0) @@ -969,22 +969,22 @@ object ConnectedTankGameTest { context.placeTank(posR, TankTier.BASE) val state = context.getFluidState() - state.removeStorage(context.getAbsolutePos(posM), context.getWorld()) + state.removeStorage(context.absolutePos(posM), context.level) - val sL = state.getStorage(context.getAbsolutePos(posL)) - val sR = state.getStorage(context.getAbsolutePos(posR)) - context.assertTrue(sL != null, Text.literal("Left storage should exist")) - context.assertTrue(sR != null, Text.literal("Right storage should exist")) - context.assertTrue(sL !== sR, Text.literal("Should be separate groups")) + val sL = state.getStorage(context.absolutePos(posL)) + val sR = state.getStorage(context.absolutePos(posR)) + context.assertTrue(sL != null, Component.literal("Left storage should exist")) + context.assertTrue(sR != null, Component.literal("Right storage should exist")) + context.assertTrue(sL !== sR, Component.literal("Should be separate groups")) val baseCap = CTServerConfig.instance.getTierCapacity(TankTier.BASE) context.assertTrue( sL!!.bucketCapacity == baseCap, - Text.literal("Left capacity should be $baseCap but was ${sL.bucketCapacity}"), + Component.literal("Left capacity should be $baseCap but was ${sL.bucketCapacity}"), ) context.assertTrue( sR!!.bucketCapacity == baseCap, - Text.literal("Right capacity should be $baseCap but was ${sR.bucketCapacity}"), + Component.literal("Right capacity should be $baseCap but was ${sR.bucketCapacity}"), ) - context.complete() + context.succeed() } } diff --git a/src/main/java/net/turtton/connectedtank/mixin/BlockItemPlaceMixin.java b/src/main/java/net/turtton/connectedtank/mixin/BlockItemPlaceMixin.java index 5449eb1..e1340ca 100644 --- a/src/main/java/net/turtton/connectedtank/mixin/BlockItemPlaceMixin.java +++ b/src/main/java/net/turtton/connectedtank/mixin/BlockItemPlaceMixin.java @@ -1,9 +1,9 @@ package net.turtton.connectedtank.mixin; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.util.ActionResult; -import net.minecraft.util.math.BlockPos; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.BlockPlaceContext; import net.turtton.connectedtank.block.ConnectedTankPlacementContext; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -19,14 +19,15 @@ public class BlockItemPlaceMixin { * getSide().getOpposite() でオフセットしてクリック先の既存ブロック座標を得る。 */ @Inject( - method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;", + method = + "place(Lnet/minecraft/world/item/context/BlockPlaceContext;)Lnet/minecraft/world/InteractionResult;", at = @At("HEAD")) - private void connectedtank$onPlaceHead(ItemPlacementContext context, CallbackInfoReturnable cir) { + private void connectedtank$onPlaceHead(BlockPlaceContext context, CallbackInfoReturnable cir) { BlockPos hitPos; - if (context.canReplaceExisting()) { - hitPos = context.getBlockPos(); + if (context.replacingClickedOnBlock()) { + hitPos = context.getClickedPos(); } else { - hitPos = context.getBlockPos().offset(context.getSide().getOpposite()); + hitPos = context.getClickedPos().relative(context.getClickedFace().getOpposite()); } ConnectedTankPlacementContext.INSTANCE.setInteractedAt(hitPos); } @@ -37,9 +38,10 @@ public class BlockItemPlaceMixin { * {@link ConnectedTankPlacementContext#consumeInteractedAt()} も参照。 */ @Inject( - method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;", + method = + "place(Lnet/minecraft/world/item/context/BlockPlaceContext;)Lnet/minecraft/world/InteractionResult;", at = @At("RETURN")) - private void connectedtank$onPlaceReturn(ItemPlacementContext context, CallbackInfoReturnable cir) { + private void connectedtank$onPlaceReturn(BlockPlaceContext context, CallbackInfoReturnable cir) { ConnectedTankPlacementContext.INSTANCE.clear(); } } diff --git a/src/main/kotlin/net/turtton/connectedtank/block/CTBlockEntityTypes.kt b/src/main/kotlin/net/turtton/connectedtank/block/CTBlockEntityTypes.kt index a80a3c6..442a3af 100644 --- a/src/main/kotlin/net/turtton/connectedtank/block/CTBlockEntityTypes.kt +++ b/src/main/kotlin/net/turtton/connectedtank/block/CTBlockEntityTypes.kt @@ -1,15 +1,15 @@ package net.turtton.connectedtank.block import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder -import net.minecraft.block.entity.BlockEntityType -import net.minecraft.registry.Registries -import net.minecraft.registry.Registry +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.Registry import net.turtton.connectedtank.extension.ModIdentifier object CTBlockEntityTypes { val CONNECTED_TANK: BlockEntityType = Registry.register( - Registries.BLOCK_ENTITY_TYPE, + BuiltInRegistries.BLOCK_ENTITY_TYPE, ModIdentifier("connected_tank"), FabricBlockEntityTypeBuilder.create(::ConnectedTankBlockEntity, *CTBlocks.ALL_TANKS.toTypedArray()).build(), ) diff --git a/src/main/kotlin/net/turtton/connectedtank/block/CTBlocks.kt b/src/main/kotlin/net/turtton/connectedtank/block/CTBlocks.kt index ca35036..f33cd5e 100644 --- a/src/main/kotlin/net/turtton/connectedtank/block/CTBlocks.kt +++ b/src/main/kotlin/net/turtton/connectedtank/block/CTBlocks.kt @@ -1,14 +1,14 @@ package net.turtton.connectedtank.block import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage -import net.minecraft.block.AbstractBlock -import net.minecraft.block.Block -import net.minecraft.registry.Registries -import net.minecraft.registry.Registry -import net.minecraft.registry.RegistryKey -import net.minecraft.registry.RegistryKeys -import net.minecraft.server.world.ServerWorld -import net.minecraft.util.math.BlockPos +import net.minecraft.world.level.block.state.BlockBehaviour +import net.minecraft.world.level.block.Block +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.Registry +import net.minecraft.resources.ResourceKey +import net.minecraft.core.registries.Registries +import net.minecraft.server.level.ServerLevel +import net.minecraft.core.BlockPos import net.turtton.connectedtank.config.CTServerConfig import net.turtton.connectedtank.extension.ModIdentifier import net.turtton.connectedtank.world.FluidStoragePersistentState @@ -37,30 +37,30 @@ object CTBlocks { fun isConnectedTank(block: Block): Boolean = block in tankBlockSet private fun register(tier: TankTier): Block { - val blockKey = RegistryKey.of(RegistryKeys.BLOCK, ModIdentifier(tier.id)) - val settings = AbstractBlock.Settings.create() - .nonOpaque() + val blockKey = ResourceKey.create(Registries.BLOCK, ModIdentifier(tier.id)) + val settings = BlockBehaviour.Properties.of() + .noOcclusion() .strength(tier.hardness) - .allowsSpawning { _, _, _, _ -> false } - .solidBlock { _, _, _ -> false } - .suffocates { _, _, _ -> false } - .blockVision { _, _, _ -> false } - .registryKey(blockKey) + .isValidSpawn { _, _, _, _ -> false } + .isRedstoneConductor { _, _, _ -> false } + .isSuffocating { _, _, _ -> false } + .isViewBlocking { _, _, _ -> false } + .setId(blockKey) val block = ConnectedTankBlock(tier, settings) - return Registry.register(Registries.BLOCK, blockKey, block) + return Registry.register(BuiltInRegistries.BLOCK, blockKey, block) } fun init() { FluidStorage.SIDED.registerForBlocks({ world, pos, _, _, _ -> - val serverWorld = world as? ServerWorld ?: return@registerForBlocks null - val state = serverWorld.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val serverWorld = world as? ServerLevel ?: return@registerForBlocks null + val state = serverWorld.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val storage = state.getStorage(pos) ?: run { val block = serverWorld.getBlockState(pos).block as? ConnectedTankBlock val cap = block?.tier?.bucketCapacity ?: CTServerConfig.instance.tankBucketCapacity TankFluidStorage(cap).also { state.addStorage(pos, it) } } storage.onChanged = { - state.markDirty() + state.setDirty() syncGroupBlockEntities(serverWorld, pos, state) } storage @@ -68,9 +68,9 @@ object CTBlocks { } fun syncGroupBlockEntities( - world: ServerWorld, + world: ServerLevel, pos: BlockPos, - state: FluidStoragePersistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE), + state: FluidStoragePersistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE), ) { val storage = state.getStorage(pos) ?: return val groupId = state.getGroupId(pos) @@ -84,15 +84,15 @@ object CTBlocks { } private fun updateConnectionStates( - world: ServerWorld, + world: ServerLevel, positions: Iterable, - state: FluidStoragePersistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE), + state: FluidStoragePersistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE), ) { val positionsToUpdate = mutableSetOf() for (pos in positions) { positionsToUpdate.add(pos) for (offset in FluidStoragePersistentState.ADJACENT_OFFSETS) { - val neighborPos = pos.add(offset) + val neighborPos = pos.offset(offset) if (isConnectedTank(world.getBlockState(neighborPos).block)) { positionsToUpdate.add(neighborPos) } @@ -104,17 +104,17 @@ object CTBlocks { val myGroupId = state.getGroupId(targetPos) var newState = currentState for ((direction, property) in ConnectedTankBlock.DIRECTION_PROPERTIES) { - val neighborPos = targetPos.offset(direction) + val neighborPos = targetPos.relative(direction) val neighborBlock = world.getBlockState(neighborPos).block val connected = if (isConnectedTank(neighborBlock) && myGroupId != null) { state.getGroupId(neighborPos) == myGroupId } else { false } - newState = newState.with(property, connected) + newState = newState.setValue(property, connected) } if (newState != currentState) { - world.setBlockState(targetPos, newState, Block.NOTIFY_LISTENERS) + world.setBlock(targetPos, newState, Block.UPDATE_CLIENTS) } } } diff --git a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt index 168470d..aee6fce 100644 --- a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt +++ b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlock.kt @@ -5,44 +5,44 @@ import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorageUtil import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributes import net.fabricmc.loader.api.FabricLoader -import net.minecraft.block.Block -import net.minecraft.block.BlockEntityProvider -import net.minecraft.block.BlockState -import net.minecraft.block.entity.BlockEntity -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.ItemPlacementContext -import net.minecraft.item.ItemStack -import net.minecraft.loot.context.LootContextParameters -import net.minecraft.loot.context.LootWorldContext -import net.minecraft.server.world.ServerWorld -import net.minecraft.state.StateManager -import net.minecraft.state.property.BooleanProperty -import net.minecraft.text.Text -import net.minecraft.util.ActionResult -import net.minecraft.util.Hand -import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction -import net.minecraft.util.math.random.Random -import net.minecraft.world.BlockView -import net.minecraft.world.World -import net.minecraft.world.WorldView -import net.minecraft.world.tick.ScheduledTickView +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.context.BlockPlaceContext +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.storage.loot.parameters.LootContextParams +import net.minecraft.world.level.storage.loot.LootParams +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.block.state.StateDefinition +import net.minecraft.world.level.block.state.properties.BooleanProperty +import net.minecraft.network.chat.Component +import net.minecraft.world.InteractionResult +import net.minecraft.world.InteractionHand +import net.minecraft.world.phys.BlockHitResult +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.util.RandomSource +import net.minecraft.world.level.BlockGetter +import net.minecraft.world.level.Level +import net.minecraft.world.level.LevelReader +import net.minecraft.world.level.ScheduledTickAccess import net.turtton.connectedtank.component.CTDataComponentTypes import net.turtton.connectedtank.config.CTServerConfig import net.turtton.connectedtank.world.FluidStoragePersistentState -class ConnectedTankBlock(val tier: TankTier, settings: Settings) : +class ConnectedTankBlock(val tier: TankTier, settings: Properties) : Block(settings), - BlockEntityProvider { + EntityBlock { companion object { - val CONNECTED_NORTH: BooleanProperty = BooleanProperty.of("connected_north") - val CONNECTED_SOUTH: BooleanProperty = BooleanProperty.of("connected_south") - val CONNECTED_EAST: BooleanProperty = BooleanProperty.of("connected_east") - val CONNECTED_WEST: BooleanProperty = BooleanProperty.of("connected_west") - val CONNECTED_UP: BooleanProperty = BooleanProperty.of("connected_up") - val CONNECTED_DOWN: BooleanProperty = BooleanProperty.of("connected_down") + val CONNECTED_NORTH: BooleanProperty = BooleanProperty.create("connected_north") + val CONNECTED_SOUTH: BooleanProperty = BooleanProperty.create("connected_south") + val CONNECTED_EAST: BooleanProperty = BooleanProperty.create("connected_east") + val CONNECTED_WEST: BooleanProperty = BooleanProperty.create("connected_west") + val CONNECTED_UP: BooleanProperty = BooleanProperty.create("connected_up") + val CONNECTED_DOWN: BooleanProperty = BooleanProperty.create("connected_down") val DIRECTION_PROPERTIES: Map = mapOf( Direction.NORTH to CONNECTED_NORTH, @@ -55,59 +55,61 @@ class ConnectedTankBlock(val tier: TankTier, settings: Settings) : } init { - defaultState = stateManager.defaultState - .with(CONNECTED_NORTH, false) - .with(CONNECTED_SOUTH, false) - .with(CONNECTED_EAST, false) - .with(CONNECTED_WEST, false) - .with(CONNECTED_UP, false) - .with(CONNECTED_DOWN, false) + registerDefaultState( + stateDefinition.any() + .setValue(CONNECTED_NORTH, false) + .setValue(CONNECTED_SOUTH, false) + .setValue(CONNECTED_EAST, false) + .setValue(CONNECTED_WEST, false) + .setValue(CONNECTED_UP, false) + .setValue(CONNECTED_DOWN, false), + ) } private val pendingDropData = ConcurrentHashMap() - override fun appendProperties(builder: StateManager.Builder) { + override fun createBlockStateDefinition(builder: StateDefinition.Builder) { builder.add(CONNECTED_NORTH, CONNECTED_SOUTH, CONNECTED_EAST, CONNECTED_WEST, CONNECTED_UP, CONNECTED_DOWN) } - override fun getPlacementState(ctx: ItemPlacementContext): BlockState { + override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState { // 設置直後は BlockEntity がまだ存在しないため、全方向 false で設置する。 // 正しい接続状態は onPlaced → syncGroupBlockEntities で確定する。 - return defaultState + return defaultBlockState() } - override fun getStateForNeighborUpdate( + override fun updateShape( state: BlockState, - world: WorldView, - tickView: ScheduledTickView, + world: LevelReader, + tickView: ScheduledTickAccess, pos: BlockPos, direction: Direction, neighborPos: BlockPos, neighborState: BlockState, - random: Random, + random: RandomSource, ): BlockState { val property = DIRECTION_PROPERTIES[direction] ?: return state if (!CTBlocks.isConnectedTank(neighborState.block)) { - return state.with(property, false) + return state.setValue(property, false) } return state } - override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = ConnectedTankBlockEntity(pos, state) + override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = ConnectedTankBlockEntity(pos, state) - override fun isSideInvisible(state: BlockState, stateFrom: BlockState, direction: Direction): Boolean { - val property = DIRECTION_PROPERTIES[direction] ?: return super.isSideInvisible(state, stateFrom, direction) - return state.get(property) || super.isSideInvisible(state, stateFrom, direction) + override fun skipRendering(state: BlockState, stateFrom: BlockState, direction: Direction): Boolean { + val property = DIRECTION_PROPERTIES[direction] ?: return super.skipRendering(state, stateFrom, direction) + return state.getValue(property) || super.skipRendering(state, stateFrom, direction) } - override fun isTransparent(state: BlockState): Boolean = true + override fun propagatesSkylightDown(state: BlockState): Boolean = true - override fun getAmbientOcclusionLightLevel(state: BlockState, world: BlockView, pos: BlockPos): Float = 1.0F + override fun getShadeBrightness(state: BlockState, world: BlockGetter, pos: BlockPos): Float = 1.0F - override fun onStateReplaced(state: BlockState, world: ServerWorld, pos: BlockPos, moved: Boolean) { - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + override fun affectNeighborsAfterRemoval(state: BlockState, world: ServerLevel, pos: BlockPos, moved: Boolean) { + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val neighborPositions = FluidStoragePersistentState.ADJACENT_OFFSETS - .map { pos.add(it) } + .map { pos.offset(it) } .filter { CTBlocks.isConnectedTank(world.getBlockState(it).block) } if (!pendingDropData.containsKey(pos)) { @@ -123,31 +125,31 @@ class ConnectedTankBlock(val tier: TankTier, settings: Settings) : } // クリエイティブモード等で getDroppedStacks が呼ばれないパスのクリーンアップ - val immutablePos = pos.toImmutable() + val immutablePos = pos.immutable() world.server?.execute { pendingDropData.remove(immutablePos) } - super.onStateReplaced(state, world, pos, moved) + super.affectNeighborsAfterRemoval(state, world, pos, moved) } - public override fun getPickStack(world: WorldView, pos: BlockPos, state: BlockState, includeData: Boolean): ItemStack { - val stack = super.getPickStack(world, pos, state, includeData) - if (!includeData || world !is ServerWorld) return stack - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + public override fun getCloneItemStack(world: LevelReader, pos: BlockPos, state: BlockState, includeData: Boolean): ItemStack { + val stack = super.getCloneItemStack(world, pos, state, includeData) + if (!includeData || world !is ServerLevel) return stack + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val fluidData = computeFluidData(persistentState, pos, world) ?: return stack stack.set(CTDataComponentTypes.TANK_FLUID, fluidData) return stack } - override fun getDroppedStacks(state: BlockState, builder: LootWorldContext.Builder): List { + override fun getDrops(state: BlockState, builder: LootParams.Builder): List { val stack = ItemStack(this) - val origin = builder.get(LootContextParameters.ORIGIN) - val pos = BlockPos.ofFloored(origin) + val origin = builder.getParameter(LootContextParams.ORIGIN) + val pos = BlockPos.containing(origin) val fluidData = pendingDropData.remove(pos) ?: run { // Explosion パス: ストレージがまだ存在する - val world = builder.world - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val world = builder.level + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) computeFluidData(persistentState, pos, world)?.also { pendingDropData[pos] = it } @@ -162,7 +164,7 @@ class ConnectedTankBlock(val tier: TankTier, settings: Settings) : private fun computeFluidData( persistentState: FluidStoragePersistentState, pos: BlockPos, - world: ServerWorld, + world: ServerLevel, ): TankFluidStorage.ExistingData? { val tankStorage = persistentState.getStorage(pos) if (tankStorage == null || tankStorage.isResourceBlank) return null @@ -171,10 +173,10 @@ class ConnectedTankBlock(val tier: TankTier, settings: Settings) : return TankFluidStorage.ExistingData(tankStorage.variant, share) } - override fun onPlaced(world: World?, pos: BlockPos?, state: BlockState?, placer: LivingEntity?, itemStack: ItemStack?) { - if (world is ServerWorld && pos != null) { - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) - val fluidData = itemStack?.get(CTDataComponentTypes.TANK_FLUID) + override fun setPlacedBy(world: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack) { + if (world is ServerLevel) { + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) + val fluidData = itemStack.get(CTDataComponentTypes.TANK_FLUID) val block = world.getBlockState(pos).block as? ConnectedTankBlock val capacity = block?.tier?.bucketCapacity ?: CTServerConfig.instance.tankBucketCapacity val tankStorage = TankFluidStorage(capacity, fluidData) @@ -186,35 +188,35 @@ class ConnectedTankBlock(val tier: TankTier, settings: Settings) : } } - override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hit: BlockHitResult): ActionResult { - if (!FabricLoader.getInstance().isDevelopmentEnvironment) return ActionResult.PASS - if (world !is ServerWorld) return ActionResult.SUCCESS + override fun useWithoutItem(state: BlockState, world: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult { + if (!FabricLoader.getInstance().isDevelopmentEnvironment) return InteractionResult.PASS + if (world !is ServerLevel) return InteractionResult.SUCCESS - val storage = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val storage = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val tankStorage = storage.getStorage(pos) if (tankStorage == null) { - player.sendMessage(Text.literal("No storage"), true) - return ActionResult.SUCCESS + 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 - player.sendMessage(Text.literal("$fluidName: %.2f / %d buckets".format(buckets, capacity)), true) - return ActionResult.SUCCESS + player.displayClientMessage(Component.literal("$fluidName: %.2f / %d buckets".format(buckets, capacity)), true) + return InteractionResult.SUCCESS } - override fun onUseWithItem(stack: ItemStack, state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult { - if (world !is ServerWorld) return ActionResult.SUCCESS + override fun useItemOn(stack: ItemStack, state: BlockState, world: Level, pos: BlockPos, player: Player, hand: InteractionHand, hit: BlockHitResult): InteractionResult { + if (world !is ServerLevel) return InteractionResult.SUCCESS - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) - val tankStorage = persistentState.getStorage(pos) ?: return ActionResult.PASS_TO_DEFAULT_BLOCK_ACTION + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) + val tankStorage = persistentState.getStorage(pos) ?: return InteractionResult.TRY_WITH_EMPTY_HAND val result = FluidStorageUtil.interactWithFluidStorage(tankStorage, player, hand) return if (result) { CTBlocks.syncGroupBlockEntities(world, pos, persistentState) - ActionResult.SUCCESS + InteractionResult.SUCCESS } else { - ActionResult.PASS_TO_DEFAULT_BLOCK_ACTION + InteractionResult.TRY_WITH_EMPTY_HAND } } } diff --git a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntity.kt b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntity.kt index e729696..e253c69 100644 --- a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntity.kt +++ b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankBlockEntity.kt @@ -3,17 +3,17 @@ package net.turtton.connectedtank.block import java.util.UUID import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant -import net.minecraft.block.BlockState -import net.minecraft.block.entity.BlockEntity -import net.minecraft.nbt.NbtCompound -import net.minecraft.network.listener.ClientPlayPacketListener -import net.minecraft.network.packet.Packet -import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket -import net.minecraft.registry.RegistryWrapper -import net.minecraft.storage.ReadView -import net.minecraft.storage.WriteView -import net.minecraft.util.Uuids -import net.minecraft.util.math.BlockPos +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.nbt.CompoundTag +import net.minecraft.network.protocol.game.ClientGamePacketListener +import net.minecraft.network.protocol.Packet +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket +import net.minecraft.core.HolderLookup +import net.minecraft.world.level.storage.ValueInput +import net.minecraft.world.level.storage.ValueOutput +import net.minecraft.core.UUIDUtil +import net.minecraft.core.BlockPos import net.turtton.connectedtank.config.CTServerConfig import org.joml.Math.clamp @@ -47,39 +47,39 @@ class ConnectedTankBlockEntity( amount = storage.amount capacity = storage.bucketCapacity.toLong() * FluidConstants.BUCKET groupId = newGroupId ?: groupId - val posCapacity = (world?.getBlockState(pos)?.block as? ConnectedTankBlock)?.tier?.bucketCapacity + val posCapacity = (level?.getBlockState(worldPosition)?.block as? ConnectedTankBlock)?.tier?.bucketCapacity ?: CTServerConfig.instance.tankBucketCapacity val posCapacityDroplets = posCapacity.toLong() * FluidConstants.BUCKET localFillLevel = if (posCapacityDroplets > 0) clamp(0f, 1f, localShare.toFloat() / posCapacityDroplets) else 0f if (variantChanged || amountChanged) { - waveStartTick = world?.time ?: 0L + waveStartTick = level?.gameTime ?: 0L } - markDirty() - world?.let { w -> - val state = w.getBlockState(pos) - w.updateListeners(pos, state, state, 3) + setChanged() + level?.let { w -> + val state = w.getBlockState(worldPosition) + w.sendBlockUpdated(worldPosition, state, state, 3) } } - override fun readData(view: ReadView) { + override fun loadAdditional(view: ValueInput) { fluidVariant = view.read("variant", FluidVariant.CODEC).orElse(FluidVariant.blank()) - amount = view.getLong("amount", 0L) - capacity = view.getLong("capacity", 0L) - waveStartTick = view.getLong("waveStartTick", 0L) - localFillLevel = view.getFloat("localFillLevel", 0f) - groupId = view.read("groupId", Uuids.CODEC).orElse(null) + amount = view.getLongOr("amount", 0L) + capacity = view.getLongOr("capacity", 0L) + waveStartTick = view.getLongOr("waveStartTick", 0L) + localFillLevel = view.getFloatOr("localFillLevel", 0f) + groupId = view.read("groupId", UUIDUtil.AUTHLIB_CODEC).orElse(null) } - override fun writeData(view: WriteView) { - view.put("variant", FluidVariant.CODEC, fluidVariant) + override fun saveAdditional(view: ValueOutput) { + view.store("variant", FluidVariant.CODEC, fluidVariant) view.putLong("amount", amount) view.putLong("capacity", capacity) view.putLong("waveStartTick", waveStartTick) view.putFloat("localFillLevel", localFillLevel) - view.putNullable("groupId", Uuids.CODEC, groupId) + view.storeNullable("groupId", UUIDUtil.AUTHLIB_CODEC, groupId) } - override fun toUpdatePacket(): Packet = BlockEntityUpdateS2CPacket.create(this) + override fun getUpdatePacket(): Packet = ClientboundBlockEntityDataPacket.create(this) - override fun toInitialChunkDataNbt(registries: RegistryWrapper.WrapperLookup): NbtCompound = createComponentlessNbt(registries) + override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag = saveCustomOnly(registries) } diff --git a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankPlacementContext.kt b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankPlacementContext.kt index 79c79f4..25b1c86 100644 --- a/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankPlacementContext.kt +++ b/src/main/kotlin/net/turtton/connectedtank/block/ConnectedTankPlacementContext.kt @@ -1,6 +1,6 @@ package net.turtton.connectedtank.block -import net.minecraft.util.math.BlockPos +import net.minecraft.core.BlockPos object ConnectedTankPlacementContext { private val interactedAt = ThreadLocal() 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 b22a2f9..0026440 100644 --- a/src/main/kotlin/net/turtton/connectedtank/compat/jade/ConnectedTankJadePlugin.kt +++ b/src/main/kotlin/net/turtton/connectedtank/compat/jade/ConnectedTankJadePlugin.kt @@ -1,8 +1,12 @@ package net.turtton.connectedtank.compat.jade import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants -import net.minecraft.server.world.ServerWorld -import net.minecraft.util.Identifier +import net.minecraft.server.level.ServerLevel +//? if >=1.21.11 { +/*import net.minecraft.resources.Identifier as ResourceLocation*/ +//?} else { +import net.minecraft.resources.ResourceLocation +//?} import net.turtton.connectedtank.MOD_ID import net.turtton.connectedtank.block.ConnectedTankBlockEntity import net.turtton.connectedtank.world.FluidStoragePersistentState @@ -33,14 +37,14 @@ class ConnectedTankJadePlugin : IWailaPlugin { object TankFluidProvider : IServerExtensionProvider, IClientExtensionProvider { - override fun getUid(): Identifier = Identifier.of(MOD_ID, "tank_fluid") + override fun getUid(): ResourceLocation = ResourceLocation.fromNamespaceAndPath(MOD_ID, "tank_fluid") override fun getGroups(accessor: Accessor<*>): List>? { val blockAccessor = accessor as BlockAccessor - val world = blockAccessor.level as ServerWorld + val world = blockAccessor.level as ServerLevel val pos = blockAccessor.position - val persistentState = world.persistentStateManager.getOrCreate(FluidStoragePersistentState.TYPE) + val persistentState = world.dataStorage.computeIfAbsent(FluidStoragePersistentState.TYPE) val storage = persistentState.getStorage(pos) ?: return null if (storage.isResourceBlank || storage.amount <= 0) return null diff --git a/src/main/kotlin/net/turtton/connectedtank/component/CTDataComponentTypes.kt b/src/main/kotlin/net/turtton/connectedtank/component/CTDataComponentTypes.kt index 216e047..7e1afcb 100644 --- a/src/main/kotlin/net/turtton/connectedtank/component/CTDataComponentTypes.kt +++ b/src/main/kotlin/net/turtton/connectedtank/component/CTDataComponentTypes.kt @@ -1,17 +1,17 @@ package net.turtton.connectedtank.component -import net.minecraft.component.ComponentType -import net.minecraft.registry.Registries -import net.minecraft.registry.Registry +import net.minecraft.core.component.DataComponentType +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.Registry import net.turtton.connectedtank.block.TankFluidStorage import net.turtton.connectedtank.extension.ModIdentifier object CTDataComponentTypes { - val TANK_FLUID: ComponentType = Registry.register( - Registries.DATA_COMPONENT_TYPE, + val TANK_FLUID: DataComponentType = Registry.register( + BuiltInRegistries.DATA_COMPONENT_TYPE, ModIdentifier("tank_fluid"), - ComponentType.builder() - .codec(TankFluidStorage.ExistingData.CODEC) + DataComponentType.builder() + .persistent(TankFluidStorage.ExistingData.CODEC) .build(), ) diff --git a/src/main/kotlin/net/turtton/connectedtank/extension/Identifier.kt b/src/main/kotlin/net/turtton/connectedtank/extension/Identifier.kt index 378dcf9..e6c8fb9 100644 --- a/src/main/kotlin/net/turtton/connectedtank/extension/Identifier.kt +++ b/src/main/kotlin/net/turtton/connectedtank/extension/Identifier.kt @@ -1,7 +1,11 @@ package net.turtton.connectedtank.extension -import net.minecraft.util.Identifier +//? if >=1.21.11 { +/*import net.minecraft.resources.Identifier as ResourceLocation*/ +//?} else { +import net.minecraft.resources.ResourceLocation +//?} import net.turtton.connectedtank.MOD_ID @Suppress("FunctionName") -fun ModIdentifier(path: String): Identifier = Identifier.of(MOD_ID, path) +fun ModIdentifier(path: String): ResourceLocation = ResourceLocation.fromNamespaceAndPath(MOD_ID, path) diff --git a/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt b/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt index 8585757..7700ab6 100644 --- a/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt +++ b/src/main/kotlin/net/turtton/connectedtank/item/CTItems.kt @@ -1,14 +1,14 @@ package net.turtton.connectedtank.item import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup -import net.minecraft.item.BlockItem -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.registry.Registries -import net.minecraft.registry.Registry -import net.minecraft.registry.RegistryKey -import net.minecraft.registry.RegistryKeys -import net.minecraft.text.Text +import net.minecraft.world.item.BlockItem +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.Registry +import net.minecraft.resources.ResourceKey +import net.minecraft.core.registries.Registries +import net.minecraft.network.chat.Component import net.turtton.connectedtank.block.CTBlocks import net.turtton.connectedtank.block.TankTier import net.turtton.connectedtank.extension.ModIdentifier @@ -20,7 +20,7 @@ object CTItems { val IRON_CONNECTED_TANK = registerTank(TankTier.IRON) val GOLD_CONNECTED_TANK = registerTank(TankTier.GOLD) val DIAMOND_CONNECTED_TANK = registerTank(TankTier.DIAMOND) - val NETHERITE_CONNECTED_TANK = registerTank(TankTier.NETHERITE) { fireproof() } + val NETHERITE_CONNECTED_TANK = registerTank(TankTier.NETHERITE) { fireResistant() } val ALL_TANK_ITEMS: List = listOf( CONNECTED_TANK, @@ -34,38 +34,38 @@ object CTItems { private fun registerTank( tier: TankTier, - additionalSettings: Item.Settings.() -> Unit = {}, + additionalSettings: Item.Properties.() -> Unit = {}, ): BlockItem { val block = CTBlocks.ALL_TANKS.first { (it as? net.turtton.connectedtank.block.ConnectedTankBlock)?.tier == tier } return register(tier.id, { BlockItem(block, it) }) { - useBlockPrefixedTranslationKey() + useBlockDescriptionPrefix() additionalSettings() } } private fun register( name: String, - factory: (Item.Settings) -> I, - settingsFactory: Item.Settings.() -> Unit = {}, + factory: (Item.Properties) -> I, + settingsFactory: Item.Properties.() -> Unit = {}, ): I { - val itemKey = RegistryKey.of(RegistryKeys.ITEM, ModIdentifier(name)) - val settings = Item.Settings().apply(settingsFactory) - val item = factory(settings.registryKey(itemKey)) - Registry.register(Registries.ITEM, itemKey, item) + val itemKey = ResourceKey.create(Registries.ITEM, ModIdentifier(name)) + val settings = Item.Properties().apply(settingsFactory) + val item = factory(settings.setId(itemKey)) + Registry.register(BuiltInRegistries.ITEM, itemKey, item) return item } - private val ITEM_GROUP_KEY = RegistryKey.of(RegistryKeys.ITEM_GROUP, ModIdentifier("item_group")) + private val ITEM_GROUP_KEY = ResourceKey.create(Registries.CREATIVE_MODE_TAB, ModIdentifier("item_group")) fun init() { Registry.register( - Registries.ITEM_GROUP, + BuiltInRegistries.CREATIVE_MODE_TAB, ITEM_GROUP_KEY, FabricItemGroup.builder() - .displayName(Text.translatable("itemGroup.connectedtank.item_group")) + .title(Component.translatable("itemGroup.connectedtank.item_group")) .icon { ItemStack(CONNECTED_TANK) } - .entries { _, entries -> - ALL_TANK_ITEMS.forEach { entries.add(it) } + .displayItems { _, entries -> + ALL_TANK_ITEMS.forEach { entries.accept(it) } } .build(), ) diff --git a/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt b/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt index 86f1adc..edd58f3 100644 --- a/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt +++ b/src/main/kotlin/net/turtton/connectedtank/network/ConfigSyncPayload.kt @@ -3,10 +3,10 @@ package net.turtton.connectedtank.network import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking -import net.minecraft.network.RegistryByteBuf -import net.minecraft.network.codec.PacketCodec -import net.minecraft.network.codec.PacketCodecs -import net.minecraft.network.packet.CustomPayload +import net.minecraft.network.RegistryFriendlyByteBuf +import net.minecraft.network.codec.StreamCodec +import net.minecraft.network.codec.ByteBufCodecs +import net.minecraft.network.protocol.common.custom.CustomPacketPayload import net.minecraft.server.MinecraftServer import net.turtton.connectedtank.config.CTServerConfig import net.turtton.connectedtank.extension.ModIdentifier @@ -14,18 +14,18 @@ import net.turtton.connectedtank.extension.ModIdentifier data class ConfigSyncPayload( val tankBucketCapacity: Int, val tierMultipliers: Map, -) : CustomPayload { - override fun getId(): CustomPayload.Id = ID +) : CustomPacketPayload { + override fun type(): CustomPacketPayload.Type = ID companion object { - val ID: CustomPayload.Id = CustomPayload.Id(ModIdentifier("config_sync")) + val ID: CustomPacketPayload.Type = CustomPacketPayload.Type(ModIdentifier("config_sync")) - private val TIER_MULTIPLIER_CODEC: PacketCodec> = - PacketCodec.ofStatic( + private val TIER_MULTIPLIER_CODEC: StreamCodec> = + StreamCodec.of( { buf, map -> buf.writeVarInt(map.size) for ((key, value) in map) { - buf.writeString(key) + buf.writeUtf(key) buf.writeVarInt(value) } }, @@ -33,19 +33,19 @@ data class ConfigSyncPayload( val size = buf.readVarInt() buildMap(size) { repeat(size) { - put(buf.readString(), buf.readVarInt()) + put(buf.readUtf(), buf.readVarInt()) } } }, ) - val CODEC: PacketCodec = PacketCodec.ofStatic( + val CODEC: StreamCodec = StreamCodec.of( { buf, payload -> - PacketCodecs.VAR_INT.encode(buf, payload.tankBucketCapacity) + ByteBufCodecs.VAR_INT.encode(buf, payload.tankBucketCapacity) TIER_MULTIPLIER_CODEC.encode(buf, payload.tierMultipliers) }, { buf -> - val capacity = PacketCodecs.VAR_INT.decode(buf) + val capacity = ByteBufCodecs.VAR_INT.decode(buf) val multipliers = TIER_MULTIPLIER_CODEC.decode(buf) ConfigSyncPayload(capacity, multipliers) }, @@ -63,7 +63,7 @@ data class ConfigSyncPayload( fun broadcastToAll(server: MinecraftServer) { val config = CTServerConfig.instance val payload = ConfigSyncPayload(config.tankBucketCapacity, config.tierMultipliers) - for (player in server.playerManager.playerList) { + for (player in server.playerList.players) { ServerPlayNetworking.send(player, payload) } } diff --git a/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt b/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt index b531a68..1a2cef9 100644 --- a/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt +++ b/src/main/kotlin/net/turtton/connectedtank/recipe/CTRecipeSerializers.kt @@ -1,13 +1,13 @@ package net.turtton.connectedtank.recipe -import net.minecraft.recipe.RecipeSerializer -import net.minecraft.registry.Registries -import net.minecraft.registry.Registry +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.Registry import net.turtton.connectedtank.extension.ModIdentifier object CTRecipeSerializers { val TANK_UPGRADE: RecipeSerializer = Registry.register( - Registries.RECIPE_SERIALIZER, + BuiltInRegistries.RECIPE_SERIALIZER, ModIdentifier("tank_upgrade"), TankUpgradeRecipe.Serializer(), ) diff --git a/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt b/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt index 6754d68..dfb2457 100644 --- a/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt +++ b/src/main/kotlin/net/turtton/connectedtank/recipe/TankUpgradeRecipe.kt @@ -1,26 +1,26 @@ package net.turtton.connectedtank.recipe import com.mojang.serialization.MapCodec -import net.minecraft.item.ItemStack -import net.minecraft.network.RegistryByteBuf -import net.minecraft.network.codec.PacketCodec -import net.minecraft.recipe.CraftingRecipe -import net.minecraft.recipe.IngredientPlacement -import net.minecraft.recipe.RecipeSerializer -import net.minecraft.recipe.ShapedRecipe -import net.minecraft.recipe.book.CraftingRecipeCategory -import net.minecraft.recipe.display.RecipeDisplay -import net.minecraft.recipe.input.CraftingRecipeInput -import net.minecraft.registry.RegistryWrapper -import net.minecraft.world.World +import net.minecraft.world.item.ItemStack +import net.minecraft.network.RegistryFriendlyByteBuf +import net.minecraft.network.codec.StreamCodec +import net.minecraft.world.item.crafting.CraftingRecipe +import net.minecraft.world.item.crafting.PlacementInfo +import net.minecraft.world.item.crafting.RecipeSerializer +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 +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: CraftingRecipeInput, world: World): Boolean = shaped.matches(input, world) + override fun matches(input: CraftingInput, world: Level): Boolean = shaped.matches(input, world) - override fun craft(input: CraftingRecipeInput, lookup: RegistryWrapper.WrapperLookup): ItemStack { - val result = shaped.craft(input, lookup) - for (stack in input.stacks) { + 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) { result.set(CTDataComponentTypes.TANK_FLUID, fluidData) @@ -32,20 +32,20 @@ class TankUpgradeRecipe(private val shaped: ShapedRecipe) : CraftingRecipe { override fun getSerializer(): RecipeSerializer = CTRecipeSerializers.TANK_UPGRADE - override fun getGroup(): String = shaped.group + override fun group(): String = shaped.group() - override fun getCategory(): CraftingRecipeCategory = shaped.category + override fun category(): CraftingBookCategory = shaped.category() - override fun getIngredientPlacement(): IngredientPlacement = shaped.ingredientPlacement + override fun placementInfo(): PlacementInfo = shaped.placementInfo() override fun showNotification(): Boolean = shaped.showNotification() - override fun getDisplays(): List = shaped.displays + override fun display(): List = shaped.display() class Serializer : RecipeSerializer { override fun codec(): MapCodec = ShapedRecipe.Serializer.CODEC.xmap(::TankUpgradeRecipe) { it.shaped } @Deprecated("Recipe is no longer synced to clients") - override fun packetCodec(): PacketCodec = ShapedRecipe.Serializer.PACKET_CODEC.xmap(::TankUpgradeRecipe) { it.shaped } + 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 43f1495..f166196 100644 --- a/src/main/kotlin/net/turtton/connectedtank/world/FluidStoragePersistentState.kt +++ b/src/main/kotlin/net/turtton/connectedtank/world/FluidStoragePersistentState.kt @@ -4,11 +4,11 @@ import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import java.util.UUID import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants -import net.minecraft.server.world.ServerWorld -import net.minecraft.util.Uuids -import net.minecraft.util.math.BlockPos -import net.minecraft.world.PersistentState -import net.minecraft.world.PersistentStateType +import net.minecraft.server.level.ServerLevel +import net.minecraft.core.UUIDUtil +import net.minecraft.core.BlockPos +import net.minecraft.world.level.saveddata.SavedData +import net.minecraft.world.level.saveddata.SavedDataType import net.turtton.connectedtank.MOD_ID import net.turtton.connectedtank.block.ConnectedTankBlock import net.turtton.connectedtank.block.TankFluidStorage @@ -17,7 +17,7 @@ import net.turtton.connectedtank.config.CTServerConfig class FluidStoragePersistentState( positionalStorageMap: Map = mapOf(), storageMap: Map = mapOf(), -) : PersistentState() { +) : SavedData() { private val positionalStorageMap: MutableMap = positionalStorageMap.toMutableMap() private val storageMap: MutableMap = storageMap.toMutableMap() @@ -28,8 +28,8 @@ class FluidStoragePersistentState( fun addIsolatedStorage(pos: BlockPos, storage: TankFluidStorage) { val uuid = UUID.randomUUID() positionalStorageMap[pos] = uuid - storageMap[uuid] = storage.also { it.onChanged = ::markDirty } - markDirty() + storageMap[uuid] = storage.also { it.onChanged = ::setDirty } + setDirty() } fun getGroupPositions(pos: BlockPos): List { @@ -43,7 +43,7 @@ class FluidStoragePersistentState( fun addStorage(pos: BlockPos, storage: TankFluidStorage, interactedAt: BlockPos? = null) { val allAdjacentPositions = adjacentOffsets - .map { pos.add(it) } + .map { pos.offset(it) } .filter { positionalStorageMap.containsKey(it) } // interactedAt が隣接座標に含まれる場合のみ有効、含まれなければ通常の優先度ロジックへフォールバック @@ -59,8 +59,8 @@ class FluidStoragePersistentState( if (adjacentPositions.isEmpty()) { val uuid = UUID.randomUUID() positionalStorageMap[pos] = uuid - storageMap[uuid] = storage.also { it.onChanged = ::markDirty } - markDirty() + storageMap[uuid] = storage.also { it.onChanged = ::setDirty } + setDirty() return } @@ -83,8 +83,8 @@ class FluidStoragePersistentState( if (primaryId == null) { val uuid = UUID.randomUUID() positionalStorageMap[pos] = uuid - storageMap[uuid] = storage.also { it.onChanged = ::markDirty } - markDirty() + storageMap[uuid] = storage.also { it.onChanged = ::setDirty } + setDirty() return } @@ -116,7 +116,7 @@ class FluidStoragePersistentState( val mergedVariant = listOfNotNull(effectiveVariant, newVariant).distinct().firstOrNull() val existingData = mergedVariant?.let { TankFluidStorage.ExistingData(it, totalAmount) } - val mergedStorage = TankFluidStorage(totalBucketCap, existingData).also { it.onChanged = ::markDirty } + val mergedStorage = TankFluidStorage(totalBucketCap, existingData).also { it.onChanged = ::setDirty } if (idsToMerge.isNotEmpty()) { val keysToRemap = positionalStorageMap.entries @@ -132,7 +132,7 @@ class FluidStoragePersistentState( storageMap[primaryId] = mergedStorage positionalStorageMap[pos] = primaryId - markDirty() + setDirty() } fun getGroupSize(pos: BlockPos): Int { @@ -142,7 +142,7 @@ class FluidStoragePersistentState( fun removeStorage( pos: BlockPos, - world: ServerWorld? = null, + world: ServerLevel? = null, removedBucketCapacity: Int? = null, ): TankFluidStorage.ExistingData? { val uuid = positionalStorageMap.remove(pos) ?: return null @@ -168,7 +168,7 @@ class FluidStoragePersistentState( if (groupPositions.isEmpty()) { storageMap.remove(uuid) - markDirty() + setDirty() return removedData } @@ -183,14 +183,14 @@ class FluidStoragePersistentState( } else { null } - storageMap[uuid] = TankFluidStorage(newBucketCap, data).also { it.onChanged = ::markDirty } + storageMap[uuid] = TankFluidStorage(newBucketCap, data).also { it.onChanged = ::setDirty } } else { // 分断あり: 位置ベースで液体を分配 val remainingShares = calculatePositionShares(groupPositions, remainingAmount, world) splitIntoComponents(components, uuid, variant, remainingShares, world) } - markDirty() + setDirty() return removedData } @@ -207,7 +207,7 @@ class FluidStoragePersistentState( while (queue.isNotEmpty()) { val current = queue.removeFirst() for (offset in adjacentOffsets) { - val neighbor = current.add(offset) + val neighbor = current.offset(offset) if (neighbor in posSet && neighbor !in reachable) { reachable.add(neighbor) queue.add(neighbor) @@ -222,7 +222,7 @@ class FluidStoragePersistentState( return components } - private fun computeGroupCapacity(positions: Iterable, world: ServerWorld?): Int = if (world != null) { + private fun computeGroupCapacity(positions: Iterable, world: ServerLevel?): Int = if (world != null) { positions.sumOf { p -> (world.getBlockState(p).block as? ConnectedTankBlock)?.tier?.bucketCapacity ?: defaultBucketCapacity } @@ -235,7 +235,7 @@ class FluidStoragePersistentState( originalUuid: UUID, variant: net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant?, positionShares: Map, - world: ServerWorld?, + world: ServerLevel?, ) { // 最大の成分に元の UUID を再利用 val sorted = components.sortedByDescending { it.size } @@ -249,7 +249,7 @@ class FluidStoragePersistentState( } else { null } - val storage = TankFluidStorage(newBucketCap, data).also { it.onChanged = ::markDirty } + val storage = TankFluidStorage(newBucketCap, data).also { it.onChanged = ::setDirty } if (index == 0) { // 最大の成分は元の UUID を再利用 @@ -266,7 +266,7 @@ class FluidStoragePersistentState( fun calculateShare( pos: BlockPos, - world: ServerWorld?, + world: ServerLevel?, selfBucketCapacity: Int? = null, ): Long { val overrides = if (selfBucketCapacity != null) mapOf(pos to selfBucketCapacity) else emptyMap() @@ -275,7 +275,7 @@ class FluidStoragePersistentState( fun calculateGroupShares( pos: BlockPos, - world: ServerWorld?, + world: ServerLevel?, capacityOverrides: Map = emptyMap(), ): Map { val uuid = positionalStorageMap[pos] ?: return emptyMap() @@ -292,7 +292,7 @@ class FluidStoragePersistentState( private fun calculatePositionShares( positions: List, totalAmount: Long, - world: ServerWorld?, + world: ServerLevel?, capacityOverrides: Map = emptyMap(), ): Map { if (positions.isEmpty() || totalAmount <= 0) return positions.associateWith { 0L } @@ -347,7 +347,7 @@ class FluidStoragePersistentState( private fun getPositionCapacityDroplets( pos: BlockPos, - world: ServerWorld?, + world: ServerLevel?, capacityOverrides: Map = emptyMap(), ): Long { val bucketCap = capacityOverrides[pos] ?: if (world != null) { @@ -363,7 +363,7 @@ class FluidStoragePersistentState( val CODEC: Codec = RecordCodecBuilder.create { it.group( BlockPos.CODEC.fieldOf("pos").forGetter(PositionalStorageEntry::pos), - Uuids.CODEC.fieldOf("id").forGetter(PositionalStorageEntry::id), + UUIDUtil.AUTHLIB_CODEC.fieldOf("id").forGetter(PositionalStorageEntry::id), ).apply(it, ::PositionalStorageEntry) } @@ -390,9 +390,14 @@ class FluidStoragePersistentState( val CODEC: Codec = RecordCodecBuilder.create { it.group( PositionalStorageEntry.MAP_CODEC.fieldOf("positionalStorageMap").forGetter(FluidStoragePersistentState::positionalStorageMap), - Codec.unboundedMap(Uuids.CODEC, TankFluidStorage.CODEC).fieldOf("storageMap").forGetter(FluidStoragePersistentState::storageMap), + Codec.unboundedMap(UUIDUtil.AUTHLIB_CODEC, TankFluidStorage.CODEC).fieldOf("storageMap").forGetter(FluidStoragePersistentState::storageMap), ).apply(it, ::FluidStoragePersistentState) } - val TYPE: PersistentStateType = PersistentStateType("${MOD_ID}_fluid_storage", ::FluidStoragePersistentState, CODEC, null) + val TYPE: SavedDataType = SavedDataType( + "${MOD_ID}_fluid_storage", + ::FluidStoragePersistentState, + CODEC, + net.minecraft.util.datafix.DataFixTypes.LEVEL, + ) } }