Botcraft 1.21.4
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 int data_array_size = ReadData<VarInt>(iter, length);
367
368 //Data array
369 std::vector<unsigned long long int> data_array(data_array_size);
370 for (int i = 0; i < data_array_size; ++i)
371 {
372 data_array[i] = ReadData<unsigned long long int>(iter, length);
373 }
374
375 //Blocks data
376 int bit_offset = 0;
377 Position pos;
378 if (block_count != 0)
379 {
380 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
381 {
382 pos.y = block_y + sectionY * SECTION_HEIGHT + min_y;
383 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
384 {
385 pos.z = block_z;
386 for (int block_x = 0; block_x < CHUNK_WIDTH; ++block_x)
387 {
388 pos.x = block_x;
389 if (palette_type == Palette::SingleValue)
390 {
391 SetBlock(pos, palette_value);
392 continue;
393 }
394
395 // Entries don't span across multiple longs
396 if (64 - (bit_offset % 64) < bits_per_block)
397 {
398 bit_offset += 64 - (bit_offset % 64);
399 }
400 int start_long_index = bit_offset / 64;
401 int end_long_index = start_long_index;
402 int start_offset = bit_offset % 64;
403 bit_offset += bits_per_block;
404
405 unsigned int raw_id;
406 if (start_long_index == end_long_index)
407 {
408 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset);
409 }
410 else
411 {
412 int end_offset = 64 - start_offset;
413 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset | data_array[end_long_index] << end_offset);
414 }
415 raw_id &= individual_value_mask;
416
417 if (palette_type == Palette::SectionPalette)
418 {
419 raw_id = palette[raw_id];
420 }
421
422 SetBlock(pos, raw_id);
423 }
424 }
425 }
426 }
427 else
428 {
429 sections[sectionY] = nullptr;
430 }
431
432 LoadSectionBiomeData(sectionY, iter, length);
433 }
434#if USE_GUI
436#endif
437 }
438#endif
439
440#if PROTOCOL_VERSION < 757 /* < 1.18 */
441 void Chunk::LoadChunkBlockEntitiesData(const std::vector<NBT::Value>& block_entities)
442#else
443 void Chunk::LoadChunkBlockEntitiesData(const std::vector<BlockEntityInfo>& block_entities)
444#endif
445 {
446 // Block entities data
447 block_entities_data.clear();
448
449 for (int i = 0; i < block_entities.size(); ++i)
450 {
451#if PROTOCOL_VERSION < 757 /* < 1.18 */
452 if (block_entities[i].HasData())
453 {
454 if (block_entities[i].contains("x") &&
455 block_entities[i]["x"].is<int>() &&
456 block_entities[i].contains("y") &&
457 block_entities[i]["y"].is<int>() &&
458 block_entities[i].contains("z") &&
459 block_entities[i]["z"].is<int>())
460 {
461 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];
462 }
463 }
464#else
465 const int x = (block_entities[i].GetPackedXZ() >> 4) & 15;
466 const int z = (block_entities[i].GetPackedXZ() & 15);
467 // And what about the type ???
468 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();
469#endif
470 }
471
472#if USE_GUI
473 modified_since_last_rendered = true;
474#endif
475 }
476
478 {
479 if (!IsInsideChunk(pos, true))
480 {
481 return;
482 }
483
484 block_entities_data[pos] = block_entity;
485
486#if USE_GUI
488#endif
489 }
490
492 {
493 block_entities_data.erase(pos);
494 }
495
497 {
498 auto it = block_entities_data.find(pos);
499 if (it == block_entities_data.end())
500 {
501 return NBT::Value();
502 }
503
504 return it->second;
505 }
506
507 const Blockstate* Chunk::GetBlock(const Position& pos) const
508 {
509 if (!IsInsideChunk(pos, false))
510 {
511 return nullptr;
512 }
513
514 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
515 if (sections[section_y] == nullptr)
516 {
517 return nullptr;
518 }
519
520#if PROTOCOL_VERSION < 347 /* < 1.13 */
521 BlockstateId block_id;
522 const unsigned short stored_id = *(sections[section_y]->data_blocks.data() + Section::CoordsToBlockIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z));
523 Blockstate::IdToIdMetadata(static_cast<unsigned int>(stored_id), block_id.first, block_id.second);
524#else
525 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)));
526#endif
527 return AssetsManager::getInstance().GetBlockstate(block_id);
528 }
529
530 void Chunk::SetBlock(const Position& pos, const Blockstate* block)
531 {
532 if (block == nullptr)
533 {
534#if PROTOCOL_VERSION < 347 /* < 1.13 */
535 SetBlock(pos, { -1, 0 });
536#else
537 SetBlock(pos, -1);
538#endif
539 }
540 else
541 {
542 SetBlock(pos, block->GetId());
543 }
544 }
545
546 void Chunk::SetBlock(const Position& pos, const BlockstateId id)
547 {
548 if (!IsInsideChunk(pos, false))
549 {
550 return;
551 }
552
553 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
554 if (!sections[section_y])
555 {
556#if PROTOCOL_VERSION < 347 /* < 1.13 */
557 if (id.first == 0)
558#else
559 if (id == 0)
560#endif
561 {
562 return;
563 }
564 else
565 {
566 AddSection(section_y);
567 }
568 }
569
570#if PROTOCOL_VERSION < 347 /* < 1.13 */
571 const unsigned short block_id = static_cast<unsigned short>(Blockstate::IdMetadataToId(id.first, id.second));
572#else
573 const unsigned short block_id = static_cast<unsigned short>(id);
574#endif
575 sections[section_y]->data_blocks[Section::CoordsToBlockIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)] = block_id;
576
577#if USE_GUI
579#endif
580 }
581
582 unsigned char Chunk::GetBlockLight(const Position& pos) const
583 {
584 if (!IsInsideChunk(pos, true))
585 {
586 return 0;
587 }
588
589 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
590 if (!sections[section_y])
591 {
592 return 0;
593 }
594
595 return (sections[section_y]->block_light[Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)] >> (4 * (pos.x % 2))) & 0x0F;
596 }
597
598 void Chunk::SetBlockLight(const Position& pos, const unsigned char v)
599 {
600 if (!IsInsideChunk(pos, true))
601 {
602 return;
603 }
604
605 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
606 if (!sections[section_y])
607 {
608 AddSection(section_y);
609 }
610
611 unsigned char* packed_value = sections[section_y]->block_light.data() + Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z);
612 if (pos.x % 2 == 1)
613 {
614 const unsigned char first_value = *packed_value & 0x0F;
615 *packed_value = first_value | ((v & 0x0F) << 4);
616 }
617 else
618 {
619 const unsigned char second_value = *packed_value & 0xF0;
620 *packed_value = second_value | (v & 0x0F);
621 }
622 // Not necessary as we don't render lights
623//#if USE_GUI
624// modified_since_last_rendered = true;
625//#endif
626 }
627
628 unsigned char Chunk::GetSkyLight(const Position& pos) const
629 {
630 if (!has_sky_light || !IsInsideChunk(pos, true))
631 {
632 return 0;
633 }
634
635 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
636 if (!sections[section_y])
637 {
638 return 0;
639 }
640
641 return (sections[section_y]->sky_light[Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z)] >> (4 * (pos.x % 2))) & 0x0F;
642 }
643
644 void Chunk::SetSkyLight(const Position& pos, const unsigned char v)
645 {
646 if (!has_sky_light || !IsInsideChunk(pos, true))
647 {
648 return;
649 }
650
651 const int section_y = (pos.y - min_y) / SECTION_HEIGHT;
652 if (!sections[section_y])
653 {
654 AddSection(section_y);
655 }
656
657 unsigned char* packed_value = sections[section_y]->sky_light.data() + Section::CoordsToLightIndex(pos.x, (pos.y - min_y) % SECTION_HEIGHT, pos.z);
658 if (pos.x % 2 == 1)
659 {
660 const unsigned char first_value = *packed_value & 0x0F;
661 *packed_value = first_value | ((v & 0x0F) << 4);
662 }
663 else
664 {
665 const unsigned char second_value = *packed_value & 0xF0;
666 *packed_value = second_value | (v & 0x0F);
667 }
668
669 // Not necessary as we don't render lights
670//#if USE_GUI
671// modified_since_last_rendered = true;
672//#endif
673 }
674
676 {
677 return dimension_index;
678 }
679
681 {
682 return has_sky_light;
683 }
684
685 bool Chunk::HasSection(const int y) const
686 {
687 return sections[y] != nullptr;
688 }
689
690 void Chunk::AddSection(const int y)
691 {
692 sections[y] = std::make_unique<Section>(has_sky_light);
693 }
694
695#if PROTOCOL_VERSION < 552 /* < 1.15 */
696 const Biome* Chunk::GetBiome(const int x, const int z) const
697 {
698 if (x < 0 || x > CHUNK_WIDTH - 1 || z < 0 || z > CHUNK_WIDTH - 1)
699 {
700 return nullptr;
701 }
702
704 }
705
706 void Chunk::SetBiome(const int x, const int z, const int b)
707 {
708 if (x < 0 || x > CHUNK_WIDTH - 1 || z < 0 || z > CHUNK_WIDTH - 1)
709 {
710 return;
711 }
712
713 biomes[z * CHUNK_WIDTH + x] = static_cast<unsigned char>(b);
714
715#if USE_GUI
717#endif
718 }
719#else
720 const Biome* Chunk::GetBiome(const int x, const int y, const int z) const
721 {
722 // y / 4 * 16 + z / 4 * 4 + x / 4
723 return GetBiome((((y - min_y) >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3));
724 }
725
726 const Biome* Chunk::GetBiome(const int i) const
727 {
728 if (i < 0 || i > biomes.size() - 1)
729 {
730 return nullptr;
731 }
732
734 }
735
736 void Chunk::SetBiomes(const std::vector<int>& new_biomes)
737 {
738 if (new_biomes.size() != 64 * height / SECTION_HEIGHT)
739 {
740 LOG_ERROR("Trying to set biomes with a wrong size");
741 return;
742 }
743 biomes = std::vector<unsigned char>(new_biomes.size());
744 for (size_t idx = 0; idx < new_biomes.size(); ++idx)
745 {
746 biomes[idx] = static_cast<unsigned char>(new_biomes[idx]);
747 }
748
749#if USE_GUI
751#endif
752 }
753
754 void Chunk::SetBiome(const int x, const int y, const int z, const int new_biome)
755 {
756 // y / 4 * 16 + z / 4 * 4 + x / 4
757 SetBiome((((y - min_y) >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3), new_biome);
758 }
759
760 void Chunk::SetBiome(const int i, const int new_biome)
761 {
762 if (i < 0 || i > biomes.size() - 1)
763 {
764 return;
765 }
766
767 biomes[i] = static_cast<unsigned char>(new_biome);
768
769#if USE_GUI
771#endif
772 }
773#endif
774
775#if PROTOCOL_VERSION > 761 /* > 1.19.3 */
776 void Botcraft::Chunk::LoadBiomesData(const std::vector<unsigned char>& data)
777 {
778 if (data.size() == 0)
779 {
780 LOG_WARNING("Cannot load chunk biomes data without data");
781 return;
782 }
783
784 std::vector<unsigned char>::const_iterator iter = data.begin();
785 size_t length = data.size();
786 for (int section_y = 0; section_y < height / SECTION_HEIGHT; ++section_y)
787 {
788 LoadSectionBiomeData(section_y, iter, length);
789 }
790 }
791#endif
792
793 void Chunk::UpdateNeighbour(Chunk* const neighbour, const Orientation direction)
794 {
795#if USE_GUI
796 Position this_dest_position = Position(0, 0, 0);
797 Position this_src_position = Position(0, 0, 0);
798 Position neighbour_dest_position = Position(0, 0, 0);
799 Position neighbour_src_position = Position(0, 0, 0);
800 switch (direction)
801 {
802 // The neighbour is on "the left" on the x axis
804 this_dest_position.x = -1;
805 this_src_position.x = 0;
806 neighbour_src_position.x = CHUNK_WIDTH - 1;
807 neighbour_dest_position.x = CHUNK_WIDTH;
808
809 for (int y = min_y; y < height + min_y; ++y)
810 {
811 this_dest_position.y = y;
812 this_src_position.y = y;
813 neighbour_src_position.y = y;
814 neighbour_dest_position.y = y;
815 for (int z = 0; z < CHUNK_WIDTH; ++z)
816 {
817 this_dest_position.z = z;
818 this_src_position.z = z;
819 neighbour_src_position.z = z;
820 neighbour_dest_position.z = z;
821 // If the neighbour chunk is not loaded, just add air instead
822 if (neighbour == nullptr)
823 {
824 SetBlock(this_dest_position, nullptr);
825 }
826 // Otherwise, get the blocks and set this chunk's blocks
827 // on the neighbour
828 else
829 {
830 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
831 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
832 }
833 }
834 }
835 break;
836 // The neighbour is on "the right" on the x axis
838 this_dest_position.x = CHUNK_WIDTH;
839 this_src_position.x = CHUNK_WIDTH - 1;
840 neighbour_src_position.x = 0;
841 neighbour_dest_position.x = -1;
842
843 for (int y = min_y; y < height + min_y; ++y)
844 {
845 this_dest_position.y = y;
846 this_src_position.y = y;
847 neighbour_src_position.y = y;
848 neighbour_dest_position.y = y;
849 for (int z = 0; z < CHUNK_WIDTH; ++z)
850 {
851 this_dest_position.z = z;
852 this_src_position.z = z;
853 neighbour_src_position.z = z;
854 neighbour_dest_position.z = z;
855 // If the neighbour chunk is not loaded, just add air instead
856 if (neighbour == nullptr)
857 {
858 SetBlock(this_dest_position, nullptr);
859 }
860 // Otherwise, get the blocks and set this chunk's blocks
861 // on the neighbour
862 else
863 {
864 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
865 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
866 }
867 }
868 }
869 break;
870 // The neighbour is on "the left" on the z axis
872 this_dest_position.z = -1;
873 this_src_position.z = 0;
874 neighbour_src_position.z = CHUNK_WIDTH - 1;
875 neighbour_dest_position.z = CHUNK_WIDTH;
876
877 for (int y = min_y; y < height + min_y; ++y)
878 {
879 this_dest_position.y = y;
880 this_src_position.y = y;
881 neighbour_src_position.y = y;
882 neighbour_dest_position.y = y;
883 for (int x = 0; x < CHUNK_WIDTH; ++x)
884 {
885 this_dest_position.x = x;
886 this_src_position.x = x;
887 neighbour_src_position.x = x;
888 neighbour_dest_position.x = x;
889 // If the neighbour chunk is not loaded, just add air instead
890 if (neighbour == nullptr)
891 {
892 SetBlock(this_dest_position, nullptr);
893 }
894 // Otherwise, get the blocks and set this chunk's blocks
895 // on the neighbour
896 else
897 {
898 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
899 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
900 }
901 }
902 }
903 break;
904 // The neighbour is on "the right" on the z axis
906 this_dest_position.z = CHUNK_WIDTH;
907 this_src_position.z = CHUNK_WIDTH - 1;
908 neighbour_src_position.z = 0;
909 neighbour_dest_position.z = -1;
910
911 for (int y = min_y; y < height + min_y; ++y)
912 {
913 this_dest_position.y = y;
914 this_src_position.y = y;
915 neighbour_src_position.y = y;
916 neighbour_dest_position.y = y;
917 for (int x = 0; x < CHUNK_WIDTH; ++x)
918 {
919 this_dest_position.x = x;
920 this_src_position.x = x;
921 neighbour_src_position.x = x;
922 neighbour_dest_position.x = x;
923 // If the neighbour chunk is not loaded, just add air instead
924 if (neighbour == nullptr)
925 {
926 SetBlock(this_dest_position, nullptr);
927 }
928 // Otherwise, get the blocks and set this chunk's blocks
929 // on the neighbour
930 else
931 {
932 SetBlock(this_dest_position, neighbour->GetBlock(neighbour_src_position));
933 neighbour->SetBlock(neighbour_dest_position, GetBlock(this_src_position));
934 }
935 }
936 }
937 break;
938 default:
939 break;
940 }
941#endif
942 }
943
944 void Chunk::AddLoader(const std::thread::id& thread_id)
945 {
946 loaded_from.insert(thread_id);
947 }
948
949 size_t Chunk::RemoveLoader(const std::thread::id& thread_id)
950 {
951 loaded_from.erase(thread_id);
952 return loaded_from.size();
953 }
954
955 bool Chunk::IsInsideChunk(const Position& pos, const bool ignore_gui_borders) const
956 {
957 if (ignore_gui_borders)
958 {
959 return pos.x >= 0 && pos.x < CHUNK_WIDTH &&
960 pos.y >= min_y && pos.y < height + min_y &&
961 pos.z >= 0 && pos.z < CHUNK_WIDTH;
962 }
963#if USE_GUI
964 return pos.x >= -1 && pos.x <= CHUNK_WIDTH &&
965 pos.y >= min_y && pos.y < height + min_y &&
966 pos.z >= -1 && pos.z <= CHUNK_WIDTH;
967#else
968 return pos.x >= 0 && pos.x < CHUNK_WIDTH &&
969 pos.y >= min_y && pos.y < height + min_y &&
970 pos.z >= 0 && pos.z < CHUNK_WIDTH;
971#endif
972 }
973
974#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
975 void Botcraft::Chunk::LoadSectionBiomeData(const int section_y, ProtocolCraft::ReadIterator& iter, size_t& length)
976 {
977 // Paletted biomes
978 unsigned char bits_per_biome = ReadData<unsigned char>(iter, length);
979
980 Palette palette_type = (bits_per_biome == 0 ? Palette::SingleValue : (bits_per_biome <= 3 ? Palette::SectionPalette : Palette::GlobalPalette));
981
982 int palette_value = 0;
983 int palette_length = 0;
984 std::vector<int> palette;
985 switch (palette_type)
986 {
988 palette_value = ReadData<VarInt>(iter, length);
989 break;
991 palette_length = ReadData<VarInt>(iter, length);
992 palette = std::vector<int>(palette_length);
993 for (int i = 0; i < palette_length; ++i)
994 {
995 palette[i] = ReadData<VarInt>(iter, length);
996 }
997 break;
999 break;
1000 default:
1001 break;
1002 }
1003
1004 //A mask 0...01..1 with bits_per_biome ones
1005 unsigned int individual_value_mask = static_cast<unsigned int>((1 << bits_per_biome) - 1);
1006
1007 //Data array length
1008 int data_array_size = ReadData<VarInt>(iter, length);
1009
1010 //Data array
1011 std::vector<unsigned long long int> data_array = std::vector<unsigned long long int>(data_array_size);
1012 for (int i = 0; i < data_array_size; ++i)
1013 {
1014 data_array[i] = ReadData<unsigned long long int>(iter, length);
1015 }
1016
1017 //Biomes data
1018 int bit_offset = 0;
1019 for (int block_y = 0; block_y < SECTION_HEIGHT / 4; ++block_y)
1020 {
1021 for (int block_z = 0; block_z < CHUNK_WIDTH / 4; ++block_z)
1022 {
1023 for (int block_x = 0; block_x < CHUNK_WIDTH / 4; ++block_x)
1024 {
1025 const int biome_index = section_y * 64 + block_y * 16 + block_z * 4 + block_x;
1026 if (palette_type == Palette::SingleValue)
1027 {
1028 SetBiome(biome_index, palette_value);
1029 continue;
1030 }
1031
1032 // Entries don't span across multiple longs
1033 if (64 - (bit_offset % 64) < bits_per_biome)
1034 {
1035 bit_offset += 64 - (bit_offset % 64);
1036 }
1037 int start_long_index = bit_offset / 64;
1038 int end_long_index = start_long_index;
1039 int start_offset = bit_offset % 64;
1040 bit_offset += bits_per_biome;
1041
1042 unsigned int raw_id;
1043 if (start_long_index == end_long_index)
1044 {
1045 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset);
1046 }
1047 else
1048 {
1049 int end_offset = 64 - start_offset;
1050 raw_id = static_cast<unsigned int>(data_array[start_long_index] >> start_offset | data_array[end_long_index] << end_offset);
1051 }
1052 raw_id &= individual_value_mask;
1053
1054 if (palette_type == Palette::SectionPalette)
1055 {
1056 raw_id = palette[raw_id];
1057 }
1058
1059 SetBiome(biome_index, raw_id);
1060 }
1061 }
1062 }
1063 }
1064#endif
1065} //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:690
size_t dimension_index
Definition Chunk.hpp:113
unsigned char GetSkyLight(const Position &pos) const
Definition Chunk.cpp:628
void SetBiomes(const std::vector< int > &new_biomes)
Definition Chunk.cpp:736
void AddLoader(const std::thread::id &thread_id)
Add a thread to the loaders list.
Definition Chunk.cpp:944
const Biome * GetBiome(const int x, const int y, const int z) const
Definition Chunk.cpp:720
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:955
void RemoveBlockEntityData(const Position &pos)
Definition Chunk.cpp:491
bool modified_since_last_rendered
Definition Chunk.hpp:124
bool GetHasSkyLight() const
Definition Chunk.cpp:680
bool HasSection(const int y) const
Definition Chunk.cpp:685
ProtocolCraft::NBT::Value GetBlockEntityData(const Position &pos) const
Definition Chunk.cpp:496
size_t RemoveLoader(const std::thread::id &thread_id)
Remove a thread from the loaders list.
Definition Chunk.cpp:949
void LoadBiomesData(const std::vector< unsigned char > &data)
Definition Chunk.cpp:776
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:975
bool GetModifiedSinceLastRender() const
Definition Chunk.cpp:93
void UpdateNeighbour(Chunk *const neighbour, const Orientation direction)
Definition Chunk.cpp:793
std::vector< std::shared_ptr< Section > > sections
Definition Chunk.hpp:108
size_t GetDimensionIndex() const
Definition Chunk.cpp:675
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:507
void SetBlockEntityData(const Position &pos, const ProtocolCraft::NBT::Value &block_entity)
Definition Chunk.cpp:477
void SetBlockLight(const Position &pos, const unsigned char v)
Definition Chunk.cpp:598
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:754
void LoadChunkBlockEntitiesData(const std::vector< ProtocolCraft::BlockEntityInfo > &block_entities)
Definition Chunk.cpp:443
void SetSkyLight(const Position &pos, const unsigned char v)
Definition Chunk.cpp:644
unsigned char GetBlockLight(const Position &pos) const
Definition Chunk.cpp:582
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:530
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