Botcraft 1.21.5
Loading...
Searching...
No Matches
Chunk.cpp
Go to the documentation of this file.
5
6using namespace ProtocolCraft;
7
8namespace Botcraft
9{
10 enum class Palette
11 {
12#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
14#endif
17 };
18
19#if PROTOCOL_VERSION < 757 /* < 1.18 */
20 Chunk::Chunk(const size_t dim_index, const bool has_sky_light_)
21#else
22 Chunk::Chunk(const int min_y_, const unsigned int height_, const size_t dim_index, const bool has_sky_light_)
23#endif
24 {
25 dimension_index = dim_index;
26 has_sky_light = has_sky_light_;
27#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
28 height = height_;
29 min_y = min_y_;
30#endif
31
32#if PROTOCOL_VERSION < 552 /* < 1.15 */
33 biomes = std::vector<unsigned char>(CHUNK_WIDTH * CHUNK_WIDTH, 0);
34#else
35 // Each section has 64 biomes, one for each 4*4*4 cubes
36 biomes = std::vector<unsigned char>(64 * height / SECTION_HEIGHT, 0);
37#endif
38 sections = std::vector<std::shared_ptr<Section> >(height / SECTION_HEIGHT);
39
40#if USE_GUI
41 modified_since_last_rendered = true;
42#endif
43 }
44
46 {
49 biomes = c.biomes;
50
51#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
52 height = c.height;
53 min_y = c.min_y;
54#endif
55
56 sections = std::vector<std::shared_ptr<Section> >(height / SECTION_HEIGHT);
57 for (int i = 0; i < c.sections.size(); i++)
58 {
59 if (c.sections[i] == nullptr)
60 {
61 sections[i] = nullptr;
62 }
63 else
64 {
65 sections[i] = std::make_shared<Section>(*c.sections[i]);
66 }
67 }
68
71 }
72
74 {
75 return Position(
76 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
77 0,
78 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
79 );
80 }
81
82 int Chunk::GetMinY() const
83 {
84 return min_y;
85 }
86
87 int Chunk::GetHeight() const
88 {
89 return height;
90 }
91
92#if USE_GUI
97
102#endif
103
104#if PROTOCOL_VERSION < 757 /* < 1.18 */
105#if PROTOCOL_VERSION < 552 /* < 1.15 */
106 void Chunk::LoadChunkData(const std::vector<unsigned char>& data, const int primary_bit_mask, const bool ground_up_continuous)
107#elif PROTOCOL_VERSION < 755 /* < 1.17 */
108 void Chunk::LoadChunkData(const std::vector<unsigned char>& data, const int primary_bit_mask)
109#else
110 void Chunk::LoadChunkData(const std::vector<unsigned char>& data, const std::vector<unsigned long long int>& primary_bit_mask)
111#endif
112 {
113 std::vector<unsigned char>::const_iterator iter = data.begin();
114 size_t length = data.size();
115
116 if (data.size() == 0)
117 {
118 LOG_ERROR("Cannot load chunk data without data");
119 return;
120 }
121
122 //The chunck sections
123 for (int sectionY = 0; sectionY < height / SECTION_HEIGHT; ++sectionY)
124 {
125#if PROTOCOL_VERSION < 755 /* < 1.17 */
126 if (!(primary_bit_mask & (1 << sectionY)))
127#else
128 if (!(primary_bit_mask[sectionY / 64] & (1 << (sectionY % 64))))
129#endif
130 {
131 continue;
132 }
133
134#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
135 short block_count = ReadData<short>(iter, length);
136#endif
137 unsigned char bits_per_block = ReadData<unsigned char>(iter, length);
138
139 Palette palette_type = (bits_per_block <= 8 ? Palette::SectionPalette : Palette::GlobalPalette);
140
141#if PROTOCOL_VERSION < 384 /* < 1.13 */
142 int palette_length = ReadData<VarInt>(iter, length);
143#else
144 int palette_length = 0;
145 if (palette_type != Palette::GlobalPalette)
146 {
147 palette_length = ReadData<VarInt>(iter, length);
148 }
149#endif
150 std::vector<int> palette(palette_length);
151 if (palette_type != Palette::GlobalPalette)
152 {
153 if (bits_per_block < 4)
154 {
155 LOG_ERROR("Bits per block must be between 4 and 8 when using a palette (current: " << bits_per_block << "). Stop loading current chunk data");
156 return;
157 }
158
159 for (int i = 0; i < palette_length; ++i)
160 {
161 palette[i] = ReadData<VarInt>(iter, length);
162 }
163 }
164 else
165 {
166 if (palette_length != 0)
167 {
168 LOG_ERROR("Palette length should be 0 for global palette (current: " << palette_length << "). Stop loading current chunk data");
169 return;
170 }
171 }
172
173 //A mask 0...01..1 with bits_per_block ones
174 unsigned int individual_value_mask = static_cast<unsigned int>((1 << bits_per_block) - 1);
175
176 //Data array length
177 int data_array_size = ReadData<VarInt>(iter, length);
178
179 //Data array
180 std::vector<unsigned long long int> data_array(data_array_size);
181 for (int i = 0; i < data_array_size; ++i)
182 {
183 data_array[i] = ReadData<unsigned long long int>(iter, length);
184 }
185
186 //Blocks data
187#if PROTOCOL_VERSION > 712 /* > 1.15.2 */
188 int bit_offset = 0;
189#endif
190 Position pos;
191 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
192 {
193 pos.y = block_y + sectionY * SECTION_HEIGHT + min_y;
194 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
195 {
196 pos.z = block_z;
197 for (int block_x = 0; block_x < CHUNK_WIDTH; ++block_x)
198 {
199 pos.x = block_x;
200#if PROTOCOL_VERSION > 712 /* > 1.15.2 */
201 // From protocol version 713, the compacted array format has been adjusted so that
202 //individual entries no longer span across multiple longs
203 if (64 - (bit_offset % 64) < bits_per_block)
204 {
205 bit_offset += 64 - (bit_offset % 64);
206 }
207 int start_long_index = bit_offset / 64;
208 int end_long_index = start_long_index;
209 int start_offset = bit_offset % 64;
210 bit_offset += bits_per_block;
211#else
212 int block_index = (((block_y * SECTION_HEIGHT) + block_z) * CHUNK_WIDTH) + block_x;
213 int start_long_index = (block_index * bits_per_block) / 64;
214 int start_offset = (block_index * bits_per_block) % 64;
215 int end_long_index = ((block_index + 1) * bits_per_block - 1) / 64;
216#endif
217 unsigned int raw_id;
218 if (start_long_index == end_long_index)
219 {
220 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset);
221 }
222 else
223 {
224 int end_offset = 64 - start_offset;
225 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset | data_array[end_long_index] << end_offset);
226 }
227 raw_id &= individual_value_mask;
228
229 if (palette_type != Palette::GlobalPalette)
230 {
231 raw_id = palette[raw_id];
232 }
233
234#if PROTOCOL_VERSION < 347 /* < 1.13 */
235 int id;
236 unsigned char metadata;
237
238 Blockstate::IdToIdMetadata(raw_id, id, metadata);
239
240 SetBlock(pos, { id, metadata });
241#else
242 SetBlock(pos, raw_id);
243#endif
244 }
245 }
246 }
247
248#if PROTOCOL_VERSION <= 404 /* <= 1.13.2 */
249 //Block light
250 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
251 {
252 pos.y = block_y + sectionY * SECTION_HEIGHT + min_y;
253 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
254 {
255 pos.z = block_z;
256 for (int block_x = 0; block_x < CHUNK_WIDTH; block_x += 2)
257 {
258 pos.x = block_x;
259 unsigned char two_light_values = ReadData<unsigned char>(iter, length);
260
261 SetBlockLight(pos, two_light_values & 0x0F);
262 pos.x = block_x + 1;
263 SetBlockLight(pos, (two_light_values >> 4) & 0x0F);
264 }
265 }
266 }
267
268 //Sky light
269 if (has_sky_light)
270 {
271 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
272 {
273 pos.y = block_y + sectionY * SECTION_HEIGHT + min_y;
274 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
275 {
276 pos.z = block_z;
277 for (int block_x = 0; block_x < CHUNK_WIDTH; block_x += 2)
278 {
279 pos.x = block_x;
280 unsigned char two_light_values = ReadData<unsigned char>(iter, length);
281
282 SetSkyLight(pos, two_light_values & 0x0F);
283 pos.x = block_x + 1;
284 SetSkyLight(pos, (two_light_values >> 4) & 0x0F);
285 }
286 }
287 }
288 }
289#endif
290 }
291
292#if PROTOCOL_VERSION < 552 /* < 1.15 */
293 //The biomes
294 if (ground_up_continuous)
295 {
296 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
297 {
298 for (int block_x = 0; block_x < CHUNK_WIDTH; ++block_x)
299 {
300#if PROTOCOL_VERSION < 358 /* < 1.13 */
301 SetBiome(block_x, block_z, ReadData<unsigned char>(iter, length));
302#else
303 SetBiome(block_x, block_z, ReadData<int>(iter, length));
304#endif
305 }
306 }
307 }
308#endif
309#if USE_GUI
310 modified_since_last_rendered = true;
311#endif
312 }
313#else
314 void Chunk::LoadChunkData(const std::vector<unsigned char>& data)
315 {
316 std::vector<unsigned char>::const_iterator iter = data.begin();
317 size_t length = data.size();
318
319 if (data.size() == 0)
320 {
321 LOG_WARNING("Cannot load chunk data without data");
322 return;
323 }
324
325 //The chunck sections
326 for (int sectionY = 0; sectionY < height / SECTION_HEIGHT; ++sectionY)
327 {
328 short block_count = ReadData<short>(iter, length);
329
330 // Paletted block states
331 unsigned char bits_per_block = ReadData<unsigned char>(iter, length);
332
333 Palette palette_type = (bits_per_block == 0 ? Palette::SingleValue : (bits_per_block <= 8 ? Palette::SectionPalette : Palette::GlobalPalette));
334
335 int palette_length = 0;
336 int palette_value = 0;
337 std::vector<int> palette;
338 switch (palette_type)
339 {
341 palette_value = ReadData<VarInt>(iter, length);
342 break;
344 if (bits_per_block < 4)
345 {
346 bits_per_block = 4;
347 }
348 palette_length = ReadData<VarInt>(iter, length);
349 palette = std::vector<int>(palette_length);
350
351 for (int i = 0; i < palette_length; ++i)
352 {
353 palette[i] = ReadData<VarInt>(iter, length);
354 }
355 break;
357 break;
358 default:
359 break;
360 }
361
362 //A mask 0...01..1 with bits_per_block ones
363 unsigned int individual_value_mask = static_cast<unsigned int>((1 << bits_per_block) - 1);
364
365 //Data array length
366#if PROTOCOL_VERSION < 770 /* < 1.21.5 */
367 const int data_array_size = ReadData<VarInt>(iter, length);
368#else
369 const int data_array_size = bits_per_block == 0 ? 0 :
370 ((SECTION_HEIGHT * CHUNK_WIDTH * CHUNK_WIDTH) / (64 / bits_per_block) +
371 static_cast<int>((SECTION_HEIGHT * CHUNK_WIDTH * CHUNK_WIDTH) % (64 / bits_per_block) != 0));
372#endif
373 //Data array
374 std::vector<unsigned long long int> data_array(data_array_size);
375 for (int i = 0; i < data_array_size; ++i)
376 {
377 data_array[i] = ReadData<unsigned long long int>(iter, length);
378 }
379
380 //Blocks data
381 int bit_offset = 0;
382 Position pos;
383 if (block_count != 0)
384 {
385 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
386 {
387 pos.y = block_y + sectionY * SECTION_HEIGHT + min_y;
388 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
389 {
390 pos.z = block_z;
391 for (int block_x = 0; block_x < CHUNK_WIDTH; ++block_x)
392 {
393 pos.x = block_x;
394 if (palette_type == Palette::SingleValue)
395 {
396 SetBlock(pos, palette_value);
397 continue;
398 }
399
400 // Entries don't span across multiple longs
401 if (64 - (bit_offset % 64) < bits_per_block)
402 {
403 bit_offset += 64 - (bit_offset % 64);
404 }
405 int start_long_index = bit_offset / 64;
406 int end_long_index = start_long_index;
407 int start_offset = bit_offset % 64;
408 bit_offset += bits_per_block;
409
410 unsigned int raw_id;
411 if (start_long_index == end_long_index)
412 {
413 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset);
414 }
415 else
416 {
417 int end_offset = 64 - start_offset;
418 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset | data_array[end_long_index] << end_offset);
419 }
420 raw_id &= individual_value_mask;
421
422 if (palette_type == Palette::SectionPalette)
423 {
424 raw_id = palette[raw_id];
425 }
426
427 SetBlock(pos, raw_id);
428 }
429 }
430 }
431 }
432 else
433 {
434 sections[sectionY] = nullptr;
435 }
436
437 LoadSectionBiomeData(sectionY, iter, length);
438 }
439#if USE_GUI
441#endif
442 }
443#endif
444
445#if PROTOCOL_VERSION < 757 /* < 1.18 */
446 void Chunk::LoadChunkBlockEntitiesData(const std::vector<NBT::Value>& block_entities)
447#else
448 void Chunk::LoadChunkBlockEntitiesData(const std::vector<BlockEntityInfo>& block_entities)
449#endif
450 {
451 // Block entities data
452 block_entities_data.clear();
453
454 for (int i = 0; i < block_entities.size(); ++i)
455 {
456#if PROTOCOL_VERSION < 757 /* < 1.18 */
457 if (block_entities[i].HasData())
458 {
459 if (block_entities[i].contains("x") &&
460 block_entities[i]["x"].is<int>() &&
461 block_entities[i].contains("y") &&
462 block_entities[i]["y"].is<int>() &&
463 block_entities[i].contains("z") &&
464 block_entities[i]["z"].is<int>())
465 {
466 block_entities_data[Position((block_entities[i]["x"].get<int>() % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, block_entities[i]["y"].get<int>(), (block_entities[i]["z"].get<int>() % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH)] = block_entities[i];
467 }
468 }
469#else
470 const int x = (block_entities[i].GetPackedXZ() >> 4) & 15;
471 const int z = (block_entities[i].GetPackedXZ() & 15);
472 // And what about the type ???
473 block_entities_data[Position((x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, block_entities[i].GetY(), (z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH)] = block_entities[i].GetTag();
474#endif
475 }
476
477#if USE_GUI
478 modified_since_last_rendered = true;
479#endif
480 }
481
483 {
484 if (!IsInsideChunk(pos, true))
485 {
486 return;
487 }
488
489 block_entities_data[pos] = block_entity;
490
491#if USE_GUI
493#endif
494 }
495
497 {
498 block_entities_data.erase(pos);
499 }
500
502 {
503 auto it = block_entities_data.find(pos);
504 if (it == block_entities_data.end())
505 {
506 return NBT::Value();
507 }
508
509 return it->second;
510 }
511
512 const Blockstate* Chunk::GetBlock(const Position& pos) const
513 {
514 if (!IsInsideChunk(pos, false))
515 {
516 return nullptr;
517 }
518
519 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
520 if (sections[section_y] == nullptr)
521 {
522 return nullptr;
523 }
524
525#if PROTOCOL_VERSION < 347 /* < 1.13 */
526 BlockstateId block_id;
527 const unsigned short stored_id = *(sections[section_y]->data_blocks.data() + Section::CoordsToBlockIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z));
528 Blockstate::IdToIdMetadata(static_cast<unsigned int>(stored_id), block_id.first, block_id.second);
529#else
530 const BlockstateId block_id = static_cast<BlockstateId>(*(sections[section_y]->data_blocks.data() + Section::CoordsToBlockIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)));
531#endif
532 return AssetsManager::getInstance().GetBlockstate(block_id);
533 }
534
535 void Chunk::SetBlock(const Position& pos, const Blockstate* block)
536 {
537 if (block == nullptr)
538 {
539#if PROTOCOL_VERSION < 347 /* < 1.13 */
540 SetBlock(pos, { -1, 0 });
541#else
542 SetBlock(pos, -1);
543#endif
544 }
545 else
546 {
547 SetBlock(pos, block->GetId());
548 }
549 }
550
551 void Chunk::SetBlock(const Position& pos, const BlockstateId id)
552 {
553 if (!IsInsideChunk(pos, false))
554 {
555 return;
556 }
557
558 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
559 if (!sections[section_y])
560 {
561#if PROTOCOL_VERSION < 347 /* < 1.13 */
562 if (id.first == 0)
563#else
564 if (id == 0)
565#endif
566 {
567 return;
568 }
569 else
570 {
571 AddSection(section_y);
572 }
573 }
574
575#if PROTOCOL_VERSION < 347 /* < 1.13 */
576 const unsigned short block_id = static_cast<unsigned short>(Blockstate::IdMetadataToId(id.first, id.second));
577#else
578 const unsigned short block_id = static_cast<unsigned short>(id);
579#endif
580 sections[section_y]->data_blocks[Section::CoordsToBlockIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)] = block_id;
581
582#if USE_GUI
584#endif
585 }
586
587 unsigned char Chunk::GetBlockLight(const Position& pos) const
588 {
589 if (!IsInsideChunk(pos, true))
590 {
591 return 0;
592 }
593
594 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
595 if (!sections[section_y])
596 {
597 return 0;
598 }
599
600 return (sections[section_y]->block_light[Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)] >> (4 * (pos.x % 2))) & 0x0F;
601 }
602
603 void Chunk::SetBlockLight(const Position& pos, const unsigned char v)
604 {
605 if (!IsInsideChunk(pos, true))
606 {
607 return;
608 }
609
610 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
611 if (!sections[section_y])
612 {
613 AddSection(section_y);
614 }
615
616 unsigned char* packed_value = sections[section_y]->block_light.data() + Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z);
617 if (pos.x % 2 == 1)
618 {
619 const unsigned char first_value = *packed_value & 0x0F;
620 *packed_value = first_value | ((v & 0x0F) << 4);
621 }
622 else
623 {
624 const unsigned char second_value = *packed_value & 0xF0;
625 *packed_value = second_value | (v & 0x0F);
626 }
627 // Not necessary as we don't render lights
628//#if USE_GUI
629// modified_since_last_rendered = true;
630//#endif
631 }
632
633 unsigned char Chunk::GetSkyLight(const Position& pos) const
634 {
635 if (!has_sky_light || !IsInsideChunk(pos, true))
636 {
637 return 0;
638 }
639
640 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
641 if (!sections[section_y])
642 {
643 return 0;
644 }
645
646 return (sections[section_y]->sky_light[Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)] >> (4 * (pos.x % 2))) & 0x0F;
647 }
648
649 void Chunk::SetSkyLight(const Position& pos, const unsigned char v)
650 {
651 if (!has_sky_light || !IsInsideChunk(pos, true))
652 {
653 return;
654 }
655
656 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
657 if (!sections[section_y])
658 {
659 AddSection(section_y);
660 }
661
662 unsigned char* packed_value = sections[section_y]->sky_light.data() + Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z);
663 if (pos.x % 2 == 1)
664 {
665 const unsigned char first_value = *packed_value & 0x0F;
666 *packed_value = first_value | ((v & 0x0F) << 4);
667 }
668 else
669 {
670 const unsigned char second_value = *packed_value & 0xF0;
671 *packed_value = second_value | (v & 0x0F);
672 }
673
674 // Not necessary as we don't render lights
675//#if USE_GUI
676// modified_since_last_rendered = true;
677//#endif
678 }
679
681 {
682 return dimension_index;
683 }
684
686 {
687 return has_sky_light;
688 }
689
690 bool Chunk::HasSection(const int y) const
691 {
692 return sections[y] != nullptr;
693 }
694
695 void Chunk::AddSection(const int y)
696 {
697 sections[y] = std::make_unique<Section>(has_sky_light);
698 }
699
700#if PROTOCOL_VERSION < 552 /* < 1.15 */
701 const Biome* Chunk::GetBiome(const int x, const int z) const
702 {
703 if (x < 0 || x > CHUNK_WIDTH - 1 || z < 0 || z > CHUNK_WIDTH - 1)
704 {
705 return nullptr;
706 }
707
709 }
710
711 void Chunk::SetBiome(const int x, const int z, const int b)
712 {
713 if (x < 0 || x > CHUNK_WIDTH - 1 || z < 0 || z > CHUNK_WIDTH - 1)
714 {
715 return;
716 }
717
718 biomes[z * CHUNK_WIDTH + x] = static_cast<unsigned char>(b);
719
720#if USE_GUI
722#endif
723 }
724#else
725 const Biome* Chunk::GetBiome(const int x, const int y, const int z) const
726 {
727 // y / 4 * 16 + z / 4 * 4 + x / 4
728 return GetBiome((((y - min_y) >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3));
729 }
730
731 const Biome* Chunk::GetBiome(const int i) const
732 {
733 if (i < 0 || i > biomes.size() - 1)
734 {
735 return nullptr;
736 }
737
739 }
740
741 void Chunk::SetBiomes(const std::vector<int>& new_biomes)
742 {
743 if (new_biomes.size() != 64 * height / SECTION_HEIGHT)
744 {
745 LOG_ERROR("Trying to set biomes with a wrong size");
746 return;
747 }
748 biomes = std::vector<unsigned char>(new_biomes.size());
749 for (size_t idx = 0; idx < new_biomes.size(); ++idx)
750 {
751 biomes[idx] = static_cast<unsigned char>(new_biomes[idx]);
752 }
753
754#if USE_GUI
756#endif
757 }
758
759 void Chunk::SetBiome(const int x, const int y, const int z, const int new_biome)
760 {
761 // y / 4 * 16 + z / 4 * 4 + x / 4
762 SetBiome((((y - min_y) >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3), new_biome);
763 }
764
765 void Chunk::SetBiome(const int i, const int new_biome)
766 {
767 if (i < 0 || i > biomes.size() - 1)
768 {
769 return;
770 }
771
772 biomes[i] = static_cast<unsigned char>(new_biome);
773
774#if USE_GUI
776#endif
777 }
778#endif
779
780#if PROTOCOL_VERSION > 761 /* > 1.19.3 */
781 void Botcraft::Chunk::LoadBiomesData(const std::vector<unsigned char>& data)
782 {
783 if (data.size() == 0)
784 {
785 LOG_WARNING("Cannot load chunk biomes data without data");
786 return;
787 }
788
789 std::vector<unsigned char>::const_iterator iter = data.begin();
790 size_t length = data.size();
791 for (int section_y = 0; section_y < height / SECTION_HEIGHT; ++section_y)
792 {
793 LoadSectionBiomeData(section_y, iter, length);
794 }
795 }
796#endif
797
798 void Chunk::UpdateNeighbour(Chunk* const neighbour, const Orientation direction)
799 {
800#if USE_GUI
801 Position this_dest_position = Position(0, 0, 0);
802 Position this_src_position = Position(0, 0, 0);
803 Position neighbour_dest_position = Position(0, 0, 0);
804 Position neighbour_src_position = Position(0, 0, 0);
805 switch (direction)
806 {
807 // The neighbour is on "the left" on the x axis
809 this_dest_position.x = -1;
810 this_src_position.x = 0;
811 neighbour_src_position.x = CHUNK_WIDTH - 1;
812 neighbour_dest_position.x = CHUNK_WIDTH;
813
814 for (int y = min_y; y < height + min_y; ++y)
815 {
816 this_dest_position.y = y;
817 this_src_position.y = y;
818 neighbour_src_position.y = y;
819 neighbour_dest_position.y = y;
820 for (int z = 0; z < CHUNK_WIDTH; ++z)
821 {
822 this_dest_position.z = z;
823 this_src_position.z = z;
824 neighbour_src_position.z = z;
825 neighbour_dest_position.z = z;
826 // If the neighbour chunk is not loaded, just add air instead
827 if (neighbour == nullptr)
828 {
829 SetBlock(this_dest_position, nullptr);
830 }
831 // Otherwise, get the blocks and set this chunk's blocks
832 // on the neighbour
833 else
834 {
835 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
836 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
837 }
838 }
839 }
840 break;
841 // The neighbour is on "the right" on the x axis
843 this_dest_position.x = CHUNK_WIDTH;
844 this_src_position.x = CHUNK_WIDTH - 1;
845 neighbour_src_position.x = 0;
846 neighbour_dest_position.x = -1;
847
848 for (int y = min_y; y < height + min_y; ++y)
849 {
850 this_dest_position.y = y;
851 this_src_position.y = y;
852 neighbour_src_position.y = y;
853 neighbour_dest_position.y = y;
854 for (int z = 0; z < CHUNK_WIDTH; ++z)
855 {
856 this_dest_position.z = z;
857 this_src_position.z = z;
858 neighbour_src_position.z = z;
859 neighbour_dest_position.z = z;
860 // If the neighbour chunk is not loaded, just add air instead
861 if (neighbour == nullptr)
862 {
863 SetBlock(this_dest_position, nullptr);
864 }
865 // Otherwise, get the blocks and set this chunk's blocks
866 // on the neighbour
867 else
868 {
869 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
870 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
871 }
872 }
873 }
874 break;
875 // The neighbour is on "the left" on the z axis
877 this_dest_position.z = -1;
878 this_src_position.z = 0;
879 neighbour_src_position.z = CHUNK_WIDTH - 1;
880 neighbour_dest_position.z = CHUNK_WIDTH;
881
882 for (int y = min_y; y < height + min_y; ++y)
883 {
884 this_dest_position.y = y;
885 this_src_position.y = y;
886 neighbour_src_position.y = y;
887 neighbour_dest_position.y = y;
888 for (int x = 0; x < CHUNK_WIDTH; ++x)
889 {
890 this_dest_position.x = x;
891 this_src_position.x = x;
892 neighbour_src_position.x = x;
893 neighbour_dest_position.x = x;
894 // If the neighbour chunk is not loaded, just add air instead
895 if (neighbour == nullptr)
896 {
897 SetBlock(this_dest_position, nullptr);
898 }
899 // Otherwise, get the blocks and set this chunk's blocks
900 // on the neighbour
901 else
902 {
903 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
904 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
905 }
906 }
907 }
908 break;
909 // The neighbour is on "the right" on the z axis
911 this_dest_position.z = CHUNK_WIDTH;
912 this_src_position.z = CHUNK_WIDTH - 1;
913 neighbour_src_position.z = 0;
914 neighbour_dest_position.z = -1;
915
916 for (int y = min_y; y < height + min_y; ++y)
917 {
918 this_dest_position.y = y;
919 this_src_position.y = y;
920 neighbour_src_position.y = y;
921 neighbour_dest_position.y = y;
922 for (int x = 0; x < CHUNK_WIDTH; ++x)
923 {
924 this_dest_position.x = x;
925 this_src_position.x = x;
926 neighbour_src_position.x = x;
927 neighbour_dest_position.x = x;
928 // If the neighbour chunk is not loaded, just add air instead
929 if (neighbour == nullptr)
930 {
931 SetBlock(this_dest_position, nullptr);
932 }
933 // Otherwise, get the blocks and set this chunk's blocks
934 // on the neighbour
935 else
936 {
937 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
938 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
939 }
940 }
941 }
942 break;
943 default:
944 break;
945 }
946#endif
947 }
948
949 void Chunk::AddLoader(const std::thread::id& thread_id)
950 {
951 loaded_from.insert(thread_id);
952 }
953
954 size_t Chunk::RemoveLoader(const std::thread::id& thread_id)
955 {
956 loaded_from.erase(thread_id);
957 return loaded_from.size();
958 }
959
960 bool Chunk::IsInsideChunk(const Position& pos, const bool ignore_gui_borders) const
961 {
962 if (ignore_gui_borders)
963 {
964 return pos.x >= 0 && pos.x < CHUNK_WIDTH &&
965 pos.y >= min_y && pos.y < height + min_y &&
966 pos.z >= 0 && pos.z < CHUNK_WIDTH;
967 }
968#if USE_GUI
969 return pos.x >= -1 && pos.x <= CHUNK_WIDTH &&
970 pos.y >= min_y && pos.y < height + min_y &&
971 pos.z >= -1 && pos.z <= CHUNK_WIDTH;
972#else
973 return pos.x >= 0 && pos.x < CHUNK_WIDTH &&
974 pos.y >= min_y && pos.y < height + min_y &&
975 pos.z >= 0 && pos.z < CHUNK_WIDTH;
976#endif
977 }
978
979#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
980 void Botcraft::Chunk::LoadSectionBiomeData(const int section_y, ProtocolCraft::ReadIterator& iter, size_t& length)
981 {
982 // Paletted biomes
983 unsigned char bits_per_biome = ReadData<unsigned char>(iter, length);
984
985 Palette palette_type = (bits_per_biome == 0 ? Palette::SingleValue : (bits_per_biome <= 3 ? Palette::SectionPalette : Palette::GlobalPalette));
986
987 int palette_value = 0;
988 int palette_length = 0;
989 std::vector<int> palette;
990 switch (palette_type)
991 {
993 palette_value = ReadData<VarInt>(iter, length);
994 break;
996 palette_length = ReadData<VarInt>(iter, length);
997 palette = std::vector<int>(palette_length);
998 for (int i = 0; i < palette_length; ++i)
999 {
1000 palette[i] = ReadData<VarInt>(iter, length);
1001 }
1002 break;
1004 break;
1005 default:
1006 break;
1007 }
1008
1009 //A mask 0...01..1 with bits_per_biome ones
1010 unsigned int individual_value_mask = static_cast<unsigned int>((1 << bits_per_biome) - 1);
1011
1012 //Data array length
1013#if PROTOCOL_VERSION < 770 /* < 1.21.5 */
1014 const int data_array_size = ReadData<VarInt>(iter, length);
1015#else
1016 const int data_array_size = bits_per_biome == 0 ? 0 :
1017 ((SECTION_HEIGHT / 4 * CHUNK_WIDTH / 4 * CHUNK_WIDTH / 4) / (64 / bits_per_biome) +
1018 static_cast<int>((SECTION_HEIGHT / 4 * CHUNK_WIDTH / 4 * CHUNK_WIDTH / 4) % (64 / bits_per_biome) != 0));
1019#endif
1020
1021 //Data array
1022 std::vector<unsigned long long int> data_array = std::vector<unsigned long long int>(data_array_size);
1023 for (int i = 0; i < data_array_size; ++i)
1024 {
1025 data_array[i] = ReadData<unsigned long long int>(iter, length);
1026 }
1027
1028 //Biomes data
1029 int bit_offset = 0;
1030 for (int block_y = 0; block_y < SECTION_HEIGHT / 4; ++block_y)
1031 {
1032 for (int block_z = 0; block_z < CHUNK_WIDTH / 4; ++block_z)
1033 {
1034 for (int block_x = 0; block_x < CHUNK_WIDTH / 4; ++block_x)
1035 {
1036 const int biome_index = section_y * 64 + block_y * 16 + block_z * 4 + block_x;
1037 if (palette_type == Palette::SingleValue)
1038 {
1039 SetBiome(biome_index, palette_value);
1040 continue;
1041 }
1042
1043 // Entries don't span across multiple longs
1044 if (64 - (bit_offset % 64) < bits_per_biome)
1045 {
1046 bit_offset += 64 - (bit_offset % 64);
1047 }
1048 int start_long_index = bit_offset / 64;
1049 int end_long_index = start_long_index;
1050 int start_offset = bit_offset % 64;
1051 bit_offset += bits_per_biome;
1052
1053 unsigned int raw_id;
1054 if (start_long_index == end_long_index)
1055 {
1056 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset);
1057 }
1058 else
1059 {
1060 int end_offset = 64 - start_offset;
1061 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset | data_array[end_long_index] << end_offset);
1062 }
1063 raw_id &= individual_value_mask;
1064
1065 if (palette_type == Palette::SectionPalette)
1066 {
1067 raw_id = palette[raw_id];
1068 }
1069
1070 SetBiome(biome_index, raw_id);
1071 }
1072 }
1073 }
1074 }
1075#endif
1076} //Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
const Biome * GetBiome(const int id) const
static AssetsManager & getInstance()
const Blockstate * GetBlockstate(const BlockstateId id) const
BlockstateId GetId() const
void AddSection(const int y)
Definition Chunk.cpp:695
size_t dimension_index
Definition Chunk.hpp:113
unsigned char GetSkyLight(const Position &pos) const
Definition Chunk.cpp:633
void SetBiomes(const std::vector< int > &new_biomes)
Definition Chunk.cpp:741
void AddLoader(const std::thread::id &thread_id)
Add a thread to the loaders list.
Definition Chunk.cpp:949
const Biome * GetBiome(const int x, const int y, const int z) const
Definition Chunk.cpp:725
std::vector< unsigned char > biomes
Definition Chunk.hpp:109
std::unordered_set< std::thread::id > loaded_from
Definition Chunk.hpp:126
bool IsInsideChunk(const Position &pos, const bool ignore_gui_borders) const
Definition Chunk.cpp:960
void RemoveBlockEntityData(const Position &pos)
Definition Chunk.cpp:496
bool modified_since_last_rendered
Definition Chunk.hpp:124
bool GetHasSkyLight() const
Definition Chunk.cpp:685
bool HasSection(const int y) const
Definition Chunk.cpp:690
ProtocolCraft::NBT::Value GetBlockEntityData(const Position &pos) const
Definition Chunk.cpp:501
size_t RemoveLoader(const std::thread::id &thread_id)
Remove a thread from the loaders list.
Definition Chunk.cpp:954
void LoadBiomesData(const std::vector< unsigned char > &data)
Definition Chunk.cpp:781
Chunk(const int min_y_, const unsigned int height_, const size_t dim_index, const bool has_sky_light_)
Definition Chunk.cpp:22
void LoadSectionBiomeData(const int section_y, ProtocolCraft::ReadIterator &iter, size_t &length)
Definition Chunk.cpp:980
bool GetModifiedSinceLastRender() const
Definition Chunk.cpp:93
void UpdateNeighbour(Chunk *const neighbour, const Orientation direction)
Definition Chunk.cpp:798
std::vector< std::shared_ptr< Section > > sections
Definition Chunk.hpp:108
size_t GetDimensionIndex() const
Definition Chunk.cpp:680
void LoadChunkData(const std::vector< unsigned char > &data)
Definition Chunk.cpp:314
int GetMinY() const
Definition Chunk.cpp:82
std::unordered_map< Position, ProtocolCraft::NBT::Value > block_entities_data
Definition Chunk.hpp:111
int GetHeight() const
Definition Chunk.cpp:87
const Blockstate * GetBlock(const Position &pos) const
Definition Chunk.cpp:512
void SetBlockEntityData(const Position &pos, const ProtocolCraft::NBT::Value &block_entity)
Definition Chunk.cpp:482
void SetBlockLight(const Position &pos, const unsigned char v)
Definition Chunk.cpp:603
void SetModifiedSinceLastRender(const bool b)
Definition Chunk.cpp:98
void SetBiome(const int x, const int y, const int z, const int new_biome)
Definition Chunk.cpp:759
void LoadChunkBlockEntitiesData(const std::vector< ProtocolCraft::BlockEntityInfo > &block_entities)
Definition Chunk.cpp:448
void SetSkyLight(const Position &pos, const unsigned char v)
Definition Chunk.cpp:649
unsigned char GetBlockLight(const Position &pos) const
Definition Chunk.cpp:587
static Position BlockCoordsToChunkCoords(const Position &pos)
Definition Chunk.cpp:73
bool has_sky_light
Definition Chunk.hpp:114
void SetBlock(const Position &pos, const Blockstate *block)
Definition Chunk.cpp:535
static constexpr int SECTION_HEIGHT
Definition Chunk.hpp:22
static constexpr int CHUNK_WIDTH
Definition Chunk.hpp:21
Vector3< int > Position
Definition Vector3.hpp:282
unsigned int BlockstateId
std::vector< unsigned char >::const_iterator ReadIterator
static size_t CoordsToLightIndex(const int x, const int y, const int z)
Definition Section.cpp:32
static size_t CoordsToBlockIndex(const int x, const int y, const int z)
Definition Section.cpp:23