PyAnvilEditor icon indicating copy to clipboard operation
PyAnvilEditor copied to clipboard

Propose fix to make it compatible with MC 1.16

Open EWitting opened this issue 4 years ago • 2 comments

TL;DR In 1.16, chunk format changed slightly, I rewrote a function so that reading works again in 1.16. I'm not used to github, so the code is simply pasted below.

Since snapshot 20w19a, block states are no longer stored directly after each other, which used to result in misalignment and some values being split over multiple longs. Now each long stores as much block states as possible, pads the rest with zeroes, and the next values are aligned perfectly from the start of the next long. The current version of PyAnvilEditor isn't updated for this and reads misaligned data when unpacking a chun k(sometimes throwing index out of range errors). I rewrote a function so that it could now read blocks without errors in 1.16

To fix: Replace"_read_width_from_loc" in world.py with:

    def _read_width_from_loc(long_list, width, position):
        #max amount of blockstates that fit in each long
        states_per_long = 64 // width
        
        #the long in which this blockstate is stored
        long_index = position // states_per_long
        
        #at which bit in the long this state is located
        position_in_long = (position % states_per_long)*width
        return Chunk._read_bits(long_list[long_index], width, position_in_long)

EWitting avatar Sep 29 '20 10:09 EWitting

also the minimum width is 4 bits, so "width = max(width,4)" could be added. source: https://minecraft.gamepedia.com/Chunk_format#Block_format (at BlockStates)

EWitting avatar Sep 29 '20 16:09 EWitting

I changed the serialize _blockstates function in world.py too, so now writing blocks works again and the enitre library should be compatible with 1.16, I haven't run into any problems at least. Update it to this:

def _serialize_blockstates(self, state_mapping):
        serial_states = nbt.LongArrayTag(tag_name='BlockStates')
        width = math.ceil(math.log(len(self.palette), 2))
        if width < 4:
            width = 4

        #max amount of states that fit in a long        
        states_per_long = 64 // width

        #amount of longs
        arraylength = math.ceil(len(self.blocks) / states_per_long)
        
        for long_index in range(arraylength):
            lng = 0
            for state in range(states_per_long):
                #insert blocks in reverse, so first one ends up most to the right
                block_index = long_index*states_per_long + (states_per_long - state - 1)
                
                if block_index < len(self.blocks):
                    block = self.blocks[block_index]                        
                    lng = (lng << width) + state_mapping[block._state]

            lng = int.from_bytes(lng.to_bytes(8, byteorder='big', signed=False), byteorder='big', signed=True)
            serial_states.add_child(nbt.LongTag(lng))
        return serial_states

EWitting avatar Sep 30 '20 15:09 EWitting