Sea Level is assumed to be Y = 63 in numerous parts of Minecraft
The bugs listed here all stem from the bugged method in the screenshot(Level.getSeaLevel). Several bug reports were NOT made because fixing this singular bugged method would fix all the bugs listed.
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:

This means all usages of this method, are possibly "broken" when this value is not changed together with Chunk Generator's sea level.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.
Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.
2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }
3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }
4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }
5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }
6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }
8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:
public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();
8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }
9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }
11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:

Snow Test Under 63(Sea level is -12) datapack:

The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Linked Issues
is duplicated by1
relates to6
Created Issue:
Sea Level is assumed to be Y 63 in numerous parts of Minecraft.
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet to send over containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: [https://youtu.be/5lfsKyAyifI
]
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Environment
Java 16, Windows 10, AMD Ryzen 5600x, GTX 1050TI.
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet to send over containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: [https://youtu.be/5lfsKyAyifI
]
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet to send over containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: [https://youtu.be/5lfsKyAyifI
]
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packetto send overcontaining the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: [https://youtu.be/5lfsKyAyifI
]
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: [https://youtu.be/5lfsKyAyifI
]
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here:
[https://youtu.be/5lfsKyAyifI]
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63.
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Aoxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63
.This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a harcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. A
oxotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }
11.PhantomSpawning changes when sea level is changed from Y 63.12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `*Level.getSeaLevel
` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `
*Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is changed.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is not changed together with Chunk Generator's sea level.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea levelin the chunk generator.I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is not changed together with Chunk Generator's sea level.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }The bugs listed here all stem from the bugged method in the screenshot(Level.getSeaLevel). Several bug reports were NOT made because fixing this singular bugged method would fix all the bugs listed.
Since 1.16's datapack system has been introduced, sea level has become a configurable argument in the chunk generator.
But there's one issue, a lot of places still assume that sea level is 63 when this is no longer the case when changing the sea level in the chunk generator.
I've attached the TallOceansTest & SnowTestUnder63 Datapack to recreate these issues listed.
This source of the issue can be narrowed down to `Level.getSeaLevel` method and usages of it as shown here:
This means all usages of this method, are possibly "broken" when this value is not changed together with Chunk Generator's sea level.
My solution:
In ServerLevel, override this method and call: this.chunkSource.getGenerator.getSeaLevel and on the client you'd most likely need some sort of packet containing the sea level of the current chunk generator/ServerLevel.Here are a list of issues that may occur when changing sea level:
1. Dolphins, Drowned, Turtles, & Squid spawning does not occur in oceans in water when sea level is above Y 63.
No real good way to visualize this. But I've attached a datapack which increases both ocean depth & and the chunk generator's sea level to show this issue. All of the code for these can be found in the respective Mob's spawn rules.2. Axolotls & Glowsquid spawning only occurs when the Ocean Floor Heightmap is under Y 63 as seen here in the `WaterAnimal` class:
public static boolean checkUndergroundWaterCreatureSpawnRules(EntityType<? extends LivingEntity> entityType, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { return pos.getY() < world.getSeaLevel() && pos.getY() < world.getHeight(Heightmap.Types.OCEAN_FLOOR, pos.getX(), pos.getZ()); }3. Ocelot spawning is not obstructed under the chunk generator sea level(when above 63) and ALWAYS obstructed when Chunk Generator sea level/terrain is under 63.
As seen here in the `Ocelot` class.
public boolean checkSpawnObstruction(LevelReader world) { if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { BlockPos blockPos = this.blockPosition(); if (blockPos.getY() < world.getSeaLevel()) { return false; } BlockState blockState = world.getBlockState(blockPos.below()); if (blockState.is(Blocks.GRASS_BLOCK) || blockState.is(BlockTags.LEAVES)) { return true; } } return false; }4. Phantom Anchor Point is set to Y 64 when under Y 63 when Chunk Generator Sea Level is different
This may or may not be correct but the intention seems to be that the anchor point when under sea level(what I would believe should be the Chunk Generator's sea level) should be +1 on the Chunk Generator Sea Level. As seen here in the `Phantom.setAnchorAboveTarget` class:
private void setAnchorAboveTarget() { Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20)); if (Phantom.this.anchorPoint.getY() < Phantom.this.level.getSeaLevel()) { Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level.getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()); } } }5. Bats do not spawn in caves under the Chunk Generator's elevated sea level and are guaranteed to spawn where the Chunk Generator's sea level is lower than 63. As seen here in Bat.checkBatSpawnRules:
public static boolean checkBatSpawnRules(EntityType<Bat> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { if (pos.getY() >= world.getSeaLevel()) { return false; } else { int i = world.getMaxLocalRawBrightness(pos); int j = 4; if (isHalloween()) { j = 7; } else if (random.nextBoolean()) { return false; } return i > random.nextInt(j) ? false : checkMobSpawnRules(type, world, spawnReason, pos, random); } }6. Drowned cannot spawn above Y 58 and are always deep enough to spawn in any fluids under Y 58.
As seen here in Drowned.isDeepEnoughToSpawn:
private static boolean isDeepEnoughToSpawn(LevelAccessor world, BlockPos pos) { return pos.getY() < world.getSeaLevel() - 5; }
7. Blue Ice Feature will fail in water bodies above Y 62.
As seen in BlueIceFeature.place:
if (blockPos.getY() > worldGenLevel.getSeaLevel() - 1) { return false; }8. Ocean monuments spawn at a hardcoded height of Y 63, disregarding ocean floor height/sea level. You can see that here: https://youtu.be/5lfsKyAyifI
The code that produces this can be found in 2 places in the `OceanMonumentPieces` class.
8a. OceanMonumentPieces.postProcess:public boolean postProcess(WorldGenLevel world, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, Random random, BoundingBox boundingBox, ChunkPos chunkPos, BlockPos pos) { int i = Math.max(world.getSeaLevel(), 64) - this.boundingBox.minY();8b. OceanMonumentPieces.generateWaterBox:
protected void generateWaterBox(WorldGenLevel world, BoundingBox box, int x, int y, int z, int width, int height, int depth) { for(int i = y; i <= height; ++i) { for(int j = x; j <= width; ++j) { for(int k = z; k <= depth; ++k) { BlockState blockState = this.getBlock(world, j, i, k, box); if (!FILL_KEEP.contains(blockState.getBlock())) { if (this.getWorldY(i) >= world.getSeaLevel() && blockState != FILL_BLOCK) { this.placeBlock(world, Blocks.AIR.defaultBlockState(), j, i, k, box); } else { this.placeBlock(world, FILL_BLOCK, j, i, k, box); } } } } } }9. Blocks of air are ALWAYS placed in Ocean ruins when the chunk generator's sea level is ABOVE 63 or blocks of water are ALWAYS placed when Chunk Generator sea level is UNDER 63 in `handleDataMarker`.
As seen in OceanRuinPieces.handleDataMarker:
else if ("drowned".equals(metadata)) { Drowned drowned = (Drowned)EntityType.DROWNED.create(world.getLevel()); drowned.setPersistenceRequired(); drowned.moveTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.STRUCTURE, (SpawnGroupData)null, (CompoundTag)null); world.addFreshEntityWithPassengers(drowned); if (pos.getY() > world.getSeaLevel()) { world.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { world.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); }
10. Axolotl & Turtle Path finding may produce inaccuracies in oceans w/ water above 53.
Not sure on enttiy AI myself but this issue can be seen in `AmphibiousNodeEvaluator.getNeighbors` when incrementing a Path Finding Node's cost malus:
public int getNeighbors(Node[] successors, Node node) { int i = super.getNeighbors(successors, node); BlockPathTypes blockPathTypes = this.getCachedBlockType(this.mob, node.x, node.y + 1, node.z); BlockPathTypes blockPathTypes2 = this.getCachedBlockType(this.mob, node.x, node.y, node.z); int k; if (this.mob.getPathfindingMalus(blockPathTypes) >= 0.0F && blockPathTypes2 != BlockPathTypes.STICKY_HONEY) { k = Mth.floor(Math.max(1.0F, this.mob.maxUpStep)); } else { k = 0; } double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z)); Node node2 = this.findAcceptedNode(node.x, node.y + 1, node.z, Math.max(0, k - 1), d, Direction.UP, blockPathTypes2); Node node3 = this.findAcceptedNode(node.x, node.y - 1, node.z, k, d, Direction.DOWN, blockPathTypes2); if (this.isNeighborValid(node2, node)) { successors[i++] = node2; } if (this.isNeighborValid(node3, node) && blockPathTypes2 != BlockPathTypes.TRAPDOOR) { successors[i++] = node3; } for(int l = 0; l < i; ++l) { Node node4 = successors[l]; if (node4.type == BlockPathTypes.WATER && this.prefersShallowSwimming && node4.y < this.mob.level.getSeaLevel() - 10) { ++node4.costMalus; } } return i; }11. Phantom Spawning changes when sea level is changed from Y 63.
Issue can be found in: PantomSpawner.tick12. Biome temperature height adjustment is hardcoded to Y 64(I presume this to be sea level + 1). You can see this issue very clearly here with these 2 images:
Vanilla:
Snow Test Under 63(Sea level is -12) datapack:
The code in question can be found in Biome.getHeightAdjustedTemperature:
private float getHeightAdjustedTemperature(BlockPos pos) { float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature()); if (pos.getY() > 64) { float g = (float)(TEMPERATURE_NOISE.getValue((double)((float)pos.getX() / 8.0F), (double)((float)pos.getZ() / 8.0F), false) * 4.0D); return f - (g + (float)pos.getY() - 64.0F) * 0.05F / 30.0F; } else { return f; } }
Sea Level is assumed to be Y 63 in numerous parts of Minecraft.
Java 16, Windows 10, AMD Ryzen 5600x, GTX 1050TI.
mappings are these? MCP?
relates to
is duplicated by
relates to
Sea Level is assumed to be Y = 63 in numerous parts of Minecraft
relates to
Thank you for your report!
We're tracking this issue in MC-226687, so this ticket is being resolved and linked as a duplicate.
If you would like to add a vote and any extra information to the main ticket it would be appreciated.
If you haven't already, you might like to make use of the search feature to see if the issue has already been mentioned.
Quick Links:
📓 Bug Tracker Guidelines – 💬 Community Support – 📧 Mojang Support
📓 Project Summary – ✍️ Feedback and Suggestions – 📖 Game Wiki
The sea level in superflat worlds is currently at Y = -63.
This causes some unintended behavior:
- MC-277216 - Players need to stay below Y=-63 instead of Y=63 to stop Phantoms spawning in superflat worlds
- MC-276319 - Aquatic mobs fail to spawn naturally in superflat worlds
- Changes to the height at which snow occurs instead of rain. In a superflat Taiga biome on 1.21.1 snow would fall starting from y=153 (sea level of 63 + 90), while on 1.21.3 snow falls starting from y=27 (sea level of -63 + 90).
Cause
The change occurred in 24w33a. The hardcoded sea level was replaced with an argument that can be set in the chunk generator. See the original issue: MC-226687
Suggestions & solutions
- Set the sea level to the highest layer of the superflat terrain when making a new world
- Set the sea level to y=63 on superflat worlds upgrading from older versions
How to recreate
Create a new creative mode superflat world. Select the Classic Flat superflat world preset and change minecraft:plains to minecraft:taiga. Enter the world and run the command
/weather rain
Then, ensure you are flying and run the command
/tp @p 0 40 0
In versions after 24w33a such as 1.21.3, it will be snowing. In versions prior to 24w33a such as 1.21.1 it will be raining.
For recreating the phantom spawning and aquatic mob spawning issues see their respective issues linked above.
This issue is very important for custom worldgen now that MC-226687 has been resolved. Floating Islands-style custom worldgen packs usually set sea-level to -64 to remove the lava at the bottom of the world. But now, doing this results in a cascade of unintended side-effects, like preventing aquatic mobs from spawning above the bottom of the world.
@osfanjoshua Counterpoint, all these seemingly separate bugs are all caused by a single bug, the world using a hardcoded value for sealevel instead of the chunkgenerator's sealevel that the world already has. This disconnection between the hardcoded sealevel and actual sealevel used for worldgen cascades and causes numerous amount of issues down the line.
It seems far to unproductive to create a bug report for every single issue caused by a single bug and have the mojang dev hunt down every single one of these reports to mark as resolved when they fix the one root cause.
Is the sky turning black under the sea level also part of this?
(MC-118103)
Can confirm in 1.18 Pre-release 2.
The code in Biome.getHeightAdjustedTemperature(...) has been changed to 80 instead of 64, however.
Also, now glow squids use their own spawn rules, but still are dependent on the hardcoded sea level.
Can confirm in 1.18 Pre-release 3.
Can confirm in 1.18 Pre-release 4.
Potentially the cause of MC-257201 Nether caves not filling to sea level - Jira (mojang.com) too?