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