diff --git a/examples/frameworkServer/main.go b/examples/frameworkServer/main.go index b9d44e6..0b901ce 100644 --- a/examples/frameworkServer/main.go +++ b/examples/frameworkServer/main.go @@ -140,7 +140,7 @@ func loadAllChunks(dim *server.SimpleDim, file string, rx, rz int) error { if err := c.Load(data); err != nil { return err } - chunk := level.ChunkFromSave(&c, 256) + chunk := level.ChunkFromSave(&c) dim.LoadChunk(level.ChunkPos{X: rx<<5 + x, Z: rz<<5 + z}, chunk) } } diff --git a/level/chunk.go b/level/chunk.go index c56e4a7..52fe7fe 100644 --- a/level/chunk.go +++ b/level/chunk.go @@ -2,6 +2,7 @@ package level import ( "bytes" + "fmt" "io" "math/bits" "strings" @@ -124,64 +125,99 @@ var biomesIDs = map[string]int{ "end_barrens": 60, } -func ChunkFromSave(c *save.Chunk, secs int) *Chunk { +// ChunkFromSave convert save.Chunk to level.Chunk. +func ChunkFromSave(c *save.Chunk) *Chunk { + secs := len(c.Sections) sections := make([]Section, secs) for _, v := range c.Sections { - var blockCount int16 - stateData := *(*[]uint64)((unsafe.Pointer)(&v.BlockStates.Data)) - statePalette := v.BlockStates.Palette - stateRawPalette := make([]int, len(statePalette)) - for i, v := range statePalette { - b, ok := block.FromID[v.Name] - if !ok { - return nil - } - if v.Properties.Data != nil { - err := v.Properties.Unmarshal(&b) - if err != nil { - return nil - } - } - s := block.ToStateID[b] - if !block.IsAir(s) { - blockCount++ - } - stateRawPalette[i] = s - } - - biomesData := *(*[]uint64)((unsafe.Pointer)(&v.Biomes.Data)) - biomesPalette := v.Biomes.Palette - biomesRawPalette := make([]int, len(biomesPalette)) - for i, v := range biomesPalette { - biomesRawPalette[i] = biomesIDs[strings.TrimPrefix(v, "minecraft:")] - } - i := int32(v.Y) - c.YPos - sections[i].BlockCount = blockCount - sections[i].States = NewStatesPaletteContainerWithData(16*16*16, stateData, stateRawPalette) - sections[i].Biomes = NewBiomesPaletteContainerWithData(4*4*4, biomesData, biomesRawPalette) - } - for i := range sections { - if sections[i].States == nil { - sections[i] = Section{ - BlockCount: 0, - States: NewStatesPaletteContainer(16*16*16, 0), - Biomes: NewBiomesPaletteContainer(4*4*4, 0), - } - } + // TODO: the error is ignored + sections[i].BlockCount, sections[i].States, _ = readStatesPalette(v.BlockStates.Palette, v.BlockStates.Data) + sections[i].Biomes, _ = readBiomesPalette(v.Biomes.Palette, v.Biomes.Data) } motionBlocking := *(*[]uint64)(unsafe.Pointer(&c.Heightmaps.MotionBlocking)) + motionBlockingNoLeaves := *(*[]uint64)(unsafe.Pointer(&c.Heightmaps.MotionBlockingNoLeaves)) + oceanFloor := *(*[]uint64)(unsafe.Pointer(&c.Heightmaps.OceanFloor)) worldSurface := *(*[]uint64)(unsafe.Pointer(&c.Heightmaps.WorldSurface)) + + bitsForHeight := bits.Len( /* chunk height in blocks */ uint(secs) * 16) return &Chunk{ Sections: sections, HeightMaps: HeightMaps{ - MotionBlocking: NewBitStorage(bits.Len(uint(secs)), 16*16, motionBlocking), - WorldSurface: NewBitStorage(bits.Len(uint(secs)), 16*16, worldSurface), + MotionBlocking: NewBitStorage(bitsForHeight, 16*16, motionBlocking), + MotionBlockingNoLeaves: NewBitStorage(bitsForHeight, 16*16, motionBlockingNoLeaves), + OceanFloor: NewBitStorage(bitsForHeight, 16*16, oceanFloor), + WorldSurface: NewBitStorage(bitsForHeight, 16*16, worldSurface), }, } } +func readStatesPalette(palette []save.BlockState, data []int64) (blockCount int16, paletteData *PaletteContainer, err error) { + stateData := *(*[]uint64)((unsafe.Pointer)(&data)) + statePalette := make([]int, len(palette)) + for i, v := range palette { + b, ok := block.FromID[v.Name] + if !ok { + return 0, nil, fmt.Errorf("unknown block id: %v", v.Name) + } + if v.Properties.Data != nil { + if err := v.Properties.Unmarshal(&b); err != nil { + return 0, nil, fmt.Errorf("unmarshal block properties fail: %v", err) + } + } + s, ok := block.ToStateID[b] + if !ok { + return 0, nil, fmt.Errorf("unknown block: %v", b) + } + if !block.IsAir(s) { + blockCount++ + } + statePalette[i] = s + } + paletteData = NewStatesPaletteContainerWithData(16*16*16, stateData, statePalette) + return +} + +func readBiomesPalette(palette []string, data []int64) (*PaletteContainer, error) { + biomesData := *(*[]uint64)((unsafe.Pointer)(&data)) + biomesRawPalette := make([]int, len(palette)) + var ok bool + for i, v := range palette { + biomesRawPalette[i], ok = biomesIDs[strings.TrimPrefix(v, "minecraft:")] + if !ok { + return nil, fmt.Errorf("unknown biomes: %s", v) + } + } + return NewBiomesPaletteContainerWithData(4*4*4, biomesData, biomesRawPalette), nil +} + +//// ChunkToSave convert level.Chunk to save.Chunk +//func ChunkToSave(c *Chunk, dst *save.Chunk) { +// secs := len(c.Sections) +// sections := make([]save.Section, secs) +// for i, v := range c.Sections { +// sections[i] = save.Section{ +// Y: int8(int32(i) + dst.YPos), +// BlockStates: struct { +// Palette []save.BlockState `nbt:"palette"` +// Data []int64 `nbt:"data"` +// }{}, +// Biomes: struct { +// Palette []string `nbt:"palette"` +// Data []int64 `nbt:"data"` +// }{}, +// SkyLight: nil, +// BlockLight: nil, +// } +// } +// dst.Sections = sections +//} + +//func writeStatesPalette(paletteData *PaletteContainer) (palette []save.BlockState, data []int64) { +// paletteData.palette.export() +//} + func (c *Chunk) WriteTo(w io.Writer) (int64, error) { data, err := c.Data() if err != nil { @@ -258,8 +294,10 @@ func (c *Chunk) PutData(data []byte) error { } type HeightMaps struct { - MotionBlocking *BitStorage - WorldSurface *BitStorage + MotionBlocking *BitStorage + MotionBlockingNoLeaves *BitStorage + OceanFloor *BitStorage + WorldSurface *BitStorage } type BlockEntity struct { diff --git a/level/palette.go b/level/palette.go index b16ef63..c3d5456 100644 --- a/level/palette.go +++ b/level/palette.go @@ -34,12 +34,20 @@ func NewStatesPaletteContainerWithData(length int, data []uint64, pat []int) *Pa p = &singleValuePalette{pat[0]} case 1, 2, 3, 4: n = 4 - fallthrough - case 5, 6, 7, 8: p = &linearPalette{ values: pat, bits: n, } + case 5, 6, 7, 8: + ids := make(map[state]int) + for i, v := range pat { + ids[v] = i + } + p = &hashPalette{ + ids: ids, + values: pat, + bits: n, + } default: p = &globalPalette{} } @@ -207,8 +215,11 @@ func (b biomesCfg) create(bits int) palette { type palette interface { pk.Field + // id return the index of state v in the palette and true if existed. + // otherwise return the new bits for resize and false. id(v state) (int, bool) value(i int) state + export() []state } type singleValuePalette struct { @@ -230,6 +241,10 @@ func (s *singleValuePalette) value(i int) state { panic("singleValuePalette: " + strconv.Itoa(i) + " out of bounds") } +func (s *singleValuePalette) export() []state { + return []int{s.v} +} + func (s *singleValuePalette) ReadFrom(r io.Reader) (n int64, err error) { var i pk.VarInt n, err = i.ReadFrom(r) @@ -269,6 +284,10 @@ func (l *linearPalette) value(i int) state { panic("linearPalette: " + strconv.Itoa(i) + " out of bounds") } +func (l *linearPalette) export() []state { + return l.values +} + func (l *linearPalette) ReadFrom(r io.Reader) (n int64, err error) { var size, value pk.VarInt if n, err = size.ReadFrom(r); err != nil { @@ -304,6 +323,71 @@ func (l *linearPalette) WriteTo(w io.Writer) (n int64, err error) { return } +type hashPalette struct { + ids map[state]int + values []state + bits int +} + +func (h *hashPalette) id(v state) (int, bool) { + if i, ok := h.ids[v]; ok { + return i, true + } + if cap(h.values)-len(h.values) > 0 { + h.ids[v] = len(h.values) + h.values = append(h.values, v) + return len(h.values) - 1, true + } + return h.bits + 1, false +} + +func (h *hashPalette) value(i int) state { + if i >= 0 && i < len(h.values) { + return h.values[i] + } + panic("hashPalette: " + strconv.Itoa(i) + " out of bounds") +} + +func (h *hashPalette) export() []state { + return h.values +} + +func (h *hashPalette) 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) + } else { + h.values = h.values[:size] + } + for i := 0; i < int(size); i++ { + if nn, err := value.ReadFrom(r); err != nil { + return n + nn, err + } else { + n += nn + } + h.values[i] = state(value) + h.ids[state(value)] = i + } + return +} + +func (h *hashPalette) WriteTo(w io.Writer) (n int64, err error) { + if n, err = pk.VarInt(len(h.values)).WriteTo(w); err != nil { + return + } + for _, v := range h.values { + if nn, err := pk.VarInt(v).WriteTo(w); err != nil { + return n + nn, err + } else { + n += nn + } + } + return +} + type globalPalette struct{} func (g *globalPalette) id(v state) (int, bool) { @@ -314,6 +398,10 @@ func (g *globalPalette) value(i int) state { return i } +func (g *globalPalette) export() []state { + return []state{} +} + func (g *globalPalette) ReadFrom(_ io.Reader) (int64, error) { return 0, nil } diff --git a/save/chunk.go b/save/chunk.go index 0355b78..dd30e6e 100644 --- a/save/chunk.go +++ b/save/chunk.go @@ -25,6 +25,14 @@ type Chunk struct { WorldSurface []int64 `nbt:"WORLD_SURFACE"` } Sections []Section `nbt:"sections"` + + BlockTicks nbt.RawMessage `nbt:"block_ticks"` + FluidTicks nbt.RawMessage `nbt:"fluid_ticks"` + PostProcessing nbt.RawMessage + InhabitedTime int64 + IsLightOn byte `nbt:"isLightOn"` + LastUpdate int64 + Status string } type Section struct { @@ -37,8 +45,8 @@ type Section struct { Palette []string `nbt:"palette"` Data []int64 `nbt:"data"` } `nbt:"biomes"` - SkyLight []byte - BlockLight []byte + SkyLight []int8 + BlockLight []int8 } type BlockState struct {