github.com/df-mc/dragonfly@v0.9.13/server/world/chunk/paletted_storage.go (about) 1 package chunk 2 3 import ( 4 "bytes" 5 "unsafe" 6 ) 7 8 const ( 9 // uint32ByteSize is the amount of bytes in a uint32. 10 uint32ByteSize = 4 11 // uint32BitSize is the amount of bits in a uint32. 12 uint32BitSize = uint32ByteSize * 8 13 ) 14 15 // PalettedStorage is a storage of 4096 blocks encoded in a variable amount of uint32s, storages may have values 16 // with a bit size per block of 0, 1, 2, 3, 4, 5, 6, 8 or 16 bits. 17 // 3 of these formats have additional padding in every uint32 and an additional uint32 at the end, to cater 18 // for the blocks that don't fit. This padding is present when the storage has a block size of 3, 5 or 6 19 // bytes. 20 // Methods on PalettedStorage must not be called simultaneously from multiple goroutines. 21 type PalettedStorage struct { 22 // bitsPerIndex is the amount of bits required to store one block. The number increases as the block 23 // storage holds more unique block states. 24 bitsPerIndex uint16 25 // filledBitsPerIndex returns the amount of blocks that are actually filled per uint32. 26 filledBitsPerIndex uint16 27 // indexMask is the equivalent of 1 << bitsPerIndex - 1. 28 indexMask uint32 29 30 // indicesStart holds an unsafe.Pointer to the first byte in the indices slice below. 31 indicesStart unsafe.Pointer 32 33 // Palette holds all block runtime IDs that the indices in the indices slice point to. These runtime IDs 34 // point to block states. 35 palette *Palette 36 37 // indices contains all indices in the PalettedStorage. This slice has a variable size, but may not be changed 38 // unless the whole PalettedStorage is resized, including the Palette. 39 indices []uint32 40 } 41 42 // newPalettedStorage creates a new block storage using the uint32 slice as the indices and the palette passed. 43 // The bits per block are calculated using the length of the uint32 slice. 44 func newPalettedStorage(indices []uint32, palette *Palette) *PalettedStorage { 45 var ( 46 bitsPerIndex = uint16(len(indices) / uint32BitSize / uint32ByteSize) 47 indexMask = (uint32(1) << bitsPerIndex) - 1 48 indicesStart = (unsafe.Pointer)(unsafe.SliceData(indices)) 49 filledBitsPerIndex uint16 50 ) 51 if bitsPerIndex != 0 { 52 filledBitsPerIndex = uint32BitSize / bitsPerIndex * bitsPerIndex 53 } 54 return &PalettedStorage{filledBitsPerIndex: filledBitsPerIndex, indexMask: indexMask, indicesStart: indicesStart, bitsPerIndex: bitsPerIndex, indices: indices, palette: palette} 55 } 56 57 // emptyStorage creates a PalettedStorage filled completely with a value v. 58 func emptyStorage(v uint32) *PalettedStorage { 59 return newPalettedStorage([]uint32{}, newPalette(0, []uint32{v})) 60 } 61 62 // Palette returns the Palette of the PalettedStorage. 63 func (storage *PalettedStorage) Palette() *Palette { 64 return storage.palette 65 } 66 67 // At returns the value of the PalettedStorage at a given x, y and z. 68 func (storage *PalettedStorage) At(x, y, z byte) uint32 { 69 return storage.palette.Value(storage.paletteIndex(x&15, y&15, z&15)) 70 } 71 72 // Set sets a value at a specific x, y and z. The Palette and PalettedStorage are expanded 73 // automatically to make space for the value, should that be needed. 74 func (storage *PalettedStorage) Set(x, y, z byte, v uint32) { 75 index := storage.palette.Index(v) 76 if index == -1 { 77 // The runtime ID was not yet available in the palette. We add it, then check if the block storage 78 // needs to be resized for the palette pointers to fit. 79 index = storage.addNew(v) 80 } 81 storage.setPaletteIndex(x&15, y&15, z&15, uint16(index)) 82 } 83 84 // Equal checks if two PalettedStorages are equal value wise. False is returned 85 // if either of the storages are nil. 86 func (storage *PalettedStorage) Equal(other *PalettedStorage) bool { 87 if storage == nil || other == nil { 88 return false 89 } 90 if len(storage.indices) == 0 || len(other.indices) == 0 || storage.palette.values[0] == 0 || other.palette.values[0] == 0 { 91 return false 92 } 93 indicesA := unsafe.Slice((*byte)(unsafe.Pointer(&storage.indices[0])), len(storage.indices)*4) 94 indicesB := unsafe.Slice((*byte)(unsafe.Pointer(&other.indices[0])), len(other.indices)*4) 95 if !bytes.Equal(indicesA, indicesB) { 96 return false 97 } 98 paletteA := unsafe.Slice((*byte)(unsafe.Pointer(&storage.palette.values[0])), len(storage.palette.values)*4) 99 paletteB := unsafe.Slice((*byte)(unsafe.Pointer(&other.palette.values[0])), len(other.palette.values)*4) 100 return bytes.Equal(paletteA, paletteB) 101 } 102 103 // addNew adds a new value to the PalettedStorage's Palette and returns its index. If needed, the storage is resized. 104 func (storage *PalettedStorage) addNew(v uint32) int16 { 105 index, resize := storage.palette.Add(v) 106 if resize { 107 storage.resize(storage.palette.size) 108 } 109 return index 110 } 111 112 // paletteIndex looks up the Palette index at a given x, y and z value in the PalettedStorage. This palette 113 // index is not the value at this offset, but merely an index in the Palette pointing to a value. 114 func (storage *PalettedStorage) paletteIndex(x, y, z byte) uint16 { 115 if storage.bitsPerIndex == 0 { 116 // Unfortunately our default logic cannot deal with 0 bits per index, meaning we'll have to special case 117 // this. This comes with a little performance hit, but it seems to be the only way to go. An alternative would 118 // be not to have 0 bits per block storages in memory, but that would cause a strongly increased memory usage 119 // by biomes. 120 return 0 121 } 122 offset := ((uint16(x) << 8) | (uint16(z) << 4) | uint16(y)) * storage.bitsPerIndex 123 uint32Offset, bitOffset := offset/storage.filledBitsPerIndex, offset%storage.filledBitsPerIndex 124 125 w := *(*uint32)(unsafe.Pointer(uintptr(storage.indicesStart) + uintptr(uint32Offset<<2))) 126 return uint16((w >> bitOffset) & storage.indexMask) 127 } 128 129 // setPaletteIndex sets the palette index at a given x, y and z to paletteIndex. This index should point 130 // to a value in the PalettedStorage's Palette. 131 func (storage *PalettedStorage) setPaletteIndex(x, y, z byte, i uint16) { 132 if storage.bitsPerIndex == 0 { 133 return 134 } 135 offset := ((uint16(x) << 8) | (uint16(z) << 4) | uint16(y)) * storage.bitsPerIndex 136 uint32Offset, bitOffset := offset/storage.filledBitsPerIndex, offset%storage.filledBitsPerIndex 137 138 ptr := (*uint32)(unsafe.Pointer(uintptr(storage.indicesStart) + uintptr(uint32Offset<<2))) 139 *ptr = (*ptr &^ (storage.indexMask << bitOffset)) | (uint32(i) << bitOffset) 140 } 141 142 // resize changes the size of a PalettedStorage to newPaletteSize. A new PalettedStorage is constructed, 143 // and all values available in the current storage are set in their appropriate locations in the 144 // new storage. 145 func (storage *PalettedStorage) resize(newPaletteSize paletteSize) { 146 if newPaletteSize == paletteSize(storage.bitsPerIndex) { 147 return // Don't resize if the size is already equal. 148 } 149 // Construct a new storage and set all values in there manually. We can't easily do this in a better 150 // way, because all values will be at a different index with a different length. 151 newStorage := newPalettedStorage(make([]uint32, newPaletteSize.uint32s()), storage.palette) 152 for x := byte(0); x < 16; x++ { 153 for y := byte(0); y < 16; y++ { 154 for z := byte(0); z < 16; z++ { 155 newStorage.setPaletteIndex(x, y, z, storage.paletteIndex(x, y, z)) 156 } 157 } 158 } 159 // Set the new storage. 160 *storage = *newStorage 161 } 162 163 // compact clears unused indexes in the palette by scanning for usages in the PalettedStorage. This is a 164 // relatively heavy task which should only happen right before the sub chunk holding this PalettedStorage is 165 // saved to disk. compact also shrinks the palette size if possible. 166 func (storage *PalettedStorage) compact() { 167 usedIndices := make([]bool, storage.palette.Len()) 168 for x := byte(0); x < 16; x++ { 169 for y := byte(0); y < 16; y++ { 170 for z := byte(0); z < 16; z++ { 171 usedIndices[storage.paletteIndex(x, y, z)] = true 172 } 173 } 174 } 175 newRuntimeIDs := make([]uint32, 0, len(usedIndices)) 176 conversion := make([]uint16, len(usedIndices)) 177 178 for index, set := range usedIndices { 179 if set { 180 conversion[index] = uint16(len(newRuntimeIDs)) 181 newRuntimeIDs = append(newRuntimeIDs, storage.palette.values[index]) 182 } 183 } 184 // Construct a new storage and set all values in there manually. We can't easily do this in a better 185 // way, because all values will be at a different index with a different length. 186 size := paletteSizeFor(len(newRuntimeIDs)) 187 newStorage := newPalettedStorage(make([]uint32, size.uint32s()), newPalette(size, newRuntimeIDs)) 188 189 for x := byte(0); x < 16; x++ { 190 for y := byte(0); y < 16; y++ { 191 for z := byte(0); z < 16; z++ { 192 // Replace all usages of the old palette indexes with the new indexes using the map we 193 // produced earlier. 194 newStorage.setPaletteIndex(x, y, z, conversion[storage.paletteIndex(x, y, z)]) 195 } 196 } 197 } 198 *storage = *newStorage 199 }