diff options
Diffstat (limited to 'mcmap')
-rw-r--r-- | mcmap/chunk.go | 24 | ||||
-rw-r--r-- | mcmap/examples/addchunk/main.go | 45 | ||||
-rw-r--r-- | mcmap/region.go | 89 |
3 files changed, 144 insertions, 14 deletions
diff --git a/mcmap/chunk.go b/mcmap/chunk.go index 6f6c4db..9196776 100644 --- a/mcmap/chunk.go +++ b/mcmap/chunk.go @@ -57,6 +57,28 @@ type Chunk struct { reg *Region } +func newChunk(reg *Region, x, z int) *Chunk { + biomes := make([]Biome, ChunkRectXZ) + for i := range biomes { + biomes[i] = BioUncalculated + } + + heightMap := make([]int32, ChunkRectXZ) + for i := range heightMap { + heightMap[i] = ChunkSizeY - 1 + } + + return &Chunk{ + x: int32(x), + z: int32(z), + ts: time.Now(), + blocks: make([]Block, ChunkSize), + biomes: biomes, + heightMap: heightMap, + reg: reg, + } +} + // MarkModified needs to be called, if some data of the chunk was modified. func (c *Chunk) MarkModified() { c.modified = true } @@ -118,7 +140,7 @@ func (c *Chunk) RecalcHeightMap() { i := 0 for z := 0; z < ChunkSizeXZ; z++ { for x := 0; x < ChunkSizeXZ; x++ { - for y := ChunkSizeY-1; y >= 0; y-- { + for y := ChunkSizeY - 1; y >= 0; y-- { blkid := c.blocks[calcBlockOffset(x, y, z)].ID if (blkid != BlkAir) && (blkid != BlkGlass) && (blkid != BlkGlassPane) { c.heightMap[i] = int32(y) diff --git a/mcmap/examples/addchunk/main.go b/mcmap/examples/addchunk/main.go new file mode 100644 index 0000000..0ca763f --- /dev/null +++ b/mcmap/examples/addchunk/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "flag" + "fmt" + "github.com/kch42/gomcmap/mcmap" + "os" +) + +func main() { + path := flag.String("path", "", "Path to region directory") + flag.Parse() + + if *path == "" { + flag.Usage() + os.Exit(1) + } + + region, err := mcmap.OpenRegion(*path, false) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not open region: %s\n", err) + os.Exit(1) + } + + chunk, err := region.NewChunk(200, 200) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not create a Chunk at 200,200: %s\n", err) + os.Exit(1) + } + + chunk.Iter(func(x, y, z int, blk *mcmap.Block) { + blk.ID = mcmap.BlkSandstone + }) + + chunk.RecalcHeightMap() + if err := chunk.MarkUnused(); err != nil { + fmt.Fprintf(os.Stderr, "Could not MarkUnused(): %s\n", err) + os.Exit(1) + } + + if err := region.Save(); err != nil { + fmt.Fprintf(os.Stderr, "Could not save region: %s\n", err) + os.Exit(1) + } +} diff --git a/mcmap/region.go b/mcmap/region.go index 8a5fd9f..34b3334 100644 --- a/mcmap/region.go +++ b/mcmap/region.go @@ -11,6 +11,7 @@ import ( var ( NotAvailable = errors.New("Chunk or Superchunk not available") + AlreadyThere = errors.New("Chunk is already there") ) type superchunk struct { @@ -29,6 +30,8 @@ type Region struct { var mcaRegex = regexp.MustCompile(`^r\.([0-9-]+)\.([0-9-]+)\.mca$`) // OpenRegion opens a region directory. If autosave is true, mcmap will save modified and unloaded chunks automatically to reduce memory usage. You still have to call Save at the end. +// +// You can also use OpenRegion to create a new region. Yust make sure the path exists. func OpenRegion(path string, autosave bool) (*Region, error) { rv := &Region{ path: path, @@ -176,11 +179,30 @@ func (reg *Region) cleanSuperchunks(forceSave bool) error { return nil } +func (sc *superchunk) loadChunk(reg *Region, rx, rz int) (*Chunk, error) { + cPos := XZPos{rx, rz} + + if chunk, ok := sc.chunks[cPos]; ok { + return chunk, nil + } + + pc, ok := sc.preChunks[cPos] + if !ok { + return nil, NotAvailable + } + + chunk, err := pc.toChunk(reg) + if err != nil { + return nil, err + } + sc.chunks[cPos] = chunk + return chunk, nil +} + // Chunk returns the chunk at x, z. If no chunk could be found, the error NotAvailable will be returned. Other errors indicate an internal error (I/O error, file format violated, ...) func (reg *Region) Chunk(x, z int) (*Chunk, error) { scx, scz, cx, cz := chunkToSuperchunk(x, z) scPos := XZPos{scx, scz} - cPos := XZPos{cx, cz} sc, ok := reg.superchunks[scPos] if !ok { @@ -190,18 +212,9 @@ func (reg *Region) Chunk(x, z int) (*Chunk, error) { sc = reg.superchunks[scPos] } - chunk, ok := sc.chunks[cPos] - if !ok { - pc, ok := sc.preChunks[cPos] - if !ok { - return nil, NotAvailable - } - - var err error - if chunk, err = pc.toChunk(reg); err != nil { - return nil, err - } - sc.chunks[cPos] = chunk + chunk, err := sc.loadChunk(reg, cx, cz) + if err != nil { + return nil, err } if err := reg.cleanSuperchunks(false); err != nil { @@ -263,6 +276,56 @@ func (reg *Region) AllChunks() <-chan XZPos { return ch } +// NewChunk adds a new, blank chunk. If the Chunk is already there, error AlreadyThere will be returned. +// Other errors indicate internal errors. +func (reg *Region) NewChunk(cx, cz int) (*Chunk, error) { + scx, scz, rx, rz := chunkToSuperchunk(cx, cz) + + scPos := XZPos{scx, scz} + + var sc *superchunk + if reg.superchunksAvail[scPos] { + var ok bool + if sc, ok = reg.superchunks[scPos]; !ok { + if err := reg.loadSuperchunk(scPos); err != nil { + return nil, err + } + sc = reg.superchunks[scPos] + } + } else { + sc = &superchunk{ + chunks: make(map[XZPos]*Chunk), + preChunks: make(map[XZPos]*preChunk), + modified: true, + } + reg.superchunksAvail[scPos] = true + reg.superchunks[scPos] = sc + } + + switch chunk, err := sc.loadChunk(reg, rx, rz); err { + case nil: + chunk.MarkUnused() + return nil, AlreadyThere + case NotAvailable: + default: + return nil, err + } + + cPos := XZPos{rx, rz} + chunk := newChunk(reg, cx, cz) + + pc, err := chunk.toPreChunk() + if err != nil { + return nil, err + } + + sc.preChunks[cPos] = pc + sc.chunks[cPos] = chunk + sc.modified = true + + return chunk, nil +} + // Save saves modified and unused chunks. func (reg *Region) Save() error { return reg.cleanSuperchunks(true) |