How do you get the world seed for the BiomeProvider? I'm trying to make a BiomeProvider to go with my WorldType. It's just supposed to return a single biome chosen randomly based on the world seed (and eventually dimension number). The problem I'm having is that WorldType.getBiomeProvider is called from both the client and server World and with different seeds (specifically, the client always has 0 for the seed).
I've tried looking through the base WorldType and BiomeProvider, but copying what they do - calling world.getWorldInfo().getSeed() - doesn't work, nor does calling world.getSeed().
I hadn't looked closely at this before, but in looking at the code it does seem that the join world packet and the respawn packet handling set the seed in the associated WorldSettings instance to 0 for the client world creation. You can verify this in the NetHandlerPlayClient#handleJoinGame() method.
If you follow the call hierarchy for the WorldSettings#seed field you'll find there is no other setter except the constructor, so it doesn't get set later.
So this means that the seed is supposed to be 0 on the client. You might wonder how the /seed command works (which prints the seed on the console message) but of course that message is sent from the server which has access to the seed.
Since the client seed is supposed to be 0, why is that a problem for you? What are you doing where you are looking for the seed with code running on the client? All the world generation stuff should be executing on the server side.
I'm trying to make a version of BiomeProviderSingle that chooses it's one biome randomly based on the world seed. But it seems to be constructing a BiomeProvider on both server and client side (WorldType.getBiomeProvider is called with both a WorldClient and a WorldServer).
If I just go ahead and use the world seeds given, I end up with a client side BiomeProvider with a different biome than the server side one, so the rendering is wrong and the grass color changes as you move around.
On the other hand, if I try and delay choosing the biome, then that info isn't available when the chunk generator goes to call world.getBiomeProvider().getBiomes().
I also tried making getBiomeProvider server side only, but it still generated a client side BiomeProvider which that spat out random biomes for every block.
Given that the vanilla biome generation uses these seeds somehow, I must be missing something.
I think the problem is there is a difference between selecting the biome randomly and generating the biome randomly. The regular use of BiomeProviderSingle passes the Biome in the constructor so it would be the same on both sides.
So what I think you need to do is probably have a custom packet that passes the world seed from the server to the client where it is then used by your custom biome provider. It looks like at the time of the server about to start event the seed should already be generated, so I'd probably handle that or similar event like server starting and grab the seed and send a custom packet to the client. Might take a bit of trial and error to figure out the best place to do this, but something like that.
Ah, I was hoping I wouldn't have to resort to such measures. Thanks for the help.
Edit: Question though, if you happen to know. How are the biomes synced in the vanilla BiomeProvider (not BiomeProviderSingle)?
That's a good question. I tried to follow the code, but couldn't really figure it out. The debug overlay GUI will show the biome for the current position, and item map also shows biome, so definitely the client is aware of the biomes at every X, Z position. However, I can't see where these get set, as they call the getter methods but I can't find the inverse setter. Maybe it is recreated on the fly using seed value, but like we've been discussing the seed seems to be 0 on the client.
How do you get the world seed for the BiomeProvider? I'm trying to make a BiomeProvider to go with my WorldType. It's just supposed to return a single biome chosen randomly based on the world seed (and eventually dimension number). The problem I'm having is that WorldType.getBiomeProvider is called from both the client and server World and with different seeds (specifically, the client always has 0 for the seed).
I've tried looking through the base WorldType and BiomeProvider, but copying what they do - calling world.getWorldInfo().getSeed() - doesn't work, nor does calling world.getSeed().
I hadn't looked closely at this before, but in looking at the code it does seem that the join world packet and the respawn packet handling set the seed in the associated WorldSettings instance to 0 for the client world creation. You can verify this in the NetHandlerPlayClient#handleJoinGame() method.
If you follow the call hierarchy for the WorldSettings#seed field you'll find there is no other setter except the constructor, so it doesn't get set later.
So this means that the seed is supposed to be 0 on the client. You might wonder how the /seed command works (which prints the seed on the console message) but of course that message is sent from the server which has access to the seed.
Since the client seed is supposed to be 0, why is that a problem for you? What are you doing where you are looking for the seed with code running on the client? All the world generation stuff should be executing on the server side.
I'm trying to make a version of BiomeProviderSingle that chooses it's one biome randomly based on the world seed. But it seems to be constructing a BiomeProvider on both server and client side (WorldType.getBiomeProvider is called with both a WorldClient and a WorldServer).
If I just go ahead and use the world seeds given, I end up with a client side BiomeProvider with a different biome than the server side one, so the rendering is wrong and the grass color changes as you move around.
On the other hand, if I try and delay choosing the biome, then that info isn't available when the chunk generator goes to call world.getBiomeProvider().getBiomes().
I also tried making getBiomeProvider server side only, but it still generated a client side BiomeProvider which that spat out random biomes for every block.
Given that the vanilla biome generation uses these seeds somehow, I must be missing something.
I think the problem is there is a difference between selecting the biome randomly and generating the biome randomly. The regular use of BiomeProviderSingle passes the Biome in the constructor so it would be the same on both sides.
So what I think you need to do is probably have a custom packet that passes the world seed from the server to the client where it is then used by your custom biome provider. It looks like at the time of the server about to start event the seed should already be generated, so I'd probably handle that or similar event like server starting and grab the seed and send a custom packet to the client. Might take a bit of trial and error to figure out the best place to do this, but something like that.
Ah, I was hoping I wouldn't have to resort to such measures. Thanks for the help.
Edit: Question though, if you happen to know. How are the biomes synced in the vanilla BiomeProvider (not BiomeProviderSingle)?
That's a good question. I tried to follow the code, but couldn't really figure it out. The debug overlay GUI will show the biome for the current position, and item map also shows biome, so definitely the client is aware of the biomes at every X, Z position. However, I can't see where these get set, as they call the getter methods but I can't find the inverse setter. Maybe it is recreated on the fly using seed value, but like we've been discussing the seed seems to be 0 on the client.
I couldn't follow the logic either. I really suspect there's a cleaner way to do this, but for now I'll try and get the packets working.