From 5b5dcddb2542deb3171d57986529174040460e0d Mon Sep 17 00:00:00 2001 From: MeAlam1 Date: Mon, 1 Jun 2026 20:16:27 +0200 Subject: [PATCH] Fixed the HeightRangeProcessor --- .../common/mixin/JigsawPlacementMixin.java | 97 +++++++++++++++++++ .../accessor/SinglePoolElementAccessor.java | 22 +++++ .../HeightRangeStructureProcessor.kt | 41 +++++++- .../resources/mixins.cobblemon-common.json | 2 + 4 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 common/src/main/java/com/cobblemon/mod/common/mixin/JigsawPlacementMixin.java create mode 100644 common/src/main/java/com/cobblemon/mod/common/mixin/accessor/SinglePoolElementAccessor.java diff --git a/common/src/main/java/com/cobblemon/mod/common/mixin/JigsawPlacementMixin.java b/common/src/main/java/com/cobblemon/mod/common/mixin/JigsawPlacementMixin.java new file mode 100644 index 0000000000..2275f120e6 --- /dev/null +++ b/common/src/main/java/com/cobblemon/mod/common/mixin/JigsawPlacementMixin.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.mixin; + +import com.cobblemon.mod.common.mixin.accessor.SinglePoolElementAccessor; +import com.cobblemon.mod.common.world.structureprocessors.HeightRangeStructureProcessor; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.pools.JigsawPlacement; +import net.minecraft.world.level.levelgen.structure.pools.LegacySinglePoolElement; +import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; +import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; +import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.Optional; + +@Mixin(JigsawPlacement.class) +public class JigsawPlacementMixin { + + @Inject( + method = "addPieces*", + at = @At("HEAD"), + cancellable = true + ) + private static void cobblemon$cancelInvalidHeightRangeStart( + CallbackInfoReturnable> cir, + @Local(argsOnly = true, ordinal = 0) Structure.GenerationContext generationContext, + @Local(argsOnly = true, ordinal = 0) Holder startPool, + @Local(argsOnly = true, ordinal = 0) BlockPos startPos + ) { + if (cobblemon$canAnyStartElementPlace(generationContext, startPool, startPos)) { + return; + } + + cir.setReturnValue(Optional.empty()); + } + + @Unique + private static boolean cobblemon$canAnyStartElementPlace( + Structure.GenerationContext generationContext, + Holder startPool, + BlockPos startPos + ) { + List elements = startPool.value().getShuffledTemplates(RandomSource.create(0L)); + for (StructurePoolElement element : elements) { + Optional heightRangeProcessor = cobblemon$getHeightRangeProcessor(element); + if (heightRangeProcessor.isEmpty()) { + return true; + } + + if (heightRangeProcessor.get().isValidPlacement( + generationContext.chunkGenerator(), + generationContext.heightAccessor(), + generationContext.randomState(), + startPos + )) { + return true; + } + } + + return false; + } + + @Unique + private static Optional cobblemon$getHeightRangeProcessor(StructurePoolElement element) { + if (!(element instanceof SinglePoolElement)) { + return Optional.empty(); + } + + StructureProcessorList processorList = ((SinglePoolElementAccessor) element).getProcessors().value(); + for (StructureProcessor processor : processorList.list()) { + if (processor instanceof HeightRangeStructureProcessor heightRangeStructureProcessor) { + return Optional.of(heightRangeStructureProcessor); + } + } + + return Optional.empty(); + } +} + diff --git a/common/src/main/java/com/cobblemon/mod/common/mixin/accessor/SinglePoolElementAccessor.java b/common/src/main/java/com/cobblemon/mod/common/mixin/accessor/SinglePoolElementAccessor.java new file mode 100644 index 0000000000..b2070fe164 --- /dev/null +++ b/common/src/main/java/com/cobblemon/mod/common/mixin/accessor/SinglePoolElementAccessor.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 Cobblemon Contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package com.cobblemon.mod.common.mixin.accessor; + +import net.minecraft.core.Holder; +import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SinglePoolElement.class) +public interface SinglePoolElementAccessor { + @Accessor("processors") + Holder getProcessors(); +} + diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/world/structureprocessors/HeightRangeStructureProcessor.kt b/common/src/main/kotlin/com/cobblemon/mod/common/world/structureprocessors/HeightRangeStructureProcessor.kt index 95072259eb..91bd29a0be 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/world/structureprocessors/HeightRangeStructureProcessor.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/world/structureprocessors/HeightRangeStructureProcessor.kt @@ -13,7 +13,10 @@ import com.mojang.serialization.MapCodec import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.core.BlockPos import net.minecraft.world.level.LevelReader +import net.minecraft.world.level.LevelHeightAccessor +import net.minecraft.world.level.chunk.ChunkGenerator import net.minecraft.world.level.levelgen.Heightmap +import net.minecraft.world.level.levelgen.RandomState import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate @@ -24,6 +27,25 @@ class HeightRangeStructureProcessor( val reference: HeightReference = HeightReference.SEA_LEVEL ) : StructureProcessor() { + fun isValidPlacement(world: LevelReader, pos: BlockPos): Boolean { + val referenceY = reference.getY(world, pos) + val minY = referenceY + minOffset + val maxY = referenceY + maxOffset + return pos.y in minY..maxY + } + + fun isValidPlacement( + chunkGenerator: ChunkGenerator, + heightAccessor: LevelHeightAccessor, + randomState: RandomState, + pos: BlockPos + ): Boolean { + val referenceY = reference.getY(chunkGenerator, heightAccessor, randomState, pos) + val minY = referenceY + minOffset + val maxY = referenceY + maxOffset + return pos.y in minY..maxY + } + override fun processBlock( world: LevelReader, pos: BlockPos, @@ -32,11 +54,7 @@ class HeightRangeStructureProcessor( currentBlockInfo: StructureTemplate.StructureBlockInfo, data: StructurePlaceSettings ): StructureTemplate.StructureBlockInfo? { - val referenceY = reference.getY(world, pos) - val minY = referenceY + minOffset - val maxY = referenceY + maxOffset - - return if (pos.y in minY..maxY) { + return if (isValidPlacement(world, pos)) { currentBlockInfo } else { null @@ -65,6 +83,19 @@ class HeightRangeStructureProcessor( } } + fun getY( + chunkGenerator: ChunkGenerator, + heightAccessor: LevelHeightAccessor, + randomState: RandomState, + pos: BlockPos + ): Int { + return if (this == SEA_LEVEL) { + chunkGenerator.seaLevel + } else { + chunkGenerator.getBaseHeight(pos.x, pos.z, type ?: Heightmap.Types.WORLD_SURFACE_WG, heightAccessor, randomState) + } + } + companion object { val CODEC: Codec = Codec.STRING.xmap( { serialized -> entries.firstOrNull { it.id == serialized } ?: SEA_LEVEL }, diff --git a/common/src/main/resources/mixins.cobblemon-common.json b/common/src/main/resources/mixins.cobblemon-common.json index 3c54a1b5a6..7c85f1e81f 100644 --- a/common/src/main/resources/mixins.cobblemon-common.json +++ b/common/src/main/resources/mixins.cobblemon-common.json @@ -60,6 +60,7 @@ "EntityStorageMixin", "EntityVillagerMixin", "ItemStackComponentizationFixMixin", + "JigsawPlacementMixin", "LecternBlockMixin", "LightningBoltMixin", "LivingEntityMixin", @@ -92,6 +93,7 @@ "accessor.ChunkMapAccessor", "accessor.DispenserBlockAccessor", "accessor.StatusEffectInstanceAccessor", + "accessor.SinglePoolElementAccessor", "accessor.TrackedEntityAccessor", "brewing.BrewingStandBlockEntityMixin", "brewing.BrewingStandMenuMixin",