Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[1.16.5] How to set biome at specific chunk?


sasetz
 Share

Recommended Posts

I want to create a block that changes chunk biome after player clicks on it. I've found some old tutorials (for Minecraft 1.10) on how to do this but when I tried to do the same, needed methods just aren't there anymore. Is it even possible on newer versions?
Thanks!

Link to comment
Share on other sites

This is no longer straightforward. Biomes are stored in BiomeContainer.

The BiomeContainer is stored in the chunk (IChunk#getBiomes). However this might be null if the chunk is not fully generated yet - in that case Minecraft will fall back to simply using the world generation algorithm to find the biomes. If however it exists, you can use reflection to hack its internal biomes array. See BiomeContainer#getNoiseBiome to see how this array is used.

This however is the unzoomed biome. World#getBiomeManager applies an additional zoom on top of this.

  • Like 1
Link to comment
Share on other sites

Posted (edited)

Thank you for your reply!
 

16 minutes ago, diesieben07 said:

However this might be null if the chunk is not fully generated yet - in that case Minecraft will fall back to simply using the world generation algorithm to find the biomes. If however it exists, you can use reflection to hack its internal biomes array. See BiomeContainer#getNoiseBiome to see how this array is used.

The block will be placed by a player, so, the chunk is generated. So, I need to create an access transformer to change biome array's signature from "private final" to "public", right?

18 minutes ago, diesieben07 said:

This however is the unzoomed biome. World#getBiomeManager applies an additional zoom on top of this.


What does it mean "unzoomed biome"? And how do I apply zoom with BiomeManager?

Edited by sasetz
Added quotes
Link to comment
Share on other sites

10 hours ago, sasetz said:

So, I need to create an access transformer to change biome array's signature from "private final" to "public", right?

Keep it final. But yes, you can AT it. But you could also use reflection.

10 hours ago, sasetz said:

What does it mean "unzoomed biome"?

Every dimension has a "biome zoomer". This is basically a post processor that runs on the result of the normal biome generation. See BiomeManager#getBiome(BlockPos). I assume this is what Mojang uses for the "large biomes" option and other things.

  • Like 1
Link to comment
Share on other sites

Thank you for your replies!

8 hours ago, diesieben07 said:

Keep it final. But yes, you can AT it. But you could also use reflection.

I created an AT and checked what's inside of this array. Why is there 1024 Biome instances? Is it 4 instances for each block in a chunk or what? I inspected BiomeContainer#getNoiseBiome, but it didn't make much sense since I don't know what 3 int parameters supposed to mean (xyz? I thought biome stretches from y=0 to y=255. Maybe it's 16x16 and 4 layers vertically? idk)
I've tried changing stuff inside of the array (using BiomeMaker methods) but after I did that, when I checked again the array and the registry names of biomes inside are all null. After I restart the world it turns back to normal (minecraft:plains or more)

9 hours ago, diesieben07 said:

See BiomeManager#getBiome(BlockPos).

So I need to call this method on every block inside of chunk, so it gets zoomed? I don't think it's what you meant though...

Link to comment
Share on other sites

3 minutes ago, sasetz said:

Why is there 1024 Biome instances? Is it 4 instances for each block in a chunk or what? I inspected BiomeContainer#getNoiseBiome, but it didn't make much sense since I don't know what 3 int parameters supposed to mean (xyz? I thought biome stretches from y=0 to y=255. Maybe it's 16x16 and 4 layers vertically? idk)

getNoiseBiome indeed takes in x, y and z. Use the formula seen in getNoiseBiome to see how to use the biomes array.

6 minutes ago, sasetz said:

So I need to call this method on every block inside of chunk, so it gets zoomed? I don't think it's what you meant though...

What I mean is that the x, y and z coords in getNoiseBiome are not the actual block coordinates, they are the coordinates after they have been processed by the zoomer. So if you want to change the biome at a certain x, y, z coord, you have to use the zoomer as well (but in reverse) before accessing the biomes array.

  • Thanks 1
Link to comment
Share on other sites

Thanks for you reply!

1 hour ago, diesieben07 said:

So if you want to change the biome at a certain x, y, z coord, you have to use the zoomer as well (but in reverse) before accessing the biomes array.

