diff --git a/level/biome/list.go b/level/biome/list.go index 33f7b6c..fc80f50 100644 --- a/level/biome/list.go +++ b/level/biome/list.go @@ -1,83 +1,102 @@ package biome -import "math/bits" +import ( + "errors" + "math/bits" +) type Type int +func (t Type) MarshalText() (text []byte, err error) { + if t >= 0 && int(t) < len(biomesNames) { + return []byte(biomesNames[t]), nil + } + return nil, errors.New("invalid type") +} + +func (t *Type) UnmarshalText(text []byte) error { + var ok bool + *t, ok = biomesIDs[string(text)] + if ok { + return nil + } + return errors.New("unknown type") +} + var ( BitsPerBiome int - BiomesIDs map[string]Type - BiomesNames = []string{ - "the_void", - "plains", - "sunflower_plains", - "snowy_plains", - "ice_spikes", - "desert", - "swamp", - "mangrove_swamp", - "forest", - "flower_forest", - "birch_forest", - "dark_forest", - "old_growth_birch_forest", - "old_growth_pine_taiga", - "old_growth_spruce_taiga", - "taiga", - "snowy_taiga", - "savanna", - "savanna_plateau", - "windswept_hills", - "windswept_gravelly_hills", - "windswept_forest", - "windswept_savanna", - "jungle", - "sparse_jungle", - "bamboo_jungle", - "badlands", - "eroded_badlands", - "wooded_badlands", - "meadow", - "grove", - "snowy_slopes", - "frozen_peaks", - "jagged_peaks", - "stony_peaks", - "river", - "frozen_river", - "beach", - "snowy_beach", - "stony_shore", - "warm_ocean", - "lukewarm_ocean", - "deep_lukewarm_ocean", - "ocean", - "deep_ocean", - "cold_ocean", - "deep_cold_ocean", - "frozen_ocean", - "deep_frozen_ocean", - "mushroom_fields", - "dripstone_caves", - "lush_caves", - "deep_dark", - "nether_wastes", - "warped_forest", - "crimson_forest", - "soul_sand_valley", - "basalt_deltas", - "the_end", - "end_highlands", - "end_midlands", - "small_end_islands", - "end_barrens", + biomesIDs map[string]Type + biomesNames = []string{ + "minecraft:the_void", + "minecraft:plains", + "minecraft:sunflower_plains", + "minecraft:snowy_plains", + "minecraft:ice_spikes", + "minecraft:desert", + "minecraft:swamp", + "minecraft:mangrove_swamp", + "minecraft:forest", + "minecraft:flower_forest", + "minecraft:birch_forest", + "minecraft:dark_forest", + "minecraft:old_growth_birch_forest", + "minecraft:old_growth_pine_taiga", + "minecraft:old_growth_spruce_taiga", + "minecraft:taiga", + "minecraft:snowy_taiga", + "minecraft:savanna", + "minecraft:savanna_plateau", + "minecraft:windswept_hills", + "minecraft:windswept_gravelly_hills", + "minecraft:windswept_forest", + "minecraft:windswept_savanna", + "minecraft:jungle", + "minecraft:sparse_jungle", + "minecraft:bamboo_jungle", + "minecraft:badlands", + "minecraft:eroded_badlands", + "minecraft:wooded_badlands", + "minecraft:meadow", + "minecraft:grove", + "minecraft:snowy_slopes", + "minecraft:frozen_peaks", + "minecraft:jagged_peaks", + "minecraft:stony_peaks", + "minecraft:river", + "minecraft:frozen_river", + "minecraft:beach", + "minecraft:snowy_beach", + "minecraft:stony_shore", + "minecraft:warm_ocean", + "minecraft:lukewarm_ocean", + "minecraft:deep_lukewarm_ocean", + "minecraft:ocean", + "minecraft:deep_ocean", + "minecraft:cold_ocean", + "minecraft:deep_cold_ocean", + "minecraft:frozen_ocean", + "minecraft:deep_frozen_ocean", + "minecraft:mushroom_fields", + "minecraft:dripstone_caves", + "minecraft:lush_caves", + "minecraft:deep_dark", + "minecraft:nether_wastes", + "minecraft:warped_forest", + "minecraft:crimson_forest", + "minecraft:soul_sand_valley", + "minecraft:basalt_deltas", + "minecraft:the_end", + "minecraft:end_highlands", + "minecraft:end_midlands", + "minecraft:small_end_islands", + "minecraft:end_barrens", } ) func init() { - BitsPerBiome = bits.Len(uint(len(BiomesNames))) - BiomesIDs = make(map[string]Type, len(BiomesNames)) - for i, v := range BiomesNames { - BiomesIDs[v] = Type(i) + BitsPerBiome = bits.Len(uint(len(biomesNames))) + biomesIDs = make(map[string]Type, len(biomesNames)) + for i, v := range biomesNames { + biomesIDs[v] = Type(i) } } diff --git a/level/bitstorage.go b/level/bitstorage.go index fcdeac3..d9de014 100644 --- a/level/bitstorage.go +++ b/level/bitstorage.go @@ -195,7 +195,7 @@ func (b *BitStorage) WriteTo(w io.Writer) (int64, error) { } // Fix recalculate BitStorage internal values for given bits. -// Typically, you should call this method after ReadFrom is called, internal data is changed. +// Typically, you should call this method after ReadFrom is called, which cause internal data is changed. func (b *BitStorage) Fix(bits int) error { if bits == 0 { b.mask = 0 diff --git a/level/chunk.go b/level/chunk.go index 3d05514..ff0584c 100644 --- a/level/chunk.go +++ b/level/chunk.go @@ -7,9 +7,7 @@ import ( "io" "math/bits" "strconv" - "strings" - "github.com/Tnze/go-mc/level/biome" "github.com/Tnze/go-mc/level/block" "github.com/Tnze/go-mc/nbt" pk "github.com/Tnze/go-mc/net/packet" @@ -59,7 +57,12 @@ func EmptyChunk(secs int) *Chunk { return &Chunk{ Sections: sections, HeightMaps: HeightMaps{ - MotionBlocking: NewBitStorage(bits.Len(uint(secs)*16), 16*16, nil), + WorldSurfaceWG: NewBitStorage(bits.Len(uint(secs)*16+1), 16*16, nil), + WorldSurface: NewBitStorage(bits.Len(uint(secs)*16+1), 16*16, nil), + OceanFloorWG: NewBitStorage(bits.Len(uint(secs)*16+1), 16*16, nil), + OceanFloor: NewBitStorage(bits.Len(uint(secs)*16+1), 16*16, nil), + MotionBlocking: NewBitStorage(bits.Len(uint(secs)*16+1), 16*16, nil), + MotionBlockingNoLeaves: NewBitStorage(bits.Len(uint(secs)*16+1), 16*16, nil), }, Status: StatusEmpty, } @@ -145,13 +148,12 @@ func readStatesPalette(palette []save.BlockState, data []uint64) (paletteData *P return } -func readBiomesPalette(palette []string, data []uint64) (*PaletteContainer[BiomesState], error) { +func readBiomesPalette(palette []save.BiomeState, data []uint64) (*PaletteContainer[BiomesState], error) { biomesRawPalette := make([]BiomesState, len(palette)) - var ok bool for i, v := range palette { - biomesRawPalette[i], ok = biome.BiomesIDs[strings.TrimPrefix(v, "minecraft:")] - if !ok { - return nil, fmt.Errorf("unknown biomes: %s", v) + err := biomesRawPalette[i].UnmarshalText([]byte(v)) + if err != nil { + return nil, err } } return NewBiomesPaletteContainerWithData(4*4*4, data, biomesRawPalette), nil @@ -180,7 +182,10 @@ func ChunkToSave(c *Chunk, dst *save.Chunk) (err error) { if err != nil { return } - biomes.Palette, biomes.Data = writeBiomesPalette(v.Biomes) + biomes.Palette, biomes.Data, err = writeBiomesPalette(v.Biomes) + if err != nil { + return + } s.SkyLight = v.SkyLight s.BlockLight = v.BlockLight } @@ -198,6 +203,7 @@ func ChunkToSave(c *Chunk, dst *save.Chunk) (err error) { func writeStatesPalette(paletteData *PaletteContainer[BlocksState]) (palette []save.BlockState, data []uint64, err error) { rawPalette := paletteData.palette.export() palette = make([]save.BlockState, len(rawPalette)) + var buffer bytes.Buffer for i, v := range rawPalette { b := block.StateList[v] @@ -213,19 +219,27 @@ func writeStatesPalette(paletteData *PaletteContainer[BlocksState]) (palette []s return } } - data = append(data, paletteData.data.Raw()...) + data = make([]uint64, len(paletteData.data.Raw())) + copy(data, paletteData.data.Raw()) return } -func writeBiomesPalette(paletteData *PaletteContainer[BiomesState]) (palette []string, data []uint64) { +func writeBiomesPalette(paletteData *PaletteContainer[BiomesState]) (palette []save.BiomeState, data []uint64, err error) { rawPalette := paletteData.palette.export() - palette = make([]string, len(rawPalette)) - for i, v := range rawPalette { - palette[i] = biome.BiomesNames[v] - } - data = append(data, paletteData.data.Raw()...) + palette = make([]save.BiomeState, len(rawPalette)) + var biomeID []byte + for i, v := range rawPalette { + biomeID, err = v.MarshalText() + if err != nil { + return + } + palette[i] = save.BiomeState(biomeID) + } + + data = make([]uint64, len(paletteData.data.Raw())) + copy(data, paletteData.data.Raw()) return } diff --git a/nbt/rawmsg.go b/nbt/rawmsg.go index 6c512d5..6588227 100644 --- a/nbt/rawmsg.go +++ b/nbt/rawmsg.go @@ -9,8 +9,11 @@ import ( ) // RawMessage stores the raw binary data of NBT. -// This is usable if you want to store an unknown NBT data and parse it later. -// Notice that this struct doesn't store the tag name. To convert RawMessage to valid NBT binary value: +// This is usable if you want to store an unknown NBT data and use it later. +// +// Notice that this struct doesn't store the root tag name. +// +// To convert RawMessage to valid NBT binary value: // Encoder.Encode(RawMessage, Name) = []byte{ Type (1 byte) | n (2 byte) | Name (n byte) | Data}. type RawMessage struct { Type byte diff --git a/save/chunk.go b/save/chunk.go index 95858bc..e4f40aa 100644 --- a/save/chunk.go +++ b/save/chunk.go @@ -34,16 +34,15 @@ type Chunk struct { type Section struct { Y int8 - BlockStates struct { - Palette []BlockState `nbt:"palette"` - Data []uint64 `nbt:"data"` - } `nbt:"block_states"` - Biomes struct { - Palette []string `nbt:"palette"` - Data []uint64 `nbt:"data"` - } `nbt:"biomes"` - SkyLight []byte - BlockLight []byte + BlockStates PaletteContainer[BlockState] `nbt:"block_states"` + Biomes PaletteContainer[BiomeState] `nbt:"biomes"` + SkyLight []byte + BlockLight []byte +} + +type PaletteContainer[T any] struct { + Palette []T `nbt:"palette"` + Data []uint64 `nbt:"data"` } type BlockState struct { @@ -51,6 +50,8 @@ type BlockState struct { Properties nbt.RawMessage } +type BiomeState string + // Load read column data from []byte func (c *Chunk) Load(data []byte) (err error) { var r io.Reader = bytes.NewReader(data[1:]) @@ -70,7 +71,7 @@ func (c *Chunk) Load(data []byte) (err error) { } d := nbt.NewDecoder(r) - //d.DisallowUnknownFields() + // d.DisallowUnknownFields() _, err = d.Decode(c) return } @@ -87,6 +88,8 @@ func (c *Chunk) Data(compressingType byte) ([]byte, error) { w = gzip.NewWriter(&buff) case 2: w = zlib.NewWriter(&buff) + case 3: + w = &buff } err := nbt.NewEncoder(w).Encode(c, "") return buff.Bytes(), err