diff --git a/level/block/block.go b/level/block/block.go index 8cb03c1..1a2e68d 100644 --- a/level/block/block.go +++ b/level/block/block.go @@ -18,13 +18,14 @@ type Block interface { //go:embed block_states.nbt var blockStates []byte -var ToStateID map[Block]int +var ToStateID map[Block]StateID var StateList []Block // BitsPerBlock indicates how many bits are needed to represent all possible // block states. This value is used to determine the size of the global palette. var BitsPerBlock int +type StateID int type State struct { Name string Properties nbt.RawMessage @@ -41,7 +42,7 @@ func init() { if _, err = nbt.NewDecoder(z).Decode(&states); err != nil { panic(err) } - ToStateID = make(map[Block]int, len(states)) + ToStateID = make(map[Block]StateID, len(states)) StateList = make([]Block, 0, len(states)) for _, state := range states { block := FromID[state.Name] @@ -54,7 +55,7 @@ func init() { if _, ok := ToStateID[block]; ok { panic(fmt.Errorf("state %#v already exist", block)) } - ToStateID[block] = len(StateList) + ToStateID[block] = StateID(len(StateList)) StateList = append(StateList, block) } BitsPerBlock = bits.Len(uint(len(StateList))) diff --git a/level/block/utilfuncs.go b/level/block/utilfuncs.go index 3a03a92..915dc55 100644 --- a/level/block/utilfuncs.go +++ b/level/block/utilfuncs.go @@ -1,6 +1,6 @@ package block -func IsAir(s int) bool { +func IsAir(s StateID) bool { switch StateList[s].(type) { case Air, CaveAir, VoidAir: return true diff --git a/level/chunk.go b/level/chunk.go index d22fdf5..7121c50 100644 --- a/level/chunk.go +++ b/level/chunk.go @@ -61,7 +61,7 @@ func EmptyChunk(secs int) *Chunk { } } -var biomesIDs = map[string]int{ +var biomesIDs = map[string]BiomesState{ "the_void": 0, "plains": 1, "sunflower_plains": 2, @@ -216,9 +216,8 @@ func ChunkFromSave(c *save.Chunk) *Chunk { } } -func readStatesPalette(palette []save.BlockState, data []int64) (blockCount int16, paletteData *PaletteContainer, err error) { - stateData := *(*[]uint64)((unsafe.Pointer)(&data)) - statePalette := make([]int, len(palette)) +func readStatesPalette(palette []save.BlockState, data []uint64) (blockCount int16, paletteData *PaletteContainer[BlocksState], err error) { + statePalette := make([]BlocksState, len(palette)) for i, v := range palette { b, ok := block.FromID[v.Name] if !ok { @@ -238,13 +237,12 @@ func readStatesPalette(palette []save.BlockState, data []int64) (blockCount int1 } statePalette[i] = s } - paletteData = NewStatesPaletteContainerWithData(16*16*16, stateData, statePalette) + paletteData = NewStatesPaletteContainerWithData(16*16*16, data, statePalette) return } -func readBiomesPalette(palette []string, data []int64) (*PaletteContainer, error) { - biomesData := *(*[]uint64)((unsafe.Pointer)(&data)) - biomesRawPalette := make([]int, len(palette)) +func readBiomesPalette(palette []string, data []uint64) (*PaletteContainer[BiomesState], error) { + biomesRawPalette := make([]BiomesState, len(palette)) var ok bool for i, v := range palette { biomesRawPalette[i], ok = biomesIDs[strings.TrimPrefix(v, "minecraft:")] @@ -252,7 +250,7 @@ func readBiomesPalette(palette []string, data []int64) (*PaletteContainer, error return nil, fmt.Errorf("unknown biomes: %s", v) } } - return NewBiomesPaletteContainerWithData(4*4*4, biomesData, biomesRawPalette), nil + return NewBiomesPaletteContainerWithData(4*4*4, data, biomesRawPalette), nil } // ChunkToSave convert level.Chunk to save.Chunk @@ -260,30 +258,18 @@ func ChunkToSave(c *Chunk, dst *save.Chunk) { secs := len(c.Sections) sections := make([]save.Section, secs) for i, v := range c.Sections { - statePalette, stateData := writeStatesPalette(v.States) - biomePalette, biomeData := writeBiomesPalette(v.Biomes) - sections[i] = save.Section{ - Y: int8(int32(i) + dst.YPos), - BlockStates: struct { - Palette []save.BlockState `nbt:"palette"` - Data []int64 `nbt:"data"` - }{ - Palette: statePalette, Data: stateData, - }, - Biomes: struct { - Palette []string `nbt:"palette"` - Data []int64 `nbt:"data"` - }{ - Palette: biomePalette, Data: biomeData, - }, - SkyLight: nil, - BlockLight: nil, - } + s := §ions[i] + states := &s.BlockStates + biomes := &s.Biomes + s.Y = int8(int32(i) + dst.YPos) + states.Palette, states.Data = writeStatesPalette(v.States) + biomes.Palette, biomes.Data = writeBiomesPalette(v.Biomes) } dst.Sections = sections + //dst.Heightmaps.MotionBlocking = c.HeightMaps.MotionBlocking.Raw() } -func writeStatesPalette(paletteData *PaletteContainer) (palette []save.BlockState, data []int64) { +func writeStatesPalette(paletteData *PaletteContainer[BlocksState]) (palette []save.BlockState, data []uint64) { rawPalette := paletteData.palette.export() palette = make([]save.BlockState, len(rawPalette)) var buffer bytes.Buffer @@ -299,22 +285,18 @@ func writeStatesPalette(paletteData *PaletteContainer) (palette []save.BlockStat panic(err) } } - - rawData := paletteData.data.Raw() - data = append(data, *(*[]int64)(unsafe.Pointer(&rawData))...) + data = append(data, paletteData.data.Raw()...) return } -func writeBiomesPalette(paletteData *PaletteContainer) (palette []string, data []int64) { +func writeBiomesPalette(paletteData *PaletteContainer[BiomesState]) (palette []string, data []uint64) { rawPalette := paletteData.palette.export() palette = make([]string, len(rawPalette)) for i, v := range rawPalette { palette[i] = biomesNames[v] } - - rawData := paletteData.data.Raw() - data = append(data, *(*[]int64)(unsafe.Pointer(&rawData))...) + data = append(data, paletteData.data.Raw()...) return } @@ -428,14 +410,14 @@ func (b *BlockEntity) ReadFrom(r io.Reader) (n int64, err error) { type Section struct { BlockCount int16 - States *PaletteContainer - Biomes *PaletteContainer + States *PaletteContainer[BlocksState] + Biomes *PaletteContainer[BiomesState] } -func (s *Section) GetBlock(i int) int { +func (s *Section) GetBlock(i int) BlocksState { return s.States.Get(i) } -func (s *Section) SetBlock(i int, v int) { +func (s *Section) SetBlock(i int, v BlocksState) { if block.IsAir(s.States.Get(i)) { s.BlockCount-- } diff --git a/level/palette.go b/level/palette.go index c3d5456..ab8e3a2 100644 --- a/level/palette.go +++ b/level/palette.go @@ -5,53 +5,58 @@ import ( "math/bits" "strconv" + "github.com/Tnze/go-mc/level/block" pk "github.com/Tnze/go-mc/net/packet" ) -type state = int +type State interface { + ~int +} +type BlocksState = block.StateID +type BiomesState int -type PaletteContainer struct { +type PaletteContainer[T State] struct { bits int - config paletteCfg - palette palette + config paletteCfg[T] + palette palette[T] data *BitStorage } -func NewStatesPaletteContainer(length int, defaultValue state) *PaletteContainer { - return &PaletteContainer{ +func NewStatesPaletteContainer(length int, defaultValue BlocksState) *PaletteContainer[BlocksState] { + return &PaletteContainer[BlocksState]{ bits: 0, config: statesCfg{}, - palette: &singleValuePalette{v: defaultValue}, + palette: &singleValuePalette[BlocksState]{v: defaultValue}, data: NewBitStorage(0, length, nil), } } -func NewStatesPaletteContainerWithData(length int, data []uint64, pat []int) *PaletteContainer { - var p palette +func NewStatesPaletteContainerWithData(length int, data []uint64, pat []BlocksState) *PaletteContainer[BlocksState] { + var p palette[BlocksState] n := bits.Len(uint(len(pat) - 1)) switch n { case 0: - p = &singleValuePalette{pat[0]} + p = &singleValuePalette[BlocksState]{pat[0]} case 1, 2, 3, 4: n = 4 - p = &linearPalette{ + p = &linearPalette[BlocksState]{ values: pat, bits: n, } case 5, 6, 7, 8: - ids := make(map[state]int) + ids := make(map[BlocksState]int) for i, v := range pat { ids[v] = i } - p = &hashPalette{ + p = &hashPalette[BlocksState]{ ids: ids, values: pat, bits: n, } default: - p = &globalPalette{} + p = &globalPalette[BlocksState]{} } - return &PaletteContainer{ + return &PaletteContainer[BlocksState]{ bits: n, config: statesCfg{}, palette: p, @@ -59,30 +64,30 @@ func NewStatesPaletteContainerWithData(length int, data []uint64, pat []int) *Pa } } -func NewBiomesPaletteContainer(length int, defaultValue state) *PaletteContainer { - return &PaletteContainer{ +func NewBiomesPaletteContainer(length int, defaultValue BiomesState) *PaletteContainer[BiomesState] { + return &PaletteContainer[BiomesState]{ bits: 0, config: biomesCfg{}, - palette: &singleValuePalette{v: defaultValue}, + palette: &singleValuePalette[BiomesState]{v: defaultValue}, data: NewBitStorage(0, length, nil), } } -func NewBiomesPaletteContainerWithData(length int, data []uint64, pat []int) *PaletteContainer { - var p palette +func NewBiomesPaletteContainerWithData(length int, data []uint64, pat []BiomesState) *PaletteContainer[BiomesState] { + var p palette[BiomesState] n := bits.Len(uint(len(pat) - 1)) switch n { case 0: - p = &singleValuePalette{pat[0]} + p = &singleValuePalette[BiomesState]{pat[0]} case 1, 2, 3: - p = &linearPalette{ + p = &linearPalette[BiomesState]{ values: pat, bits: n, } default: - p = &globalPalette{} + p = &globalPalette[BiomesState]{} } - return &PaletteContainer{ + return &PaletteContainer[BiomesState]{ bits: n, config: biomesCfg{}, palette: p, @@ -90,17 +95,17 @@ func NewBiomesPaletteContainerWithData(length int, data []uint64, pat []int) *Pa } } -func (p *PaletteContainer) Get(i int) state { +func (p *PaletteContainer[T]) Get(i int) T { return p.palette.value(p.data.Get(i)) } -func (p *PaletteContainer) Set(i int, v state) { +func (p *PaletteContainer[T]) Set(i int, v T) { if vv, ok := p.palette.id(v); ok { p.data.Set(i, vv) } else { // resize oldLen := p.data.Len() - newPalette := PaletteContainer{ + newPalette := PaletteContainer[T]{ bits: vv, config: p.config, palette: p.config.create(vv), @@ -109,7 +114,7 @@ func (p *PaletteContainer) Set(i int, v state) { // copy for i := 0; i < oldLen; i++ { raw := p.data.Get(i) - if vv, ok := newPalette.palette.id(raw); !ok { + if vv, ok := newPalette.palette.id(T(raw)); !ok { panic("not reachable") } else { newPalette.data.Set(i, vv) @@ -125,14 +130,14 @@ func (p *PaletteContainer) Set(i int, v state) { } } -func (p *PaletteContainer) ReadFrom(r io.Reader) (n int64, err error) { - var bits pk.UnsignedByte - n, err = bits.ReadFrom(r) +func (p *PaletteContainer[T]) ReadFrom(r io.Reader) (n int64, err error) { + var nBits pk.UnsignedByte + n, err = nBits.ReadFrom(r) if err != nil { return } - p.bits = p.config.bits(int(bits)) - p.palette = p.config.create(int(bits)) + p.bits = p.config.bits(int(nBits)) + p.palette = p.config.create(int(nBits)) nn, err := p.palette.ReadFrom(r) n += nn @@ -148,7 +153,7 @@ func (p *PaletteContainer) ReadFrom(r io.Reader) (n int64, err error) { return n, nil } -func (p *PaletteContainer) WriteTo(w io.Writer) (n int64, err error) { +func (p *PaletteContainer[T]) WriteTo(w io.Writer) (n int64, err error) { return pk.Tuple{ pk.UnsignedByte(p.bits), p.palette, @@ -156,9 +161,9 @@ func (p *PaletteContainer) WriteTo(w io.Writer) (n int64, err error) { }.WriteTo(w) } -type paletteCfg interface { +type paletteCfg[T State] interface { bits(int) int - create(bits int) palette + create(bits int) palette[T] } type statesCfg struct{} @@ -176,17 +181,17 @@ func (s statesCfg) bits(bits int) int { } } -func (s statesCfg) create(bits int) palette { +func (s statesCfg) create(bits int) palette[BlocksState] { switch bits { case 0: - return &singleValuePalette{v: -1} + return &singleValuePalette[BlocksState]{v: -1} case 1, 2, 3, 4: - return &linearPalette{bits: 4, values: make([]state, 0, 1<<4)} + return &linearPalette[BlocksState]{bits: 4, values: make([]BlocksState, 0, 1<<4)} case 5, 6, 7, 8: // TODO: HashMapPalette - return &linearPalette{bits: bits, values: make([]state, 0, 1<= 0 && i < len(l.values) { return l.values[i] } panic("linearPalette: " + strconv.Itoa(i) + " out of bounds") } -func (l *linearPalette) export() []state { +func (l *linearPalette[T]) export() []T { return l.values } -func (l *linearPalette) ReadFrom(r io.Reader) (n int64, err error) { +func (l *linearPalette[T]) ReadFrom(r io.Reader) (n int64, err error) { var size, value pk.VarInt if n, err = size.ReadFrom(r); err != nil { return } if int(size) > cap(l.values) { - l.values = make([]state, size) + l.values = make([]T, size) } else { l.values = l.values[:size] } @@ -304,12 +309,12 @@ func (l *linearPalette) ReadFrom(r io.Reader) (n int64, err error) { } else { n += nn } - l.values[i] = state(value) + l.values[i] = T(value) } return } -func (l *linearPalette) WriteTo(w io.Writer) (n int64, err error) { +func (l *linearPalette[T]) WriteTo(w io.Writer) (n int64, err error) { if n, err = pk.VarInt(len(l.values)).WriteTo(w); err != nil { return } @@ -323,13 +328,13 @@ func (l *linearPalette) WriteTo(w io.Writer) (n int64, err error) { return } -type hashPalette struct { - ids map[state]int - values []state +type hashPalette[T State] struct { + ids map[T]int + values []T bits int } -func (h *hashPalette) id(v state) (int, bool) { +func (h *hashPalette[T]) id(v T) (int, bool) { if i, ok := h.ids[v]; ok { return i, true } @@ -341,24 +346,24 @@ func (h *hashPalette) id(v state) (int, bool) { return h.bits + 1, false } -func (h *hashPalette) value(i int) state { +func (h *hashPalette[T]) value(i int) T { if i >= 0 && i < len(h.values) { return h.values[i] } panic("hashPalette: " + strconv.Itoa(i) + " out of bounds") } -func (h *hashPalette) export() []state { +func (h *hashPalette[T]) export() []T { return h.values } -func (h *hashPalette) ReadFrom(r io.Reader) (n int64, err error) { +func (h *hashPalette[T]) ReadFrom(r io.Reader) (n int64, err error) { var size, value pk.VarInt if n, err = size.ReadFrom(r); err != nil { return } if int(size) > cap(h.values) { - h.values = make([]state, size) + h.values = make([]T, size) } else { h.values = h.values[:size] } @@ -368,13 +373,13 @@ func (h *hashPalette) ReadFrom(r io.Reader) (n int64, err error) { } else { n += nn } - h.values[i] = state(value) - h.ids[state(value)] = i + h.values[i] = T(value) + h.ids[T(value)] = i } return } -func (h *hashPalette) WriteTo(w io.Writer) (n int64, err error) { +func (h *hashPalette[T]) WriteTo(w io.Writer) (n int64, err error) { if n, err = pk.VarInt(len(h.values)).WriteTo(w); err != nil { return } @@ -388,24 +393,24 @@ func (h *hashPalette) WriteTo(w io.Writer) (n int64, err error) { return } -type globalPalette struct{} +type globalPalette[T State] struct{} -func (g *globalPalette) id(v state) (int, bool) { - return v, true +func (g *globalPalette[T]) id(v T) (int, bool) { + return int(v), true } -func (g *globalPalette) value(i int) state { - return i +func (g *globalPalette[T]) value(i int) T { + return T(i) } -func (g *globalPalette) export() []state { - return []state{} +func (g *globalPalette[T]) export() []T { + return []T{} } -func (g *globalPalette) ReadFrom(_ io.Reader) (int64, error) { +func (g *globalPalette[T]) ReadFrom(_ io.Reader) (int64, error) { return 0, nil } -func (g *globalPalette) WriteTo(_ io.Writer) (int64, error) { +func (g *globalPalette[T]) WriteTo(_ io.Writer) (int64, error) { return 0, nil } diff --git a/save/chunk.go b/save/chunk.go index dd30e6e..a233003 100644 --- a/save/chunk.go +++ b/save/chunk.go @@ -19,10 +19,10 @@ type Chunk struct { BlockEntities nbt.RawMessage `nbt:"block_entities"` Structures nbt.RawMessage `nbt:"structures"` Heightmaps struct { - MotionBlocking []int64 `nbt:"MOTION_BLOCKING"` - MotionBlockingNoLeaves []int64 `nbt:"MOTION_BLOCKING_NO_LEAVES"` - OceanFloor []int64 `nbt:"OCEAN_FLOOR"` - WorldSurface []int64 `nbt:"WORLD_SURFACE"` + MotionBlocking []uint64 `nbt:"MOTION_BLOCKING"` + MotionBlockingNoLeaves []uint64 `nbt:"MOTION_BLOCKING_NO_LEAVES"` + OceanFloor []uint64 `nbt:"OCEAN_FLOOR"` + WorldSurface []uint64 `nbt:"WORLD_SURFACE"` } Sections []Section `nbt:"sections"` @@ -39,11 +39,11 @@ type Section struct { Y int8 BlockStates struct { Palette []BlockState `nbt:"palette"` - Data []int64 `nbt:"data"` + Data []uint64 `nbt:"data"` } `nbt:"block_states"` Biomes struct { Palette []string `nbt:"palette"` - Data []int64 `nbt:"data"` + Data []uint64 `nbt:"data"` } `nbt:"biomes"` SkyLight []int8 BlockLight []int8