spatial-db/world/data_format.go

128 lines
2.5 KiB
Go

package world
import (
"fmt"
"math/rand"
)
const (
// Slice size is the total number of blocks in a horizontal slice of a chunk
sliceSize = 16 * 16
)
type BlockPos struct {
X int `json:"x"`
Y uint `json:"y"`
Z int `json:"z"`
}
func RandomBlockPosWithRange(maxRange float64) BlockPos {
return BlockPos{
X: int(rand.NormFloat64() * maxRange),
Y: uint(rand.NormFloat64() * maxRange),
Z: int(rand.NormFloat64() * maxRange),
}
}
func (b BlockPos) ToChunkPos() ChunkPos {
return ChunkPos{
X: b.X / 16,
Z: b.Z / 16,
}
}
type ChunkData struct {
Pos ChunkPos `json:"pos"`
Sections [16]ChunkSection `json:"sections"`
}
func (cd *ChunkData) SectionFor(pos BlockPos) *ChunkSection {
return &cd.Sections[pos.Y%16]
}
type ChunkPos struct {
X int `json:"x"`
Z int `json:"z"`
}
func (cp ChunkPos) ToFileName() string {
return fmt.Sprintf("p.%d.%d.chunk", cp.X, cp.Z)
}
func (cp ChunkPos) StringCoords() string {
return fmt.Sprintf("%d, %d", cp.X, cp.Z)
}
type ChunkSection struct {
// The count of full blocks in the chunk
BlockCount uint `json:"block_count"`
BlockStates [16 * 16 * 16]BlockID `json:"block_states"`
}
func rem_euclid(a, b int) int {
return (a%b + b) % b
}
func (cs *ChunkSection) IndexOfBlock(pos BlockPos) int {
baseX := rem_euclid(pos.X, 16)
baseY := rem_euclid(int(pos.Y), 16)
baseZ := rem_euclid(pos.Z, 16)
return (baseY * sliceSize) + (baseZ * 16) + baseX
}
func (cs *ChunkSection) UpdateBlockAtIndex(index int, targetState BlockID) {
// TODO: Keep track of the block count
cs.BlockStates[index] = targetState
}
func (cs *ChunkSection) UpdateBlock(pos BlockPos, targetState BlockID) {
cs.BlockStates[cs.IndexOfBlock(pos)] = targetState
}
func (cs *ChunkSection) FetchBlock(pos BlockPos) BlockID {
return cs.BlockStates[cs.IndexOfBlock(pos)]
}
type BlockID uint8
const (
Empty BlockID = iota
Generic
)
func (b *BlockID) UnmarshalJSON(data []byte) error {
idName := string(data)
if len(idName) < 2 {
return fmt.Errorf("error decoding blockid, input was too short")
}
switch idName[1 : len(idName)-1] {
case "Empty":
*b = Empty
case "Generic":
*b = Generic
default:
return fmt.Errorf("unknown block id: %s", string(data))
}
return nil
}
func (b BlockID) MarshalJSON() ([]byte, error) {
var encoded []byte
switch b {
case Empty:
encoded = []byte("\"Empty\"")
case Generic:
encoded = []byte("\"Generic\"")
default:
return []byte{}, fmt.Errorf("could not turn block id %d into data", b)
}
return encoded, nil
}