Shipwrecks that generate on land/beaches can get split on the Y axis when on chunk boundaries. To reproduce, use the seed 551270389
and run the following command:
/execute in minecraft:overworld run tp @p 202 68 144
Code analysis
Code analysis by @unknown can be found in this comment.
Linked issues
is duplicated by 32
Attachments
Comments 22
I can't see this issue with your given seed and coordinates. Can you post the seed/coordinates of the other villages and shipwrecks in your screenshots?
It seems to work just fine for me. Check your seed and coordinates again and look at the shipwreck in game to see if it looks any different from the screenshots (including the ones I just added). It always worked for me regardless of render distance or performance so it should be easier than cake to teleport to the given coordinates and look at the glitched shipwreck in game. This also doesn't happen very often so I posted more screenshots to show the glitched shipwreck for now.
I see it now and can confirm this issue.
For some reason the game must have converted the seed I pasted as if it wasn't a numeric string, and it just happened to have a shipwreck at those coordinates specified.
That's good. I think you accidentally put a space before the seed I provided and teleported to a shipwreck that was like mine, but sideways and non-glitched (I used the same seed but with a space at the start). Just make sure the seed you paste doesn't have a space at the start because it actually changes the seed.
Code Analysis (Yarn 22w15a)
net.minecraft.structure.ShipwreckGenerator.java
public void generate(StructureWorldAccess world, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, AbstractRandom abstractRandom, BlockBox chunkBox, ChunkPos chunkPos, BlockPos pos) {
int minY = world.getTopY(), topY = 0;
Vec3i size = this.structure.getSize();
Heightmap.Type type = this.grounded ? Heightmap.Type.WORLD_SURFACE_WG : Heightmap.Type.OCEAN_FLOOR_WG;
int k = size.getX() * size.getZ();
if (k == 0) { // If size is 0, not relevant here
topY = world.getTopY(type, this.pos.getX(), this.pos.getZ());
} else {
BlockPos blockPos = this.pos.add(size.getX() - 1, 0, size.getZ() - 1);
for(BlockPos blockPos2 : BlockPos.iterate(this.pos, blockPos)) {
int y = world.getTopY(type, blockPos2.getX(), blockPos2.getZ());
topY += y;
minY = Math.min(minY, y);
}
topY /= k;
}
int newY = this.grounded ? minY - size.getY() / 2 - abstractRandom.nextInt(3) : topY;
this.pos = new BlockPos(this.pos.getX(), newY, this.pos.getZ());
super.generate(world, structureAccessor, chunkGenerator, abstractRandom, chunkBox, chunkPos, pos);
}
If you look at the code above, the bug is only visible when this.grounded
is true
which matches all previous sightings of this bug, basically if it's beached.
There are actually 3 major issues at play here. I'm going to first talk about the use of abstractRandom.nextInt(3)
The same abstractRandom
is used for all pieces, so by doing .nextInt(3)
you guarantee that all individual pieces have a different value. This results in the different pieces of the shipwreck being at different heights. Instead, you should use positional random which you do later in the shipwreck code. Such as:this.placementData.getRandom(this.pos).nextInt(3)
The next issue is that this.pos
's Y value get's set by the results of the random. In turn, making the next result get a different random outcome due to getting a different position. We would fix this by adding something like:this.pos.withY(0)
The last issue is that the ship palette changes between the chunk borders. Although the above fixes already fix this issue, let me explain:
net.minecraft.structure.Structure.java
public boolean place(ServerWorldAccess world, BlockPos pos, BlockPos pivot, StructurePlacementData placementData, AbstractRandom abstractRandom, int flags) {
// ...
// In the next code block I will show you `getRandomBlockInfos` we will be following the `pos`
List<Structure.StructureBlockInfo> list = placementData.getRandomBlockInfos(blockInfoLists, pos).getAll();
// ...
net.minecraft.structure.StructurePlacementData.java
public Structure.PalettedBlockInfoList getRandomBlockInfos(List list, BlockPos pos) {
// ...
// In the next code block ill show the code for `this.getRandom(pos)` the positional random being used
return list.get(this.getRandom(pos).nextInt(i));
// ...
}
public AbstractRandom getRandom(@Nullable BlockPos pos) {
// ...
// This creates a random based on the blockPos hashCode, so it's positional
return pos == null ? AbstractRandom.method_43049(Util.getMeasuringTimeMs()) : AbstractRandom.method_43049(MathHelper.hashCode(pos));
// ...
}
All that is just to say that placementData.getRandomBlockInfos
picks which palette to use based on the position of the structure. This means that by fixing the position issues in the 2 other problems, the palette gets chosen correctly.
This only affects shipwrecks, other border chunk issues are either related to there own structures or based on a larger issue
Can confirm in 1.19.1.
[media]Version: 1.19.1
Seed: -1541328582584882083
Coordinates: /execute in minecraft:overworld run tp @s 55.84 70.91 392.37 -59.10 40.60
[^1.20.1 large biomes x:-978 y:74 z:-1111 seed:-6466533622157603850.png]
[media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media][media]