From 5bc8513039519f0890a340e7028fe682037ad1d6 Mon Sep 17 00:00:00 2001 From: Tnze Date: Mon, 20 Dec 2021 01:50:31 +0800 Subject: [PATCH] convert between save.Chunk and level.Chunk --- examples/genmaps/genmaps.go | 54 ++++++++-------- level/palette.go | 14 ++++ save/chunk.go | 34 +++++----- save/chunk_test.go | 4 +- save/playerdata.go | 2 +- server/dimension.go | 126 +++++++++++++++++++++++------------- server/gameplay.go | 2 +- 7 files changed, 143 insertions(+), 93 deletions(-) diff --git a/examples/genmaps/genmaps.go b/examples/genmaps/genmaps.go index be8ff84..a7f3ab1 100644 --- a/examples/genmaps/genmaps.go +++ b/examples/genmaps/genmaps.go @@ -3,7 +3,6 @@ package main import ( "flag" "fmt" - "github.com/Tnze/go-mc/level" "image" "image/color" "image/draw" @@ -11,10 +10,12 @@ import ( "os" "path/filepath" "runtime" + "strings" "sync" "unsafe" "github.com/Tnze/go-mc/data/block" + "github.com/Tnze/go-mc/level" "github.com/Tnze/go-mc/save" "github.com/Tnze/go-mc/save/region" ) @@ -97,7 +98,7 @@ func main() { var wg sync.WaitGroup for i := 0; i < *regionWorkerNum; i++ { go func() { - var column save.Column + var column save.Chunk for task := range c { if err := column.Load(task.data); err != nil { log.Printf("Decode column (%d.%d) error: %v", task.pos[0], task.pos[1], err) @@ -155,10 +156,10 @@ func main() { } } -func drawColumn(column *save.Column) (img *image.RGBA) { +func drawColumn(column *save.Chunk) (img *image.RGBA) { img = image.NewRGBA(image.Rect(0, 0, 16, 16)) - s := column.Level.Sections - c := make(chan *save.Chunk) + s := column.Sections + c := make(chan *save.Section) var wg sync.WaitGroup for i := 0; i < sectionWorkerNum; i++ { go func() { @@ -179,33 +180,20 @@ func drawColumn(column *save.Column) (img *image.RGBA) { return } -func drawSection(s *save.Chunk, img *image.RGBA) { - // calculate bits per block - bpb := len(s.BlockStates) * 64 / (16 * 16 * 16) - // skip empty - if len(s.BlockStates) == 0 { - return +func drawSection(s *save.Section, img *image.RGBA) { + data := *(*[]uint64)((unsafe.Pointer)(&s.BlockStates.Data)) + palette := s.BlockStates.Palette + rawPalette := make([]int, len(palette)) + for i, v := range palette { + // TODO: Consider the properties of block, not only index the block name + rawPalette[i] = int(stateIDs[strings.TrimPrefix(v.Name, "minecraft:")]) } - // decode section - - // decode status - data := *(*[]uint64)(unsafe.Pointer(&s.BlockStates)) // convert []int64 into []uint64 - bs := level.NewBitStorage(bpb, 4096, data) + c := level.NewStatesPaletteContainerWithData(16*16*16, data, rawPalette) for y := 0; y < 16; y++ { layerImg := image.NewRGBA(image.Rect(0, 0, 16, 16)) for i := 16*16 - 1; i >= 0; i-- { var bid block.ID - switch { - case bpb > 9: - bid = block.StateID[uint32(bs.Get(y*16*16+i))] - case bpb > 4: - fallthrough - case bpb <= 4: - b := s.Palette[bs.Get(y*16*16+i)] - if id, ok := idByName[b.Name]; ok { - bid = block.StateID[id] - } - } + bid = block.ID(c.Get(i)) c := colors[block.ByID[bid].ID] layerImg.Set(i%16, i/16, c) } @@ -217,3 +205,15 @@ func drawSection(s *save.Chunk, img *image.RGBA) { } return } + +// TODO: This map should be moved to data/block. +var stateIDs map[string]uint32 + +func init() { + for i, v := range block.StateID { + name := block.ByID[v].Name + if _, ok := stateIDs[name]; !ok { + stateIDs[name] = i + } + } +} diff --git a/level/palette.go b/level/palette.go index 58e6935..15b458b 100644 --- a/level/palette.go +++ b/level/palette.go @@ -2,6 +2,7 @@ package level import ( "io" + "math/bits" "strconv" pk "github.com/Tnze/go-mc/net/packet" @@ -25,6 +26,19 @@ func NewStatesPaletteContainer(length int, defaultValue state) *PaletteContainer } } +func NewStatesPaletteContainerWithData(length int, data []uint64, palette []int) *PaletteContainer { + n := bits.Len(uint(len(palette))) + return &PaletteContainer{ + bits: n, + config: createStatesPalette, + palette: &linearPalette{ + values: palette, + bits: n, + }, + data: NewBitStorage(n, length, data), + } +} + func NewBiomesPaletteContainer(length int, defaultValue state) *PaletteContainer { return &PaletteContainer{ bits: 0, diff --git a/save/chunk.go b/save/chunk.go index a1f4256..122a76a 100644 --- a/save/chunk.go +++ b/save/chunk.go @@ -9,8 +9,8 @@ import ( "io" ) -// Column is 16* chunk -type Column struct { +// Chunk is 16* chunk +type Chunk struct { DataVersion int32 XPos int32 `nbt:"xPos"` YPos int32 `nbt:"yPos"` @@ -23,19 +23,21 @@ type Column struct { OceanFloor []int64 `nbt:"OCEAN_FLOOR"` WorldSurface []int64 `nbt:"WORLD_SURFACE"` } - Sections []struct { - Y byte - BlockStates struct { - Palette []BlockState `nbt:"palette"` - Data []int64 `nbt:"data"` - } `nbt:"block_states"` - Biomes struct { - Palette []string `nbt:"palette"` - Data []int64 `nbt:"data"` - } `nbt:"biomes"` - SkyLight []byte - BlockLight []byte - } `nbt:"sections"` + Sections []Section `nbt:"sections"` +} + +type Section struct { + Y byte + BlockStates struct { + Palette []BlockState `nbt:"palette"` + Data []int64 `nbt:"data"` + } `nbt:"block_states"` + Biomes struct { + Palette []string `nbt:"palette"` + Data []int64 `nbt:"data"` + } `nbt:"biomes"` + SkyLight []byte + BlockLight []byte } type BlockState struct { @@ -44,7 +46,7 @@ type BlockState struct { } // Load read column data from []byte -func (c *Column) Load(data []byte) (err error) { +func (c *Chunk) Load(data []byte) (err error) { var r io.Reader = bytes.NewReader(data[1:]) switch data[0] { diff --git a/save/chunk_test.go b/save/chunk_test.go index 498b7b9..19403e5 100644 --- a/save/chunk_test.go +++ b/save/chunk_test.go @@ -12,7 +12,7 @@ func TestColumn(t *testing.T) { } defer r.Close() - var c Column + var c Chunk data, err := r.ReadSector(0, 0) if err != nil { t.Fatal(err) @@ -27,7 +27,7 @@ func TestColumn(t *testing.T) { func BenchmarkColumn_Load(b *testing.B) { // Test how many times we load a chunk - var c Column + var c Chunk r, err := region.Open("testdata/region/r.-1.-1.mca") if err != nil { b.Fatal(err) diff --git a/save/playerdata.go b/save/playerdata.go index 36cd16f..99f7866 100644 --- a/save/playerdata.go +++ b/save/playerdata.go @@ -10,7 +10,7 @@ import ( type PlayerData struct { DataVersion int32 - Dimension int32 + Dimension string Pos [3]float64 Motion [3]float64 Rotation [2]float32 diff --git a/server/dimension.go b/server/dimension.go index 542c032..ffcbb91 100644 --- a/server/dimension.go +++ b/server/dimension.go @@ -4,20 +4,24 @@ import ( "bytes" "io" "math/bits" + "strings" "sync" + "unsafe" + "github.com/Tnze/go-mc/data/block" "github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/level" pk "github.com/Tnze/go-mc/net/packet" + "github.com/Tnze/go-mc/save" ) -type Dimension interface { - Info() DimInfo +type Level interface { + Info() LevelInfo PlayerJoin(p *Player) PlayerQuit(p *Player) } -type DimInfo struct { +type LevelInfo struct { Name string HashedSeed uint64 } @@ -29,6 +33,77 @@ type Chunk struct { HeightMaps *level.BitStorage } +func EmptyChunk(secs int) *Chunk { + sections := make([]Section, secs) + for i := range sections { + sections[i] = Section{ + blockCount: 0, + States: level.NewStatesPaletteContainer(16*16*16, 0), + Biomes: level.NewBiomesPaletteContainer(4*4*4, 0), + } + } + return &Chunk{ + Sections: sections, + HeightMaps: level.NewBitStorage(bits.Len(uint(secs)*16), 16*16, nil), + } +} + +func ChunkFromSave(c *save.Chunk) *Chunk { + sections := make([]Section, len(c.Sections)) + for i := range sections { + data := *(*[]uint64)((unsafe.Pointer)(&c.Sections[i].BlockStates.Data)) + palette := c.Sections[i].BlockStates.Palette + rawPalette := make([]int, len(palette)) + for i, v := range palette { + // TODO: Consider the properties of block, not only index the block name + rawPalette[i] = int(stateIDs[strings.TrimPrefix(v.Name, "minecraft:")]) + } + sections[i].States = level.NewStatesPaletteContainerWithData(16*16*16, data, rawPalette) + } + return &Chunk{ + Sections: sections, + HeightMaps: nil, + } +} + +// TODO: This map should be moved to data/block. +var stateIDs map[string]uint32 + +func init() { + for i, v := range block.StateID { + name := block.ByID[v].Name + if _, ok := stateIDs[name]; !ok { + stateIDs[name] = i + } + } +} + +func (c *Chunk) WriteTo(w io.Writer) (int64, error) { + data, err := c.Data() + if err != nil { + return 0, err + } + return pk.Tuple{ + // Heightmaps + pk.NBT(struct { + MotionBlocking []uint64 `nbt:"MOTION_BLOCKING"` + }{c.HeightMaps.Raw()}), + pk.ByteArray(data), + pk.VarInt(0), // TODO: Block Entity + }.WriteTo(w) +} + +func (c *Chunk) Data() ([]byte, error) { + var buff bytes.Buffer + for _, section := range c.Sections { + _, err := section.WriteTo(&buff) + if err != nil { + return nil, err + } + } + return buff.Bytes(), nil +} + type Section struct { blockCount int16 States *level.PaletteContainer @@ -65,47 +140,6 @@ func (s *Section) ReadFrom(r io.Reader) (int64, error) { }.ReadFrom(r) } -func EmptyChunk(secs int) *Chunk { - sections := make([]Section, secs) - for i := range sections { - sections[i] = Section{ - blockCount: 0, - States: level.NewStatesPaletteContainer(16*16*16, 0), - Biomes: level.NewBiomesPaletteContainer(4*4*4, 0), - } - } - return &Chunk{ - Sections: sections, - HeightMaps: level.NewBitStorage(bits.Len(uint(secs)*16), 16*16, nil), - } -} - -func (c *Chunk) WriteTo(w io.Writer) (int64, error) { - data, err := c.Data() - if err != nil { - return 0, err - } - return pk.Tuple{ - // Heightmaps - pk.NBT(struct { - MotionBlocking []uint64 `nbt:"MOTION_BLOCKING"` - }{c.HeightMaps.Raw()}), - pk.ByteArray(data), - pk.VarInt(0), // TODO: Block Entity - }.WriteTo(w) -} - -func (c *Chunk) Data() ([]byte, error) { - var buff bytes.Buffer - for _, section := range c.Sections { - _, err := section.WriteTo(&buff) - if err != nil { - return nil, err - } - } - return buff.Bytes(), nil -} - type lightData struct { SkyLightMask pk.BitSet BlockLightMask pk.BitSet @@ -149,8 +183,8 @@ func (s *SimpleDim) LoadChunk(pos ChunkPos, c *Chunk) { s.Columns[pos] = c } -func (s *SimpleDim) Info() DimInfo { - return DimInfo{ +func (s *SimpleDim) Info() LevelInfo { + return LevelInfo{ Name: "minecraft:overworld", HashedSeed: 1234567, } diff --git a/server/gameplay.go b/server/gameplay.go index 068325d..c4905ef 100644 --- a/server/gameplay.go +++ b/server/gameplay.go @@ -23,7 +23,7 @@ type GamePlay interface { type Game struct { eid int32 - Dim Dimension + Dim Level *PlayerList }