Ok, I've figured it out that chunk is divided to cubes with 4 blocks side and each of the cubes has it's own biome. But still, I want to change biome in the whole chunk, so I change every element of the array to another biome and it doesn't change (registry name says it's null biome), and the changes don't save (when I reload the world, biomes are back to normal)

Link to comment
Share on other sites

12 minutes ago, diesieben07 said:

Show your code.

if(!level.isClientSide()){
    Biome[] biomes = level.getChunkAt(worldPosition).getBiomes().biomes;
    sendToChat(BiomeMaker.desertBiome(.125f, .05f, false, false, false).toString());
    for (int i = 0, biomesLength = biomes.length; i < biomesLength; i++) {
        biomes[i] = BiomeMaker.desertBiome(.125f, .05f, false, false, false);
        sendToChat("" + i + ": " + biomes[i].getRegistryName());
    }
}

This is a piece of code is called when I right click on a block, it's inside of a TileEntity

Link to comment
Share on other sites

Just now, diesieben07 said:

Oh... well that would explain it then.

You can't create a new biome. You have to use the desert biome from the world's dynamic registries.

I've tried this, but how do I turn RegistryKey to Biome?

Link to comment
Share on other sites

7 minutes ago, diesieben07 said:

World#registryAccess gives you the DynamicRegistries. From there you can get the biome registry and then look up the biomes.

And then what field or method should I call? I just couldn't find anything suitable

Link to comment
Share on other sites

3 minutes ago, sasetz said:

And then what field or method should I call? I just couldn't find anything suitable

DynamicRegistries#registry with the RegistryKey for biomes (Registry.BIOME_REGISTRY). Then you have the biome registry. Then Registry#get.

Link to comment
Share on other sites

32 minutes ago, diesieben07 said:

DynamicRegistries#registry with the RegistryKey for biomes (Registry.BIOME_REGISTRY). Then you have the biome registry. Then Registry#get.

Now it's doing a NullPointerException... 
I just did this

level.registryAccess().registry(Registry.BIOME_REGISTRY).get().get(Biomes.DESERT.getRegistryName()).toString()

The second get method returns null

Link to comment
Share on other sites

14 minutes ago, diesieben07 said:

Hm, that is curious. Use the debugger to see what the registry contains.

For some reason the code Biomes.DESERT.getRegistryName().toString() returns "minecraft:worldgen/biome"...
 

Link to comment
Share on other sites

Posted (edited)

Instead of getting the desert biome I just typed new ResourceLocation("minecraft:desert") and it finally worked!

The final code is

public void changeBiome() {
    Biome[] biomes = level.getChunkAt(worldPosition).getBiomes().biomes;
    for (int i = 0, biomesLength = biomes.length; i < biomesLength; i++) {
        DynamicRegistries reg = level.registryAccess();
        MutableRegistry<Biome> a = reg.registry(Registry.BIOME_REGISTRY).get();
        Biome biome = a.get(new ResourceLocation("minecraft:desert"));
        biomes[i] = biome;
    }
}

 

But the only thing left is that the grass color doesn't change instantly, but when I click F3 + A (only if I click the combination twice) it looks how it should be (grass changes from plains green to yellow)
If I hit F3 + A only 1 time, the borders between biomes become sharp between the chunks
https://imgur.com/a/9qNBhxS (screenshot)

Is there a way to reload a chunk like F3 + A does?
And thank you for all of your replies!

Edited by sasetz
updated code
Link to comment
Share on other sites

10 hours ago, sasetz said:

For some reason the code Biomes.DESERT.getRegistryName().toString() returns "minecraft:worldgen/biome"...

Biomes.DESERT is a registry key. Calling getRegistryName gives you the name of the registry. RegistryKey#location gives you the name of the biome.

The problem with the colors is that 1) the client doesn't know that the biome has changed and 2) that the client doesn't re-render the chunk. If you don't do this often, you can just send an SChunkDataPacket. If you do do it more often, you need to make a custom packet that sends only the biome data and invalidates the render sections (see SChunkDataPacket for how to do that).

Link to comment
Share on other sites

23 minutes ago, diesieben07 said:

Biomes.DESERT is a registry key. Calling getRegistryName gives you the name of the registry. RegistryKey#location gives you the name of the biome.

Biomes.DESERT.location() returns the correct ResourceLocation (minecraft:desert)

1 hour ago, diesieben07 said:

1) the client doesn't know that the biome has changed

