API change for getStructurePos to support new structs for Nether and End

This commit is contained in:
Cubitect 2021-03-18 09:22:02 +01:00
parent 19aa9b4944
commit 0224865935
2 changed files with 173 additions and 72 deletions

191
finders.c
View File

@ -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;
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;
}
return pos;
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

View File

@ -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);