convert between save.Chunk and level.Chunk
This commit is contained in:
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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] {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ type GamePlay interface {
|
|||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
eid int32
|
eid int32
|
||||||
Dim Dimension
|
Dim Level
|
||||||
*PlayerList
|
*PlayerList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user