From 496c6a2f6af57117cdf8586e6fac6164525f187c Mon Sep 17 00:00:00 2001 From: Cubitect Date: Sun, 27 Dec 2020 15:36:41 +0100 Subject: [PATCH] Check MC in structure validation + scanning for quad-huts. --- finders.c | 107 ++++++++++++++++++++++++++++++++++++++++-------------- finders.h | 35 +++++++++++++----- javarnd.h | 27 +++++++++++++- 3 files changed, 132 insertions(+), 37 deletions(-) diff --git a/finders.c b/finders.c index abe9352..9bbcc2c 100644 --- a/finders.c +++ b/finders.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -150,8 +151,8 @@ int isTreasureChunk(int64_t seed, int chunkX, int chunkZ) */ int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk) { - int minX = 3e7, minZ = 3e7, maxX = -3e7, maxZ = -3e7; - int bestr, bestn, i, x, z, px, pz; + int64_t minX = INT_MAX, minZ = INT_MAX, maxX = INT_MIN, maxZ = INT_MIN; + int64_t bestr, bestn, i, x, z, px, pz; // Find corners @@ -169,6 +170,7 @@ int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk) maxZ += az; bestr = 0; bestn = 0; + double afkx = 0, afkz = 0; double thsq = 128.0*128.0 - ay*ay/4.0; @@ -198,8 +200,8 @@ int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk) { if (afk) { - afk->x = x; - afk->z = z; + afkx = x; + afkz = z; bestn = 1; } bestr = inrange; @@ -208,8 +210,8 @@ int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk) { if (afk) { - afk->x += x; - afk->z += z; + afkx += x; + afkz += z; bestn++; } } @@ -218,8 +220,8 @@ int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk) if (afk && bestn) { - afk->x /= bestn; - afk->z /= bestn; + afk->x = (int)round(afkx / bestn); + afk->z = (int)round(afkz / bestn); } return bestr; @@ -593,6 +595,40 @@ L_ERR: } +int scanForQuads(const StructureConfig sconf, int64_t s48, int64_t low20, + int x, int z, int w, int h, Pos *qplist, int n) +{ + const int m = (1LL << 20); + const int64_t A = 341873128712LL; + const int64_t invB = 132477LL; // = mulInv(132897987541LL, m); + + if (n < 1) + return 0; + + int i, j, cnt = 0; + for (i = x; i <= x+w; i++) + { + int64_t sx = s48 + A * i; + j = (z & ~(m-1)) | ((low20 - sx) * invB & (m-1)); + if (j < z) + j += m; + for (; j <= z+h; j += m) + { + int64_t sp = moveStructure(s48, -i, -j); + if (isQuadBase(sconf, sp - sconf.salt, 128)) + { + qplist[cnt].x = i; + qplist[cnt].z = j; + cnt++; + if (cnt >= n) + return cnt; + } + } + } + + return cnt; +} + //============================================================================== // Checking Biomes & Biome Helper Functions @@ -1193,7 +1229,9 @@ int isViableFeatureBiome(int structureType, int biomeID) case Mansion: return biomeID == dark_forest || biomeID == dark_forest_hills; default: - fprintf(stderr, "ERR isViableFeatureBiome: not implemented for structure type.\n"); + fprintf(stderr, + "isViableFeatureBiome: not implemented for structure type %d.\n", + structureType); exit(1); } return 0; @@ -1359,13 +1397,18 @@ int isViableStructurePos(int structureType, int mcversion, LayerStack *g, switch (structureType) { - case Desert_Pyramid: - case Jungle_Pyramid: - case Swamp_Hut: - case Igloo: case Ocean_Ruin: case Shipwreck: case Treasure: + if (mcversion < MC_1_13) goto L_NOT_VIABLE; + goto L_FEATURE; + case Igloo: + if (mcversion < MC_1_9) goto L_NOT_VIABLE; + goto L_FEATURE; + case Desert_Pyramid: + case Jungle_Pyramid: + case Swamp_Hut: +L_FEATURE: if (mcversion < MC_1_16) { l = &g->layers[L_VORONOI_ZOOM_1]; @@ -1414,7 +1457,7 @@ int isViableStructurePos(int structureType, int mcversion, LayerStack *g, case Outpost: { - if (!testOutpostPos(seed, chunkX, chunkZ)) + if (mcversion < MC_1_14 || !testOutpostPos(seed, chunkX, chunkZ)) goto L_NOT_VIABLE; if (mcversion < MC_1_16) { @@ -1458,7 +1501,18 @@ int isViableStructurePos(int structureType, int mcversion, LayerStack *g, } case Monument: - if (mcversion >= MC_1_9) + if (mcversion < MC_1_8) + goto L_NOT_VIABLE; + else if (mcversion == MC_1_8) + { + // In 1.8 monuments require only a single deep ocean block. + l = g->entry_1; + setWorldSeed(l, seed); + map = allocCache(l, 1, 1); + if (genArea(l, map, blockX, blockZ, 1, 1)) + goto L_NOT_VIABLE; + } + else { // Monuments require two viability checks with the ocean layer // branch => worth checking for potential deep ocean beforehand. @@ -1468,15 +1522,6 @@ int isViableStructurePos(int structureType, int mcversion, LayerStack *g, if (genArea(l, map, chunkX, chunkZ, 1, 1)) goto L_NOT_VIABLE; } - else - { - // In 1.8 monuments require only a single deep ocean block. - l = g->entry_1; - setWorldSeed(l, seed); - map = allocCache(l, 1, 1); - if (genArea(l, map, blockX, blockZ, 1, 1)) - goto L_NOT_VIABLE; - } if (!isDeepOcean(map[0])) goto L_NOT_VIABLE; if (mcversion >= MC_1_13) @@ -1490,6 +1535,8 @@ int isViableStructurePos(int structureType, int mcversion, LayerStack *g, goto L_NOT_VIABLE; case Mansion: + if (mcversion < MC_1_11) + goto L_NOT_VIABLE; l = &g->layers[L_RIVER_MIX_4]; setWorldSeed(l, seed); if (areBiomesViable(l, NULL, blockX, blockZ, 32, getValidMansionBiomes())) @@ -1497,10 +1544,14 @@ int isViableStructurePos(int structureType, int mcversion, LayerStack *g, goto L_NOT_VIABLE; case Ruined_Portal: - goto L_VIABLE; + if (mcversion >= MC_1_16) + goto L_VIABLE; + goto L_NOT_VIABLE; default: - fprintf(stderr, "ERR isViableStructurePos: validation for structure type not implemented"); + fprintf(stderr, + "isViableStructurePos: validation for structure type %d not implemented", + structureType); goto L_NOT_VIABLE; } @@ -1594,7 +1645,7 @@ BiomeFilter setupBiomeFilter(const int *biomeList, int listLen) id = biomeList[i]; if (id & ~0xbf) // i.e. not in ranges [0,64),[128,192) { - fprintf(stderr, "ERR: biomeID=%d not supported by filter.\n", id); + fprintf(stderr, "setupBiomeFilter: biomeID=%d not supported.\n", id); exit(-1); } @@ -1642,6 +1693,8 @@ BiomeFilter setupBiomeFilter(const int *biomeList, int listLen) if (id == bamboo_jungle || id == bamboo_jungle_hills) { // bamboo%64 are End biomes, so we can reuse the edgesToFind bf.edgesToFind |= (1ULL << (bamboo_jungle & 0x3f)); + bf.raresToFindM |= (1ULL << (id-128)); + bf.riverToFindM |= (1ULL << (id-128)); } else if (id == jungle_edge) { // un-modified jungle_edge can be created at shore layer bf.riverToFind |= (1ULL << jungle_edge); diff --git a/finders.h b/finders.h index 8d6a537..c5586bd 100644 --- a/finders.h +++ b/finders.h @@ -25,7 +25,7 @@ extern "C" { #endif -#define MASK48 ((1LL << 48) - 1) +#define MASK48 (((int64_t)1 << 48) - 1) #define PI 3.141592653589793 #define LARGE_STRUCT 1 @@ -66,14 +66,14 @@ STRUCT(StructureConfig) /* for desert pyramids, jungle temples, witch huts and igloos prior to 1.13 */ static const StructureConfig FEATURE_CONFIG = { 14357617, 32, 24, Feature, 0}; -static const StructureConfig DESERT_CONFIG_17 = { 14357617, 32, 24, Desert_Pyramid, 0}; -static const StructureConfig IGLOO_CONFIG_17 = { 14357617, 32, 24, Igloo, 0}; -static const StructureConfig JUNGLE_CONFIG_17 = { 14357617, 32, 24, Jungle_Pyramid, 0}; -static const StructureConfig SWAMP_HUT_CONFIG_17 = { 14357617, 32, 24, Swamp_Hut, 0}; +static const StructureConfig IGLOO_CONFIG_112 = { 14357617, 32, 24, Igloo, 0}; +static const StructureConfig SWAMP_HUT_CONFIG_112 = { 14357617, 32, 24, Swamp_Hut, 0}; +static const StructureConfig DESERT_PYRAMID_CONFIG_112 = { 14357617, 32, 24, Desert_Pyramid, 0}; +static const StructureConfig JUNGLE_PYRAMID_CONFIG_112 = { 14357617, 32, 24, Jungle_Pyramid, 0}; /* ocean features before 1.16 */ -static const StructureConfig OCEAN_RUIN_CONFIG_113 = { 14357621, 16, 8, Ocean_Ruin, 0}; -static const StructureConfig SHIPWRECK_CONFIG_113 = {165745295, 15, 7, Shipwreck, 0}; +static const StructureConfig OCEAN_RUIN_CONFIG_115 = { 14357621, 16, 8, Ocean_Ruin, 0}; +static const StructureConfig SHIPWRECK_CONFIG_115 = {165745295, 15, 7, Shipwreck, 0}; /* 1.13 separated feature seeds by type */ static const StructureConfig DESERT_PYRAMID_CONFIG = { 14357617, 32, 24, Desert_Pyramid, 0}; @@ -399,6 +399,23 @@ int searchAll48( int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk); +/* Scans the seed 's48' for quad-structures in the given area of region + * coordiantes. The search is performed for only a specific lower 20-bits of + * the transformed bases (i.e. each call looks for only one constellation of + * quad-structure). + * + * @sconf : structure config (SWAMP_HUT_CONFIG or FEATURE_CONFIG) + * @s48 : 48-bit seed to scan + * @low20 : only consider transformations that yield these lower bits + * @x,z,w,h : area to scan in region coordinates (inclusive) + * @qplist : output region coordinates for the descovered quad-structures + * @n : maximum number of quad-structures to look for + * + * Returns the number of quad-structures found (up to 'n'). + */ +int scanForQuads(const StructureConfig sconf, int64_t s48, int64_t low20, + int x, int z, int w, int h, Pos *qplist, int n); + //============================================================================== // Checking Biomes & Biome Helper Functions //============================================================================== @@ -835,8 +852,8 @@ static inline float isQuadBase(const StructureConfig sconf, int64_t seed, int ra //case Ruined_Portal: default: - fprintf(stderr, "ERR isQuadBase: not implemented for structure type" - " %d\n", sconf.structType); + fprintf(stderr, "isQuadBase: not implemented for structure type %d\n", + sconf.structType); exit(-1); } diff --git a/javarnd.h b/javarnd.h index dcd4d9e..41bbb57 100644 --- a/javarnd.h +++ b/javarnd.h @@ -81,7 +81,8 @@ static inline void skipNextN(int64_t *seed, const int n) * Returns the previous 48-bit seed which will generate 'nseed'. * The upper 16 bits are ignored, both here and in the generator. */ -static inline int64_t invSeed48(int64_t nseed) +static inline __attribute__((const)) +int64_t invSeed48(int64_t nseed) { const int64_t x = 0x5deece66d; const int64_t xinv = 0xdfe05bcb1365LL; @@ -111,4 +112,28 @@ static inline int64_t invSeed48(int64_t nseed) } +/* Find the modular inverse: (1/x) | mod m. + * Assumes x and m are positive and co-prime. + */ +static inline __attribute__((const)) +int64_t mulInv(int64_t x, int64_t m) +{ + int64_t t, q, a, b; + if (m == 1) + return 0; // no solution + + a = 0; b = 1; + + while (x > 1) + { + if (m == 0) + return 0; // x and m are co-prime + q = x / m; + t = m; m = x % m; x = t; + t = a; a = b - q * a; b = t; + } + + return b; +} + #endif /* JAVARND_H_ */