simple conversion between save.Chunk and level.Chunk
This commit is contained in:
@ -4,12 +4,12 @@
|
|||||||
package block
|
package block
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BitsPerBlock indicates how many bits are needed to represent all possible
|
// 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.
|
// block states. This value is used to determine the size of the global palette.
|
||||||
var BitsPerBlock = int(math.Ceil(math.Log2(float64(len(StateID)))))
|
var BitsPerBlock = bits.Len(uint(len(StateID)))
|
||||||
|
|
||||||
// ID describes the numeric ID of a block.
|
// ID describes the numeric ID of a block.
|
||||||
type ID uint32
|
type ID uint32
|
||||||
|
@ -24,12 +24,12 @@ const (
|
|||||||
package block
|
package block
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BitsPerBlock indicates how many bits are needed to represent all possible
|
// 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.
|
// block states. This value is used to determine the size of the global palette.
|
||||||
var BitsPerBlock = int(math.Ceil(math.Log2(float64(len(StateID)))))
|
var BitsPerBlock = bits.Len(uint(len(StateID)))
|
||||||
|
|
||||||
// ID describes the numeric ID of a block.
|
// ID describes the numeric ID of a block.
|
||||||
type ID uint32
|
type ID uint32
|
||||||
|
@ -3,6 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"github.com/Tnze/go-mc/chat"
|
"github.com/Tnze/go-mc/chat"
|
||||||
|
"github.com/Tnze/go-mc/level"
|
||||||
|
"github.com/Tnze/go-mc/save"
|
||||||
|
"github.com/Tnze/go-mc/save/region"
|
||||||
"github.com/Tnze/go-mc/server"
|
"github.com/Tnze/go-mc/server"
|
||||||
"image"
|
"image"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
@ -21,14 +24,9 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Set server info error: %v", err)
|
log.Fatalf("Set server info error: %v", err)
|
||||||
}
|
}
|
||||||
defaultDimension := server.NewSimpleDim(16)
|
defaultDimension := server.NewSimpleDim(256)
|
||||||
chunk00 := server.EmptyChunk(16)
|
chunk00 := level.ChunkFromSave(readChunk00(), 256)
|
||||||
for s := 0; s < 16; s++ {
|
defaultDimension.LoadChunk(level.ChunkPos{X: 0, Z: 0}, chunk00)
|
||||||
for i := 0; i < 16*16; i++ {
|
|
||||||
chunk00.Sections[s].SetBlock(i, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defaultDimension.LoadChunk(server.ChunkPos{X: 0, Z: 0}, chunk00)
|
|
||||||
s := server.Server{
|
s := server.Server{
|
||||||
ListPingHandler: serverInfo,
|
ListPingHandler: serverInfo,
|
||||||
LoginHandler: &server.MojangLoginHandler{
|
LoginHandler: &server.MojangLoginHandler{
|
||||||
@ -61,3 +59,22 @@ func readIcon() image.Image {
|
|||||||
}
|
}
|
||||||
return icon
|
return icon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readChunk00() *save.Chunk {
|
||||||
|
r, err := region.Open("./save/testdata/region/r.0.0.mca")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
var c save.Chunk
|
||||||
|
data, err := r.ReadSector(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = c.Load(data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
@ -2,9 +2,10 @@ package level
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
const indexOutOfBounds = "index out of bounds"
|
const indexOutOfBounds = "index out of bounds"
|
||||||
|
264
level/chunk.go
Normal file
264
level/chunk.go
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
package level
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"math/bits"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/data/block"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
"github.com/Tnze/go-mc/save"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChunkPos struct{ X, Z int }
|
||||||
|
type Chunk struct {
|
||||||
|
sync.Mutex
|
||||||
|
Sections []Section
|
||||||
|
HeightMaps HeightMaps
|
||||||
|
}
|
||||||
|
type HeightMaps struct {
|
||||||
|
MotionBlocking *BitStorage
|
||||||
|
WorldSurface *BitStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
func EmptyChunk(secs int) *Chunk {
|
||||||
|
sections := make([]Section, secs)
|
||||||
|
for i := range sections {
|
||||||
|
sections[i] = Section{
|
||||||
|
blockCount: 0,
|
||||||
|
States: NewStatesPaletteContainer(16*16*16, 0),
|
||||||
|
Biomes: NewBiomesPaletteContainer(4*4*4, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Chunk{
|
||||||
|
Sections: sections,
|
||||||
|
HeightMaps: HeightMaps{
|
||||||
|
MotionBlocking: NewBitStorage(bits.Len(uint(secs)*16), 16*16, nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var biomesIDs = map[string]int{"ocean": 0,
|
||||||
|
"deep_ocean": 24,
|
||||||
|
"frozen_ocean": 10,
|
||||||
|
"deep_frozen_ocean": 50,
|
||||||
|
"cold_ocean": 46,
|
||||||
|
"deep_cold_ocean": 49,
|
||||||
|
"lukewarm_ocean": 45,
|
||||||
|
"deep_lukewarm_ocean": 48,
|
||||||
|
"warm_ocean": 44,
|
||||||
|
"river": 7,
|
||||||
|
"frozen_river": 11,
|
||||||
|
"beach": 16,
|
||||||
|
"stony_shore": 25,
|
||||||
|
"snowy_beach": 26,
|
||||||
|
"forest": 4,
|
||||||
|
"flower_forest": 132,
|
||||||
|
"birch_forest": 27,
|
||||||
|
"old_growth_birch_forest": 155,
|
||||||
|
"dark_forest": 29,
|
||||||
|
"jungle": 21,
|
||||||
|
"sparse_jungle": 23,
|
||||||
|
"bamboo_jungle": 168,
|
||||||
|
"taiga": 5,
|
||||||
|
"snowy_taiga": 30,
|
||||||
|
"old_growth_pine_taiga": 32,
|
||||||
|
"old_growth_spruce_taiga": 160,
|
||||||
|
"mushroom_fields": 14,
|
||||||
|
"swamp": 6,
|
||||||
|
"savanna": 35,
|
||||||
|
"savanna_plateau": 36,
|
||||||
|
"windswept_savanna": 163,
|
||||||
|
"plains": 1,
|
||||||
|
"sunflower_plains": 129,
|
||||||
|
"desert": 2,
|
||||||
|
"snowy_plains": 12,
|
||||||
|
"ice_spikes": 140,
|
||||||
|
"windswept_hills": 3,
|
||||||
|
"windswept_forest": 34,
|
||||||
|
"windswept_gravelly_hills": 131,
|
||||||
|
"badlands": 37,
|
||||||
|
"wooded_badlands": 38,
|
||||||
|
"eroded_badlands": 165,
|
||||||
|
"dripstone_caves": 174,
|
||||||
|
"lush_caves": 175,
|
||||||
|
"nether_wastes": 8,
|
||||||
|
"crimson_forest": 171,
|
||||||
|
"warped_forest": 172,
|
||||||
|
"soul_sand_valley": 170,
|
||||||
|
"basalt_deltas": 173,
|
||||||
|
"the_end": 9,
|
||||||
|
"small_end_islands": 40,
|
||||||
|
"end_midlands": 41,
|
||||||
|
"end_highlands": 42,
|
||||||
|
"end_barrens": 43,
|
||||||
|
"the_void": 127,
|
||||||
|
"meadow": 177,
|
||||||
|
"grove": 178,
|
||||||
|
"snowy_slopes": 179,
|
||||||
|
"frozen_peaks": 180,
|
||||||
|
"jagged_peaks": 181,
|
||||||
|
"stony_peaks": 182,
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChunkFromSave(c *save.Chunk, secs int) *Chunk {
|
||||||
|
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 {
|
||||||
|
// TODO: Consider the properties of block, not only index the block name
|
||||||
|
stateRawPalette[i] = int(stateIDs[strings.TrimPrefix(v.Name, "minecraft:")])
|
||||||
|
if v.Name != "minecraft:air" {
|
||||||
|
blockCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
biomesData := *(*[]uint64)((unsafe.Pointer)(&v.BlockStates.Data))
|
||||||
|
biomesPalette := v.Biomes.Palette
|
||||||
|
biomesRawPalette := make([]int, len(biomesPalette))
|
||||||
|
for i, v := range biomesPalette {
|
||||||
|
biomesRawPalette[i] = biomesIDs[strings.TrimPrefix(v, "minecraft:")]
|
||||||
|
}
|
||||||
|
|
||||||
|
i := int32(int8(v.Y)) - c.YPos
|
||||||
|
sections[i].blockCount = blockCount
|
||||||
|
sections[i].States = NewStatesPaletteContainerWithData(16*16*16, stateData, stateRawPalette)
|
||||||
|
sections[i].Biomes = NewBiomesPaletteContainerWithData(16*16*16*2, 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
motionBlocking := *(*[]uint64)(unsafe.Pointer(&c.Heightmaps.MotionBlocking))
|
||||||
|
worldSurface := *(*[]uint64)(unsafe.Pointer(&c.Heightmaps.WorldSurface))
|
||||||
|
return &Chunk{
|
||||||
|
Sections: sections,
|
||||||
|
HeightMaps: HeightMaps{
|
||||||
|
MotionBlocking: NewBitStorage(bits.Len(uint(secs)), 16*16, motionBlocking),
|
||||||
|
WorldSurface: NewBitStorage(bits.Len(uint(secs)), 16*16, worldSurface),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This map should be moved to data/block.
|
||||||
|
var stateIDs = make(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"`
|
||||||
|
WorldSurface []uint64 `nbt:"WORLD_SURFACE"`
|
||||||
|
}{
|
||||||
|
MotionBlocking: c.HeightMaps.MotionBlocking.Raw(),
|
||||||
|
WorldSurface: c.HeightMaps.MotionBlocking.Raw(),
|
||||||
|
}),
|
||||||
|
pk.ByteArray(data),
|
||||||
|
pk.VarInt(0), // TODO: Block Entity
|
||||||
|
&lightData{
|
||||||
|
SkyLightMask: make(pk.BitSet, (16*16*16-1)>>6+1),
|
||||||
|
BlockLightMask: make(pk.BitSet, (16*16*16-1)>>6+1),
|
||||||
|
SkyLight: []pk.ByteArray{},
|
||||||
|
BlockLight: []pk.ByteArray{},
|
||||||
|
},
|
||||||
|
}.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 *PaletteContainer
|
||||||
|
Biomes *PaletteContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Section) GetBlock(i int) int {
|
||||||
|
return s.States.Get(i)
|
||||||
|
}
|
||||||
|
func (s *Section) SetBlock(i int, v int) {
|
||||||
|
// TODO: Handle cave air and void air
|
||||||
|
if s.States.Get(i) != 0 {
|
||||||
|
s.blockCount--
|
||||||
|
}
|
||||||
|
if v != 0 {
|
||||||
|
s.blockCount++
|
||||||
|
}
|
||||||
|
s.States.Set(i, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Section) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
return pk.Tuple{
|
||||||
|
pk.Short(s.blockCount),
|
||||||
|
s.States,
|
||||||
|
s.Biomes,
|
||||||
|
}.WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Section) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
return pk.Tuple{
|
||||||
|
pk.Short(s.blockCount),
|
||||||
|
s.States,
|
||||||
|
s.Biomes,
|
||||||
|
}.ReadFrom(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type lightData struct {
|
||||||
|
SkyLightMask pk.BitSet
|
||||||
|
BlockLightMask pk.BitSet
|
||||||
|
SkyLight []pk.ByteArray
|
||||||
|
BlockLight []pk.ByteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitSetRev(set pk.BitSet) pk.BitSet {
|
||||||
|
rev := make(pk.BitSet, len(set))
|
||||||
|
for i := range rev {
|
||||||
|
rev[i] = ^set[i]
|
||||||
|
}
|
||||||
|
return rev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lightData) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
return pk.Tuple{
|
||||||
|
pk.Boolean(true), // Trust Edges
|
||||||
|
l.SkyLightMask,
|
||||||
|
l.BlockLightMask,
|
||||||
|
bitSetRev(l.SkyLightMask),
|
||||||
|
bitSetRev(l.BlockLightMask),
|
||||||
|
pk.Array(l.SkyLight),
|
||||||
|
pk.Array(l.BlockLight),
|
||||||
|
}.WriteTo(w)
|
||||||
|
}
|
@ -12,7 +12,7 @@ type state = int
|
|||||||
|
|
||||||
type PaletteContainer struct {
|
type PaletteContainer struct {
|
||||||
bits int
|
bits int
|
||||||
config func(bits int) palette
|
config paletteCfg
|
||||||
palette palette
|
palette palette
|
||||||
data *BitStorage
|
data *BitStorage
|
||||||
}
|
}
|
||||||
@ -20,34 +20,63 @@ type PaletteContainer struct {
|
|||||||
func NewStatesPaletteContainer(length int, defaultValue state) *PaletteContainer {
|
func NewStatesPaletteContainer(length int, defaultValue state) *PaletteContainer {
|
||||||
return &PaletteContainer{
|
return &PaletteContainer{
|
||||||
bits: 0,
|
bits: 0,
|
||||||
config: createStatesPalette,
|
config: statesCfg{},
|
||||||
palette: &singleValuePalette{v: defaultValue},
|
palette: &singleValuePalette{v: defaultValue},
|
||||||
data: NewBitStorage(0, length, nil),
|
data: NewBitStorage(0, length, nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStatesPaletteContainerWithData(length int, data []uint64, palette []int) *PaletteContainer {
|
func NewStatesPaletteContainerWithData(length int, data []uint64, pat []int) *PaletteContainer {
|
||||||
n := bits.Len(uint(len(palette)))
|
var p palette
|
||||||
return &PaletteContainer{
|
var n int
|
||||||
bits: n,
|
if len(pat) == 1 {
|
||||||
config: createStatesPalette,
|
p = &singleValuePalette{pat[0]}
|
||||||
palette: &linearPalette{
|
n = 0
|
||||||
values: palette,
|
} else {
|
||||||
|
n = statesCfg{}.bits(bits.Len(uint(len(pat))))
|
||||||
|
p = &linearPalette{
|
||||||
|
values: pat,
|
||||||
bits: n,
|
bits: n,
|
||||||
},
|
}
|
||||||
data: NewBitStorage(n, length, data),
|
}
|
||||||
|
return &PaletteContainer{
|
||||||
|
bits: n,
|
||||||
|
config: statesCfg{},
|
||||||
|
palette: p,
|
||||||
|
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,
|
||||||
config: createBiomesPalette,
|
config: biomesCfg{},
|
||||||
palette: &singleValuePalette{v: defaultValue},
|
palette: &singleValuePalette{v: defaultValue},
|
||||||
data: NewBitStorage(0, length, nil),
|
data: NewBitStorage(0, length, nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewBiomesPaletteContainerWithData(length int, data []uint64, pat []int) *PaletteContainer {
|
||||||
|
var p palette
|
||||||
|
var n int
|
||||||
|
if len(pat) == 1 {
|
||||||
|
p = &singleValuePalette{pat[0]}
|
||||||
|
n = 0
|
||||||
|
} else {
|
||||||
|
n = biomesCfg{}.bits(bits.Len(uint(len(pat))))
|
||||||
|
p = &linearPalette{
|
||||||
|
values: pat,
|
||||||
|
bits: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &PaletteContainer{
|
||||||
|
bits: n,
|
||||||
|
config: biomesCfg{},
|
||||||
|
palette: p,
|
||||||
|
data: NewBitStorage(n, length, data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PaletteContainer) Get(i int) state {
|
func (p *PaletteContainer) Get(i int) state {
|
||||||
return p.palette.value(p.data.Get(i))
|
return p.palette.value(p.data.Get(i))
|
||||||
}
|
}
|
||||||
@ -61,7 +90,7 @@ func (p *PaletteContainer) Set(i int, v state) {
|
|||||||
newPalette := PaletteContainer{
|
newPalette := PaletteContainer{
|
||||||
bits: vv,
|
bits: vv,
|
||||||
config: p.config,
|
config: p.config,
|
||||||
palette: p.config(vv),
|
palette: p.config.create(vv),
|
||||||
data: NewBitStorage(vv, oldLen+1, nil),
|
data: NewBitStorage(vv, oldLen+1, nil),
|
||||||
}
|
}
|
||||||
// copy
|
// copy
|
||||||
@ -89,7 +118,7 @@ func (p *PaletteContainer) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.palette = p.config(int(bits))
|
p.palette = p.config.create(int(bits))
|
||||||
|
|
||||||
nn, err := p.palette.ReadFrom(r)
|
nn, err := p.palette.ReadFrom(r)
|
||||||
n += nn
|
n += nn
|
||||||
@ -105,7 +134,27 @@ func (p *PaletteContainer) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStatesPalette(bits int) palette {
|
type paletteCfg interface {
|
||||||
|
bits(int) int
|
||||||
|
create(bits int) palette
|
||||||
|
}
|
||||||
|
|
||||||
|
type statesCfg struct{}
|
||||||
|
|
||||||
|
func (s statesCfg) bits(bits int) int {
|
||||||
|
switch bits {
|
||||||
|
case 0:
|
||||||
|
return 0
|
||||||
|
case 1, 2, 3, 4:
|
||||||
|
return 4
|
||||||
|
case 5, 6, 7, 8:
|
||||||
|
return bits
|
||||||
|
default:
|
||||||
|
return bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statesCfg) create(bits int) palette {
|
||||||
switch bits {
|
switch bits {
|
||||||
case 0:
|
case 0:
|
||||||
return &singleValuePalette{v: -1}
|
return &singleValuePalette{v: -1}
|
||||||
@ -119,7 +168,19 @@ func createStatesPalette(bits int) palette {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBiomesPalette(bits int) palette {
|
type biomesCfg struct{}
|
||||||
|
|
||||||
|
func (b biomesCfg) bits(bits int) int {
|
||||||
|
switch bits {
|
||||||
|
case 0:
|
||||||
|
return 0
|
||||||
|
case 1, 2, 3:
|
||||||
|
return bits
|
||||||
|
default:
|
||||||
|
return bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (b biomesCfg) create(bits int) palette {
|
||||||
switch bits {
|
switch bits {
|
||||||
case 0:
|
case 0:
|
||||||
return &singleValuePalette{v: -1}
|
return &singleValuePalette{v: -1}
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
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/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 Level interface {
|
type Level interface {
|
||||||
@ -26,160 +17,19 @@ type LevelInfo struct {
|
|||||||
HashedSeed uint64
|
HashedSeed uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChunkPos struct{ X, Z int }
|
|
||||||
type Chunk struct {
|
|
||||||
sync.Mutex
|
|
||||||
Sections []Section
|
|
||||||
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
|
|
||||||
Biomes *level.PaletteContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Section) GetBlock(i int) int {
|
|
||||||
return s.States.Get(i)
|
|
||||||
}
|
|
||||||
func (s *Section) SetBlock(i int, v int) {
|
|
||||||
// TODO: Handle cave air and void air
|
|
||||||
if s.States.Get(i) != 0 {
|
|
||||||
s.blockCount--
|
|
||||||
}
|
|
||||||
if v != 0 {
|
|
||||||
s.blockCount++
|
|
||||||
}
|
|
||||||
s.States.Set(i, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Section) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
return pk.Tuple{
|
|
||||||
pk.Short(s.blockCount),
|
|
||||||
s.States,
|
|
||||||
s.Biomes,
|
|
||||||
}.WriteTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Section) ReadFrom(r io.Reader) (int64, error) {
|
|
||||||
return pk.Tuple{
|
|
||||||
pk.Short(s.blockCount),
|
|
||||||
s.States,
|
|
||||||
s.Biomes,
|
|
||||||
}.ReadFrom(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
type lightData struct {
|
|
||||||
SkyLightMask pk.BitSet
|
|
||||||
BlockLightMask pk.BitSet
|
|
||||||
SkyLight []pk.ByteArray
|
|
||||||
BlockLight []pk.ByteArray
|
|
||||||
}
|
|
||||||
|
|
||||||
func bitSetRev(set pk.BitSet) pk.BitSet {
|
|
||||||
rev := make(pk.BitSet, len(set))
|
|
||||||
for i := range rev {
|
|
||||||
rev[i] = ^set[i]
|
|
||||||
}
|
|
||||||
return rev
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lightData) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
return pk.Tuple{
|
|
||||||
pk.Boolean(true), // Trust Edges
|
|
||||||
l.SkyLightMask,
|
|
||||||
l.BlockLightMask,
|
|
||||||
bitSetRev(l.SkyLightMask),
|
|
||||||
bitSetRev(l.BlockLightMask),
|
|
||||||
pk.Array(l.SkyLight),
|
|
||||||
pk.Array(l.BlockLight),
|
|
||||||
}.WriteTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SimpleDim struct {
|
type SimpleDim struct {
|
||||||
numOfSection int
|
numOfSection int
|
||||||
Columns map[ChunkPos]*Chunk
|
Columns map[level.ChunkPos]*level.Chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSimpleDim(secs int) *SimpleDim {
|
func NewSimpleDim(secs int) *SimpleDim {
|
||||||
return &SimpleDim{
|
return &SimpleDim{
|
||||||
numOfSection: secs,
|
numOfSection: secs,
|
||||||
Columns: make(map[ChunkPos]*Chunk),
|
Columns: make(map[level.ChunkPos]*level.Chunk),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleDim) LoadChunk(pos ChunkPos, c *Chunk) {
|
func (s *SimpleDim) LoadChunk(pos level.ChunkPos, c *level.Chunk) {
|
||||||
s.Columns[pos] = c
|
s.Columns[pos] = c
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,12 +47,6 @@ func (s *SimpleDim) PlayerJoin(p *Player) {
|
|||||||
packetid.ClientboundLevelChunkWithLight,
|
packetid.ClientboundLevelChunkWithLight,
|
||||||
pk.Int(pos.X), pk.Int(pos.Z),
|
pk.Int(pos.X), pk.Int(pos.Z),
|
||||||
column,
|
column,
|
||||||
&lightData{
|
|
||||||
SkyLightMask: make(pk.BitSet, (16*16*16-1)>>6+1),
|
|
||||||
BlockLightMask: make(pk.BitSet, (16*16*16-1)>>6+1),
|
|
||||||
SkyLight: []pk.ByteArray{},
|
|
||||||
BlockLight: []pk.ByteArray{},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
column.Unlock()
|
column.Unlock()
|
||||||
|
|
||||||
@ -214,7 +58,7 @@ func (s *SimpleDim) PlayerJoin(p *Player) {
|
|||||||
|
|
||||||
err := p.WritePacket(Packet757(pk.Marshal(
|
err := p.WritePacket(Packet757(pk.Marshal(
|
||||||
packetid.ClientboundPlayerPosition,
|
packetid.ClientboundPlayerPosition,
|
||||||
pk.Double(0), pk.Double(0), pk.Double(0),
|
pk.Double(0), pk.Double(143), pk.Double(0),
|
||||||
pk.Float(0), pk.Float(0),
|
pk.Float(0), pk.Float(0),
|
||||||
pk.Byte(0),
|
pk.Byte(0),
|
||||||
pk.VarInt(0),
|
pk.VarInt(0),
|
||||||
|
Reference in New Issue
Block a user