1 package minecraft 2 3 import ( 4 "strconv" 5 6 "vimagination.zapto.org/minecraft/nbt" 7 ) 8 9 var ( 10 chunkRequired = []struct { 11 name string 12 nbt.TagID 13 }{ 14 {"HeightMap", nbt.TagIntArray}, 15 {"InhabitedTime", nbt.TagLong}, 16 {"LastUpdate", nbt.TagLong}, 17 {"Sections", nbt.TagList}, 18 {"TerrainPopulated", nbt.TagByte}, 19 {"xPos", nbt.TagInt}, 20 {"zPos", nbt.TagInt}, 21 } 22 chunkOther = []struct { 23 name string 24 tagType nbt.TagID 25 listType nbt.TagID 26 emptyByte bool 27 }{ 28 {"Biomes", nbt.TagByteArray, nbt.TagByte, false}, 29 {"Entities", nbt.TagList, nbt.TagCompound, true}, 30 {"TileEntities", nbt.TagList, nbt.TagCompound, true}, 31 {"TileTicks", nbt.TagList, nbt.TagCompound, false}, 32 } 33 ) 34 35 type chunk struct { 36 sections [16]*section 37 biomes nbt.ByteArray 38 data nbt.Compound 39 heightMap nbt.IntArray 40 tileEntities map[uint16]nbt.Compound 41 tileTicks map[uint16][]nbt.Compound 42 } 43 44 func (c *chunk) GetNBT() nbt.Tag { 45 data := c.data.Copy().(nbt.Compound) 46 sections := make([]nbt.Data, 0, 16) 47 for i := 0; i < 16; i++ { 48 if c.sections[i] != nil { 49 sections = append(sections, c.sections[i].section) 50 } 51 } 52 sectionList := nbt.NewEmptyList(nbt.TagCompound) 53 sectionList.Append(sections...) 54 data.Set(nbt.NewTag("Sections", sectionList)) 55 tileEntities := make(nbt.ListCompound, 0, len(c.tileEntities)) 56 for _, cmp := range c.tileEntities { 57 if cmp != nil { 58 tileEntities = append(tileEntities, cmp) 59 } 60 } 61 data.Set(nbt.NewTag("TileEntities", &tileEntities)) 62 tileTicks := make(nbt.ListCompound, 0, len(c.tileTicks)) 63 for _, cmpa := range c.tileTicks { 64 for _, cmp := range cmpa { 65 tileTicks = append(tileTicks, cmp) 66 } 67 } 68 if len(tileTicks) == 0 { 69 data.Remove("TileTicks") 70 } else { 71 data.Set(nbt.NewTag("TileTicks", &tileTicks)) 72 } 73 return nbt.NewTag("", nbt.Compound{nbt.NewTag("Level", data)}) 74 } 75 76 func newChunk(x, z int32, data nbt.Tag) (*chunk, error) { 77 if data.TagID() == 0 { 78 biomes := make(nbt.ByteArray, 256) 79 for i := 0; i < 256; i++ { 80 biomes[i] = -1 81 } 82 data = nbt.NewTag("", nbt.Compound{ 83 nbt.NewTag("Level", nbt.Compound{ 84 nbt.NewTag("xPos", nbt.Int(x)), 85 nbt.NewTag("zPos", nbt.Int(z)), 86 nbt.NewTag("Biomes", biomes), 87 nbt.NewTag("HeightMap", make(nbt.IntArray, 256)), 88 nbt.NewTag("InhabitedTime", nbt.Long(0)), 89 nbt.NewTag("LastUpdate", nbt.Long(0)), 90 nbt.NewTag("Sections", nbt.NewEmptyList(nbt.TagCompound)), 91 nbt.NewTag("TerrainPopulated", nbt.Byte(1)), 92 }), 93 }) 94 } 95 c := new(chunk) 96 if data.TagID() != nbt.TagCompound { 97 return nil, WrongTypeError{"[Chunk Base]", nbt.TagCompound, data.TagID()} 98 } 99 100 tag := data.Data().(nbt.Compound).Get("Level") 101 102 if tag.TagID() == 0 { 103 return nil, MissingTagError{"[Chunk Base]->Level"} 104 } else if tag.TagID() != nbt.TagCompound { 105 return nil, WrongTypeError{"[Chunk Base]->Level", nbt.TagCompound, tag.TagID()} 106 } 107 108 c.data = tag.Data().(nbt.Compound) 109 110 for _, req := range chunkRequired { 111 if tag := c.data.Get(req.name); tag.TagID() == 0 { 112 return nil, MissingTagError{req.name} 113 } else if tagID := tag.TagID(); tagID != req.TagID { 114 return nil, WrongTypeError{req.name, req.TagID, tagID} 115 } 116 } 117 118 if tX := int32(c.data.Get("xPos").Data().(nbt.Int)); tX != x { 119 return nil, UnexpectedValue{"[Chunk Base]->Level->xPos", strconv.FormatInt(int64(x), 10), strconv.FormatInt(int64(tX), 10)} 120 } 121 if tZ := int32(c.data.Get("zPos").Data().(nbt.Int)); tZ != z { 122 return nil, UnexpectedValue{"[Chunk Base]->Level->zPos", strconv.FormatInt(int64(z), 10), strconv.FormatInt(int64(tZ), 10)} 123 } 124 125 for _, co := range chunkOther { 126 if tag := c.data.Get(co.name); tag.TagID() == 0 { 127 continue 128 } else if tagID := tag.TagID(); tagID != co.tagType { 129 return nil, WrongTypeError{co.name, co.tagType, tagID} 130 } else if tagID == nbt.TagList { 131 list := tag.Data().(nbt.List) 132 if list.TagType() != co.listType { 133 if co.emptyByte && list.Len() == 0 { 134 if tt := list.TagType(); tt == nbt.TagByte || tt == nbt.TagEnd { 135 continue 136 } 137 } 138 return nil, WrongTypeError{co.name, co.listType, list.TagType()} 139 } 140 } 141 } 142 143 if biomes := c.data.Get("Biomes"); biomes.TagID() != 0 { 144 c.biomes = biomes.Data().(nbt.ByteArray) 145 } else { 146 c.biomes = make(nbt.ByteArray, 256) 147 for i := 0; i < 256; i++ { 148 c.biomes[i] = -1 149 } 150 c.data.Set(nbt.NewTag("Biomes", c.biomes)) 151 } 152 c.heightMap = c.data.Get("HeightMap").Data().(nbt.IntArray) 153 c.tileEntities = make(map[uint16]nbt.Compound) 154 if tileEntities := c.data.Get("TileEntities"); tileEntities.TagID() != 0 { 155 if lTileEntities, ok := tileEntities.Data().(*nbt.ListCompound); ok { 156 for _, tag := range *lTileEntities { 157 if tag == nil { 158 return nil, MissingTagError{"TileEntities->Child"} 159 } 160 x, y, z, err := getCoords(tag) 161 if err != nil { 162 return nil, err 163 } 164 c.tileEntities[xyz(x, y, z)] = tag 165 } 166 } 167 } 168 c.data.Remove("TileEntities") 169 c.tileTicks = make(map[uint16][]nbt.Compound) 170 if tileTicks := c.data.Get("TileTicks"); tileTicks.TagID() != 0 { 171 if lTileTicks, ok := tileTicks.Data().(*nbt.ListCompound); ok { 172 for _, tag := range *lTileTicks { 173 if tag == nil { 174 return nil, MissingTagError{"TileTicks->Child"} 175 } 176 x, y, z, err := getCoords(tag) 177 if err != nil { 178 return nil, err 179 } 180 if id := tag.Get("i"); id.TagID() == 0 { 181 return nil, MissingTagError{"TileTicks->Child->i"} 182 } else if j := id.TagID(); j != nbt.TagInt { 183 return nil, WrongTypeError{"TileTicks->Child->i", nbt.TagInt, j} 184 } 185 if t := tag.Get("t"); t.TagID() == 0 { 186 return nil, MissingTagError{"TileTicks->Child->t"} 187 } else if j := t.TagID(); j != nbt.TagInt { 188 return nil, WrongTypeError{"TileTicks->Child->t", nbt.TagInt, j} 189 } 190 if p := tag.Get("p"); p.TagID() == 0 { 191 return nil, MissingTagError{"TileTicks->Child->p"} 192 } else if j := p.TagID(); j != nbt.TagInt { 193 return nil, WrongTypeError{"TileTicks->Child->p", nbt.TagInt, j} 194 } 195 pos := xyz(x, y, z) 196 c.tileTicks[pos] = append(c.tileTicks[pos], tag) 197 } 198 } 199 } 200 c.data.Remove("TileTicks") 201 for _, section := range *(c.data.Get("Sections").Data().(*nbt.ListCompound)) { 202 if yc := section.Get("Y"); yc.TagID() == 0 { 203 return nil, MissingTagError{"Sections->Child->Y"} 204 } else if yc.TagID() != nbt.TagByte { 205 return nil, WrongTypeError{"Sections->Child->Y", nbt.TagByte, yc.TagID()} 206 } else { 207 y := int32(yc.Data().(nbt.Byte)) 208 var err error 209 if c.sections[y], err = loadSection(section); err != nil { 210 return nil, err 211 } 212 } 213 } 214 c.data.Remove("Sections") 215 return c, nil 216 } 217 218 func (c *chunk) GetBlock(x, y, z int32) Block { 219 ys := y >> 4 220 if c.sections[ys] == nil { 221 return Block{} 222 } 223 b := c.sections[ys].GetBlock(x, y, z) 224 pos := xyz(x, y, z) 225 if md, ok := c.tileEntities[pos]; ok && md != nil { 226 b.SetMetadata(md) 227 } 228 if tt, ok := c.tileTicks[pos]; ok && tt != nil { 229 b.ticks = make([]Tick, len(tt)) 230 for n, tick := range tt { 231 b.ticks[n] = Tick{ 232 int32(tick.Get("i").Data().(nbt.Int)), 233 int32(tick.Get("t").Data().(nbt.Int)), 234 int32(tick.Get("p").Data().(nbt.Int)), 235 } 236 } 237 } 238 return b 239 } 240 241 func (c *chunk) SetBlock(x, y, z int32, b Block) { 242 ys := y >> 4 243 if c.sections[ys] == nil { 244 if b.EqualBlock(Block{}) { 245 return 246 } 247 c.sections[ys] = newSection(y) 248 } 249 c.sections[ys].SetBlock(x, y, z, b) 250 if hmpos := x&15<<4 | z&15; b.Opacity() <= 1 { //All transparent blocks block 1 light when they are below the highest non-transparent block 251 if y == c.heightMap[hmpos]-1 { 252 c.heightMap[hmpos] = 0 253 for i := y; i >= 0; i-- { 254 if yt := i >> 4; c.sections[yt] != nil { 255 if c.sections[yt].GetOpacity(x, i, z) > 1 { 256 c.heightMap[hmpos] = i + 1 257 break 258 } 259 } 260 } 261 } 262 } else if y >= c.heightMap[hmpos] { 263 c.heightMap[hmpos] = y + 1 264 } 265 pos := xyz(x, y, z) 266 if b.metadata == nil { 267 delete(c.tileEntities, pos) 268 } else { 269 comp := b.GetMetadata() 270 comp.Set(nbt.NewTag("x", nbt.Int(x))) 271 comp.Set(nbt.NewTag("y", nbt.Int(y))) 272 comp.Set(nbt.NewTag("z", nbt.Int(z))) 273 c.tileEntities[pos] = comp 274 } 275 if b.HasTicks() { 276 ticks := b.GetTicks() 277 c.tileTicks[pos] = make([]nbt.Compound, len(ticks)) 278 for n, tick := range ticks { 279 c.tileTicks[pos][n] = nbt.Compound{ 280 nbt.NewTag("i", nbt.Int(tick.I)), 281 nbt.NewTag("p", nbt.Int(tick.P)), 282 nbt.NewTag("t", nbt.Int(tick.T)), 283 nbt.NewTag("x", nbt.Int(x)), 284 nbt.NewTag("y", nbt.Int(y)), 285 nbt.NewTag("z", nbt.Int(z)), 286 } 287 } 288 } else { 289 delete(c.tileTicks, pos) 290 } 291 } 292 293 func (c *chunk) GetBiome(x, z int32) Biome { 294 return Biome(c.biomes[x&15<<4|z&15]) 295 } 296 297 func (c *chunk) SetBiome(x, z int32, b Biome) { 298 c.biomes[x&15<<4|z&15] = int8(b) 299 } 300 301 func (c *chunk) GetOpacity(x, y, z int32) uint8 { 302 if y >= c.heightMap[x&15<<4|z&15] { 303 return 1 304 } 305 ys := y >> 4 306 if c.sections[ys] == nil { 307 return 1 308 } 309 return c.sections[ys].GetOpacity(x, y, z) 310 } 311 312 func (c *chunk) GetHeight(x, z int32) int32 { 313 return c.heightMap[x&15<<4|z&15] 314 } 315 316 func (c *chunk) GetBlockLight(x, y, z int32) uint8 { 317 ys := y >> 4 318 if ys < 16 && c.sections[ys] == nil { 319 return 0 320 } else if y > 255 { 321 return 15 322 } else if y < 0 { 323 return 0 324 } 325 return c.sections[ys].GetBlockLight(x, y, z) 326 } 327 328 func (c *chunk) SetBlockLight(x, y, z int32, l uint8) { 329 ys := y >> 4 330 if ys < 16 && c.sections[ys] != nil { 331 c.sections[ys].SetBlockLight(x, y, z, l) 332 } 333 } 334 335 func (c *chunk) GetSkyLight(x, y, z int32) uint8 { 336 if ys := y >> 4; ys >= 0 && ys < 16 && c.sections[ys] != nil { 337 return c.sections[ys].GetSkyLight(x, y, z) 338 } else if y >= c.heightMap[x&15<<4|z&15] || y > 255 { 339 return 15 340 } else if y < 0 { 341 return 0 342 } else if ys < 15 && c.sections[ys+1] != nil { 343 sl := c.sections[ys+1].GetSkyLight(x, 0, z) 344 if d := uint8((ys+1)<<4 - y); d < sl { 345 sl -= d 346 } else { 347 sl = 0 348 } 349 return sl 350 } 351 return 0 352 } 353 354 func (c *chunk) SetSkyLight(x, y, z int32, l uint8) { 355 ys := y >> 4 356 if ys < 16 && c.sections[ys] != nil { 357 c.sections[ys].SetSkyLight(x, y, z, l) 358 } 359 } 360 361 func (c *chunk) createSection(y int32) bool { 362 if ys := y >> 4; ys >= 0 && ys < 16 && c.sections[ys] == nil { 363 c.sections[ys] = newSection(y) 364 return true 365 } 366 return false 367 } 368 369 func xyz(x, y, z int32) uint16 { 370 return (uint16(y) << 8) | (uint16(z&15) << 4) | uint16(x&15) 371 } 372 373 func getCoord(name string, data nbt.Compound) (int32, error) { 374 tag := data.Get(name) 375 if tag.TagID() == 0 { 376 return 0, MissingTagError{name} 377 } else if tag.TagID() != nbt.TagInt { 378 return 0, WrongTypeError{name, nbt.TagInt, tag.TagID()} 379 } 380 return int32(tag.Data().(nbt.Int)), nil 381 } 382 383 func getCoords(data nbt.Compound) (x, y, z int32, err error) { 384 if x, err = getCoord("x", data); err != nil { 385 return 386 } else if y, err = getCoord("y", data); err != nil { 387 return 388 } 389 z, err = getCoord("z", data) 390 return 391 } 392