Merge branch 'fix-/locate-lie' into 'main'

Fixed the HeightRangeProcessor

See merge request cable-mc/cobblemon!2297
This commit is contained in:
Polymeta
2026-06-04 10:43:12 +00:00
4 changed files with 157 additions and 5 deletions
@@ -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<Optional<Structure.GenerationStub>> cir,
@Local(argsOnly = true, ordinal = 0) Structure.GenerationContext generationContext,
@Local(argsOnly = true, ordinal = 0) Holder<StructureTemplatePool> 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<StructureTemplatePool> startPool,
BlockPos startPos
) {
List<StructurePoolElement> elements = startPool.value().getShuffledTemplates(RandomSource.create(0L));
for (StructurePoolElement element : elements) {
Optional<HeightRangeStructureProcessor> 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<HeightRangeStructureProcessor> 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();
}
}
@@ -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<StructureProcessorList> getProcessors();
}
@@ -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<HeightReference> = Codec.STRING.xmap(
{ serialized -> entries.firstOrNull { it.id == serialized } ?: SEA_LEVEL },
@@ -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",