No, the client knows about the biome change, since this method fires on both sides. If it works only on server side, the client needs to revisit a chunk, F3 + A is not enough

2 hours ago, diesieben07 said:

2) that the client doesn't re-render the chunk

Yes, that's basically what I need. Is SChunkDataPacket going to fix this issue? If so, how can I send this vanilla packet?

Link to comment
Share on other sites

You should really only do this server side if you can help it and then update the clients using the packet.

9 minutes ago, sasetz said:

Yes, that's basically what I need. Is SChunkDataPacket going to fix this issue? If so, how can I send this vanilla packet?

Yes, but it's the brute-force approach because it re-sends the entire chunk data.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • just want to know if it's possible to show people's session id's when they join your minecraft server  
    • I am currently working on a mod that needs to listen to player connect/disconnect events.  I am currently subscribing to both ServerDisconnectionFromClientEvent and PlayerLoggedOutEvent.  For the most part, when the server is running normally the PlayerLoggedOutEvent is called. I haven't really found much on the ServerDisconnectionFromClientEvent, but my testing would suggest that this event is only raised when the server itself has an issue and forces the clients to disconnect - rather than the clients themselves disconnecting when they think the server is no longer responding. It just seems like sometimes during my testing when I let the debugger just sit there awhile (simulating the server not responding) the PlayerLoggedOutEvent is raised and other times ServerDisconnectionFromClientEvent Here is my understanding of the events: PlayerLoggedOutEvent is raised when a player manually returns to main menu, or when the client believes the server is not responding. ServerDisconnectionFromClientEvent is raised when the server has an issue and is forcing players to disconnect.  I'm not sure if it is always the case that the server is closing after this event, or if it can happen on an otherwise healthy server, like maybe there was some issue sending packets to the client and the server doesn't think the client is there. I'm not sure if my understanding is correct.  I would like to have test cases for both events with several clients connected to the server, as I need to make sure that my code is working properly no matter the order in which people disconnect.  Some players cause effects on disconnecting, and some don't, so it's quite important to know if the logic is working properly in BOTH events. Can anyone enlighten me on when exactly each event is raised, and how to force ServerDisconnectionFromClientEvent to be raised?
    • There isnt a way for me to qccess vampire GUI and settings says nothing, the button the click to get into gui from inventory just isnt there. Is there a way to fix this
    • I've been trying to sort this out for a few days but unfortunately I've had no success. I was wondering if anyone has figured out how to add xp drops to an ore for 1.17.1. Anyone help would be appreciated, thanks!
    • JVM info: Oracle Corporation - 1.8.0_281 - 25.281-b09 java.net.preferIPv4Stack=true Found java version 1.8.0_281 Extracting main jar:   Extracted successfully Considering minecraft server jar java.net.UnknownHostException: launchermeta.mojang.com     at java.net.AbstractPlainSocketImpl.connect(Unknown Source)     at java.net.PlainSocketImpl.connect(Unknown Source)     at java.net.SocksSocketImpl.connect(Unknown Source)     at java.net.Socket.connect(Unknown Source)     at sun.security.ssl.SSLSocketImpl.connect(Unknown Source)     at sun.net.NetworkClient.doConnect(Unknown Source)     at sun.net.www.http.HttpClient.openServer(Unknown Source)     at sun.net.www.http.HttpClient.openServer(Unknown Source)     at sun.net.www.protocol.https.HttpsClient.<init>(Unknown Source)     at sun.net.www.protocol.https.HttpsClient.New(Unknown Source)     at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(Unknown Source)     at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)     at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)     at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)     at java.net.HttpURLConnection.getResponseCode(Unknown Source)     at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)     at net.minecraftforge.installer.DownloadUtils.getConnection(DownloadUtils.java:203)     at net.minecraftforge.installer.DownloadUtils.downloadManifest(DownloadUtils.java:267)     at net.minecraftforge.installer.json.Util.getVanillaVersion(Util.java:62)     at net.minecraftforge.installer.actions.ServerInstall.run(ServerInstall.java:70)     at net.minecraftforge.installer.InstallerPanel.run(InstallerPanel.java:428)     at net.minecraftforge.installer.SimpleInstaller.launchGui(SimpleInstaller.java:170)     at net.minecraftforge.installer.SimpleInstaller.main(SimpleInstaller.java:143) Failed to download version manifest, can not find server jar URL.  
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.