Fix 1.18 spawn and stronghold finders (please fix MC-241546, Mojang)

This commit is contained in:
Cubitect 2021-11-24 23:14:19 +01:00
parent 65414ac3be
commit 8fea920b49
5 changed files with 179 additions and 63 deletions

View File

@ -277,7 +277,7 @@ Strongholds as well as the world spawn point actually search until they find a s
int main() int main()
{ {
int mc = MC_1_17; int mc = MC_1_18;
uint64_t seed = 3055141959546LL; uint64_t seed = 3055141959546LL;
// Only the first stronghold has a position which can be estimated // Only the first stronghold has a position which can be estimated

165
finders.c
View File

@ -944,12 +944,17 @@ Pos locateBiome(
x >>= 2; x >>= 2;
z >>= 2; z >>= 2;
radius >>= 2; radius >>= 2;
uint64_t dat = 0;
for (i = -radius; i <= radius; i++) for (j = -radius; j <= radius; j++)
{ {
for (j = -radius; j <= radius; j++) for (i = -radius; i <= radius; i++)
{ {
int id = getBiomeAt(g, 4, x+i, y, z+j); int id, xi = x+i, zj = z+j;
// emulate dependent biome generation MC-241546
//id = getBiomeAt(g, 4, xi, y, zj);
id = sampleBiomeNoise(&g->bn, NULL, xi, y, zj, &dat, 0);
if (!validBiomes[id]) continue; if (!validBiomes[id]) continue;
if (found == 0 || nextInt(rng, found+1) == 0) if (found == 0 || nextInt(rng, found+1) == 0)
{ {
@ -1114,28 +1119,29 @@ const char* getValidStrongholdBiomes(int mc)
modified_gravelly_mountains, shattered_savanna, modified_gravelly_mountains, shattered_savanna,
shattered_savanna_plateau, eroded_badlands, shattered_savanna_plateau, eroded_badlands,
modified_wooded_badlands_plateau, modified_badlands_plateau, modified_wooded_badlands_plateau, modified_badlands_plateau,
bamboo_jungle, bamboo_jungle_hills, bamboo_jungle, bamboo_jungle_hills, dripstone_caves, lush_caves, meadow,
grove, snowy_slopes, stony_peaks, jagged_peaks, frozen_peaks,
}; };
static char isValid115[256], isValid[256];
unsigned int i; unsigned int i;
static char v15[256], v17[256], v18[256];
char *valid = (mc <= MC_1_15 ? v15 : mc <= MC_1_17 ? v17 : v18);
if (mc <= MC_1_15) if (!valid[strongholdBiomes[0]])
{ {
if (!isValid115[strongholdBiomes[0]]) for (i = 0; i < sizeof(strongholdBiomes)/sizeof(int); i++)
for (i = 0; i < sizeof(strongholdBiomes) / sizeof(int); i++) valid[ strongholdBiomes[i] ] = 1;
isValid115[ strongholdBiomes[i] ] = 1;
return isValid115; if (mc >= MC_1_18)
} {
else valid[stone_shore] = 0;
{ // simulate MC-199298 }
if (!isValid[strongholdBiomes[0]]) else if (mc >= MC_1_16)
for (i = 0; i < sizeof(strongholdBiomes) / sizeof(int); i++) { // simulate MC-199298
isValid[ strongholdBiomes[i] ] = 1; valid[bamboo_jungle] = 0;
isValid[bamboo_jungle] = 0; valid[bamboo_jungle_hills] = 0;
isValid[bamboo_jungle_hills] = 0; }
return isValid;
} }
return valid;
} }
Pos initFirstStronghold(StrongholdIter *sh, int mc, uint64_t s48) Pos initFirstStronghold(StrongholdIter *sh, int mc, uint64_t s48)
@ -1343,6 +1349,79 @@ static int findServerSpawn(const Generator *g, int chunkX, int chunkZ,
} }
} }
static
uint64_t getSpawnDist(const Generator *g, int x, int z)
{
int64_t np[6];
uint32_t flags = SAMPLE_NO_DEPTH | SAMPLE_NO_BIOME;
sampleBiomeNoise(&g->bn, np, x>>2, 0, z>>2, NULL, flags);
const int64_t spawn_np[][2] = {
{-10000,10000},{-10000,10000},{-1100,10000},{-10000,10000},{0,0},
{-10000,-1600},{1600,10000} // [6]: weirdness for the second noise point
};
uint64_t ds = 0, ds1 = 0, ds2 = 0;
uint64_t a, b, q, i;
for (i = 0; i < 5; i++)
{
a = +np[i] - (uint64_t)spawn_np[i][1];
b = -np[i] + (uint64_t)spawn_np[i][0];
q = (int64_t)a > 0 ? a : (int64_t)b > 0 ? b : 0;
ds += q * q;
}
a = +np[5] - (uint64_t)spawn_np[5][1];
b = -np[5] + (uint64_t)spawn_np[5][0];
q = (int64_t)a > 0 ? a : (int64_t)b > 0 ? b : 0;
ds1 = ds + q*q;
a = +np[5] - (uint64_t)spawn_np[6][1];
b = -np[5] + (uint64_t)spawn_np[6][0];
q = (int64_t)a > 0 ? a : (int64_t)b > 0 ? b : 0;
ds2 = ds + q*q;
return ds1 <= ds2 ? ds1 : ds2;
}
static
void findFittest(const Generator *g, Pos *pos, uint64_t *fitness, double maxrad, double step)
{
double rad = step, ang = 0;
Pos p = *pos;
while (rad <= maxrad)
{
int x = p.x + (int)(sin(ang) * rad);
int z = p.z + (int)(cos(ang) * rad);
// calc fitness
double d = ((double)x*x + (double)z*z) / (2500*2500);
uint64_t fit = (uint64_t)(d*d * 1e8);
// calculate the distance to the noise points for spawn
fit += getSpawnDist(g, x, z);
if (fit < *fitness)
{
pos->x = x;
pos->z = z;
*fitness = fit;
}
ang += step / rad;
if (ang <= M_PI*2)
continue;
ang = 0;
rad += step;
}
}
static
Pos findFittestPos(const Generator *g)
{
Pos spawn = {0, 0};
uint64_t fitness = getSpawnDist(g, 0, 0);
findFittest(g, &spawn, &fitness, 2048.0, 512.0);
findFittest(g, &spawn, &fitness, 512.0, 32.0);
// center of chunk
spawn.x = ((spawn.x >> 4) << 4) + 8;
spawn.z = ((spawn.z >> 4) << 4) + 8;
return spawn;
}
Pos getSpawn(const Generator *g) Pos getSpawn(const Generator *g)
{ {
const char *isSpawnBiome = getValidSpawnBiomes(); const char *isSpawnBiome = getValidSpawnBiomes();
@ -1351,13 +1430,18 @@ Pos getSpawn(const Generator *g)
int i; int i;
uint64_t rnd; uint64_t rnd;
setSeed(&rnd, g->seed); if (g->mc <= MC_1_17)
spawn = locateBiome(g, 0, 63, 0, 256, isSpawnBiome, &rnd, &found);
if (!found)
{ {
//printf("Unable to find spawn biome.\n"); setSeed(&rnd, g->seed);
spawn.x = spawn.z = 8; spawn = locateBiome(g, 0, 63, 0, 256, isSpawnBiome, &rnd, &found);
if (!found)
{
spawn.x = spawn.z = 8;
}
}
else
{
spawn = findFittestPos(g);
} }
double accum = 1; double accum = 1;
@ -1374,7 +1458,7 @@ Pos getSpawn(const Generator *g)
{ {
if (j > -16 && j <= 16 && k > -16 && k <= 16) if (j > -16 && j <= 16 && k > -16 && k <= 16)
{ {
if (findServerSpawn(g, (spawn.x>>4)+j, (spawn.x>>4)+k, if (findServerSpawn(g, (spawn.x>>4)+j, (spawn.z>>4)+k,
&bx, &bz, &bn, &accum)) &bx, &bz, &bn, &accum))
{ {
spawn.x = (int) round(bx / bn); spawn.x = (int) round(bx / bn);
@ -1420,25 +1504,30 @@ Pos getSpawn(const Generator *g)
return spawn; return spawn;
} }
Pos estimateSpawn(const Generator *g) Pos estimateSpawn(const Generator *g)
{ {
const char *isSpawnBiome = getValidSpawnBiomes(); const char *isSpawnBiome = getValidSpawnBiomes();
Pos spawn; Pos spawn;
int found;
uint64_t rnd;
setSeed(&rnd, g->seed);
spawn = locateBiome(g, 0, 63, 0, 256, isSpawnBiome, &rnd, &found);
if (!found) if (g->mc <= MC_1_17)
{ {
spawn.x = spawn.z = 8; int found;
uint64_t rnd;
setSeed(&rnd, g->seed);
spawn = locateBiome(g, 0, 63, 0, 256, isSpawnBiome, &rnd, &found);
if (!found)
{
spawn.x = spawn.z = 8;
}
if (g->mc >= MC_1_13)
{
spawn.x &= ~0xf;
spawn.z &= ~0xf;
}
} }
else
if (g->mc >= MC_1_13)
{ {
spawn.x &= ~0xf; spawn = findFittestPos(g);
spawn.z &= ~0xf;
} }
return spawn; return spawn;

View File

@ -1354,11 +1354,12 @@ int p2overworld(const uint64_t np[6], uint64_t *dat);
#endif #endif
/// Biome sampler for MC 1.18 /// Biome sampler for MC 1.18
int sampleBiomeNoise(const BiomeNoise *bn, int x, int y, int z, uint64_t *dat, int approx) int sampleBiomeNoise(const BiomeNoise *bn, int64_t *np, int x, int y, int z,
uint64_t *dat, uint32_t flags)
{ {
float t = 0, h = 0, c = 0, e = 0, d = 0, w = 0; float t = 0, h = 0, c = 0, e = 0, d = 0, w = 0;
double px = x, pz = z; double px = x, pz = z;
if (approx == 0) if (!(flags & SAMPLE_NO_SHIFT))
{ {
px += sampleDoublePerlin(&bn->shift, x, 0, z) * 4.0; px += sampleDoublePerlin(&bn->shift, x, 0, z) * 4.0;
pz += sampleDoublePerlin(&bn->shift, z, x, 0) * 4.0; pz += sampleDoublePerlin(&bn->shift, z, x, 0) * 4.0;
@ -1368,27 +1369,32 @@ int sampleBiomeNoise(const BiomeNoise *bn, int x, int y, int z, uint64_t *dat, i
e = sampleDoublePerlin(&bn->erosion, px, 0, pz); e = sampleDoublePerlin(&bn->erosion, px, 0, pz);
w = sampleDoublePerlin(&bn->weirdness, px, 0, pz); w = sampleDoublePerlin(&bn->weirdness, px, 0, pz);
float np_param[] = { if (!(flags & SAMPLE_NO_DEPTH))
c, e, -3.0F * ( fabsf( fabsf(w) - 0.6666667F ) - 0.33333334F ), w, {
}; float np_param[] = {
double off = getSpline(bn->sp, np_param) + 0.015F; c, e, -3.0F * ( fabsf( fabsf(w) - 0.6666667F ) - 0.33333334F ), w,
};
double off = getSpline(bn->sp, np_param) + 0.015F;
//double py = y + sampleDoublePerlin(&bn->shift, y, z, x) * 4.0; //double py = y + sampleDoublePerlin(&bn->shift, y, z, x) * 4.0;
d = 1.0 - (y << 2) / 128.0 - 83.0/160.0 + off; d = 1.0 - (y << 2) / 128.0 - 83.0/160.0 + off;
}
t = sampleDoublePerlin(&bn->temperature, px, 0, pz); t = sampleDoublePerlin(&bn->temperature, px, 0, pz);
h = sampleDoublePerlin(&bn->humidity, px, 0, pz); h = sampleDoublePerlin(&bn->humidity, px, 0, pz);
int64_t np[] = { int64_t l_np[6];
(int64_t)(10000.0F*t), int64_t *p_np = np ? np : l_np;
(int64_t)(10000.0F*h), p_np[0] = (int64_t)(10000.0F*t);
(int64_t)(10000.0F*c), p_np[1] = (int64_t)(10000.0F*h);
(int64_t)(10000.0F*e), p_np[2] = (int64_t)(10000.0F*c);
(int64_t)(10000.0F*d), p_np[3] = (int64_t)(10000.0F*e);
(int64_t)(10000.0F*w), p_np[4] = (int64_t)(10000.0F*d);
}; p_np[5] = (int64_t)(10000.0F*w);
int id = p2overworld((const uint64_t*)np, dat); int id = none;
if (!(flags & SAMPLE_NO_BIOME))
id = p2overworld((const uint64_t*)p_np, dat);
return id; return id;
} }
@ -1396,6 +1402,8 @@ int sampleBiomeNoise(const BiomeNoise *bn, int x, int y, int z, uint64_t *dat, i
static void genBiomeNoise3D(const BiomeNoise *bn, int *out, Range r, int opt) static void genBiomeNoise3D(const BiomeNoise *bn, int *out, Range r, int opt)
{ {
uint64_t dat = 0; uint64_t dat = 0;
uint64_t *p_dat = opt ? &dat : NULL;
uint32_t flags = opt ? SAMPLE_NO_SHIFT : 0;
int i, j, k; int i, j, k;
int *p = out; int *p = out;
int scale = r.scale > 4 ? r.scale / 4 : 1; int scale = r.scale > 4 ? r.scale / 4 : 1;
@ -1409,7 +1417,7 @@ static void genBiomeNoise3D(const BiomeNoise *bn, int *out, Range r, int opt)
for (i = 0; i < r.sx; i++) for (i = 0; i < r.sx; i++)
{ {
int xi = (r.x+i)*scale + mid; int xi = (r.x+i)*scale + mid;
*p = sampleBiomeNoise(bn, xi, yk, zj, opt ? &dat : NULL, opt); *p = sampleBiomeNoise(bn, NULL, xi, yk, zj, p_dat, flags);
p++; p++;
} }
} }
@ -1457,7 +1465,7 @@ int genBiomeNoiseScaled(const BiomeNoise *bn, int *out, Range r, int mc, uint64_
} }
else else
{ {
*p = sampleBiomeNoise(bn, x4, y4, z4, 0, 0); *p = sampleBiomeNoise(bn, 0, x4, y4, z4, 0, 0);
} }
p++; p++;
} }

View File

@ -436,9 +436,15 @@ int genEndScaled(const EndNoise *en, int *out, Range r, int mc, uint64_t sha);
* The 1.18 End generation remains similar to 1.17 and does NOT use the * The 1.18 End generation remains similar to 1.17 and does NOT use the
* biome noise. * biome noise.
*/ */
enum {
SAMPLE_NO_SHIFT = 0x1,
SAMPLE_NO_DEPTH = 0x2,
SAMPLE_NO_BIOME = 0x4,
};
void initBiomeNoise(BiomeNoise *bn, int mc); void initBiomeNoise(BiomeNoise *bn, int mc);
void setBiomeSeed(BiomeNoise *bn, uint64_t seed, int large); void setBiomeSeed(BiomeNoise *bn, uint64_t seed, int large);
int sampleBiomeNoise(const BiomeNoise *bn, int x, int y, int z, uint64_t *dat, int approx); int sampleBiomeNoise(const BiomeNoise *bn, int64_t *np, int x, int y, int z,
uint64_t *dat, uint32_t flags);
/** /**
* The scaled biome noise generation applies for the Overworld version 1.18. * The scaled biome noise generation applies for the Overworld version 1.18.
* The 'sha' hash of the seed is only required for voronoi at scale 1:1. * The 'sha' hash of the seed is only required for voronoi at scale 1:1.

21
tests.c
View File

@ -112,7 +112,7 @@ int testBiomeGen1x1(const int *mc, const uint32_t *expect, int dim, int bits, in
fflush(stdout); fflush(stdout);
double t = -now(); double t = -now();
h = getRef(mc[test], dim, bits, spread, "t16"); h = getRef(mc[test], dim, bits, spread, NULL);
t += now(); t += now();
printf("got %08x %s\e[0m (%ld msec)\n", printf("got %08x %s\e[0m (%ld msec)\n",
h, h == expect[test] ? "\e[1;92mOK" : "\e[1;91mFAILED", h, h == expect[test] ? "\e[1;92mOK" : "\e[1;91mFAILED",
@ -158,6 +158,8 @@ uint32_t testAreas(int mc, int dim, int scale)
} }
int testGeneration() int testGeneration()
{ {
const int mc_vers[] = { const int mc_vers[] = {
@ -172,9 +174,20 @@ int testGeneration()
}; };
const int testcnt = sizeof(mc_vers) / sizeof(int); const int testcnt = sizeof(mc_vers) / sizeof(int);
printf("Testing 1x1 biome generation (quick):\n"); //printf("Testing 1x1 biome generation (quick):\n");
if (!testBiomeGen1x1(mc_vers, b6_hashes, 0, 6, 1, 1))// testcnt)) //if (!testBiomeGen1x1(mc_vers, b6_hashes, 0, 6, 1, testcnt))
;//return -1; // return -1;
Generator g;
setupGenerator(&g, MC_1_18, 0);
applySeed(&g, 0, 1234);
Pos p = getSpawn(&g);
printf("%d %d\n", p.x, p.z);
StrongholdIter sh;
initFirstStronghold(&sh, g.mc, g.seed);
while (nextStronghold(&sh, &g) > 0)
printf("Stronghold #: (%6d, %6d)\n", sh.pos.x, sh.pos.z);
printf("Area generation tests:\n"); printf("Area generation tests:\n");
testAreas(MC_1_18, 0, 1); testAreas(MC_1_18, 0, 1);