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  }