summaryrefslogtreecommitdiff
path: root/mcmap
diff options
context:
space:
mode:
Diffstat (limited to 'mcmap')
-rw-r--r--mcmap/chunk.go24
-rw-r--r--mcmap/examples/addchunk/main.go45
-rw-r--r--mcmap/region.go89
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)