convert between save.Chunk and level.Chunk

This commit is contained in:
Tnze
2021-12-20 01:50:31 +08:00
parent 8eaae6a202
commit 5bc8513039
7 changed files with 143 additions and 93 deletions

View File

@ -3,7 +3,6 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/Tnze/go-mc/level"
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
@ -11,10 +10,12 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"sync" "sync"
"unsafe" "unsafe"
"github.com/Tnze/go-mc/data/block" "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"
"github.com/Tnze/go-mc/save/region" "github.com/Tnze/go-mc/save/region"
) )
@ -97,7 +98,7 @@ func main() {
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < *regionWorkerNum; i++ { for i := 0; i < *regionWorkerNum; i++ {
go func() { go func() {
var column save.Column var column save.Chunk
for task := range c { for task := range c {
if err := column.Load(task.data); err != nil { if err := column.Load(task.data); err != nil {
log.Printf("Decode column (%d.%d) error: %v", task.pos[0], task.pos[1], err) 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)) img = image.NewRGBA(image.Rect(0, 0, 16, 16))
s := column.Level.Sections s := column.Sections
c := make(chan *save.Chunk) c := make(chan *save.Section)
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < sectionWorkerNum; i++ { for i := 0; i < sectionWorkerNum; i++ {
go func() { go func() {
@ -179,33 +180,20 @@ func drawColumn(column *save.Column) (img *image.RGBA) {
return return
} }
func drawSection(s *save.Chunk, img *image.RGBA) { func drawSection(s *save.Section, img *image.RGBA) {
// calculate bits per block data := *(*[]uint64)((unsafe.Pointer)(&s.BlockStates.Data))
bpb := len(s.BlockStates) * 64 / (16 * 16 * 16) palette := s.BlockStates.Palette
// skip empty rawPalette := make([]int, len(palette))
if len(s.BlockStates) == 0 { for i, v := range palette {
return // TODO: Consider the properties of block, not only index the block name
rawPalette[i] = int(stateIDs[strings.TrimPrefix(v.Name, "minecraft:")])
} }
// decode section c := level.NewStatesPaletteContainerWithData(16*16*16, data, rawPalette)
// decode status
data := *(*[]uint64)(unsafe.Pointer(&s.BlockStates)) // convert []int64 into []uint64
bs := level.NewBitStorage(bpb, 4096, data)
for y := 0; y < 16; y++ { for y := 0; y < 16; y++ {
layerImg := image.NewRGBA(image.Rect(0, 0, 16, 16)) layerImg := image.NewRGBA(image.Rect(0, 0, 16, 16))
for i := 16*16 - 1; i >= 0; i-- { for i := 16*16 - 1; i >= 0; i-- {
var bid block.ID var bid block.ID
switch { bid = block.ID(c.Get(i))
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]
}
}
c := colors[block.ByID[bid].ID] c := colors[block.ByID[bid].ID]
layerImg.Set(i%16, i/16, c) layerImg.Set(i%16, i/16, c)
} }
@ -217,3 +205,15 @@ func drawSection(s *save.Chunk, img *image.RGBA) {
} }
return 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
}
}
}

View File

@ -2,6 +2,7 @@ package level
import ( import (
"io" "io"
"math/bits"
"strconv" "strconv"
pk "github.com/Tnze/go-mc/net/packet" 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 { func NewBiomesPaletteContainer(length int, defaultValue state) *PaletteContainer {
return &PaletteContainer{ return &PaletteContainer{
bits: 0, bits: 0,

View File

@ -9,8 +9,8 @@ import (
"io" "io"
) )
// Column is 16* chunk // Chunk is 16* chunk
type Column struct { type Chunk struct {
DataVersion int32 DataVersion int32
XPos int32 `nbt:"xPos"` XPos int32 `nbt:"xPos"`
YPos int32 `nbt:"yPos"` YPos int32 `nbt:"yPos"`
@ -23,19 +23,21 @@ type Column struct {
OceanFloor []int64 `nbt:"OCEAN_FLOOR"` OceanFloor []int64 `nbt:"OCEAN_FLOOR"`
WorldSurface []int64 `nbt:"WORLD_SURFACE"` WorldSurface []int64 `nbt:"WORLD_SURFACE"`
} }
Sections []struct { Sections []Section `nbt:"sections"`
Y byte }
BlockStates struct {
Palette []BlockState `nbt:"palette"` type Section struct {
Data []int64 `nbt:"data"` Y byte
} `nbt:"block_states"` BlockStates struct {
Biomes struct { Palette []BlockState `nbt:"palette"`
Palette []string `nbt:"palette"` Data []int64 `nbt:"data"`
Data []int64 `nbt:"data"` } `nbt:"block_states"`
} `nbt:"biomes"` Biomes struct {
SkyLight []byte Palette []string `nbt:"palette"`
BlockLight []byte Data []int64 `nbt:"data"`
} `nbt:"sections"` } `nbt:"biomes"`
SkyLight []byte
BlockLight []byte
} }
type BlockState struct { type BlockState struct {
@ -44,7 +46,7 @@ type BlockState struct {
} }
// Load read column data from []byte // 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:]) var r io.Reader = bytes.NewReader(data[1:])
switch data[0] { switch data[0] {

View File

@ -12,7 +12,7 @@ func TestColumn(t *testing.T) {
} }
defer r.Close() defer r.Close()
var c Column var c Chunk
data, err := r.ReadSector(0, 0) data, err := r.ReadSector(0, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -27,7 +27,7 @@ func TestColumn(t *testing.T) {
func BenchmarkColumn_Load(b *testing.B) { func BenchmarkColumn_Load(b *testing.B) {
// Test how many times we load a chunk // Test how many times we load a chunk
var c Column var c Chunk
r, err := region.Open("testdata/region/r.-1.-1.mca") r, err := region.Open("testdata/region/r.-1.-1.mca")
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)

View File

@ -10,7 +10,7 @@ import (
type PlayerData struct { type PlayerData struct {
DataVersion int32 DataVersion int32
Dimension int32 Dimension string
Pos [3]float64 Pos [3]float64
Motion [3]float64 Motion [3]float64
Rotation [2]float32 Rotation [2]float32

View File

@ -4,20 +4,24 @@ import (
"bytes" "bytes"
"io" "io"
"math/bits" "math/bits"
"strings"
"sync" "sync"
"unsafe"
"github.com/Tnze/go-mc/data/block"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/level" "github.com/Tnze/go-mc/level"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/save"
) )
type Dimension interface { type Level interface {
Info() DimInfo Info() LevelInfo
PlayerJoin(p *Player) PlayerJoin(p *Player)
PlayerQuit(p *Player) PlayerQuit(p *Player)
} }
type DimInfo struct { type LevelInfo struct {
Name string Name string
HashedSeed uint64 HashedSeed uint64
} }
@ -29,6 +33,77 @@ type Chunk struct {
HeightMaps *level.BitStorage 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 { type Section struct {
blockCount int16 blockCount int16
States *level.PaletteContainer States *level.PaletteContainer
@ -65,47 +140,6 @@ func (s *Section) ReadFrom(r io.Reader) (int64, error) {
}.ReadFrom(r) }.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 { type lightData struct {
SkyLightMask pk.BitSet SkyLightMask pk.BitSet
BlockLightMask pk.BitSet BlockLightMask pk.BitSet
@ -149,8 +183,8 @@ func (s *SimpleDim) LoadChunk(pos ChunkPos, c *Chunk) {
s.Columns[pos] = c s.Columns[pos] = c
} }
func (s *SimpleDim) Info() DimInfo { func (s *SimpleDim) Info() LevelInfo {
return DimInfo{ return LevelInfo{
Name: "minecraft:overworld", Name: "minecraft:overworld",
HashedSeed: 1234567, HashedSeed: 1234567,
} }

View File

@ -23,7 +23,7 @@ type GamePlay interface {
type Game struct { type Game struct {
eid int32 eid int32
Dim Dimension Dim Level
*PlayerList *PlayerList
} }