From 02248659355e2022a4e8ff2784252885864e31ec Mon Sep 17 00:00:00 2001 From: Cubitect Date: Thu, 18 Mar 2021 09:22:02 +0100 Subject: [PATCH] API change for getStructurePos to support new structs for Nether and End --- finders.c | 193 ++++++++++++++++++++++++++++++++++++++---------------- finders.h | 52 ++++++++++----- 2 files changed, 173 insertions(+), 72 deletions(-) diff --git a/finders.c b/finders.c index 7f07435..be69bca 100644 --- a/finders.c +++ b/finders.c @@ -74,57 +74,109 @@ int64_t *loadSavedSeeds(const char *fnam, int64_t *scnt) //============================================================================== -static int testOutpostPos(int64_t s, int cx, int cz) +void setAttemptSeed(int64_t *s, int cx, int cz) { - s ^= (cx >> 4) ^ ( (cz >> 4) << 4 ); - setSeed(&s, s); - next(&s, 32); - return nextInt(&s, 5) == 0; + *s ^= (cx >> 4) ^ ( (cz >> 4) << 4 ); + setSeed(s, *s); + next(s, 31); } -Pos getStructurePos(StructureConfig config, int64_t seed, int regX, int regZ, int *valid) +int getStructurePos(int structureType, int mc, int64_t seed, int regX, int regZ, Pos *pos) { - Pos pos = {0,0}; - if (valid) *valid = 0; + StructureConfig sconf; + switch (structureType) + { + case Feature: + if (mc > MC_1_12) return 0; + sconf = FEATURE_CONFIG; + goto L_feature; + case Desert_Pyramid: + sconf = mc <= MC_1_12 ? DESERT_PYRAMID_CONFIG_112 : DESERT_PYRAMID_CONFIG; + goto L_feature; + case Jungle_Pyramid: + sconf = mc <= MC_1_12 ? JUNGLE_PYRAMID_CONFIG_112 : JUNGLE_PYRAMID_CONFIG; + goto L_feature; + case Swamp_Hut: + sconf = mc <= MC_1_12 ? SWAMP_HUT_CONFIG_112 : SWAMP_HUT_CONFIG; + goto L_feature; + case Igloo: + if (mc < MC_1_9) return 0; + sconf = mc <= MC_1_12 ? IGLOO_CONFIG_112 : IGLOO_CONFIG; + goto L_feature; + case Village: + sconf = VILLAGE_CONFIG; + goto L_feature; + case Ocean_Ruin: + if (mc < MC_1_13) return 0; + sconf = mc <= MC_1_15 ? OCEAN_RUIN_CONFIG_115 : OCEAN_RUIN_CONFIG; + goto L_feature; + case Shipwreck: + if (mc < MC_1_13) return 0; + sconf = mc <= MC_1_15 ? SHIPWRECK_CONFIG_115 : SHIPWRECK_CONFIG; + goto L_feature; + case Ruined_Portal: + if (mc < MC_1_16) return 0; + sconf = RUINED_PORTAL_CONFIG; +L_feature: + *pos = getFeaturePos(sconf, seed, regX, regZ); + return 1; - if (config.properties == 0) - { - pos = getFeaturePos(config, seed, regX, regZ); - if (valid) - { - if (config.structType == Outpost) - { - *valid = testOutpostPos(seed, pos.x >> 4, pos.z >> 4); - // Outposts also require that there are no villages nearby. - // However, before 1.16 this would include a biome check, so it - // should be tested for in the position viability check. - } - else - { - *valid = 1; - } - } - } - else if (config.properties == LARGE_STRUCT) - { - if ((config.chunkRange & (config.chunkRange-1))) - { - pos = getLargeStructurePos(config, seed, regX, regZ); - if (valid) *valid = 1; - } - } - else if (config.properties == CHUNK_STRUCT) - { - pos.x = (regX << 4) + 9; - pos.z = (regZ << 4) + 9; - if (valid) - { - if (config.structType == Treasure) - *valid = isTreasureChunk(seed, regX, regZ); - } - } + case Monument: + if (mc < MC_1_8) return 0; + sconf = MONUMENT_CONFIG; + goto L_large_struct; + case End_City: + if (mc < MC_1_9) return 0; + sconf = END_CITY_CONFIG; + goto L_large_struct; + case Mansion: + if (mc < MC_1_11) return 0; + sconf = MANSION_CONFIG; +L_large_struct: + *pos = getLargeStructurePos(sconf, seed, regX, regZ); + return 1; - return pos; + case Outpost: + if (mc < MC_1_14) return 0; + *pos = getFeaturePos(OUTPOST_CONFIG, seed, regX, regZ); + setAttemptSeed(&seed, (pos->x) >> 4, (pos->z) >> 4); + return nextInt(&seed, 5) == 0; + + case Treasure: + if (mc < MC_1_13) return 0; + pos->x = (regX << 4) + 9; + pos->z = (regZ << 4) + 9; + return isTreasureChunk(seed, regX, regZ); + + case Fortress: + sconf = FORTRESS_CONFIG; + if (mc < MC_1_16) { + setAttemptSeed(&seed, regX << 4, regZ << 4); + int valid = nextInt(&seed, 3) == 0; + pos->x = ((regX << 4) + nextInt(&seed, 8) + 4) << 4; + pos->z = ((regZ << 4) + nextInt(&seed, 8) + 4) << 4; + return valid; + } else { + setSeed(&seed, regX*341873128712 + regZ*132897987541 + seed + sconf.salt); + pos->x = (regX * sconf.regionSize + nextInt(&seed, 24)) << 4; + pos->z = (regZ * sconf.regionSize + nextInt(&seed, 24)) << 4; + return nextInt(&seed, 5) < 2; + } + + case Bastion: + if (mc < MC_1_16) return 0; + sconf = BASTION_CONFIG; + setSeed(&seed, regX*341873128712 + regZ*132897987541 + seed + sconf.salt); + pos->x = (regX * sconf.regionSize + nextInt(&seed, 24)) << 4; + pos->z = (regZ * sconf.regionSize + nextInt(&seed, 24)) << 4; + return nextInt(&seed, 5) >= 2; + + default: + fprintf(stderr, + "ERR getStructurePos: unsupported structure type %d\n", structureType); + exit(-1); + } + return 0; } int isMineshaftChunk(int64_t seed, int chunkX, int chunkZ) @@ -503,7 +555,7 @@ int searchAll48( // split path into directory and file and create missing directories if (pathlen + 8 >= sizeof(dpath)) - goto L_ERR; + goto L_err; strcpy(dpath, path); for (i = pathlen-1; i >= 0; i--) @@ -512,7 +564,7 @@ int searchAll48( { dpath[i] = 0; if (mkdirp(dpath)) - goto L_ERR; + goto L_err; break; } } @@ -520,7 +572,7 @@ int searchAll48( else if (seedbuf == NULL || buflen == NULL) { // no file and no buffer return: no output possible - goto L_ERR; + goto L_err; } // prepare the thread info and load progress if present @@ -540,7 +592,7 @@ int searchAll48( snprintf(info[t].path, sizeof(info[t].path), "%s.part%d", path, t); FILE *fp = fopen(info[t].path, "a+"); if (fp == NULL) - goto L_ERR; + goto L_err; int c, nnl = 0; char buf[32]; @@ -607,7 +659,7 @@ int searchAll48( // merge partial files FILE *fp = fopen(path, "w"); if (fp == NULL) - goto L_ERR; + goto L_err; for (t = 0; t < threads; t++) { @@ -620,7 +672,7 @@ int searchAll48( if (!fwrite(buffer, sizeof(char), n, fp)) { fclose(fp); - goto L_ERR; + goto L_err; } } @@ -673,7 +725,7 @@ int searchAll48( } if (0) -L_ERR: +L_err: err = 1; free(tids); @@ -1303,8 +1355,8 @@ Pos estimateSpawn(const int mcversion, const LayerStack *g, int *cache, int64_t if (mcversion >= MC_1_13) { - spawn.x = (spawn.x >> 4) << 4; - spawn.z = (spawn.z >> 4) << 4; + spawn.x &= ~0xf; + spawn.z &= ~0xf; } return spawn; @@ -1370,6 +1422,20 @@ int isViableFeatureBiome(int mc, int structureType, int biomeID) if (mc < MC_1_11) return 0; return biomeID == dark_forest || biomeID == dark_forest_hills; + case Fortress: + return (biomeID == nether_wastes || biomeID == soul_sand_valley || + biomeID == warped_forest || biomeID == crimson_forest); + + case Bastion: + if (mc < MC_1_16) return 0; + return (biomeID == nether_wastes || biomeID == soul_sand_valley || + biomeID == warped_forest || biomeID == crimson_forest || + biomeID == basalt_deltas); + + case End_City: + if (mc < MC_1_9) return 0; + return biomeID == end_midlands || biomeID == end_highlands; + default: fprintf(stderr, "isViableFeatureBiome: not implemented for structure type %d.\n", @@ -1599,7 +1665,11 @@ L_feature: case Outpost: { - if (mc < MC_1_14 || !testOutpostPos(seed, chunkX, chunkZ)) + if (mc < MC_1_14) + goto L_not_viable; + int64_t rnd = seed; + setAttemptSeed(&rnd, chunkX, chunkZ); + if (nextInt(&rnd, 5) != 0) goto L_not_viable; if (mc < MC_1_16) { @@ -1712,6 +1782,19 @@ L_not_viable: return viable; } +int isViableNetherStructurePos(int structureType, int mc, NetherNoise *nn, + int64_t seed, int blockX, int blockZ) +{ + if (mc < MC_1_16) + return structureType == Fortress; + + blockX = ((blockX >> 4) << 2) + 2; + blockZ = ((blockZ >> 4) << 2) + 2; + setNetherSeed(nn, seed); + int biomeID = getNetherBiome(nn, blockX, 0, blockZ); + return isViableFeatureBiome(mc, structureType, biomeID); +} + //============================================================================== // Finding Properties of Structures diff --git a/finders.h b/finders.h index 64b8072..ad10d6e 100644 --- a/finders.h +++ b/finders.h @@ -46,6 +46,9 @@ enum StructureType Outpost, Ruined_Portal, Treasure, + Fortress, + Bastion, + End_City, }; enum // village house types prior to 1.14 @@ -92,6 +95,12 @@ static const StructureConfig RUINED_PORTAL_CONFIG = { 34222645, 40, 25, Ruined_ // structures that check each chunk individually static const StructureConfig TREASURE_CONFIG = { 10387320, 1, 1, Treasure, CHUNK_STRUCT}; +// nether and end structures +static const StructureConfig FORTRESS_CONFIG = { 30084232, 27, 4, Fortress, 0}; +static const StructureConfig BASTION_CONFIG = { 30084232, 27, 4, Bastion, 0}; +static const StructureConfig END_CITY_CONFIG = { 10387313, 20, 9, End_City, LARGE_STRUCT}; + + //============================================================================== // Biome Tables //============================================================================== @@ -270,21 +279,28 @@ int64_t *loadSavedSeeds(const char *fnam, int64_t *scnt); // Finding Structure Positions //============================================================================== -/* Finds the block position at which the structure generation attempt will - * occur within the specified region. This function is a wrapper for the more - * specific inlinable functions, which can be found below. You can use - * isViableStructurePos() to test if the necessary biome requirements are met - * for the structure to actually generate at the returned position (much much - * slower than checking attempts). - * - * @config : the structure configuration - * @seed : world seed (only the lower 48-bits are relevant) - * @regX,regZ : region coordinates - * @valid : some structures, like outposts, can have invalid positions, - * use NULL to ignore this options - */ -Pos getStructurePos(StructureConfig config, int64_t seed, int regX, int regZ, int *valid); +/* Finds the block position of the structure generation attempt in a given + * region. You can use isViableStructurePos() to test if the necessary biome + * requirements are met for the structure to actually generate at that position. + * Some structure types may fail to produce a valid position in the given + * region regardless of biomes, in which case the function returns zero. + * + * @structureType : structure type + * @mc : minecraft version + * @seed : world seed (only the lower 48-bits are relevant) + * @regX,regZ : region coordinates (the region size depends on type) + * @pos : output block position + * + * Returns zero if the position is invalid, or non-zero otherwise. + */ +int getStructurePos(int structureType, int mc, int64_t seed, int regX, int regZ, Pos *pos); + +/* The inline functions below get the generation attempt position given a + * structure configuration. Most small structures use the getFeature.. + * variants, which have a uniform distribution, while large structures + * (monuments and mansions) have a triangular distribution. + */ static inline __attribute__((const)) Pos getFeaturePos(StructureConfig config, int64_t seed, int regX, int regZ); @@ -611,7 +627,7 @@ Pos estimateSpawn(const int mcversion, const LayerStack *g, int *cache, int64_t /* This function performs a biome check at the specified block coordinates to * determine whether the corresponding structure would spawn there. You can get - * the block positions using the appropriate getXXXPos() function. + * the block positions using getStructurePos(). * * @structureType : structure type to be checked * @mc : minecraft version @@ -623,6 +639,9 @@ Pos estimateSpawn(const int mcversion, const LayerStack *g, int *cache, int64_t */ int isViableStructurePos(int structureType, int mc, LayerStack *g, int64_t seed, int blockX, int blockZ); +int isViableNetherStructurePos(int structureType, int mc, NetherNoise *nn, + int64_t seed, int blockX, int blockZ); +// TODO: viability checks for end cities /* Checks if the specified structure type could generate in the given biome. */ @@ -727,8 +746,7 @@ Pos getFeatureChunkInRegion(StructureConfig config, int64_t seed, int regX, int { /* // Vanilla like implementation. - seed = regionX*341873128712 + regionZ*132897987541 + seed + structureSeed; - setSeed(&(seed)); + setSeed(&seed, regX*341873128712 + regZ*132897987541 + seed + config.salt); Pos pos; pos.x = nextInt(&seed, 24);