diff --git a/data/biomes.go b/data/biomes.go index 2cae67a..64af1c1 100644 --- a/data/biomes.go +++ b/data/biomes.go @@ -1,5 +1,7 @@ package data +import "errors" + // [ // // { @@ -48,6 +50,8 @@ func LookupBiomeById(version string, id int32) (biome *Biome, ok bool) { return } +var UnknownBiomeNameErr = errors.New("unknown biome Name") + func LookupBiomeByName(version string, name string) (biome *Biome, ok bool) { for _, b := range BiomesForVersion(version) { if b.Name == name { diff --git a/data/blocks.go b/data/blocks.go index b211c9e..f3bae8a 100644 --- a/data/blocks.go +++ b/data/blocks.go @@ -81,6 +81,8 @@ func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool) return } +var UnknownBlockNameErr = errors.New("unknown block Name") + func LookupBlockByName(version string, name string) (block *Block, ok bool) { for _, b := range BlocksForVersion(version) { if b.Name == name { diff --git a/level/chunk_builder.go b/level/chunk_builder.go new file mode 100644 index 0000000..1903f9e --- /dev/null +++ b/level/chunk_builder.go @@ -0,0 +1,93 @@ +package level + +import ( + "bytes" + "errors" + + "github.com/admin-else/strom/data" + "github.com/admin-else/strom/util" +) + +type ChunkBuilder struct { + Biomes, Blocks [][]int32 + Bottom, Top int32 + Version string +} + +func NewChunkBuilder(bottom, top int32, version string) *ChunkBuilder { + return &ChunkBuilder{Bottom: bottom, Top: top, Version: version} +} + +func NewOverworldChunkBuilder(version string) *ChunkBuilder { + return NewChunkBuilder(-64, 320, version) +} + +func (cb *ChunkBuilder) ColumnHeight() int32 { + absBottom := cb.Bottom + if absBottom < 0 { + absBottom *= -1 + } + return absBottom + cb.Top +} + +func (cb *ChunkBuilder) FillAllById(block, biome int32) { + numSections := cb.ColumnHeight() / ChunkWidth + blockSection := util.MakeSingleValuedSlice(block, BlocksPerChunkSection) + biomeSection := util.MakeSingleValuedSlice(biome, BiomesPerChunkSection) + cb.Blocks = make([][]int32, numSections) + cb.Biomes = make([][]int32, numSections) + for i := range cb.Blocks { + cb.Blocks[i] = blockSection + cb.Biomes[i] = biomeSection + } + return +} + +func (cb *ChunkBuilder) FillAll(biome, block string) (err error) { + blockData, ok := data.LookupBlockByName(cb.Version, block) + if !ok { + err = data.UnknownBlockNameErr + return + } + biomeData, ok := data.LookupBiomeByName(cb.Version, biome) + if !ok { + err = data.UnknownBiomeNameErr + return + } + cb.FillAllById(blockData.DefaultState, biomeData.Id) + return +} + +var InvalidChunkPositionErr = errors.New("invalid chunk position") + +func (cb *ChunkBuilder) SetBlockId(x, y, z, id int32) (err error) { + if x < 0 || x >= ChunkWidth || z < 0 || z >= ChunkWidth || y < cb.Bottom || y >= cb.Top { + err = InvalidChunkPositionErr + return + } + + y += cb.Bottom * -1 + section := util.FloorDiv(y, ChunkWidth) + i := (y%ChunkWidth)*ChunkWidth*ChunkWidth + z*ChunkWidth + x + cb.Blocks[section][i] = id + return +} + +func (cb *ChunkBuilder) SetBlock(x, y, z int32, block string) (err error) { + blockData, ok := data.LookupBlockByName(cb.Version, block) + if !ok { + err = data.UnknownBlockNameErr + return + } + return cb.SetBlockId(x, y, z, blockData.DefaultState) +} + +func (cb *ChunkBuilder) Build() (data []byte, err error) { + b := bytes.NewBuffer(nil) + err = PackSections(cb.Blocks, cb.Biomes, cb.Version, b) + if err != nil { + return + } + data = b.Bytes() + return +} diff --git a/level/packet.go b/level/packet.go index 4d3a597..1df53f2 100644 --- a/level/packet.go +++ b/level/packet.go @@ -12,7 +12,8 @@ import ( ) const ( - BlocksPerChunkSection = 16 * 16 * 16 + ChunkWidth = 16 + BlocksPerChunkSection = ChunkWidth * ChunkWidth * ChunkWidth BiomesPerChunkSection = 4 * 4 * 4 ) diff --git a/util/math.go b/util/math.go new file mode 100644 index 0000000..3c8e383 --- /dev/null +++ b/util/math.go @@ -0,0 +1,5 @@ +package util + +func FloorDiv(a, b int32) int32 { + return (a - ((a%b + b) % b)) / b +} diff --git a/util/must.go b/util/must.go index 12e2053..6ecbe3b 100644 --- a/util/must.go +++ b/util/must.go @@ -6,3 +6,16 @@ func MustOk[T any](v T, ok bool) T { } return v } + +func MustT[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func Must(err error) { + if err != nil { + panic(err) + } +}