diff --git a/common/common-core/src/main/java/com/pg85/otg/gen/OTGChunkGenerator.java b/common/common-core/src/main/java/com/pg85/otg/gen/OTGChunkGenerator.java index 7653c9949..b9a1e2e5b 100644 --- a/common/common-core/src/main/java/com/pg85/otg/gen/OTGChunkGenerator.java +++ b/common/common-core/src/main/java/com/pg85/otg/gen/OTGChunkGenerator.java @@ -249,7 +249,11 @@ private BlendedColumn blendColumnParams(int noiseX, int noiseZ) { cacheX = x1 + largestRadius; for (int z1 = -smoothRadius; z1 <= smoothRadius; ++z1) { cacheZ = z1 + largestRadius; - biome = biomes[cacheX * areaSize + cacheZ]; + int biomeIndex = cacheX * areaSize + cacheZ; + if (biomeIndex >= biomes.length || (biome = biomes[biomeIndex]) == null) { + // Provider can come up short at region edges; skip instead of crashing + continue; + } biomeTerrainSettings = biome.getTerrainSettings(); heightAt = biomeTerrainSettings.getBiomeHeight(); // TODO: vanilla reduces the weight by half when the depth here is greater than the center depth, but OTG doesn't do that? @@ -277,7 +281,10 @@ private BlendedColumn blendColumnParams(int noiseX, int noiseZ) { cacheX = x1 + largestRadius; for (int z1 = -chcSmoothRadius; z1 <= chcSmoothRadius; ++z1) { cacheZ = z1 + largestRadius; - biome = biomes[cacheX * areaSize + cacheZ]; + int chcBiomeIndex = cacheX * areaSize + cacheZ; + if (chcBiomeIndex >= biomes.length || (biome = biomes[chcBiomeIndex]) == null) { + continue; + } heightAt = biome.getTerrainSettings().getBiomeHeight(); weightAt = BIOME_WEIGHT_TABLE[x1 + 32 + (z1 + 32) * 65] / (heightAt + 2.0F); diff --git a/common/common-generator/src/main/java/com/pg85/otg/gen/carver/Carver.java b/common/common-generator/src/main/java/com/pg85/otg/gen/carver/Carver.java index 11765d1d3..7b268e0db 100644 --- a/common/common-generator/src/main/java/com/pg85/otg/gen/carver/Carver.java +++ b/common/common-generator/src/main/java/com/pg85/otg/gen/carver/Carver.java @@ -135,7 +135,8 @@ protected boolean carveRegion( currentY, currentZ, foundSurface, - biomeConfig + biomeConfig, + otgWorldInfo ); } } @@ -155,10 +156,13 @@ protected boolean carveAtPoint( int y, int relativeZ, MutableBoolean foundSurface, - BiomeSettings biomeConfig + BiomeSettings biomeConfig, + OTGWorldInfo otgWorldInfo ) { SurfaceSettings surface = biomeConfig.getSurfaceSettings(); - int i = relativeX | relativeZ << 4 | y << 8; + // Offset Y by minY to ensure a non-negative BitSet index (1.18+ worlds can have negative Y) + int offsetY = y - otgWorldInfo.minY(); + int i = relativeX | relativeZ << 4 | offsetY << 8; if (carvingMask.get(i)) { return false; } diff --git a/common/common-generator/src/main/java/com/pg85/otg/gen/resource/DungeonResource.java b/common/common-generator/src/main/java/com/pg85/otg/gen/resource/DungeonResource.java index 91d102e8e..8938b7ef3 100644 --- a/common/common-generator/src/main/java/com/pg85/otg/gen/resource/DungeonResource.java +++ b/common/common-generator/src/main/java/com/pg85/otg/gen/resource/DungeonResource.java @@ -19,9 +19,12 @@ public DungeonResource(BiomeSettings biomeConfig, List args) throws Inva super(biomeConfig, args); assureSize(3, args); + // Legacy presets use Dungeon(Frequency,Rarity,MinAltitude,MaxAltitude); + // the current format drops Frequency (always 1). Accept both. + int offset = args.size() >= 4 ? 1 : 0; this.frequency = 1; - this.rarity = readRarity(args.get(0)); - Pair elevations = readElevations(args.get(1), args.get(2)); + this.rarity = readRarity(args.get(offset)); + Pair elevations = readElevations(args.get(offset + 1), args.get(offset + 2)); this.minAltitude = elevations.getFirst(); this.maxAltitude = elevations.getSecond(); diff --git a/common/common-util/src/main/java/com/pg85/otg/config/settings/biome/BiomeSettings.java b/common/common-util/src/main/java/com/pg85/otg/config/settings/biome/BiomeSettings.java index be0c68a2c..a17b6a272 100644 --- a/common/common-util/src/main/java/com/pg85/otg/config/settings/biome/BiomeSettings.java +++ b/common/common-util/src/main/java/com/pg85/otg/config/settings/biome/BiomeSettings.java @@ -73,7 +73,13 @@ public String getConfigName() { // Height / volatility public double getCHCData(int controlLayer) { - return this.getTerrainSettings().getCustomHeightControl().get(controlLayer); + var chc = this.getTerrainSettings().getCustomHeightControl(); + if (controlLayer < 0 || controlLayer >= chc.size()) { + // Legacy presets carry CHC arrays sized for 256-high worlds; extended + // height worlds index past them. Treat missing layers as no control. + return 0.0; + } + return chc.get(controlLayer); } // OTG Custom structures (BO's) diff --git a/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/FabricWorldGenRegion.java b/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/FabricWorldGenRegion.java index 2af1f37ad..f1ebf78a8 100644 --- a/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/FabricWorldGenRegion.java +++ b/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/FabricWorldGenRegion.java @@ -20,7 +20,6 @@ import com.pg85.otg.util.materials.LocalMaterials; import com.pg85.otg.util.minecraft.TreeType; import com.pg85.otg.util.nbt.NamedBinaryTag; -import net.minecraft.ReportedException; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.data.worldgen.features.CaveFeatures; @@ -28,7 +27,7 @@ import net.minecraft.data.worldgen.features.TreeFeatures; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.TagParser; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.*; import net.minecraft.world.entity.monster.Guardian; @@ -46,9 +45,6 @@ import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; import java.text.MessageFormat; import java.util.Optional; import java.util.Random; @@ -513,26 +509,17 @@ public void spawnEntity(IEntityFunction entityData) { nbtTagCompound = new CompoundTag(); if (entityData.getNameTagOrNBTFileName().toLowerCase().trim().endsWith(".txt")) { try { - var inputStream = - new DataInputStream(new ByteArrayInputStream(entityData.getMetaData().getBytes())); - nbtTagCompound = NbtIo.read(inputStream); - } catch (IOException | ReportedException e) { - if (OTGLog.getLogger().getLogCategoryEnabled(LogCategory.CUSTOM_OBJECTS)) { - OTGLog.log( - LogLevel.ERROR, - LogCategory.CUSTOM_OBJECTS, - "Could not parse nbt for Entity() " - + entityData.makeString() - + ", file: " - + entityData.getNameTagOrNBTFileName() - ); - } - throw new RuntimeException( - "Could not parse nbt for Entity() " - + entityData.makeString() - + ", file: " - + entityData.getNameTagOrNBTFileName(), e + // .txt files contain text NBT (SNBT), not the binary format + nbtTagCompound = TagParser.parseTag(entityData.getMetaData()); + } catch (Exception e) { + // A broken preset file shouldn't kill chunk generation — skip the entity + OTGLog.getLogger().error(LogCategory.CUSTOM_OBJECTS, + "Could not parse nbt for Entity() %s, file: %s, error: %s", + entityData.makeString(), + entityData.getNameTagOrNBTFileName(), + e.getMessage() ); + return; } // Specify which type of entity to spawn nbtTagCompound.putString("id", entityData.getResourceLocation()); diff --git a/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/noise/OTGNoiseRouterFactory.java b/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/noise/OTGNoiseRouterFactory.java index 0dbb42ebc..a66553212 100644 --- a/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/noise/OTGNoiseRouterFactory.java +++ b/platforms/fabric/src/main/java/com/pg85/otg/fabric/gen/noise/OTGNoiseRouterFactory.java @@ -39,7 +39,9 @@ * cave threshold depth; caves are carved out of solid terrain only. * - No slideOverworld: OTG's pipeline already fades the top of the world, and vanilla's slide * anchors are hardcoded to -64..320, which mis-anchors on configurable world heights. - * - Aquifers and ore veins disabled; fluid placement comes from {@link OTGFluidPicker}. + * - Ore veins disabled. Aquifers run with vanilla noises on top of {@link OTGFluidPicker} + * as the global picker: open terrain floods to the per-biome water level, while caves + * below it stay dry apart from vanilla-style aquifer pockets. * - Climate/biome slots are zero; OTG does its own biome placement. */ public final class OTGNoiseRouterFactory { @@ -73,11 +75,15 @@ public static OTGNoiseCaveContext create( DensityFunction depthProxy = new OTGDepthProxyFunction(internalGenerator, depthGradient); DensityFunction finalDensity = finalDensity(otgTerrain, depthProxy, functions, noises); + // Vanilla aquifer noises (NoiseRouterData.overworld, 1.20.1). With aquifers enabled, + // the aquifer keeps caves below the water level dry apart from local pockets; + // OTGFluidPicker stays the global picker, so per-biome water levels still apply + // to open terrain (oceans, lakes). NoiseRouter router = new NoiseRouter( - DensityFunctions.zero(), // barrierNoise - DensityFunctions.zero(), // fluidLevelFloodednessNoise - DensityFunctions.zero(), // fluidLevelSpreadNoise - DensityFunctions.zero(), // lavaNoise + DensityFunctions.noise(noises.getOrThrow(Noises.AQUIFER_BARRIER), 0.5), // barrierNoise + DensityFunctions.noise(noises.getOrThrow(Noises.AQUIFER_FLUID_LEVEL_FLOODEDNESS), 0.67), // fluidLevelFloodednessNoise + DensityFunctions.noise(noises.getOrThrow(Noises.AQUIFER_FLUID_LEVEL_SPREAD), 0.7142857142857143), // fluidLevelSpreadNoise + DensityFunctions.noise(noises.getOrThrow(Noises.AQUIFER_LAVA)), // lavaNoise DensityFunctions.zero(), // temperature DensityFunctions.zero(), // vegetation DensityFunctions.zero(), // continents @@ -100,7 +106,7 @@ public static OTGNoiseCaveContext create( registeredSettings.spawnTarget(), registeredSettings.seaLevel(), registeredSettings.disableMobGeneration(), - false, // aquifersEnabled: NoiseChunk uses Aquifer.createDisabled(fluidPicker) + true, // aquifersEnabled: without them every cave below the water level floods false, // oreVeinsEnabled: vein router slots are zero registeredSettings.useLegacyRandomSource() );