19MisterX98 / Nether_Bedrock_Cracker

Cracks nether seeds from bedrock. JAVA EDITION ONLY
62 stars 8 forks source link

What is the idea behind used algorithm? #21

Closed MiranCZ closed 2 months ago

MiranCZ commented 2 months ago

Hello, I was thinking about making a mod in Java with the same functionality... If you don't mind, I would just like to ask what is the general idea behind the algorithm to get the "main seed" of the generator for the bedrock? I have looked at the Minecraft's code, to see how it generates the bedrock in the nether and I think I have at least surface level understanding on how it works... If I understood it correctly, the only information you are given is that (((mainSeed ^ MathHelper.hashCode(x,y,z)) * MULTIPLY + ADD & MASK) >> 24) * (1f/(1<<24)) < HEIGHT_VALUE where HEIGHT_VALUE is calculated based on the y coordinate and the range where the bedrock generates. And mainSeed is unknown... The naive approach I thought of was just to go trough all 2^48 possibilities and calculate if it fits the bedrock pattern for each one, but I feel like there must be some better approach to this. I tried to look at the source code of this repository, but I don't really understand Rust😅... I ended up going through the lib.rs and layer.rs files and it almost seems like you are discarding the 12 bottom bits and just calculating with the upper 36? I completely understand it you don't want to answer...

19MisterX98 commented 2 months ago

It's been some time but I can probably still give an overview. You're correct in that I combine seeds in sets of 2^12. And only iterate over the upper 2^36 bits.

First of all, the floating point conversion is unnecessary, annoying and can be left out like this: ((mainSeed ^ MathHelper.hashCode(x,y,z)) * MULTIPLY + ADD & MASK) < HEIGHT_VALUE_INT

If you look at the code, you'll notice that two consecutive seeds(changing the last bit) can only result in two outputs that are at max one times the multiplier away from each other.

Formulated in code: abs((mainSeed ^ MathHelper.hashCode(x,y,z)) MULTIPLY + ADD & MASK) - ((mainSeed^1) ^ MathHelper.hashCode(x,y,z)) MULTIPLY + ADD & MASK)) <= 1*MULTIPLY

4 consecutive seeds(changing the bottom 2 bits) result in a range of outputs, that is not wider than 3*MULTIPLY

8 consecutive seeds(changing the bottom 3 bits) result in a range of outputs, that is not wider than 7*MULTIPLY

Changing the bottom N bits results in 2^^N consecutive seeds and a range of outputs that is (2^^N - 1)*Multiply big

This range is the maximal amount of error that you get when comparing to a value with a truncated seed.

In code: long out = ((truncatedSeed ^ MathHelper.hashCode(x,y,z)) MULTIPLY + ADD & MASK); long max_error = (2^^N - 1)Multiply; // Account for overflow if (out + max_error < HEIGHT_VALUE_INT || (out - max_error)%MASK < HEIGHT_VALUE_INT) { //check against the next block } else { //all seeds in this range are invalid }

After checking that a seed range is valid for all blocks, I split the range in half and run both halves through the checks again. This time the checks are more precise due to having one lower bit less. Doing this just recursively until the seed ranges consist of single seeds. If all checks pass, the seeds get displayed.

I hope I could help you. There might be some inaccuracies since it's been a while

MiranCZ commented 2 months ago

Oh my god, that's smart!! Thank you so much