mirror of
https://github.com/Cubitect/cubiomes.git
synced 2025-09-23 03:33:50 -04:00
Focus update towards library.
1) Update readme to include structure finders + removed outdated references to seed lists. 2) Removed example programs find_quadhuts.c and find_compactbiomes.c in favor of small examples inside readme. 3) Replaced search4QuadBases with a more general searchAll48. This does, however, redefine the format for quad bases.
This commit is contained in:
parent
9cbb8c4d9d
commit
fc5a42180c
321
README.md
321
README.md
@ -1,7 +1,7 @@
|
||||
# cubiomes
|
||||
|
||||
Cubiomes is a standalone library, written in C, that mimics the Minecraft biome and feature generation.
|
||||
It is intended as a powerful tool to devise very fast, custom seed finding applications and large scale map viewers.
|
||||
Cubiomes is a standalone library, written in C, that mimics the Minecraft biome and feature generation (currently only for the overworld).
|
||||
It is intended as a powerful tool to devise very fast, custom seed finding applications and large scale map viewers with minimal memory usage.
|
||||
|
||||
|
||||
#### Audience
|
||||
@ -11,20 +11,21 @@ You should be familiar with the C programming language, also a basic understandi
|
||||
|
||||
## Getting Started
|
||||
|
||||
This section is meant to give you a quick starting point if you want to use this library to find your own biome dependent features.
|
||||
This section is meant to give you a quick starting point with small example programs if you want to use this library to find your own biome dependent features.
|
||||
|
||||
### Biome Generator
|
||||
|
||||
Let's create a simple program called `find_jedge.c` which tests seeds for a Junge Edge biome at a predefined location.
|
||||
|
||||
```C
|
||||
// check the biome at a block position
|
||||
#include "finders.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
// First initialize the global biome table 'int biomes[256]'. This sets up
|
||||
// properties such as the category and temperature of each biome.
|
||||
// First initialize the global biome table. This sets up properties such as
|
||||
// the category and temperature of each biome.
|
||||
initBiomes();
|
||||
|
||||
// Initialize a stack of biome layers that reflects the biome generation of
|
||||
@ -73,6 +74,7 @@ Seed 615 has a Junge Edge biome at block position (0, 0).
|
||||
We can also generate the biomes for a rectangular region using `getArea()` which also offers control over the entry layer, see the layer documentation for more information.
|
||||
|
||||
```C
|
||||
// generate an image of the world
|
||||
#include "generator.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -91,7 +93,7 @@ int main()
|
||||
// Extract the desired layer.
|
||||
Layer *layer = &g.layers[L_SHORE_16];
|
||||
|
||||
int64_t seed = 1661454332289;
|
||||
int64_t seed = 1661454332289LL;
|
||||
int areaX = -60, areaZ = -60;
|
||||
unsigned int areaWidth = 120, areaHeight = 120;
|
||||
unsigned int scale = 4;
|
||||
@ -118,137 +120,230 @@ int main()
|
||||
```
|
||||
|
||||
|
||||
### Layer Documentation
|
||||
#### Layer Documentation
|
||||
|
||||
There is a reference document for the generator layers which contains a summary for most generator layers and their function within the generation process.
|
||||
There is a reference document for the generator layers which contains a summary for most generator layers and their function within the generation process (a little out of date, since 1.13).
|
||||
|
||||
|
||||
### Examples
|
||||
#### Biome Filters
|
||||
|
||||
There are two example programs in this repository which can be compiled using the makefile.
|
||||
Biome filters provide a way of generating an area, but only if that area contains certain biomes. Rather than generating first and then checking that the area contains what we want, the requirements are tested during the generation process. This can be a dramatic speed up, particularly if we require several wildly different biomes.
|
||||
|
||||
```C
|
||||
// find seeds that have certain biomes near the origin
|
||||
#include "finders.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
initBiomes();
|
||||
|
||||
int mc = MC_1_16;
|
||||
LayerStack g;
|
||||
BiomeFilter filter;
|
||||
|
||||
setupGenerator(&g, mc);
|
||||
|
||||
// Define the required biomes.
|
||||
int wanted[] = {
|
||||
dark_forest,
|
||||
ice_spikes,
|
||||
mushroom_fields,
|
||||
};
|
||||
filter = setupBiomeFilter(wanted, sizeof(wanted) / sizeof(int));
|
||||
|
||||
int x = -200, z = -200, w = 400, h = 400;
|
||||
int entry = L_VORONOI_ZOOM_1;
|
||||
int *area = allocCache(&g.layers[entry], w, h);
|
||||
|
||||
int64_t seed;
|
||||
for (seed = 0; ; seed++)
|
||||
if (checkForBiomes(&g, entry, area, seed, x, z, w, h, filter, 1) > 0)
|
||||
break;
|
||||
|
||||
printf("Seed %" PRId64 " has the required biomes in (%d, %d) - (%d, %d).\n",
|
||||
seed, x, z, x+w, z+h);
|
||||
|
||||
free(area);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Finding Quad-Witch-Huts at a Specific Location
|
||||
### Structure Generation
|
||||
|
||||
This classic type of finder uses several optimisations reguarding positioning of temples in the world. One of which allows you to specify the exact region (512x512) position about which the quad-hut should generate, without affecting the performance. For example:
|
||||
The generation of structures can usually be regarded as a two stage process: generation attempts and biome checks. For most structures, Minecraft divides the world into a grid of regions (usually 32x32 chunks) and performs one generation attempt in each. We can use `getStructurePos` to get the position of such a generation attempt and then test whether a structure will actually generate there with `isViableStructurePos`, however, this is more expensive to compute (a few µsec rather than nsec).
|
||||
|
||||
`./find_quadhut 0 0`
|
||||
```C
|
||||
// find a seed with a certain structure at the origin chunk
|
||||
#include "finders.h"
|
||||
#include <stdio.h>
|
||||
|
||||
will start a search with a regional positioning around the origin. (Actually the huts will be positioned in regions (-1,-1) to (0,0) this way.)
|
||||
int main()
|
||||
{
|
||||
initBiomes();
|
||||
|
||||
To my knowlege, as of the time of writing, this is fastest single-thread quad-hut-finder out there. However, note that the current implementation of the biome finding optimisations causes the finder to miss some seeds (< 2%) in favour for speed.
|
||||
// Note that some structure configs changed with the Minecraft versions.
|
||||
const StructureConfig sconf = OUTPOST_CONFIG;
|
||||
int mc = MC_1_16;
|
||||
|
||||
LayerStack g;
|
||||
setupGenerator(&g, mc);
|
||||
|
||||
int64_t lower48;
|
||||
for (lower48 = 0; ; lower48++)
|
||||
{
|
||||
// The structure position depends only on the region coordinates and
|
||||
// the lower 48-bits of the world seed.
|
||||
int valid;
|
||||
Pos p = getStructurePos(sconf, lower48, 0, 0, &valid);
|
||||
|
||||
// Look for a seed with the structure at the origin chunk.
|
||||
if (!valid || p.x >= 16 || p.z >= 16)
|
||||
continue;
|
||||
|
||||
// Look for a full 64-bit seed with viable biomes.
|
||||
int64_t upper16;
|
||||
for (upper16 = 0; upper16 < 0x10000; upper16++)
|
||||
{
|
||||
int64_t seed = lower48 | (upper16 << 48);
|
||||
if (isViableStructurePos(sconf.structType, mc, &g, seed, p.x, p.z))
|
||||
{
|
||||
printf("Seed %" PRId64 " has a Pillager Outpost at the origin.\n",
|
||||
seed, p.x, p.z);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Quad-Witch-Huts
|
||||
|
||||
A commonly desired feature are Quad-Witch-Huts or similar multi-structure clusters. To test for these types of seeds we can look a little deeper into how the generation attemps are determined. Notice that the positions depend only on the structure type, region coordinates and the lower 48 bits of the seed. Also, once we have found a seed with the desired generation attemps, we can move them around by transforming the 48-bit seed using `moveStructure`. This means there is a set of seed bases which can function as a starting point to generate all other seeds with similar structure placement.
|
||||
|
||||
The function `searchAll48` can be used to find a complete set of 48-bit seed bases for a custom criterion. Given that in general it can take a very long time to check all 2^48 seeds (days or weeks), the function provides some functionality to save the results to disk which can be loaded again using `loadSavedSeeds`. Luckly, in some cases it is possible to reduce the search space even further. For Swamp Huts and structures with a similar structure configuration there are only a handfull of constellations where the structures are close enough together to run simultaneously. Conveniently, these constellations differ uniquely at the lower 20 bits. (This is hard to prove, or at least I haven't found a riggerous proof that doesn't rely on brute forcing.) By specifying a list of lower 20-bit values we can reduce the search space to the order of 2^28, which can be checked in a reasonable amount of time.
|
||||
|
||||
|
||||
#### Finding Compact Biome Seeds
|
||||
```C
|
||||
// find seeds with a quad-witch-hut about the origin
|
||||
#include "finders.h"
|
||||
#include <stdio.h>
|
||||
|
||||
This finder searches for seeds that contain all major biome types within 1024 blocks of the origin. These seeds are very rare and it might take a moment for the finder to yield any. The commandline arguments are:
|
||||
int check(int64_t s48, void *data)
|
||||
{
|
||||
const StructureConfig sconf = *(const StructureConfig*) data;
|
||||
return isQuadBase(sconf, s48 - sconf.salt, 128);
|
||||
}
|
||||
|
||||
`./find_compactbiomes [starting_seed] [end_seed] [threads]`
|
||||
int main()
|
||||
{
|
||||
initBiomes();
|
||||
|
||||
// Note that some structure configs changed with the Minecraft versions.
|
||||
StructureConfig sconf = SWAMP_HUT_CONFIG;
|
||||
int mc = MC_1_16;
|
||||
int64_t basecnt = 0;
|
||||
int64_t *bases = NULL;
|
||||
int threads = 8;
|
||||
LayerStack g;
|
||||
|
||||
// Get all 48-bit quad-witch-hut bases, but consider only the best 20-bit
|
||||
// constellations where the structures are the closest together.
|
||||
int err = searchAll48(
|
||||
&bases, &basecnt, NULL, threads,
|
||||
low20QuadIdeal, sizeof(low20QuadIdeal) / sizeof(int64_t), 20,
|
||||
check, &sconf
|
||||
);
|
||||
|
||||
if (err || !bases)
|
||||
{
|
||||
printf("Failed to generate seed bases.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
setupGenerator(&g, mc);
|
||||
|
||||
int64_t i;
|
||||
for (i = 0; i < basecnt; i++)
|
||||
{
|
||||
// The quad bases by themselves have structures in regions (0,0)-(1,1)
|
||||
// so we can move them by -1 regions to have them around the origin.
|
||||
int64_t s48 = moveStructure(bases[i] - sconf.salt, -1, -1);
|
||||
|
||||
Pos pos[4] = {
|
||||
getStructurePos(sconf, s48, -1, -1, NULL),
|
||||
getStructurePos(sconf, s48, -1, 0, NULL),
|
||||
getStructurePos(sconf, s48, 0, -1, NULL),
|
||||
getStructurePos(sconf, s48, 0, 0, NULL)
|
||||
};
|
||||
|
||||
int64_t high;
|
||||
for (high = 0; high < 0x10000; high++)
|
||||
{
|
||||
int64_t seed = s48 | (high << 48);
|
||||
int styp = sconf.structType;
|
||||
|
||||
if (isViableStructurePos(styp, mc, &g, seed, pos[0].x, pos[0].z) &&
|
||||
isViableStructurePos(styp, mc, &g, seed, pos[1].x, pos[1].z) &&
|
||||
isViableStructurePos(styp, mc, &g, seed, pos[2].x, pos[2].z) &&
|
||||
isViableStructurePos(styp, mc, &g, seed, pos[3].x, pos[3].z))
|
||||
{
|
||||
printf("%" PRId64 "\n", seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(bases);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
#### Strongholds and Spawn
|
||||
|
||||
Strongholds as well as the world spawn point actually search until they find a suitable location, rather than checking a single spot like most other structures. This causes them to be particularly performance expensive to find. Furthermore, the positions of stongholds have to be generated in a certain order, which can be done in an iterator fashion with `initFirstStronghold` and `nextStronghold`. For the world spawn, the generation starts with a search for a suitable biome near the origin, but will continue until a grass or podzol block is found. There is no reliable way of checking actual blocks, which means the search relies on a statistic, matching grass presence to biomes. Alternatively, we can simply use `estimateSpawn` and terminate the search after the first biome check and assume that grass is near by.
|
||||
|
||||
|
||||
# Cool Seeds
|
||||
```C
|
||||
// find spawn and the first N strongholds
|
||||
#include "finders.h"
|
||||
#include <stdio.h>
|
||||
|
||||
## All Biomes Near the Origin
|
||||
int main()
|
||||
{
|
||||
initBiomes();
|
||||
|
||||
Below is a list of some very rare seeds that have all the interesing biomes in very close proximity to the origin, offering some unique scenery. TIP: If you are creating a new world you can use the Custom World Generation setting to reduce the biome size by a factor of up to 4, which puts all the biomes even closer together.
|
||||
int mc = MC_1_16;
|
||||
int64_t seed = 3055141959546LL;
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Seed</th>
|
||||
<th>Notable biomes within 250 blocks of spawn</th>
|
||||
<th>Remarks</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2458028242930</td>
|
||||
<td>Ocean, Plains, Forest, Taiga, Swampland,<br>Ice Plains, Roofed Forest, Cold Taiga,<br>Mega Taiga, Sunflower Plains, Ice Spikes</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Decently sized Mushroom Island </li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3659308845421</td>
|
||||
<td>Ocean, Plains, Extreme Hills, Forest, Taiga,<br>Swampland, Deep Ocean, Mega Taiga,<br>Sunflower Plains</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3055141959546</td>
|
||||
<td>Ocean, Plains, Extreme Hills, Forest, Taiga,<br>Deep Ocean, Flower Forest</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>All biomes are arranged in a<br>ring around a central ocean</li>
|
||||
<li>Well suited for custom worlds</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1618761219563</td>
|
||||
<td>Ocean, Plains, Extreme Hills, Forest, Swampland,<br>Ice Plains, Mushroom Island, Deep Ocean,<br>Birch Forest, Roofed Forest, Savanna</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Village at spawn</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1661454332289</td>
|
||||
<td>Ocean, Plains, Desert, Mushroom Island,<br>Deep Ocean, Roofed Forest, Savanna</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>All biomes are arranged in a<br>ring around a central ocean</li>
|
||||
<li>Mushroom Island at (0,0)</li>
|
||||
<li>Extremely Small Ice Spike Biome</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
// Only the first stronghold has a position which can be estimated
|
||||
// (+/-112 blocks) without biome check.
|
||||
StrongholdIter sh;
|
||||
Pos pos = initFirstStronghold(&sh, mc, seed);
|
||||
|
||||
</table>
|
||||
printf("Seed: %" PRId64 "\n", seed);
|
||||
printf("Estimated position of first stronghold: (%d, %d)\n", pos.x, pos.z);
|
||||
|
||||
// The finders for the strongholds and spawn require that the seed is
|
||||
// applied to the generator beforehand.
|
||||
LayerStack g;
|
||||
setupGenerator(&g, mc);
|
||||
applySeed(&g, seed);
|
||||
|
||||
pos = getSpawn(mc, &g, NULL, seed);
|
||||
printf("Spawn: (%d, %d)\n", pos.x, pos.z);
|
||||
|
||||
## Lots of Witch Huts
|
||||
|
||||
No more than four huts can generate close enough together to be operated by a single player. However, if you have a server, or you just want multiple witch farms in your world, then you can consider seeds with more than one multi-hut.
|
||||
|
||||
For seeds that have a quad-hut with 2 additional tri-huts, I present to you (possibly all) seeds that have the multi-huts in the closest proximity to spawn. These seeds have one quad-hut and 2 tri-huts within 40000 blocks.
|
||||
|
||||
| Seed | Quad Hut | Tri Hut #1 | Tri Hut #2 |
|
||||
|----------------------|---------------|----------------|----------------|
|
||||
| 181201211981019340 | ( 5480, 4984) | (10616, -8344) | (-23688,28520) |
|
||||
| 2178171917826985089 | (-3736,-3720) | ( 1400,-17048) | (-32904,19816) |
|
||||
| -2263221739690455935 | (-3736,-3720) | ( 1400,-17048) | (-32904,19816) |
|
||||
| 3382334921639955859 | (28008, -648) | (33144,-13976) | ( -1160,22888) |
|
||||
| -8386696804585992813 | (28008, -648) | (33144,-13976) | ( -1160,22888) |
|
||||
| 4027541812768105332 | (-5784,-7304) | ( -648,-20632) | (-34952,16232) |
|
||||
|
||||
For a mostly complete list of double-quad-huts within 32000 blocks see: `./seeds/alldoublequadhuts32k.txt`.
|
||||
|
||||
|
||||
|
||||
## Advancement Related
|
||||
|
||||
If you are looking to get the "Adventuring Time" achievment you might consider one of the following seeds. All of these seeds have all 36 required biomes within less than 650 blocks from the origin:
|
||||
|
||||
| Seed | All biome radius |
|
||||
|---------------|------------------|
|
||||
| -880424771806 | 644 |
|
||||
| 48382691805 | 633 |
|
||||
| 480800992945 | 649 |
|
||||
| 1065757415811 | 612 |
|
||||
| 1509124018794 | 645 |
|
||||
| 1550633690354 | 616 |
|
||||
| 1571479851306 | 631 |
|
||||
| 1925837979058 | 621 |
|
||||
| 2082386353360 | 649 |
|
||||
| 2087339213306 | 632 |
|
||||
| 2810140768300 | 637 |
|
||||
| 3053313529066 | 648 |
|
||||
| 3457626356584 | 649 |
|
||||
| 3548624619264 | 646 |
|
||||
|
||||
int i, N = 12;
|
||||
for (i = 1; i <= N; i++)
|
||||
{
|
||||
if (nextStronghold(&sh, &g, NULL) <= 0)
|
||||
break;
|
||||
printf("Stronghold #%-3d: (%6d, %6d)\n", i, sh.pos.x, sh.pos.z);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -1,171 +0,0 @@
|
||||
#include "finders.h"
|
||||
#include "generator.h"
|
||||
|
||||
|
||||
struct compactinfo_t
|
||||
{
|
||||
int64_t seedStart, seedEnd;
|
||||
unsigned int range;
|
||||
BiomeFilter filter;
|
||||
int withHut, withMonument;
|
||||
int minscale;
|
||||
};
|
||||
|
||||
|
||||
#ifdef USE_PTHREAD
|
||||
static void *searchCompactBiomesThread(void *data)
|
||||
#else
|
||||
static DWORD WINAPI searchCompactBiomesThread(LPVOID data)
|
||||
#endif
|
||||
{
|
||||
struct compactinfo_t info = *(struct compactinfo_t *)data;
|
||||
int ax = -info.range, az = -info.range;
|
||||
int w = 2*info.range, h = 2*info.range;
|
||||
int64_t s;
|
||||
|
||||
int mcversion = MC_1_14;
|
||||
LayerStack g;
|
||||
setupGenerator(&g, mcversion);
|
||||
int *cache = allocCache(&g.layers[L_VORONOI_ZOOM_1], w, h);
|
||||
|
||||
for (s = info.seedStart; s != info.seedEnd; s++)
|
||||
{
|
||||
if (!hasAllTemps(&g, s, 0, 0))
|
||||
continue;
|
||||
|
||||
if (checkForBiomes(&g, L_VORONOI_ZOOM_1, cache, s, ax, az, w, h, info.filter, 1) > 0)
|
||||
{
|
||||
int x, z;
|
||||
if (info.withHut)
|
||||
{
|
||||
int r = info.range / SWAMP_HUT_CONFIG.regionSize;
|
||||
for (z = -r; z < r; z++)
|
||||
{
|
||||
for (x = -r; x < r; x++)
|
||||
{
|
||||
Pos p;
|
||||
p = getStructurePos(SWAMP_HUT_CONFIG, s, x, z, NULL);
|
||||
if (isViableStructurePos(Swamp_Hut, mcversion, &g, s, p.x, p.z))
|
||||
goto L_hut_found;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
L_hut_found:;
|
||||
}
|
||||
if (info.withMonument)
|
||||
{
|
||||
int r = info.range / MONUMENT_CONFIG.regionSize;
|
||||
for (z = -r; z < r; z++)
|
||||
{
|
||||
for (x = -r; x < r; x++)
|
||||
{
|
||||
Pos p;
|
||||
p = getStructurePos(MONUMENT_CONFIG, s, x, z, NULL);
|
||||
if (isViableStructurePos(Monument, mcversion, &g, s, p.x, p.z))
|
||||
goto L_monument_found;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
L_monument_found:;
|
||||
}
|
||||
|
||||
printf("%" PRId64 "\n", s);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
free(cache);
|
||||
|
||||
#ifdef USE_PTHREAD
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
initBiomes();
|
||||
|
||||
int64_t seedStart, seedEnd;
|
||||
unsigned int threads, t, range;
|
||||
BiomeFilter filter;
|
||||
int withHut, withMonument;
|
||||
int minscale;
|
||||
|
||||
// arguments
|
||||
if (argc <= 1)
|
||||
{
|
||||
printf( "find_compactbiomes [seed_start] [seed_end] [threads] [range]\n"
|
||||
"\n"
|
||||
" seed_start starting seed for search [long, default=0]\n"
|
||||
" end_start end seed for search [long, default=-1]\n"
|
||||
" threads number of threads to use [uint, default=1]\n"
|
||||
" range search range (in blocks) [uint, default=1024]\n");
|
||||
exit(1);
|
||||
}
|
||||
if (argc <= 1 || sscanf(argv[1], "%" PRId64, &seedStart) != 1) seedStart = 0;
|
||||
if (argc <= 2 || sscanf(argv[2], "%" PRId64, &seedEnd) != 1) seedEnd = -1;
|
||||
if (argc <= 3 || sscanf(argv[3], "%u", &threads) != 1) threads = 1;
|
||||
if (argc <= 4 || sscanf(argv[4], "%u", &range) != 1) range = 1024;
|
||||
|
||||
// TODO: set up a customisable biome filter
|
||||
filter = setupBiomeFilter(BIOMES_L13_OCEAN_MIX_4,
|
||||
sizeof(BIOMES_L13_OCEAN_MIX_4)/sizeof(int));
|
||||
minscale = 1; // terminate search at this layer scale
|
||||
// TODO: simple structure filter
|
||||
withHut = 0;
|
||||
withMonument = 0;
|
||||
|
||||
printf("Starting search through seeds %" PRId64 " to %" PRId64", using %u threads.\n"
|
||||
"Search radius = %u.\n", seedStart, seedEnd, threads, range);
|
||||
|
||||
thread_id_t threadID[threads];
|
||||
struct compactinfo_t info[threads];
|
||||
|
||||
// store thread information
|
||||
uint64_t seedCnt = ((uint64_t)seedEnd - (uint64_t)seedStart) / threads;
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
info[t].seedStart = (int64_t)(seedStart + seedCnt * t);
|
||||
info[t].seedEnd = (int64_t)(seedStart + seedCnt * (t+1));
|
||||
info[t].range = range;
|
||||
info[t].filter = filter;
|
||||
info[t].withHut = withHut;
|
||||
info[t].withMonument = withMonument;
|
||||
info[t].minscale = minscale;
|
||||
}
|
||||
info[threads-1].seedEnd = seedEnd;
|
||||
|
||||
// start threads
|
||||
#ifdef USE_PTHREAD
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
pthread_create(&threadID[t], NULL, searchCompactBiomesThread, (void*)&info[t]);
|
||||
}
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
pthread_join(threadID[t], NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
threadID[t] = CreateThread(NULL, 0, searchCompactBiomesThread, (LPVOID)&info[t], 0, NULL);
|
||||
}
|
||||
|
||||
WaitForMultipleObjects(threads, threadID, TRUE, INFINITE);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
245
find_quadhuts.c
245
find_quadhuts.c
@ -1,245 +0,0 @@
|
||||
/**
|
||||
* This is an example program that demonstrates how to find seeds with a
|
||||
* quad-witch-hut located around the specified region (512x512 area).
|
||||
*
|
||||
* It uses some optimisations that cause it miss a small number of seeds, in
|
||||
* exchange for a major speed upgrade. (~99% accuracy, ~1200% speed)
|
||||
*/
|
||||
|
||||
#include "finders.h"
|
||||
#include "generator.h"
|
||||
#include "layers.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Always initialize the biome list before starting any seed finder or
|
||||
// biome generator.
|
||||
initBiomes();
|
||||
LayerStack g;
|
||||
|
||||
// Translate the positions to the desired regions.
|
||||
int regPosX = 0;
|
||||
int regPosZ = 0;
|
||||
|
||||
int mcversion = MC_1_16;
|
||||
StructureConfig featureConfig;
|
||||
|
||||
char seedFileName[256] = {};
|
||||
enum LowBitSet lbitset = LBIT_ALL;
|
||||
int radius = 128;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
if (sscanf(argv[1], "%d", ®PosX) != 1) regPosX = 0;
|
||||
if (sscanf(argv[2], "%d", ®PosZ) != 1) regPosZ = 0;
|
||||
|
||||
if (argc > 3)
|
||||
{
|
||||
int mcarg1 = 0, mcarg2 = 0;
|
||||
int ac = sscanf(argv[3], "%d.%d", &mcarg1, &mcarg2);
|
||||
if (ac < 1)
|
||||
{
|
||||
printf("Bad version format\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ac > 1)
|
||||
mcarg1 = 100 * mcarg1 + mcarg2;
|
||||
if (mcarg1 < 113)
|
||||
mcversion = MC_1_7;
|
||||
else if (mcarg1 < 116)
|
||||
mcversion = MC_1_13;
|
||||
else
|
||||
mcversion = MC_1_16;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("MC version not specified. Defaulting to 1.16\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Usage:\n"
|
||||
"find_quadhuts [regionX] [regionZ] [mcversion]\n"
|
||||
"Defaulting to origin.\n\n");
|
||||
}
|
||||
|
||||
regPosX -= 1;
|
||||
regPosZ -= 1;
|
||||
|
||||
if (mcversion >= MC_1_13)
|
||||
{
|
||||
featureConfig = SWAMP_HUT_CONFIG;
|
||||
}
|
||||
else
|
||||
{
|
||||
featureConfig = FEATURE_CONFIG;
|
||||
}
|
||||
|
||||
setupGenerator(&g, mcversion);
|
||||
|
||||
#if defined(_WIN32)
|
||||
_mkdir("./seeds");
|
||||
#else
|
||||
mkdir("./seeds", 0773);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
sprintf(seedFileName, "./seeds/quadbase_ideal_%d.txt", featureConfig.salt);
|
||||
lbitset = LBIT_IDEAL;
|
||||
radius = 128;
|
||||
#elif 0
|
||||
sprintf(seedFileName, "./seeds/quadbase_classic_%d.txt", featureConfig.salt);
|
||||
lbitset = LBIT_CLASSIC;
|
||||
radius = 128;
|
||||
#elif 1
|
||||
sprintf(seedFileName, "./seeds/quadbase_normal_%d.txt", featureConfig.salt);
|
||||
lbitset = LBIT_HUT_NORMAL;
|
||||
radius = 128;
|
||||
#else
|
||||
sprintf(seedFileName, "./seeds/quadbase_barely_%d.txt", featureConfig.salt);
|
||||
lbitset = LBIT_HUT_BARELY;
|
||||
radius = 128;
|
||||
#endif
|
||||
|
||||
if (access(seedFileName, F_OK))
|
||||
{
|
||||
printf("Seed base file does not exist: Creating new one.\n"
|
||||
"This can take a while...\n");
|
||||
int threads = 6;
|
||||
search4QuadBases(seedFileName, threads, featureConfig, radius, lbitset);
|
||||
}
|
||||
|
||||
int64_t i, j, qhcnt;
|
||||
int64_t base, seed;
|
||||
int64_t *qhcandidates = loadSavedSeeds(seedFileName, &qhcnt);
|
||||
|
||||
|
||||
Layer *lFilterBiome = &g.layers[L_BIOME_256];
|
||||
int *biomeCache = allocCache(lFilterBiome, 3, 3);
|
||||
|
||||
|
||||
// Load the positions of the four structures that make up the quad-structure
|
||||
// so we can test the biome at these positions.
|
||||
Pos qpos[4];
|
||||
|
||||
// layerSeed for Layer 19: Biome, to make preliminary seed tests.
|
||||
int64_t lsBiome = g.layers[L_BIOME_256].layerSeed;
|
||||
|
||||
|
||||
int areaX = (regPosX << 1) + 1;
|
||||
int areaZ = (regPosZ << 1) + 1;
|
||||
|
||||
int check_swamp_hut_specifics = 1;
|
||||
int64_t ss, cs;
|
||||
|
||||
// Search for a swamp at the structure positions
|
||||
for (i = 0; i < qhcnt; i++)
|
||||
{
|
||||
base = moveStructure(qhcandidates[i], regPosX, regPosZ);
|
||||
|
||||
qpos[0] = getStructurePos(featureConfig, base, 0+regPosX, 0+regPosZ, NULL);
|
||||
qpos[1] = getStructurePos(featureConfig, base, 0+regPosX, 1+regPosZ, NULL);
|
||||
qpos[2] = getStructurePos(featureConfig, base, 1+regPosX, 0+regPosZ, NULL);
|
||||
qpos[3] = getStructurePos(featureConfig, base, 1+regPosX, 1+regPosZ, NULL);
|
||||
|
||||
/*
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
printf("(%d,%d) ", qpos[j].x, qpos[j].z);
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
|
||||
if (check_swamp_hut_specifics)
|
||||
{
|
||||
// This little magic code checks if there is a meaningful chance for
|
||||
// this seed base to generate swamps in the area.
|
||||
// The idea is, that the conversion from Lush temperature to swamp
|
||||
// is independent of surroundings, so we can test for this
|
||||
// conversion beforehand. Furthermore, biomes tend to leak into the
|
||||
// negative coordinates because of the Zoom layers, so the majority
|
||||
// of hits will occur when SouthEast corner (at a 1:256 scale) of
|
||||
// the quad-hut has a swamp. (This assumption misses about 1 in 500
|
||||
// quad-hut seeds.) Finally, here we also exploit that the minecraft
|
||||
// random number generator is quite bad, the "mcNextRand() mod 6"
|
||||
// check has a period pattern of ~3 on the high seed-bits, which
|
||||
// means we can avoid checking all 16 high-bit combinations.
|
||||
for (j = 0; j < 5; j++)
|
||||
{
|
||||
seed = base + ((j+0x53) << 48);
|
||||
ss = getStartSeed(seed, lsBiome);
|
||||
cs = getChunkSeed(ss, areaX+1, areaZ+1);
|
||||
if (mcFirstInt(cs, 6) == 5)
|
||||
break;
|
||||
}
|
||||
if (j >= 5)
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t hits = 0, swpc;
|
||||
|
||||
for (j = 0; j < 0x10000; j++)
|
||||
{
|
||||
seed = base + (j << 48);
|
||||
|
||||
if (check_swamp_hut_specifics)
|
||||
{
|
||||
/** Pre-Generation Checks **/
|
||||
// We can check that at least one swamp could generate in this
|
||||
// area before doing the biome generator checks.
|
||||
ss = getStartSeed(seed, lsBiome);
|
||||
cs = getChunkSeed(ss, areaX+1, areaZ+1);
|
||||
if (mcFirstInt(cs, 6) != 5)
|
||||
continue;
|
||||
|
||||
// This seed base does not seem to contain many quad huts, so
|
||||
// make a more detailed analysis of the surroundings and see if
|
||||
// there is enough potential for more swamps to justify
|
||||
// searching further.
|
||||
if (hits == 0 && (j & 0xfff) == 0xfff)
|
||||
{
|
||||
swpc = 0;
|
||||
cs = getChunkSeed(ss, areaX, areaZ+1);
|
||||
swpc += mcFirstInt(cs, 6) == 5;
|
||||
cs = getChunkSeed(ss, areaX+1, areaZ);
|
||||
swpc += mcFirstInt(cs, 6) == 5;
|
||||
cs = getChunkSeed(ss, areaX, areaZ);
|
||||
swpc += mcFirstInt(cs, 6) == 5;
|
||||
|
||||
if (swpc < (j > 0x1000 ? 2 : 1))
|
||||
break;
|
||||
}
|
||||
|
||||
// Dismiss seeds that don't have a swamp near the quad temple.
|
||||
setWorldSeed(lFilterBiome, seed);
|
||||
genArea(lFilterBiome, biomeCache, (regPosX<<1)+2, (regPosZ<<1)+2, 1, 1);
|
||||
|
||||
if (biomeCache[0] != swamp)
|
||||
continue;
|
||||
}
|
||||
|
||||
// we need to use config here for pre 1.13 as that would not
|
||||
// specify that we are only interested in swamp huts
|
||||
if (!isViableStructurePos(Swamp_Hut, mcversion, &g, seed, qpos[0].x, qpos[0].z)) continue;
|
||||
if (!isViableStructurePos(Swamp_Hut, mcversion, &g, seed, qpos[1].x, qpos[1].z)) continue;
|
||||
if (!isViableStructurePos(Swamp_Hut, mcversion, &g, seed, qpos[2].x, qpos[2].z)) continue;
|
||||
if (!isViableStructurePos(Swamp_Hut, mcversion, &g, seed, qpos[3].x, qpos[3].z)) continue;
|
||||
|
||||
printf("%" PRId64 "\n", seed);
|
||||
hits++;
|
||||
}
|
||||
}
|
||||
|
||||
free(biomeCache);
|
||||
|
||||
return 0;
|
||||
}
|
482
finders.c
482
finders.c
@ -5,6 +5,17 @@
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <direct.h>
|
||||
#define IS_DIR_SEP ((C) == '/' || (C) == '\\')
|
||||
#define stat _stat
|
||||
#define mkdir(P,X) _mkdir(P)
|
||||
#define S_IFDIR _S_IFDIR
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#define IS_DIR_SEP(C) ((C) == '/')
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// Globals
|
||||
@ -26,10 +37,7 @@ int64_t *loadSavedSeeds(const char *fnam, int64_t *scnt)
|
||||
int64_t *baseSeeds;
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
perror("ERR loadSavedSeeds: ");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*scnt = 0;
|
||||
|
||||
@ -39,6 +47,9 @@ int64_t *loadSavedSeeds(const char *fnam, int64_t *scnt)
|
||||
else while (!feof(fp) && fgetc(fp) != '\n');
|
||||
}
|
||||
|
||||
if (*scnt == 0)
|
||||
return NULL;
|
||||
|
||||
baseSeeds = (int64_t*) calloc(*scnt, sizeof(*baseSeeds));
|
||||
|
||||
rewind(fp);
|
||||
@ -71,7 +82,7 @@ static int testOutpostPos(int64_t s, int cx, int cz)
|
||||
|
||||
Pos getStructurePos(StructureConfig config, int64_t seed, int regX, int regZ, int *valid)
|
||||
{
|
||||
Pos pos = {};
|
||||
Pos pos = {0,0};
|
||||
if (valid) *valid = 0;
|
||||
|
||||
if (config.properties == 0)
|
||||
@ -214,88 +225,236 @@ int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk)
|
||||
}
|
||||
|
||||
|
||||
STRUCT(quad_threadinfo_t)
|
||||
#define MAX_PATHLEN 4096
|
||||
|
||||
STRUCT(linked_seeds_t)
|
||||
{
|
||||
int64_t start, end;
|
||||
StructureConfig sconf;
|
||||
int threadID;
|
||||
int radius;
|
||||
int lbitset;
|
||||
const char *fnam;
|
||||
int64_t seeds[100];
|
||||
size_t len;
|
||||
linked_seeds_t *next;
|
||||
};
|
||||
|
||||
STRUCT(threadinfo_t)
|
||||
{
|
||||
// seed range
|
||||
int64_t start, end;
|
||||
const int64_t *lowBits;
|
||||
int lowBitCnt;
|
||||
int lowBitN;
|
||||
|
||||
// testing function
|
||||
int (*check)(int64_t, void*);
|
||||
void *data;
|
||||
|
||||
// output
|
||||
char path[MAX_PATHLEN];
|
||||
FILE *fp;
|
||||
linked_seeds_t ls;
|
||||
};
|
||||
|
||||
|
||||
static int mkdirp(char *path)
|
||||
{
|
||||
int err = 0, len = strlen(path);
|
||||
char *p = path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (p[1] == ':') p += 2;
|
||||
#endif
|
||||
while (IS_DIR_SEP(*p)) p++;
|
||||
|
||||
while (!err && p < path+len)
|
||||
{
|
||||
char *q = p;
|
||||
while (*q && !IS_DIR_SEP(*q))
|
||||
q++;
|
||||
|
||||
if (p != path) p[-1] = '/';
|
||||
*q = 0;
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) == -1)
|
||||
err = mkdir(path, 0773);
|
||||
else if (!(st.st_mode & S_IFDIR))
|
||||
err = 1;
|
||||
|
||||
p = q+1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_PTHREAD
|
||||
static void *search4QuadBasesThread(void *data)
|
||||
static void *searchAll48Thread(void *data)
|
||||
#else
|
||||
static DWORD WINAPI search4QuadBasesThread(LPVOID data)
|
||||
static DWORD WINAPI searchAll48Thread(LPVOID data)
|
||||
#endif
|
||||
{
|
||||
quad_threadinfo_t info = *(quad_threadinfo_t*)data;
|
||||
// TODO TEST:
|
||||
// lower bits with various ranges
|
||||
|
||||
const int64_t start = info.start;
|
||||
const int64_t end = info.end;
|
||||
const int64_t salt = info.sconf.salt;
|
||||
threadinfo_t *info = (threadinfo_t*)data;
|
||||
|
||||
int64_t seed;
|
||||
int64_t seed = info->start;
|
||||
int64_t end = info->end;
|
||||
linked_seeds_t *lp = &info->ls;
|
||||
lp->len = 0;
|
||||
lp->next = NULL;
|
||||
|
||||
int64_t *lowerBits;
|
||||
int lowerBitsCnt;
|
||||
int lowerBitsIdx = 0;
|
||||
int i;
|
||||
if (info->lowBits)
|
||||
{
|
||||
int64_t hstep = 1LL << info->lowBitN;
|
||||
int64_t hmask = ~(hstep - 1);
|
||||
int64_t mid;
|
||||
int idx;
|
||||
|
||||
lowerBits = (int64_t *) malloc(0x100000 * sizeof(int64_t));
|
||||
if (lowerBits == NULL)
|
||||
mid = info->start & hmask;
|
||||
for (idx = 0; (seed = mid | info->lowBits[idx]) < info->start; idx++);
|
||||
|
||||
while (seed <= end)
|
||||
{
|
||||
if U(info->check(seed, info->data))
|
||||
{
|
||||
if (info->fp)
|
||||
{
|
||||
fprintf(info->fp, "%" PRId64"\n", seed);
|
||||
fflush(info->fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
lp->seeds[lp->len] = seed;
|
||||
lp->len++;
|
||||
if (lp->len >= sizeof(lp->seeds)/sizeof(int64_t))
|
||||
{
|
||||
linked_seeds_t *n =
|
||||
(linked_seeds_t*) malloc(sizeof(linked_seeds_t));
|
||||
if (n == NULL)
|
||||
exit(1);
|
||||
|
||||
switch (info.lbitset)
|
||||
{
|
||||
case LBIT_IDEAL:
|
||||
lowerBitsCnt = sizeof(lowerBaseBitsIdeal) / sizeof(int64_t);
|
||||
for (i = 0; i < lowerBitsCnt; i++)
|
||||
lowerBits[i] = (lowerBaseBitsIdeal[i] - salt) & 0xfffff;
|
||||
break;
|
||||
|
||||
case LBIT_CLASSIC:
|
||||
lowerBitsCnt = sizeof(lowerBaseBitsClassic) / sizeof(int64_t);
|
||||
for (i = 0; i < lowerBitsCnt; i++)
|
||||
lowerBits[i] = (lowerBaseBitsClassic[i] - salt) & 0xfffff;
|
||||
break;
|
||||
|
||||
case LBIT_HUT_NORMAL:
|
||||
lowerBitsCnt = sizeof(lowerBaseBitsHutNormal) / sizeof(int64_t);
|
||||
for (i = 0; i < lowerBitsCnt; i++)
|
||||
lowerBits[i] = (lowerBaseBitsHutNormal[i] - salt) & 0xfffff;
|
||||
break;
|
||||
|
||||
case LBIT_HUT_BARELY:
|
||||
lowerBitsCnt = sizeof(lowerBaseBitsHutBarely) / sizeof(int64_t);
|
||||
for (i = 0; i < lowerBitsCnt; i++)
|
||||
lowerBits[i] = (lowerBaseBitsHutBarely[i] - salt) & 0xfffff;
|
||||
break;
|
||||
|
||||
default:
|
||||
lowerBitsCnt = 0x100000;
|
||||
for (i = 0; i < lowerBitsCnt; i++) lowerBits[i] = i;
|
||||
break;
|
||||
lp->next = n;
|
||||
lp = n;
|
||||
lp->len = 0;
|
||||
lp->next = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char fnam[256];
|
||||
sprintf(fnam, "%s.part%d", info.fnam, info.threadID);
|
||||
idx++;
|
||||
if (idx >= info->lowBitCnt)
|
||||
{
|
||||
idx = 0;
|
||||
mid += hstep;
|
||||
}
|
||||
|
||||
FILE *fp = fopen(fnam, "a+");
|
||||
seed = mid | info->lowBits[idx];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (seed <= end)
|
||||
{
|
||||
if U(info->check(seed, info->data))
|
||||
{
|
||||
if (info->fp)
|
||||
{
|
||||
fprintf(info->fp, "%" PRId64"\n", seed);
|
||||
fflush(info->fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
lp->seeds[lp->len] = seed;
|
||||
lp->len++;
|
||||
if (lp->len >= sizeof(lp->seeds)/sizeof(int64_t))
|
||||
{
|
||||
linked_seeds_t *n =
|
||||
(linked_seeds_t*) malloc(sizeof(linked_seeds_t));
|
||||
if (n == NULL)
|
||||
exit(1);
|
||||
lp->next = n;
|
||||
lp = n;
|
||||
lp->len = 0;
|
||||
lp->next = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
seed++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_PTHREAD
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int searchAll48(
|
||||
int64_t ** seedbuf,
|
||||
int64_t * buflen,
|
||||
const char * path,
|
||||
int threads,
|
||||
const int64_t * lowBits,
|
||||
int lowBitCnt,
|
||||
int lowBitN,
|
||||
int (*check)(int64_t s48, void *data),
|
||||
void * data
|
||||
)
|
||||
{
|
||||
threadinfo_t *info = (threadinfo_t*) malloc(threads* sizeof(*info));
|
||||
thread_id_t *tids = (thread_id_t*) malloc(threads* sizeof(*tids));
|
||||
int i, t;
|
||||
int err = 0;
|
||||
|
||||
if (path)
|
||||
{
|
||||
size_t pathlen = strlen(path);
|
||||
char dpath[MAX_PATHLEN];
|
||||
|
||||
// split path into directory and file and create missing directories
|
||||
if (pathlen + 8 >= sizeof(dpath))
|
||||
goto L_ERR;
|
||||
strcpy(dpath, path);
|
||||
|
||||
for (i = pathlen-1; i >= 0; i--)
|
||||
{
|
||||
if (IS_DIR_SEP(dpath[i]))
|
||||
{
|
||||
dpath[i] = 0;
|
||||
if (mkdirp(dpath))
|
||||
goto L_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (seedbuf == NULL || buflen == NULL)
|
||||
{
|
||||
// no file and no buffer return: no output possible
|
||||
goto L_ERR;
|
||||
}
|
||||
|
||||
// prepare the thread info and load progress if present
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
info[t].start = (t * (MASK48+1) / threads);
|
||||
info[t].end = ((t+1) * (MASK48+1) / threads - 1);
|
||||
info[t].lowBits = lowBits;
|
||||
info[t].lowBitCnt = lowBitCnt;
|
||||
info[t].lowBitN = lowBitN;
|
||||
info[t].check = check;
|
||||
info[t].data = data;
|
||||
|
||||
if (path)
|
||||
{
|
||||
// progress file of this thread
|
||||
snprintf(info[t].path, sizeof(info[t].path), "%s.part%d", path, t);
|
||||
FILE *fp = fopen(info[t].path, "a+");
|
||||
if (fp == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not open \"%s\" for writing.\n", fnam);
|
||||
free(lowerBits);
|
||||
exit(-1);
|
||||
}
|
||||
goto L_ERR;
|
||||
|
||||
seed = start;
|
||||
|
||||
// Check the last entry in the file and use it as a starting point if it
|
||||
// exists. (I.e. loading the saved progress.)
|
||||
int c, nnl = 0;
|
||||
char buf[32];
|
||||
|
||||
// find the last newline
|
||||
for (i = 1; i < 32; i++)
|
||||
{
|
||||
if (fseek(fp, -i, SEEK_END)) break;
|
||||
@ -306,141 +465,130 @@ static DWORD WINAPI search4QuadBasesThread(LPVOID data)
|
||||
|
||||
if (i < 32 && !fseek(fp, 1-i, SEEK_END) && fread(buf, i-1, 1, fp) > 0)
|
||||
{
|
||||
if (sscanf(buf, "%" PRId64, &seed) == 1)
|
||||
// read the last entry, and replace the start seed accordingly
|
||||
int64_t lentry;
|
||||
if (sscanf(buf, "%" PRId64, &lentry) == 1)
|
||||
{
|
||||
while (lowerBits[lowerBitsIdx] <= (seed & 0xfffff))
|
||||
lowerBitsIdx++;
|
||||
info[t].start = lentry;
|
||||
printf("Continuing thread %d at seed %" PRId64 "\n",
|
||||
t, lentry);
|
||||
}
|
||||
}
|
||||
|
||||
seed = (seed & 0x0000fffffff00000) + lowerBits[lowerBitsIdx];
|
||||
|
||||
printf("Thread %d starting from: %" PRId64"\n", info.threadID, seed);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
info[t].fp = fp;
|
||||
}
|
||||
else
|
||||
{
|
||||
seed = start;
|
||||
info[t].path[0] = 0;
|
||||
info[t].fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
|
||||
while (seed < end)
|
||||
{
|
||||
float r = isQuadBase(info.sconf, seed, info.radius);
|
||||
if (r)
|
||||
{
|
||||
fprintf(fp, "%" PRId64"\n", seed);
|
||||
fflush(fp);
|
||||
//FILE *ftmp = fopen("./seeds/hex", "a");
|
||||
//fprintf(ftmp, "0x%05lx %.6f\n", (seed + salt) & 0xfffff, r);
|
||||
//fflush(ftmp);
|
||||
//fclose(ftmp);
|
||||
}
|
||||
|
||||
lowerBitsIdx++;
|
||||
if (lowerBitsIdx >= lowerBitsCnt)
|
||||
{
|
||||
lowerBitsIdx = 0;
|
||||
seed += 0x100000;
|
||||
}
|
||||
seed = (seed & 0x0000fffffff00000) + lowerBits[lowerBitsIdx];
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(lowerBits);
|
||||
|
||||
#ifdef USE_PTHREAD
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void search4QuadBases(const char *fnam, int threads,
|
||||
const StructureConfig structureConfig, int radius, int lbitset)
|
||||
{
|
||||
thread_id_t threadID[threads];
|
||||
quad_threadinfo_t info[threads];
|
||||
int64_t t;
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
info[t].threadID = t;
|
||||
info[t].start = (t * SEED_BASE_MAX / threads) & 0x0000fffffff00000;
|
||||
info[t].end = ((info[t].start + (SEED_BASE_MAX-1) / threads) & 0x0000fffffff00000) + 1;
|
||||
info[t].fnam = fnam;
|
||||
info[t].radius = radius;
|
||||
info[t].lbitset = lbitset;
|
||||
info[t].sconf = structureConfig;
|
||||
}
|
||||
|
||||
|
||||
// run the threads
|
||||
#ifdef USE_PTHREAD
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
pthread_create(&threadID[t], NULL, search4QuadBasesThread, (void*)&info[t]);
|
||||
pthread_create(&tids[t], NULL, searchAll48Thread, (void*)&info[t]);
|
||||
}
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
pthread_join(threadID[t], NULL);
|
||||
pthread_join(tids[t], NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
threadID[t] = CreateThread(NULL, 0, search4QuadBasesThread, (LPVOID)&info[t], 0, NULL);
|
||||
tids[t] = CreateThread(NULL, 0, searchAll48Thread,
|
||||
(LPVOID)&info[t], 0, NULL);
|
||||
}
|
||||
|
||||
WaitForMultipleObjects(threads, threadID, TRUE, INFINITE);
|
||||
WaitForMultipleObjects(threads, tids, TRUE, INFINITE);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// merge thread parts
|
||||
|
||||
char fnamThread[256];
|
||||
char buffer[4097];
|
||||
FILE *fp = fopen(fnam, "w");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Could not open \"%s\" for writing.\n", fnam);
|
||||
exit(-1);
|
||||
}
|
||||
FILE *fpart;
|
||||
int n;
|
||||
if (path)
|
||||
{
|
||||
// merge partial files
|
||||
FILE *fp = fopen(path, "w");
|
||||
if (fp == NULL)
|
||||
goto L_ERR;
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
sprintf(fnamThread, "%s.part%d", info[t].fnam, info[t].threadID);
|
||||
rewind(info[t].fp);
|
||||
|
||||
fpart = fopen(fnamThread, "r");
|
||||
|
||||
if (fpart == NULL)
|
||||
{
|
||||
perror("ERR search4QuadBases: ");
|
||||
break;
|
||||
}
|
||||
|
||||
while ((n = fread(buffer, sizeof(char), 4096, fpart)))
|
||||
char buffer[4097];
|
||||
size_t n;
|
||||
while ((n = fread(buffer, sizeof(char), 4096, info[t].fp)))
|
||||
{
|
||||
if (!fwrite(buffer, sizeof(char), n, fp))
|
||||
{
|
||||
perror("ERR search4QuadBases: ");
|
||||
fclose(fp);
|
||||
fclose(fpart);
|
||||
return;
|
||||
goto L_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fpart);
|
||||
|
||||
remove(fnamThread);
|
||||
fclose(info[t].fp);
|
||||
remove(info[t].path);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (seedbuf && buflen)
|
||||
{
|
||||
*seedbuf = loadSavedSeeds(path, buflen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// merge linked seed buffers
|
||||
*buflen = 0;
|
||||
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
linked_seeds_t *lp = &info[t].ls;
|
||||
do
|
||||
{
|
||||
*buflen += lp->len;
|
||||
lp = lp->next;
|
||||
}
|
||||
while (lp);
|
||||
}
|
||||
|
||||
*seedbuf = (int64_t*) malloc((*buflen) * sizeof(int64_t));
|
||||
if (*seedbuf == NULL)
|
||||
exit(1);
|
||||
|
||||
i = 0;
|
||||
for (t = 0; t < threads; t++)
|
||||
{
|
||||
linked_seeds_t *lp = &info[t].ls;
|
||||
do
|
||||
{
|
||||
memcpy(*seedbuf + i, lp->seeds, lp->len * sizeof(int64_t));
|
||||
i += lp->len;
|
||||
linked_seeds_t *tmp = lp;
|
||||
lp = lp->next;
|
||||
if (tmp != &info[t].ls)
|
||||
free(tmp);
|
||||
}
|
||||
while (lp);
|
||||
}
|
||||
}
|
||||
|
||||
if (0)
|
||||
L_ERR:
|
||||
err = 1;
|
||||
|
||||
free(tids);
|
||||
free(info);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -797,6 +945,7 @@ int findStrongholds(const int mcversion, const LayerStack *g, int *cache,
|
||||
|
||||
static double getGrassProbability(int64_t seed, int biome, int x, int z)
|
||||
{
|
||||
(void) seed, (void) biome, (void) x, (void) z;
|
||||
// TODO: Use ChunkGeneratorOverworld.generateHeightmap for better estimate.
|
||||
// TODO: Try to determine the actual probabilities and build a statistic.
|
||||
switch (biome)
|
||||
@ -853,6 +1002,7 @@ static double getGrassProbability(int64_t seed, int biome, int x, int z)
|
||||
|
||||
static int canCoordinateBeSpawn(const int64_t seed, const LayerStack *g, int *cache, Pos pos)
|
||||
{
|
||||
(void) cache;
|
||||
int biome = getBiomeAtPos(g, pos);
|
||||
return getGrassProbability(seed, biome, pos.x, pos.z) >= 0.5;
|
||||
}
|
||||
@ -1413,6 +1563,7 @@ BiomeFilter setupBiomeFilter(const int *biomeList, int listLen)
|
||||
case mushroom_fields:
|
||||
// mushroom shores can generate with hills and at rivers
|
||||
bf.raresToFind |= (1ULL << mushroom_fields);
|
||||
// fall through
|
||||
case mushroom_field_shore:
|
||||
bf.tempsToFind |= (1ULL << Oceanic);
|
||||
bf.majorToFind |= (1ULL << mushroom_fields);
|
||||
@ -1571,6 +1722,7 @@ BiomeFilter setupBiomeFilter(const int *biomeList, int listLen)
|
||||
|
||||
case snowy_beach:
|
||||
bf.tempsToFind |= (1ULL << Freezing);
|
||||
// fall through
|
||||
case beach:
|
||||
case stone_shore:
|
||||
bf.riverToFind |= (1ULL << id);
|
||||
@ -1578,12 +1730,14 @@ BiomeFilter setupBiomeFilter(const int *biomeList, int listLen)
|
||||
|
||||
case mountains:
|
||||
bf.majorToFind |= (1ULL << mountains);
|
||||
// fall through
|
||||
case wooded_mountains:
|
||||
bf.raresToFind |= (1ULL << id);
|
||||
bf.riverToFind |= (1ULL << id);
|
||||
break;
|
||||
case gravelly_mountains:
|
||||
bf.majorToFind |= (1ULL << mountains);
|
||||
// fall through
|
||||
case modified_gravelly_mountains:
|
||||
bf.raresToFindM |= (1ULL << (id-128));
|
||||
bf.riverToFindM |= (1ULL << (id-128));
|
||||
@ -1955,8 +2109,8 @@ int checkForBiomes(
|
||||
l = &g->layers[L_SPECIAL_1024];
|
||||
x0 = (bx) / l->scale; if (x < 0) x0--;
|
||||
z0 = (bz) / l->scale; if (z < 0) z0--;
|
||||
x1 = (bx + bw) / l->scale; if (x+w >= 0) x1++;
|
||||
z1 = (bz + bh) / l->scale; if (z+h >= 0) z1++;
|
||||
x1 = (bx + bw) / l->scale; if (x+(int)w >= 0) x1++;
|
||||
z1 = (bz + bh) / l->scale; if (z+(int)h >= 0) z1++;
|
||||
ss = getStartSeed(seed, l->layerSeed);
|
||||
|
||||
for (j = z0; j <= z1; j++)
|
||||
@ -1975,8 +2129,8 @@ int checkForBiomes(
|
||||
l = &g->layers[L_BIOME_256];
|
||||
x0 = bx / l->scale; if (x < 0) x0--;
|
||||
z0 = bz / l->scale; if (z < 0) z0--;
|
||||
x1 = (bx + bw) / l->scale; if (x+w >= 0) x1++;
|
||||
z1 = (bz + bh) / l->scale; if (z+h >= 0) z1++;
|
||||
x1 = (bx + bw) / l->scale; if (x+(int)w >= 0) x1++;
|
||||
z1 = (bz + bh) / l->scale; if (z+(int)h >= 0) z1++;
|
||||
|
||||
if (filter.majorToFind & (1ULL << mushroom_fields))
|
||||
{
|
||||
|
110
finders.h
110
finders.h
@ -25,7 +25,7 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define SEED_BASE_MAX (1LL << 48)
|
||||
#define MASK48 ((1LL << 48) - 1)
|
||||
#define PI 3.141592653589793
|
||||
|
||||
#define LARGE_STRUCT 1
|
||||
@ -55,38 +55,6 @@ enum // village house types prior to 1.14
|
||||
};
|
||||
|
||||
|
||||
// only the very best constellations
|
||||
static const int64_t lowerBaseBitsIdeal[] =
|
||||
{
|
||||
0x43f18,0xc751a,0xf520a,
|
||||
};
|
||||
|
||||
// for the classic quad-structure constellations
|
||||
static const int64_t lowerBaseBitsClassic[] =
|
||||
{
|
||||
0x43f18,0x79a0a,0xc751a,0xf520a,
|
||||
};
|
||||
|
||||
// for any valid quad-structure constellation with a structure size:
|
||||
// (7+1,7+43+1,9+1) which corresponds to a fall-damage based quad-witch-farm,
|
||||
// but may require a perfect player position
|
||||
static const int64_t lowerBaseBitsHutNormal[] =
|
||||
{
|
||||
0x43f18,0x65118,0x75618,0x79a0a, 0x89718,0x9371a,0xa5a08,0xb5e18,
|
||||
0xc751a,0xf520a,
|
||||
};
|
||||
|
||||
// for any valid quad-structure constellation with a structure size:
|
||||
// (7+1,7+1,9+1) which corresponds to quad-witch-farms without drop chute
|
||||
static const int64_t lowerBaseBitsHutBarely[] =
|
||||
{
|
||||
0x1272d,0x17908,0x367b9,0x43f18, 0x487c9,0x487ce,0x50aa7,0x647b5,
|
||||
0x65118,0x75618,0x79a0a,0x89718, 0x9371a,0x967ec,0xa3d0a,0xa5918,
|
||||
0xa591d,0xa5a08,0xb5e18,0xc6749, 0xc6d9a,0xc751a,0xd7108,0xd717a,
|
||||
0xe2739,0xe9918,0xee1c4,0xf520a,
|
||||
};
|
||||
|
||||
|
||||
STRUCT(StructureConfig)
|
||||
{
|
||||
int salt;
|
||||
@ -226,6 +194,37 @@ STRUCT(StrongholdIter)
|
||||
*/
|
||||
|
||||
|
||||
// lower 20 bits, only the very best constellations
|
||||
// (the structure salt has to be subtracted before use)
|
||||
static const int64_t low20QuadIdeal[] =
|
||||
{
|
||||
0x43f18,0xc751a,0xf520a,
|
||||
};
|
||||
|
||||
// lower 20 bits, the classic quad-structure constellations
|
||||
static const int64_t low20QuadClassic[] =
|
||||
{
|
||||
0x43f18,0x79a0a,0xc751a,0xf520a,
|
||||
};
|
||||
|
||||
// for any valid quad-structure constellation with a structure size:
|
||||
// (7+1,7+43+1,9+1) which corresponds to a fall-damage based quad-witch-farm,
|
||||
// but may require a perfect player position
|
||||
static const int64_t low20QuadHutNormal[] =
|
||||
{
|
||||
0x43f18,0x65118,0x75618,0x79a0a, 0x89718,0x9371a,0xa5a08,0xb5e18,
|
||||
0xc751a,0xf520a,
|
||||
};
|
||||
|
||||
// for any valid quad-structure constellation with a structure size:
|
||||
// (7+1,7+1,9+1) which corresponds to quad-witch-farms without drop chute
|
||||
static const int64_t low20QuadHutBarely[] =
|
||||
{
|
||||
0x1272d,0x17908,0x367b9,0x43f18, 0x487c9,0x487ce,0x50aa7,0x647b5,
|
||||
0x65118,0x75618,0x79a0a,0x89718, 0x9371a,0x967ec,0xa3d0a,0xa5918,
|
||||
0xa591d,0xa5a08,0xb5e18,0xc6749, 0xc6d9a,0xc751a,0xd7108,0xd717a,
|
||||
0xe2739,0xe9918,0xee1c4,0xf520a,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Moving Structures
|
||||
@ -365,23 +364,38 @@ static inline __attribute__((always_inline, const))
|
||||
float isQuadBaseLarge (const StructureConfig sconf, int64_t seed,
|
||||
int ax, int ay, int az, int radius);
|
||||
|
||||
// Defines how the search should limit the lower bits of the seed bases.
|
||||
// Conveniently this also provides a way of specifying a quality category.
|
||||
enum LowBitSet
|
||||
{
|
||||
LBIT_ALL, // all bit configurations
|
||||
LBIT_HUT_BARELY, // any constellation for huts within 128 blocks
|
||||
LBIT_HUT_NORMAL, // sufficiently close for standard farm designs
|
||||
LBIT_CLASSIC, // only classic constellations
|
||||
LBIT_IDEAL, // only the very best constellations that exist
|
||||
};
|
||||
|
||||
/* Starts a multi-threaded search for quad-bases, given a maximum block radius
|
||||
* for the enclosing sphere. The result is saved in a file of path 'fnam'.
|
||||
/* Starts a multi-threaded search through all 48-bit seeds. Since this can
|
||||
* potentially be a lengthy calculation, results can be written to temporary
|
||||
* files immediately, in order to save progress in case of interruption. Seeds
|
||||
* are tested using the function 'check' which takes a 48-bit seed and a custom
|
||||
* 'data' argument. The output can be a dynamically allocated seed buffer
|
||||
* and/or a destination file [which can be loaded using loadSavedSeeds()].
|
||||
* Optionally, only a subset of the lower 20 bits are searched.
|
||||
*
|
||||
* @seedbuf output seed buffer (nullable for file only)
|
||||
* @buflen length of output buffer (nullable)
|
||||
* @path output file path (nullable, also toggles temporary files)
|
||||
* @threads number of threads to use
|
||||
* @lowBits lower bit subset (nullable)
|
||||
* @lowBitCnt length of lower bit subset
|
||||
* @lowBitN number of bits in the subset values
|
||||
* @check the testing function, should return non-zero for desired seeds
|
||||
* @data custon data argument passed to 'check'
|
||||
*
|
||||
* Returns zero upon success.
|
||||
*/
|
||||
void search4QuadBases(const char *fnam, int threads,
|
||||
const StructureConfig structureConfig, int radius, int lbitset);
|
||||
|
||||
int searchAll48(
|
||||
int64_t ** seedbuf,
|
||||
int64_t * buflen,
|
||||
const char * path,
|
||||
int threads,
|
||||
const int64_t * lowBits,
|
||||
int lowBitCnt,
|
||||
int lowBitN,
|
||||
int (*check)(int64_t s48, void *data),
|
||||
void * data
|
||||
);
|
||||
|
||||
int countBlocksInSpawnRange(Pos p[4], int ax, int ay, int az, Pos *afk);
|
||||
|
||||
|
22
makefile
22
makefile
@ -2,13 +2,13 @@ CC = gcc
|
||||
AR = ar
|
||||
ARFLAGS = cr
|
||||
override LDFLAGS = -lm
|
||||
override CFLAGS += -Wall -fwrapv
|
||||
override CFLAGS += -Wall -Wextra -fwrapv
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
override CFLAGS += -D_WIN32
|
||||
RM = del
|
||||
else
|
||||
override LDFLAGS += -lX11 -pthread
|
||||
override LDFLAGS += -pthread
|
||||
#RM = rm
|
||||
endif
|
||||
|
||||
@ -17,26 +17,14 @@ endif
|
||||
all: release
|
||||
|
||||
debug: CFLAGS += -DDEBUG -O0 -ggdb3
|
||||
debug: libcubiomes find_quadhuts find_compactbiomes
|
||||
debug: libcubiomes
|
||||
release: CFLAGS += -O3 -march=native
|
||||
release: libcubiomes find_quadhuts find_compactbiomes
|
||||
release: libcubiomes
|
||||
|
||||
libcubiomes: CFLAGS += -fPIC
|
||||
libcubiomes: layers.o generator.o finders.o util.o
|
||||
$(AR) $(ARFLAGS) libcubiomes.a $^
|
||||
|
||||
find_compactbiomes: find_compactbiomes.o layers.o generator.o finders.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
find_compactbiomes.o: find_compactbiomes.c
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
find_quadhuts: find_quadhuts.o layers.o generator.o finders.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
find_quadhuts.o: find_quadhuts.c
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
|
||||
finders.o: finders.c finders.h
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
@ -51,5 +39,5 @@ util.o: util.c util.h
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
clean:
|
||||
$(RM) *.o libcubiomes.a find_quadhuts find_compactbiomes
|
||||
$(RM) *.o libcubiomes.a
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user