1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
package mcmap
import (
"errors"
"github.com/kch42/gonbt/nbt"
"time"
)
func calcBlockOffset(x, y, z int) int {
if (x < 0) || (y < 0) || (z < 0) || (x >= ChunkSizeXZ) || (y >= ChunkSizeY) || (z >= ChunkSizeXZ) {
return -1
}
return x | (z << 4) | (y << 8)
}
func offsetToPos(off int) (x, y, z int) {
x = off & 0xf
z = (off >> 4) & 0xf
y = (off >> 8) & 0xff
return
}
// BlockToChunk calculates the chunk (cx, cz) and the block position in this chunk(rbx, rbz) of a block position given global coordinates.
func BlockToChunk(bx, bz int) (cx, cz, rbx, rbz int) {
cx = bx >> 4
cz = bz >> 4
rbx = ((bx % ChunkSizeXZ) + ChunkSizeXZ) % ChunkSizeXZ
rbz = ((bz % ChunkSizeXZ) + ChunkSizeXZ) % ChunkSizeXZ
return
}
// ChunkToBlock calculates the global position of a block, given the chunk position (cx, cz) and the plock position in that chunk (rbx, rbz).
func ChunkToBlock(cx, cz, rbx, rbz int) (bx, bz int) {
bx = cx*ChunkSizeXZ + rbx
bz = cz*ChunkSizeXZ + rbz
return
}
// Chunk represents a 16*16*256 Chunk of the region.
type Chunk struct {
Entities []nbt.TagCompound
x, z int32
lastUpdate int64
populated bool
inhabitedTime int64
ts time.Time
heightMap []int32 // Ordered ZX
modified bool
blocks []Block // Ordered YZX
biomes []Biome // Ordered ZX
deleted bool
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 }
// Coords returns the Chunk's coordinates.
func (c *Chunk) Coords() (X, Z int32) { return c.x, c.z }
// Block gives you a reference to the Block located at x, y, z. If you modify the block data, you need to call the MarkModified() function of the chunk.
//
// x and z must be in [0, 15], y in [0, 255]. Otherwise a nil pointer is returned.
func (c *Chunk) Block(x, y, z int) *Block {
off := calcBlockOffset(x, y, z)
if off < 0 {
return nil
}
return &(c.blocks[off])
}
// Height returns the height at x, z.
//
// x and z must be in [0, 15]. Height will panic, if this is violated!
func (c *Chunk) Height(x, z int) int {
if (x < 0) || (x >= ChunkSizeXZ) || (z < 0) || (z >= ChunkSizeXZ) {
panic(errors.New("x or z parameter was out of range"))
}
return int(c.heightMap[z*ChunkSizeXZ+x])
}
// Iter iterates ofer all blocks of this chunk and calls the function fx with the coords (x,y,z) and a pointer to the block.
func (c *Chunk) Iter(fx func(int, int, int, *Block)) {
for x := 0; x < ChunkSizeXZ; x++ {
for y := 0; y < ChunkSizeY; y++ {
for z := 0; z < ChunkSizeXZ; z++ {
fx(x, y, z, &(c.blocks[calcBlockOffset(x, y, z)]))
}
}
}
}
// Biome gets the Biome at x,z.
func (c *Chunk) Biome(x, z int) Biome { return c.biomes[z*ChunkSizeXZ+x] }
// SetBiome sets the biome at x,z.
func (c *Chunk) SetBiome(x, z int, bio Biome) { c.biomes[z*ChunkSizeXZ+x] = bio }
// MarkUnused marks the chunk as unused. If all chunks of a superchunk are marked as unused, the underlying superchunk will be unloaded and saved (if needed).
//
// You must not use the chunk any longer, after you called this function.
//
// If the chunk was modified, call MarkModified BEFORE.
func (c *Chunk) MarkUnused() error { return c.reg.unloadChunk(int(c.x), int(c.z)) }
// MarkDeleted marks this chunk as deleted. After marking it as unused, it will be deleted and can no longer be used.
func (c *Chunk) MarkDeleted() { c.deleted = true }
// RecalcHeightMap recalculates the internal height map.
//
// You should use this function before marking the chunk as unused, if you modified the chunk
// (unless you know, your changes wouldn't affect the height map).
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-- {
blkid := c.blocks[calcBlockOffset(x, y, z)].ID
if (blkid != BlkAir) && (blkid != BlkGlass) && (blkid != BlkGlassPane) {
c.heightMap[i] = int32(y)
break
}
}
i++
}
}
}
|