Skip to content

Structure search error #9

@ghost

Description

  • There is a 1 in 10 chance that you will be misjudged when using this code to find an end city:
   private static List<BlockPos> locateStructure(Seed seed, Feature feature, EndCityMode endCityMode, BlockPos center, int radius, int maxResults) {
        Dimension dimension = getDimension(feature);
        MCVersion mcVersion = seed.version;
        Structure<?, ?> structure = getStructure(feature, mcVersion);
        if (structure == null) return null;
        BiomeSource biomeSource = BiomeSource.of(dimension, mcVersion, seed.seed);
        if (!structure.isValidDimension(biomeSource.getDimension()))
            return null;
        List<BPos> structurePos = locateStructures(structure, new BPos(center.getX(), center.getY(), center.getZ()), endCityMode, radius, maxResults, new ChunkRand(), biomeSource, TerrainGenerator.of(biomeSource));

        return toBlockPosList(structurePos);
    }

private static List<BPos> locateStructures(Structure<?, ?> structure, BPos center, EndCityMode endCityMode, int radius, int maxResults, ChunkRand chunkRand, BiomeSource source, TerrainGenerator terrainGenerator) {
        List<BPos> result = new ArrayList<>();
        if (structure instanceof EndCity endCity) {
            SpiralIterator<CPos> spiralIterator = new SpiralIterator<>(new CPos(center.getX() >> 4, center.getZ() >> 4), new CPos(radius, radius), (x, y, z) -> new CPos(x, z));

            StreamSupport.stream(spiralIterator.spliterator(), false)
                    .filter(cPos -> {
                        com.seedfinding.mcfeature.Feature.Data<?> data = endCity.at(cPos.getX(), cPos.getZ());
                        EndCityGenerator endCityGenerator = new EndCityGenerator(source.getVersion());
                        return data.testStart(source.getWorldSeed(), chunkRand)
                                && data.testBiome(source)
                                && data.testGenerate(terrainGenerator)
                                && switch (endCityMode) {
                            case OnlyHasShip -> endCityGenerator.generate(terrainGenerator, cPos) && endCityGenerator.hasShip();
                            case OnlyNotShip -> endCityGenerator.generate(terrainGenerator, cPos) && !endCityGenerator.hasShip();
                            case Both -> endCityGenerator.generate(terrainGenerator, cPos);
                        };
                    })
                    .limit(maxResults)
                    .forEach(cPos -> result.add(cPos.toBlockPos().add(9, 0, 9)));
        } else if (structure instanceof RegionStructure<?, ?> regionStructure) {
            int chunkInRegion = regionStructure.getSpacing();
            int regionSize = chunkInRegion * 16;

            final int border = 30_000_000;
            SpiralIterator<RPos> spiralIterator = new SpiralIterator<>(center.toRegionPos(regionSize), new BPos(-border, 0, -border).toRegionPos(regionSize), new BPos(border, 0, border).toRegionPos(regionSize), 1, (x, y, z) -> new RPos(x, z, regionSize));


            StreamSupport.stream(spiralIterator.spliterator(), false)
                    .map(rPos -> regionStructure.getInRegion(source.getWorldSeed(), rPos.getX(), rPos.getZ(), chunkRand))
                    .filter(Objects::nonNull)
                    .filter(cPos -> (regionStructure.canSpawn(cPos, source)) && (terrainGenerator == null || regionStructure.canGenerate(cPos, terrainGenerator)))
                    .limit(maxResults)
                    .forEach(cPos -> result.add(cPos.toBlockPos().add(9, 0, 9)));
        } else {
            if (structure instanceof Stronghold strongholdStructure) {
                CPos currentChunkPos = center.toChunkPos();
                int squaredDistance = Integer.MAX_VALUE;


                for (CPos stronghold : strongholdStructure.getAllStarts(source, chunkRand)) {
                    if (result.size() >= maxResults) {
                        break;
                    }
                    int newSquaredDistance = (currentChunkPos.getX() - stronghold.getX()) * (currentChunkPos.getX() - stronghold.getX()) + (currentChunkPos.getZ() - stronghold.getZ()) * (currentChunkPos.getZ() - stronghold.getZ());
                    if (newSquaredDistance < squaredDistance) {
                        squaredDistance = newSquaredDistance;
                        result.clear();
                        result.add(stronghold.toBlockPos().add(9, 0, 9));
                    }
                }
            } else if (structure instanceof Mineshaft mineshaft) {
                SpiralIterator<CPos> spiralIterator = new SpiralIterator<>(new CPos(center.getX() >> 4, center.getZ() >> 4), new CPos(radius, radius), (x, y, z) -> new CPos(x, z));

                StreamSupport.stream(spiralIterator.spliterator(), false)
                        .filter(cPos -> {
                            com.seedfinding.mcfeature.Feature.Data<Mineshaft> data = mineshaft.at(cPos.getX(), cPos.getZ());
                            return data.testStart(source.getWorldSeed(), chunkRand) && data.testBiome(source) && data.testGenerate(terrainGenerator);
                        })
                        .limit(maxResults)
                        .forEach(cPos -> result.add(cPos.toBlockPos().add(9, 0, 9)));
            }
        }

        return result;
    }


    private static Dimension getDimension(Feature feature) {
        return switch (feature) {
            case buried_treasure -> Dimension.OVERWORLD;
            case mansion -> Dimension.OVERWORLD;
            case stronghold -> Dimension.OVERWORLD;
            case nether_fortress -> Dimension.NETHER;
            case ocean_monument -> Dimension.OVERWORLD;
            case bastion_remnant -> Dimension.NETHER;
            case slime_chunk -> Dimension.OVERWORLD;
            case village -> Dimension.OVERWORLD;
            case mineshaft -> Dimension.OVERWORLD;
            case end_city -> Dimension.END;
            case desert_pyramid -> Dimension.OVERWORLD;
            default -> Dimension.OVERWORLD;
        };
    }

    private static boolean checkIfInDimension(Dimension dimension) {
        return switch (dimension) {
            case OVERWORLD -> (PlayerUtils.getDimension() == meteordevelopment.meteorclient.utils.world.Dimension.Overworld);
            case NETHER -> (PlayerUtils.getDimension() == meteordevelopment.meteorclient.utils.world.Dimension.Nether);
            case END -> (PlayerUtils.getDimension() == meteordevelopment.meteorclient.utils.world.Dimension.End);
        };
    }

    private static Structure<?, ?> getStructure(Feature feature, MCVersion version) {
        return switch (feature) {
            case buried_treasure -> new BuriedTreasure(version);
            case mansion -> new Mansion(version);
            case stronghold -> new Stronghold(version);
            case nether_fortress -> new Fortress(version);
            case ocean_monument -> new Monument(version);
            case bastion_remnant -> new BastionRemnant(version);
            case end_city -> new EndCity(version);
            case village -> new Village(version);
            case mineshaft -> new Mineshaft(version);
            case desert_pyramid -> new DesertPyramid(version);
            default -> null;
        };
    }
    
    private static List<BlockPos> toBlockPosList(List<BPos> posList) {
        List<BlockPos> blockPosList = new ArrayList<>();
        for (BPos pos : posList) {
            blockPosList.add(new BlockPos(pos.getX(), pos.getY(), pos.getZ()));
        }
        return blockPosList;
    }

Seed: -3396536410594980164, Location: -3428 60-13070.

Judging from the seedMap, the location below 1.18.2 will refresh the end city, but above 1.18.2 is useless, I also tried to change the MCVersion of BiomeSource, but the result remains the same, so I suspect that it is an API problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions