2

Problem

I have a pretty technical question about Minecraft,
So I'm building a Minecraft server wrapper, and want to add an option to edit the world when the server is offline. That means messing with the save file. I have managed to make it work for 1.8 because the blocks were saved as a list of IDs, but in 1.19 the blocks are saved in a palette (which I understand) and a data array, I understand the data it stores (indices that point to the palette), But I don't understand how to extract (I was able to do it but only by copy-paste) and modify data in there. I really want to understand the mechanics behind it so I can do it myself, I am aware that there are other libs that do that but I want to learn how to do it myself. I have tried different approaches, the most official one I tried is this wiki article but it seemed to not work.


Info

  • Language: Python
  • Python Version: 3.9.8
  • Desired Minecraft Version: 1.19 (1.15 too, but its a bit different)
  • Using NBTExplorer to view world data
  • OS: Windows 10

Other tries

  1. Tried to take each 4 bits of a long and shift them 8 and 4 bits to the left or right (all combs didn't work)
  2. Tried taking 4 bits of each long and putting it inside a nibble function:
def nibble(byte_array, index):
    value = byte_array[index // 2]
    if index % 2:
        return (value >> 4) &0x0F
    else:
        return value & 0x0F
  1. Tried taking the HEX value of the bits and somehow convert it to a number.

No approach from the above worked... P.S. If you need the code from the WIKI tell me, I couldn't find it but if you need it I'll write it again.


TLDR

Please explain to me the mechanics and logic behind the data tag in the NBT of each section under a chunk NBT data so I can extract and modify the indices in it (data tag) using python for Minecraft versions 1.15-1.20


See data

To see the data I'm talking about for your-self you could install NBTExplorer and then open a world save file (%appdata%/.minecraft/saves/[world name]/region/[any region file]), each region file in there is 32x32 chunks, drag-and-drop it into NBTExplorer, pick a chunk then go to section, then pick a section, go to block_states and there you'll be able to see the data tag. That's the data I'm trying to decode, understand and modify.


Sources

These are sources I tried taking info from, there are more, but not useful ones.


Please answer in detail, and be patient with me. Thank you so much!
And of course any other information you might need to help me, feel free to ask!
NOTE: Please don't tell me there are libraries that do this I want to understand the logic and not just copy code, same for source code, if you find a resource or code online that might help me I'd like an explanation so could understand (If you don't know how it works but know that it works please point me to the exact code so I can attempt to understand myself)

Ilai K
  • 61
  • 7
  • You can check how ViaVersion or Minestom does to read those data. Else, I don't know exactly – Elikill58 Jun 09 '23 at 13:22
  • Thanks, read is a bit less of a problem as I said (I at least got it done), but the modify data part is the real kicker. – Ilai K Jun 09 '23 at 15:56

1 Answers1

1

The chunk data uses compaction, its complex this https://github.com/PREMIEREHELL/Amulet-Plugins/blob/main/force_blending.py plugin I made has all the raw access functions that deal with the raw chunk data. The calculations are in the functions. This code adds bedrock to Y zero and I think it still puts it back to an older version if it changes the the first byte to x08 thats 1.17- 1.18+ is x09. It works for blending but it makes lava lakes under exsiting chunks. Hope this helps you figure it out it's a complpex but simple when you see whats happening, i also made a verson with the bitwise operator but it was slower than numpy. Also in 1.18+ they added the Y level to the header so it uses 4 bytes now == version, layers, Y level, compaction level. Layers is for extra block data , like waterlogged you can even wrap glass around any block. Only 2 Layers are supported in bedrock.

The most important parts are

bits per value = max(int(numpy.amax(b)).bit_length(), 1)

This is how many block are in the pallet and compact_level = bytes([bits_per_value << 1]) sets the compaction level Because it local it only uses 7 bits of the other bit of that byte is a flag

        bits per word = (32 // bits per value) find how many indexes are in each word 4 bytes
        TOTAL BYTES = -(-4096 // bpw)

You Need Java it looks like:

Java Minecraft uses longs and is https://github.com/PREMIEREHELL/Amulet-Plugins/blob/main/change_block_java_1.18.py the library is simple it converts the longs. https://github.com/Amulet-Team/Amulet-Core/blob/dafef97fe4fd1f2f713ef1e3503d6b13b20c1c1f/amulet/utils/world_utils.py#L138 this is the code that decodes and below encodes the longs array

https://github.com/PREMIEREHELL/Amulet-Plugins/blob/main/change_block_java_1.17-.py this code may work for 1.15 the path changes after 1.17 or at the end of that version, dense=True that is the longs overlap until version 1.18 then each long has as many as it can hold no over lapping the order of the bits is Y Z X and is never less than 4 bits( that means no more than 16 block indexes that Point to the pallet will be in one long), all the groups of bits stretched out. So the first 256 group values are the bottom layer index of the subchunk. If there is only one block no longs data will/should exist.

I THINK that wiki is wrong MC 1.15 and MC 1.16 i haven't tested 1.15 yet test I have done showed the change occurred in 1.17 to 1.18. When they stopped spaning the longs. dence=True for 1.17 and False for 1.18.

Dj Martin
  • 57
  • 4