Botcraft 1.21.11
Loading...
Searching...
No Matches
World.cpp
Go to the documentation of this file.
4
6
7namespace Botcraft
8{
9 World::World(const bool is_shared_) : is_shared(is_shared_)
10 {
11#if PROTOCOL_VERSION < 719 /* < 1.16 */
12 current_dimension = Dimension::None;
13#else
15#endif
16
17#if PROTOCOL_VERSION > 758 /* > 1.18.2 */
19#endif
20 }
21
23 {
24
25 }
26
27 bool World::IsLoaded(const Position& pos) const
28 {
29 std::shared_lock<std::shared_mutex> lock(world_mutex);
30
31 const int chunk_x = static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH)));
32 const int chunk_z = static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)));
33
34 return terrain.find({ chunk_x, chunk_z }) != terrain.end();
35 }
36
37 bool World::IsShared() const
38 {
39 return is_shared;
40 }
41
42 int World::GetHeight() const
43 {
44#if PROTOCOL_VERSION < 757 /* < 1.18 */
45 return 256;
46#else
47 std::shared_lock<std::shared_mutex> lock(world_mutex);
48 return GetHeightImpl();
49#endif
50 }
51
52 int World::GetMinY() const
53 {
54#if PROTOCOL_VERSION < 757 /* < 1.18 */
55 return 0;
56#else
57 std::shared_lock<std::shared_mutex> lock(world_mutex);
58 return GetMinYImpl();
59#endif
60 }
61
63 {
64 std::shared_lock<std::shared_mutex> lock(world_mutex);
65#if PROTOCOL_VERSION < 719 /* < 1.16 */
66 return current_dimension == Dimension::Nether;
67#else
69#endif
70 }
71
72 bool World::HasChunkBeenModified(const int x, const int z)
73 {
74#if USE_GUI
75 std::shared_lock<std::shared_mutex> lock(world_mutex);
76 auto it = terrain.find({ x,z });
77 if (it == terrain.end())
78 {
79 return true;
80 }
81 return it->second.GetModifiedSinceLastRender();
82#else
83 return false;
84#endif
85 }
86
87 std::optional<Chunk> World::ResetChunkModificationState(const int x, const int z)
88 {
89#if USE_GUI
90 std::scoped_lock<std::shared_mutex> lock(world_mutex);
91 auto it = terrain.find({ x,z });
92 if (it == terrain.end())
93 {
94 return std::optional<Chunk>();
95 }
96 it->second.SetModifiedSinceLastRender(false);
97 return std::optional<Chunk>(it->second);
98#else
99 return std::optional<Chunk>();
100#endif
101 }
102
103#if PROTOCOL_VERSION < 719 /* < 1.16 */
104 void World::LoadChunk(const int x, const int z, const Dimension dim, const std::thread::id& loader_id)
105#else
106 void World::LoadChunk(const int x, const int z, const std::string& dim, const std::thread::id& loader_id)
107#endif
108 {
109 std::scoped_lock<std::shared_mutex> lock(world_mutex);
110 LoadChunkImpl(x, z, dim, loader_id);
111 }
112
113 void World::UnloadChunk(const int x, const int z, const std::thread::id& loader_id)
114 {
115 std::scoped_lock<std::shared_mutex> lock(world_mutex);
116 UnloadChunkImpl(x, z, loader_id);
117 }
118
119 void World::UnloadAllChunks(const std::thread::id& loader_id)
120 {
121 std::scoped_lock<std::shared_mutex> lock(world_mutex);
122 for (auto it = terrain.begin(); it != terrain.end();)
123 {
124 const int load_count = it->second.RemoveLoader(loader_id);
125 if (load_count == 0)
126 {
127 terrain.erase(it++);
128 }
129 else
130 {
131 ++it;
132 }
133 }
134 }
135
136 void World::SetBlock(const Position& pos, const BlockstateId id)
137 {
138 std::scoped_lock<std::shared_mutex> lock(world_mutex);
139 SetBlockImpl(pos, id);
140 }
141
142 const Blockstate* World::GetBlock(const Position& pos) const
143 {
144 std::shared_lock<std::shared_mutex> lock(world_mutex);
145 return GetBlockImpl(pos);
146 }
147
148 std::vector<const Blockstate*> World::GetBlocks(const std::vector<Position>& pos) const
149 {
150 std::shared_lock<std::shared_mutex> lock(world_mutex);
151 std::vector<const Blockstate*> output(pos.size());
152 for (size_t i = 0; i < pos.size(); ++i)
153 {
154 output[i] = GetBlockImpl(pos[i]);
155 }
156
157 return output;
158 }
159
160 std::vector<AABB> World::GetColliders(const AABB& aabb, const Vector3<double>& movement) const
161 {
162 const AABB movement_extended_aabb(aabb.GetCenter() + movement * 0.5, aabb.GetHalfSize() + movement.Abs() * 0.5);
163 const Vector3<double> min_aabb = movement_extended_aabb.GetMin();
164 const Vector3<double> max_aabb = movement_extended_aabb.GetMax();
165 std::vector<AABB> output;
166 output.reserve(32);
167 Position current_pos;
168 std::shared_lock<std::shared_mutex> lock(world_mutex);
169 for (int y = static_cast<int>(std::floor(min_aabb.y)) - 1; y <= static_cast<int>(std::floor(max_aabb.y)); ++y)
170 {
171 current_pos.y = y;
172 for (int z = static_cast<int>(std::floor(min_aabb.z)); z <= static_cast<int>(std::floor(max_aabb.z)); ++z)
173 {
174 current_pos.z = z;
175 for (int x = static_cast<int>(std::floor(min_aabb.x)); x <= static_cast<int>(std::floor(max_aabb.x)); ++x)
176 {
177 current_pos.x = x;
178 const Blockstate* block = GetBlockImpl(current_pos);
179 if (block == nullptr || !block->IsSolid())
180 {
181 continue;
182 }
183
184 const std::set<AABB> colliders = block->GetCollidersAtPos(current_pos);
185 output.insert(output.end(), colliders.begin(), colliders.end());
186 }
187 }
188 }
189 return output;
190 }
191
193 {
194 std::shared_lock<std::shared_mutex> lock(world_mutex);
195 Vector3<double> flow(0.0);
196 std::vector<Position> horizontal_neighbours = {
197 Position(0, 0, -1), Position(1, 0, 0),
198 Position(0, 0, 1), Position(-1, 0, 0)
199 };
200 const Blockstate* block = GetBlockImpl(pos);
201 if (block == nullptr || !block->IsFluidOrWaterlogged())
202 {
203 return flow;
204 }
205
206 const float current_fluid_height = block->GetFluidHeight();
207 for (const Position& neighbour_pos : horizontal_neighbours)
208 {
209 const Blockstate* neighbour = GetBlockImpl(pos + neighbour_pos);
210 if (neighbour == nullptr || (neighbour->IsFluidOrWaterlogged() && neighbour->IsWaterOrWaterlogged() != block->IsWaterOrWaterlogged()))
211 {
212 continue;
213 }
214 const float neighbour_fluid_height = neighbour->GetFluidHeight();
215 if (neighbour_fluid_height == 0.0f)
216 {
217 if (!neighbour->IsSolid())
218 {
219 const Blockstate* block_below_neighbour = GetBlockImpl(pos + neighbour_pos + Position(0, -1, 0));
220 if (block_below_neighbour != nullptr &&
221 (!block_below_neighbour->IsFluidOrWaterlogged() || block_below_neighbour->IsWaterOrWaterlogged() == block->IsWaterOrWaterlogged()))
222 {
223 const float block_below_neighbour_fluid_height = block_below_neighbour->GetFluidHeight();
224 if (block_below_neighbour_fluid_height > 0.0f)
225 {
226 flow.x += (current_fluid_height - block_below_neighbour_fluid_height + 0.8888889f) * neighbour_pos.x;
227 flow.z += (current_fluid_height - block_below_neighbour_fluid_height + 0.8888889f) * neighbour_pos.z;
228 }
229 }
230 }
231 }
232 else
233 {
234 flow.x += (current_fluid_height - neighbour_fluid_height) * neighbour_pos.x;
235 flow.z += (current_fluid_height - neighbour_fluid_height) * neighbour_pos.z;
236 }
237 }
238
239 if (block->IsFluidFalling())
240 {
241 for (const Position& neighbour_pos : horizontal_neighbours)
242 {
243 const Blockstate* neighbour = GetBlockImpl(pos + neighbour_pos);
244 if (neighbour == nullptr)
245 {
246 continue;
247 }
248 const Blockstate* above_neighbour = GetBlockImpl(pos + neighbour_pos + Position(0, 1, 0));
249 if (above_neighbour == nullptr)
250 {
251 continue;
252 }
253 if (neighbour->IsSolid() && above_neighbour->IsSolid())
254 {
255 flow.Normalize();
256 flow.y -= 6.0;
257 break;
258 }
259 }
260 }
261
262 flow.Normalize();
263 return flow;
264 }
265
270
271#if PROTOCOL_VERSION < 358 /* < 1.13 */
272 void World::SetBiome(const int x, const int z, const unsigned char biome)
273#elif PROTOCOL_VERSION < 552 /* < 1.15 */
274 void World::SetBiome(const int x, const int z, const int biome)
275#else
276 void World::SetBiome(const int x, const int y, const int z, const int biome)
277#endif
278 {
279 std::scoped_lock<std::shared_mutex> lock(world_mutex);
280#if PROTOCOL_VERSION < 552 /* < 1.15 */
281 SetBiomeImpl(x, z, biome);
282#else
283 SetBiomeImpl(x, y, z, biome);
284#endif
285 }
286
287 const Biome* World::GetBiome(const Position& pos) const
288 {
289 std::shared_lock<std::shared_mutex> lock(world_mutex);
290 auto it = terrain.find({
291 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
292 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
293 });
294 if (it == terrain.end())
295 {
296 return nullptr;
297 }
298
299 const int in_chunk_x = (pos.x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
300 const int in_chunk_z = (pos.z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
301
302#if PROTOCOL_VERSION < 552 /* < 1.15 */
303 return it->second.GetBiome(in_chunk_x, in_chunk_z);
304#else
305 return it->second.GetBiome(in_chunk_x, pos.y, in_chunk_z);
306#endif
307 }
308
309 void World::SetSkyLight(const Position& pos, const unsigned char skylight)
310 {
311 std::scoped_lock<std::shared_mutex> lock(world_mutex);
312 auto it = terrain.find({
313 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
314 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
315 });
316
317 if (it != terrain.end() && it->second.GetHasSkyLight())
318 {
319 const int in_chunk_x = (pos.x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
320 const int in_chunk_z = (pos.z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
321 it->second.SetSkyLight(Position(in_chunk_x, pos.y, in_chunk_z), skylight);
322 }
323 }
324
325 void World::SetBlockLight(const Position& pos, const unsigned char blocklight)
326 {
327 std::scoped_lock<std::shared_mutex> lock(world_mutex);
328 auto it = terrain.find({
329 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
330 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
331 });
332
333 if (it != terrain.end())
334 {
335 const int in_chunk_x = (pos.x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
336 const int in_chunk_z = (pos.z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
337 it->second.SetBlockLight(Position(in_chunk_x, pos.y, in_chunk_z), blocklight);
338 }
339 }
340
341 unsigned char World::GetSkyLight(const Position& pos) const
342 {
343 std::shared_lock<std::shared_mutex> lock(world_mutex);
344 auto it = terrain.find({
345 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
346 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
347 });
348 if (it == terrain.end())
349 {
350 return 0;
351 }
352
353 const int in_chunk_x = (pos.x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
354 const int in_chunk_z = (pos.z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
355 return it->second.GetSkyLight(Position(in_chunk_x, pos.y, in_chunk_z));
356 }
357
358 unsigned char World::GetBlockLight(const Position& pos) const
359 {
360 std::shared_lock<std::shared_mutex> lock(world_mutex);
361 auto it = terrain.find({
362 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
363 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
364 });
365 if (it == terrain.end())
366 {
367 return 0;
368 }
369
370 const int in_chunk_x = (pos.x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
371 const int in_chunk_z = (pos.z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH;
372 return it->second.GetBlockLight(Position(in_chunk_x, pos.y, in_chunk_z));
373 }
374
376 {
377 std::scoped_lock<std::shared_mutex> lock(world_mutex);
378 auto it = terrain.find({
379 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
380 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
381 });
382 if (it == terrain.end())
383 {
384 return;
385 }
386
387 const Position chunk_pos(
389 pos.y,
391 );
392
393 if (data.HasData())
394 {
395 it->second.SetBlockEntityData(chunk_pos, data);
396 }
397 else
398 {
399 it->second.RemoveBlockEntityData(chunk_pos);
400 }
401 }
402
404 {
405 std::shared_lock<std::shared_mutex> lock(world_mutex);
406 auto it = terrain.find({
407 static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH))),
408 static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)))
409 });
410
411 if (it == terrain.end())
412 {
414 }
415
416 const Position chunk_pos(
418 pos.y,
420 );
421
422 return it->second.GetBlockEntityData(chunk_pos);
423 }
424
425#if PROTOCOL_VERSION < 719 /* < 1.16 */
426 Dimension World::GetDimension(const int x, const int z) const
427#else
428 std::string World::GetDimension(const int x, const int z) const
429#endif
430 {
431 std::shared_lock<std::shared_mutex> lock(world_mutex);
432 auto it = terrain.find({ x, z });
433 if (it == terrain.end())
434 {
435#if PROTOCOL_VERSION < 719 /* < 1.16 */
436 return Dimension::None;
437#else
438 return "";
439#endif
440 }
441
442 return index_dimension_map.at(it->second.GetDimensionIndex());
443 }
444
445#if PROTOCOL_VERSION < 719 /* < 1.16 */
446 Dimension World::GetCurrentDimension() const
447#else
448 std::string World::GetCurrentDimension() const
449#endif
450 {
451 std::shared_lock<std::shared_mutex> lock(world_mutex);
452 return current_dimension;
453 }
454
455#if PROTOCOL_VERSION < 719 /* < 1.16 */
456 void World::SetCurrentDimension(const Dimension dimension)
457#else
458 void World::SetCurrentDimension(const std::string& dimension)
459#endif
460 {
461 std::scoped_lock<std::shared_mutex> lock(world_mutex);
462 SetCurrentDimensionImpl(dimension);
463 }
464
465#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
466 void World::SetDimensionHeight(const std::string& dimension, const int height)
467 {
468 std::scoped_lock<std::shared_mutex> lock(world_mutex);
469 dimension_height[dimension] = height;
470 }
471
472 void World::SetDimensionMinY(const std::string& dimension, const int min_y)
473 {
474 std::scoped_lock<std::shared_mutex> lock(world_mutex);
475 dimension_min_y[dimension] = min_y;
476 }
477#endif
478
479#if PROTOCOL_VERSION > 718 /* > 1.15.2 */
480 void World::SetDimensionFastLava(const std::string& dimension, const bool fast_lava)
481 {
482 std::scoped_lock<std::shared_mutex> lock(world_mutex);
483 dimension_fast_lava[dimension] = fast_lava;
484 }
485#endif
486
487 const Blockstate* World::Raycast(const Vector3<double>& origin, const Vector3<double>& direction, const float max_radius, Position& out_pos, Position& out_normal)
488 {
489 // Inspired from https://gist.github.com/dogfuntom/cc881c8fc86ad43d55d8
490 // Searching along origin + t * direction line
491
492 // Position of the current cube examined
493 out_pos = Position(
494 static_cast<int>(std::floor(origin.x)),
495 static_cast<int>(std::floor(origin.y)),
496 static_cast<int>(std::floor(origin.z))
497 );
498
499 // Increment on each axis
500 Vector3<double> step((0.0 < direction.x) - (direction.x < 0.0), (0.0 < direction.y) - (direction.y < 0.0), (0.0 < direction.z) - (direction.z < 0.0));
501
502 // tMax is the t-value to cross a cube boundary
503 // for each axis. The axis with the least tMax
504 // value is the one the ray crosses in first
505 // tDelta is the increment of t for each step
506 Vector3<double> tMax, tDelta;
507
508 for (int i = 0; i < 3; ++i)
509 {
510 bool isInteger = std::round(origin[i]) == origin[i];
511 if (direction[i] < 0 && isInteger)
512 {
513 tMax[i] = 0.0;
514 }
515 else
516 {
517 if (direction[i] > 0)
518 {
519 tMax[i] = ((origin[i] == 0.0f) ? 1.0f : std::ceil(origin[i]) - origin[i]) / std::abs(direction[i]);
520 }
521 else if (direction[i] < 0)
522 {
523 tMax[i] = (origin[i] - std::floor(origin[i])) / std::abs(direction[i]);
524 }
525 else
526 {
527 tMax[i] = std::numeric_limits<double>::max();
528 }
529 }
530
531 if (direction[i] == 0)
532 {
533 tDelta[i] = std::numeric_limits<double>::max();
534 }
535 else
536 {
537 tDelta[i] = step[i] / direction[i];
538 }
539 }
540
541 if (direction.x == 0 && direction.y == 0 && direction.z == 0)
542 {
543 throw std::runtime_error("Raycasting with null direction");
544 }
545
546 const float radius = max_radius / static_cast<float>(std::sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z));
547
548 while (true)
549 {
550 const Blockstate* block = GetBlock(out_pos);
551
552 if (block != nullptr && !block->IsAir())
553 {
554 for (const auto& collider : block->GetCollidersAtPos(out_pos))
555 {
556 if (collider.Intersect(origin, direction))
557 {
558 return block;
559 }
560 }
561 }
562
563 // select the direction in which the next face is
564 // the closest
565 if (tMax.x < tMax.y && tMax.x < tMax.z)
566 {
567 if (tMax.x > radius)
568 {
569 return nullptr;
570 }
571
572 out_pos.x += static_cast<int>(std::round(step.x));
573 tMax.x += tDelta.x;
574 out_normal.x = -static_cast<int>(std::round(step.x));
575 out_normal.y = 0;
576 out_normal.z = 0;
577 }
578 else if (tMax.y < tMax.x && tMax.y < tMax.z)
579 {
580 if (tMax.y > radius)
581 {
582 return nullptr;
583 }
584 out_pos.y += static_cast<int>(std::round(step.y));
585 tMax.y += tDelta.y;
586 out_normal[0] = 0;
587 out_normal[1] = -static_cast<int>(std::round(step.y));
588 out_normal[2] = 0;
589 }
590 else // tMax.z < tMax.x && tMax.z < tMax.y
591 {
592 if (tMax.z > radius)
593 {
594 return nullptr;
595 }
596
597 out_pos.z += static_cast<int>(std::round(step.z));
598 tMax.z += tDelta.z;
599 out_normal.x = 0;
600 out_normal.x = 0;
601 out_normal.x = -static_cast<int>(std::round(step.z));
602 }
603 }
604 }
605
606#if PROTOCOL_VERSION > 758 /* > 1.18.2 */
611#endif
612
613 bool World::IsFree(const AABB& aabb, const bool fluid_collide) const
614 {
615 std::shared_lock<std::shared_mutex> lock(world_mutex);
616
617 const Vector3<double> min_aabb = aabb.GetMin();
618 const Vector3<double> max_aabb = aabb.GetMax();
619
620 Position cube_pos;
621 for (int y = static_cast<int>(std::floor(min_aabb.y)) - 1; y <= static_cast<int>(std::floor(max_aabb.y)); ++y)
622 {
623 cube_pos.y = y;
624 for (int z = static_cast<int>(std::floor(min_aabb.z)); z <= static_cast<int>(std::floor(max_aabb.z)); ++z)
625 {
626 cube_pos.z = z;
627 for (int x = static_cast<int>(std::floor(min_aabb.x)); x <= static_cast<int>(std::floor(max_aabb.x)); ++x)
628 {
629 cube_pos.x = x;
630 const Blockstate* block = GetBlockImpl(cube_pos);
631
632 if (block == nullptr)
633 {
634 continue;
635 }
636
637 if (block->IsFluid())
638 {
639 if (!fluid_collide)
640 {
641 continue;
642 }
643 }
644 else if (!block->IsSolid())
645 {
646 continue;
647 }
648
649 for (const auto& collider : block->GetCollidersAtPos(cube_pos))
650 {
651 if (aabb.Collide(collider))
652 {
653 return false;
654 }
655 }
656 }
657 }
658 }
659
660 return true;
661 }
662
663 std::optional<Position> World::GetSupportingBlockPos(const AABB& aabb) const
664 {
665 std::shared_lock<std::shared_mutex> lock(world_mutex);
666
667 const Vector3<double> min_aabb = aabb.GetMin();
668 const Vector3<double> max_aabb = aabb.GetMax();
669
670 Position cube_pos;
671 std::optional<Position> output = std::optional<Position>();
672 double min_distance = std::numeric_limits<double>::max();
673 for (int y = static_cast<int>(std::floor(min_aabb.y)) - 1; y <= static_cast<int>(std::floor(max_aabb.y)); ++y)
674 {
675 cube_pos.y = y;
676 for (int z = static_cast<int>(std::floor(min_aabb.z)); z <= static_cast<int>(std::floor(max_aabb.z)); ++z)
677 {
678 cube_pos.z = z;
679 for (int x = static_cast<int>(std::floor(min_aabb.x)); x <= static_cast<int>(std::floor(max_aabb.x)); ++x)
680 {
681 cube_pos.x = x;
682 const Blockstate* block = GetBlockImpl(cube_pos);
683
684 if (block == nullptr || !block->IsSolid())
685 {
686 continue;
687 }
688
689 for (const auto& collider : block->GetCollidersAtPos(cube_pos))
690 {
691 if (aabb.Collide(collider))
692 {
693 const double distance = aabb.GetCenter().SqrDist(collider.GetCenter());
694 if (distance < min_distance)
695 {
696 min_distance = distance;
697 output = cube_pos;
698 }
699 }
700 }
701 }
702 }
703 }
704
705 return output;
706 }
707
709 {
710 std::scoped_lock<std::shared_mutex> lock(world_mutex);
711#if PROTOCOL_VERSION < 719 /* < 1.16 */
712 SetCurrentDimensionImpl(static_cast<Dimension>(packet.GetDimension()));
713#elif PROTOCOL_VERSION < 764 /* < 1.20.2 */
714 SetCurrentDimensionImpl(packet.GetDimension().GetFull());
715#else
716 SetCurrentDimensionImpl(packet.GetCommonPlayerSpawnInfo().GetDimension().GetFull());
717#endif
718
719#if PROTOCOL_VERSION > 718 /* > 1.15.2 */
720#if PROTOCOL_VERSION < 751 /* < 1.16.2 */
721 for (const auto& d : packet.GetRegistryHolder()["dimension"].as_list_of<ProtocolCraft::NBT::TagCompound>())
722 {
723 const std::string& dim_name = d["name"].get<std::string>();
724 dimension_fast_lava[dim_name] = static_cast<bool>(d["ultrawarm"].get<char>());
725 }
726#elif PROTOCOL_VERSION < 764 /* < 1.20.2 */
727 for (const auto& d : packet.GetRegistryHolder()["minecraft:dimension_type"]["value"].as_list_of<ProtocolCraft::NBT::TagCompound>())
728 {
729 const std::string& dim_name = d["name"].get<std::string>();
730 dimension_fast_lava[dim_name] = static_cast<bool>(d["element"]["ultrawarm"].get<char>());
731#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
732 dimension_height[dim_name] = static_cast<unsigned int>(d["element"]["height"].get<int>());
733 dimension_min_y[dim_name] = d["element"]["min_y"].get<int>();
734#endif
735 }
736#endif
737#endif
738 }
739
741 {
742 UnloadAllChunks(std::this_thread::get_id());
743
744 std::scoped_lock<std::shared_mutex> lock(world_mutex);
745#if PROTOCOL_VERSION < 719 /* < 1.16 */
746 SetCurrentDimensionImpl(static_cast<Dimension>(packet.GetDimension()));
747#elif PROTOCOL_VERSION < 764 /* < 1.20.2 */
748 SetCurrentDimensionImpl(packet.GetDimension().GetFull());
749#else
750 SetCurrentDimensionImpl(packet.GetCommonPlayerSpawnInfo().GetDimension().GetFull());
751#endif
752
753#if PROTOCOL_VERSION > 747 /* > 1.16.1 */ && PROTOCOL_VERSION < 759 /* < 1.19 */
754 dimension_fast_lava[current_dimension] = static_cast<bool>(packet.GetDimensionType()["ultrawarm"].get<char>());
755#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
756 dimension_height[current_dimension] = packet.GetDimensionType()["height"].get<int>();
757 dimension_min_y[current_dimension] = packet.GetDimensionType()["min_y"].get<int>();
758#endif
759#endif
760 }
761
763 {
764 std::scoped_lock<std::shared_mutex> lock(world_mutex);
765#if PROTOCOL_VERSION < 347 /* < 1.13 */
766 int id;
767 unsigned char metadata;
768 Blockstate::IdToIdMetadata(packet.GetBlockstate(), id, metadata);
769 SetBlockImpl(packet.GetPos(), { id, metadata });
770#else
771 SetBlockImpl(packet.GetPos(), packet.GetBlockstate());
772#endif
773 }
774
776 {
777 std::scoped_lock<std::shared_mutex> lock(world_mutex);
778#if PROTOCOL_VERSION < 739 /* < 1.16.2 */
779 for (size_t i = 0; i < packet.GetRecords().size(); ++i)
780 {
781 unsigned char x = (packet.GetRecords()[i].GetHorizontalPosition() >> 4) & 0x0F;
782 unsigned char z = packet.GetRecords()[i].GetHorizontalPosition() & 0x0F;
783
784 const int x_pos = CHUNK_WIDTH * packet.GetChunkX() + x;
785 const int y_pos = packet.GetRecords()[i].GetYCoordinate();
786 const int z_pos = CHUNK_WIDTH * packet.GetChunkZ() + z;
787#else
788 const int chunk_x = CHUNK_WIDTH * (packet.GetSectionPos() >> 42); // 22 bits
789 const int chunk_z = CHUNK_WIDTH * (packet.GetSectionPos() << 22 >> 42); // 22 bits
790 const int chunk_y = SECTION_HEIGHT * (packet.GetSectionPos() << 44 >> 44); // 20 bits
791
792 for (size_t i = 0; i < packet.GetPosState().size(); ++i)
793 {
794 const unsigned int block_id = packet.GetPosState()[i] >> 12;
795 const short position = packet.GetPosState()[i] & 0xFFFl;
796
797 const int x_pos = chunk_x + ((position >> 8) & 0xF);
798 const int z_pos = chunk_z + ((position >> 4) & 0xF);
799 const int y_pos = chunk_y + ((position >> 0) & 0xF);
800#endif
801 Position cube_pos(x_pos, y_pos, z_pos);
802
803 {
804#if PROTOCOL_VERSION < 347 /* < 1.13 */
805 int id;
806 unsigned char metadata;
807 Blockstate::IdToIdMetadata(packet.GetRecords()[i].GetBlockId(), id, metadata);
808
809 SetBlockImpl(cube_pos, { id, metadata });
810#elif PROTOCOL_VERSION < 739 /* < 1.16.2 */
811 SetBlockImpl(cube_pos, packet.GetRecords()[i].GetBlockId());
812#else
813 SetBlockImpl(cube_pos, block_id);
814#endif
815 }
816 }
817 }
818
820 {
821#if PROTOCOL_VERSION < 764 /* < 1.20.2 */
822 UnloadChunk(packet.GetX(), packet.GetZ(), std::this_thread::get_id());
823#else
824 UnloadChunk(packet.GetPos().GetX(), packet.GetPos().GetZ(), std::this_thread::get_id());
825#endif
826 }
827
828#if PROTOCOL_VERSION < 757 /* < 1.18 */
829 void World::Handle(ProtocolCraft::ClientboundLevelChunkPacket& packet)
830 {
831
832#if PROTOCOL_VERSION < 755 /* < 1.17 */
833 if (packet.GetFullChunk())
834 {
835#endif
836 LoadChunk(packet.GetX(), packet.GetZ(), current_dimension, std::this_thread::get_id());
837#if PROTOCOL_VERSION < 755 /* < 1.17 */
838 }
839#endif
840
841 { // lock scope
842 std::scoped_lock<std::shared_mutex> lock(world_mutex);
843#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
844 if (auto it = delayed_light_updates.find({ packet.GetX(), packet.GetZ() }); it != delayed_light_updates.end())
845 {
846 UpdateChunkLight(it->second.GetX(), it->second.GetZ(), current_dimension,
847 it->second.GetSkyYMask(), it->second.GetEmptySkyYMask(), it->second.GetSkyUpdates(), true);
848 UpdateChunkLight(it->second.GetX(), it->second.GetZ(), current_dimension,
849 it->second.GetBlockYMask(), it->second.GetEmptyBlockYMask(), it->second.GetBlockUpdates(), false);
850 delayed_light_updates.erase(it);
851 }
852#endif
853#if PROTOCOL_VERSION < 552 /* < 1.15 */
854 LoadDataInChunk(packet.GetX(), packet.GetZ(), packet.GetBuffer(), packet.GetAvailableSections(), packet.GetFullChunk());
855#else
856 LoadDataInChunk(packet.GetX(), packet.GetZ(), packet.GetBuffer(), packet.GetAvailableSections());
857#if PROTOCOL_VERSION < 755 /* < 1.17 */
858 if (packet.GetBiomes().has_value())
859 {
860#if PROTOCOL_VERSION < 751 /* < 1.16.2 */
861 // Copy to convert the std::array to std::vector
862 LoadBiomesInChunk(packet.GetX(), packet.GetZ(), std::vector<int>(packet.GetBiomes().value().begin(), packet.GetBiomes().value().end()));
863#else
864 LoadBiomesInChunk(packet.GetX(), packet.GetZ(), packet.GetBiomes().value());
865#endif
866 }
867#else
868 LoadBiomesInChunk(packet.GetX(), packet.GetZ(), packet.GetBiomes());
869#endif
870#endif
871 LoadBlockEntityDataInChunk(packet.GetX(), packet.GetZ(), packet.GetBlockEntitiesTags());
872 }
873 }
874#else
876 {
877 std::scoped_lock<std::shared_mutex> lock(world_mutex);
878 LoadChunkImpl(packet.GetX(), packet.GetZ(), current_dimension, std::this_thread::get_id());
879 LoadDataInChunk(packet.GetX(), packet.GetZ(), packet.GetChunkData().GetBuffer());
880 LoadBlockEntityDataInChunk(packet.GetX(), packet.GetZ(), packet.GetChunkData().GetBlockEntitiesData());
881 UpdateChunkLight(packet.GetX(), packet.GetZ(), current_dimension,
882 packet.GetLightData().GetSkyYMask(), packet.GetLightData().GetEmptySkyYMask(), packet.GetLightData().GetSkyUpdates(), true);
883 UpdateChunkLight(packet.GetX(), packet.GetZ(), current_dimension,
884 packet.GetLightData().GetBlockYMask(), packet.GetLightData().GetEmptyBlockYMask(), packet.GetLightData().GetBlockUpdates(), false);
885 }
886#endif
887
888#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
890 {
891 std::scoped_lock<std::shared_mutex> lock(world_mutex);
892#if PROTOCOL_VERSION < 757 /* < 1.18 */
893 if (terrain.find({ packet.GetX(), packet.GetZ() }) == terrain.end())
894 {
895 delayed_light_updates[{packet.GetX(), packet.GetZ()}] = packet;
896 return;
897 }
898 UpdateChunkLight(packet.GetX(), packet.GetZ(), current_dimension,
899 packet.GetSkyYMask(), packet.GetEmptySkyYMask(), packet.GetSkyUpdates(), true);
900 UpdateChunkLight(packet.GetX(), packet.GetZ(), current_dimension,
901 packet.GetBlockYMask(), packet.GetEmptyBlockYMask(), packet.GetBlockUpdates(), false);
902#else
903 UpdateChunkLight(packet.GetX(), packet.GetZ(), current_dimension,
904 packet.GetLightData().GetSkyYMask(), packet.GetLightData().GetEmptySkyYMask(), packet.GetLightData().GetSkyUpdates(), true);
905 UpdateChunkLight(packet.GetX(), packet.GetZ(), current_dimension,
906 packet.GetLightData().GetBlockYMask(), packet.GetLightData().GetEmptyBlockYMask(), packet.GetLightData().GetBlockUpdates(), false);
907#endif
908 }
909#endif
910
912 {
913 SetBlockEntityData(packet.GetPos(), packet.GetTag());
914 }
915
916#if PROTOCOL_VERSION > 761 /* > 1.19.3 */
918 {
919 std::scoped_lock<std::shared_mutex> lock(world_mutex);
920 for (const auto& chunk_data : packet.GetChunkBiomeData())
921 {
922 auto it = terrain.find({ chunk_data.GetPos().GetX(), chunk_data.GetPos().GetZ()});
923 if (it != terrain.end())
924 {
925 it->second.LoadBiomesData(chunk_data.GetBuffer());
926 }
927 else
928 {
929 LOG_WARNING("Trying to load biomes data in non loaded chunk (" << chunk_data.GetPos().GetX() << ", " << chunk_data.GetPos().GetZ() << ")");
930 }
931 }
932 }
933#endif
934
935#if PROTOCOL_VERSION > 763 /* > 1.20.1 */
937 {
938 std::scoped_lock<std::shared_mutex> lock(world_mutex);
939#if PROTOCOL_VERSION < 766 /* < 1.20.5 */
940 for (const auto& d : packet.GetRegistryHolder()["minecraft:dimension_type"]["value"].as_list_of<ProtocolCraft::NBT::TagCompound>())
941 {
942 const std::string& dim_name = d["name"].get<std::string>();
943 dimension_height[dim_name] = static_cast<unsigned int>(d["element"]["height"].get<int>());
944 dimension_min_y[dim_name] = d["element"]["min_y"].get<int>();
945 dimension_fast_lava[dim_name] = static_cast<bool>(d["element"]["ultrawarm"].get<char>());
946 }
947#else
948 if (packet.GetRegistry().GetFull() != "minecraft:dimension_type")
949 {
950 return;
951 }
952
953 const auto& entries = packet.GetEntries();
954 for (size_t i = 0; i < entries.size(); ++i)
955 {
956 const std::string dim_name = entries[i].GetId().GetFull();
957 // Make sure we use the same indices as minecraft registry
958 // Not really useful but just in case
959 dimension_index_map.insert({ dim_name, i });
960 index_dimension_map.insert({ i, dim_name });
961
962 if (entries[i].GetData().has_value())
963 {
964 const ProtocolCraft::NBT::Value& data = entries[i].GetData().value();
965 dimension_height[dim_name] = static_cast<unsigned int>(data["height"].get<int>());
966 dimension_min_y[dim_name] = data["min_y"].get<int>();
967#if PROTOCOL_VERSION < 774 /* < 1.21.11 */
968 dimension_fast_lava[dim_name] = static_cast<bool>(data["ultrawarm"].get<char>());
969#else
970 dimension_fast_lava[dim_name] = static_cast<bool>(data["attributes"].contains("minecraft:gameplay/fast_lava") && data["attributes"]["minecraft:gameplay/fast_lava"].get<char>());
971#endif
972 }
973 }
974#endif
975 }
976#endif
977
978#if PROTOCOL_VERSION < 719 /* < 1.16 */
979 void World::LoadChunkImpl(const int x, const int z, const Dimension dim, const std::thread::id& loader_id)
980#else
981 void World::LoadChunkImpl(const int x, const int z, const std::string& dim, const std::thread::id& loader_id)
982#endif
983 {
984#if PROTOCOL_VERSION < 719 /* < 1.16 */
985 const bool has_sky_light = dim == Dimension::Overworld;
986#else
987 const bool has_sky_light = dim == "minecraft:overworld";
988#endif
989 const size_t dim_index = GetDimIndex(dim);
990 auto it = terrain.find({ x,z });
991 if (it == terrain.end())
992 {
993#if PROTOCOL_VERSION < 757 /* < 1.18 */
994 auto inserted = terrain.insert({ {x, z}, Chunk(dim_index, has_sky_light) });
995#else
996 auto inserted = terrain.insert({ { x, z }, Chunk(dimension_min_y.at(dim), dimension_height.at(dim), dim_index, has_sky_light)});
997#endif
998 inserted.first->second.AddLoader(loader_id);
999 }
1000 // This may already exists in this dimension if this is a shared world
1001 else if (it->second.GetDimensionIndex() != dim_index)
1002 {
1003 if (is_shared)
1004 {
1005 LOG_WARNING("Changing dimension with a shared world is not supported and can lead to wrong world data");
1006 }
1007 UnloadChunkImpl(x, z, loader_id);
1008#if PROTOCOL_VERSION < 757 /* < 1.18 */
1009 it->second = Chunk(dim_index, has_sky_light);
1010#else
1011 it->second = Chunk(dimension_min_y.at(dim), dimension_height.at(dim), dim_index, has_sky_light);
1012#endif
1013 it->second.AddLoader(loader_id);
1014 }
1015 else
1016 {
1017 it->second.AddLoader(loader_id);
1018 }
1019
1020 //Not necessary, from void to air, there is no difference
1021 //UpdateChunk(x, z);
1022 }
1023
1024 void World::UnloadChunkImpl(const int x, const int z, const std::thread::id& loader_id)
1025 {
1026 auto it = terrain.find({ x, z });
1027 if (it != terrain.end())
1028 {
1029 const size_t load_counter = it->second.RemoveLoader(loader_id);
1030 if (load_counter == 0)
1031 {
1032 terrain.erase(it);
1033#if USE_GUI
1034 UpdateChunk(x, z);
1035#endif
1036 }
1037 }
1038 }
1039
1040 void World::SetBlockImpl(const Position& pos, const BlockstateId id)
1041 {
1042 const int chunk_x = static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH)));
1043 const int chunk_z = static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)));
1044
1045 auto it = terrain.find({ chunk_x, chunk_z });
1046
1047 // Can't set block in unloaded chunk
1048 if (it == terrain.end())
1049 {
1050 return;
1051 }
1052
1053 const Position set_pos(
1055 pos.y,
1057 );
1058
1059 it->second.SetBlock(set_pos, id);
1060
1061#if USE_GUI
1062 // If this block is on the edge, update neighbours chunks
1063 UpdateChunk(chunk_x, chunk_z, pos);
1064#endif
1065 }
1066
1067 const Blockstate* World::GetBlockImpl(const Position& pos) const
1068 {
1069 const int chunk_x = static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH)));
1070 const int chunk_z = static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)));
1071
1072 auto it = terrain.find({ chunk_x, chunk_z });
1073
1074 // Can't get block in unloaded chunk
1075 if (it == terrain.end())
1076 {
1077 return nullptr;
1078 }
1079
1080 const Position chunk_pos(
1082 pos.y,
1084 );
1085
1086 const Blockstate* output = it->second.GetBlock(chunk_pos);
1087
1088 // As we are in a loaded chunk, nullptr means it's in an empty section
1089 // (or the chunk position was invalid but we know it's valid given how it's constructed above)
1090 // --> return air block instead of nullptr
1091 return output != nullptr ?
1092 output :
1093#if PROTOCOL_VERSION < 347 /* < 1.13 */
1094 AssetsManager::getInstance().GetBlockstate(std::make_pair(0, 0));
1095#else
1097#endif
1098 }
1099
1100#if PROTOCOL_VERSION < 719 /* < 1.16 */
1101 void World::SetCurrentDimensionImpl(const Dimension dimension)
1102#else
1103 void World::SetCurrentDimensionImpl(const std::string& dimension)
1104#endif
1105 {
1106 current_dimension = dimension;
1107#if PROTOCOL_VERSION > 404 /* > 1.13.2 */ && PROTOCOL_VERSION < 757 /* < 1.18 */
1108 delayed_light_updates.clear();
1109#endif
1110 }
1111
1113 {
1114#if PROTOCOL_VERSION < 757 /* < 1.18 */
1115 return 256;
1116#else
1118#endif
1119 }
1120
1122 {
1123#if PROTOCOL_VERSION < 757 /* < 1.18 */
1124 return 0;
1125#else
1127#endif
1128 }
1129
1130#if PROTOCOL_VERSION < 358 /* < 1.13 */
1131 void World::SetBiomeImpl(const int x, const int z, const unsigned char biome)
1132#elif PROTOCOL_VERSION < 552 /* < 1.15 */
1133 void World::SetBiomeImpl(const int x, const int z, const int biome)
1134#else
1135 void World::SetBiomeImpl(const int x, const int y, const int z, const int biome)
1136#endif
1137 {
1138 auto it = terrain.find({
1139 static_cast<int>(std::floor(x / static_cast<double>(CHUNK_WIDTH))),
1140 static_cast<int>(std::floor(z / static_cast<double>(CHUNK_WIDTH)))
1141 });
1142
1143 if (it != terrain.end())
1144 {
1145#if PROTOCOL_VERSION < 552 /* < 1.15 */
1146 it->second.SetBiome((x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, (z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, biome);
1147#else
1148 it->second.SetBiome((x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, y, (z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, biome);
1149#endif
1150 }
1151 }
1152
1153 void World::UpdateChunk(const int x, const int z, const Position& pos)
1154 {
1155#if USE_GUI
1156 const Position this_chunk_position(
1158 pos.y,
1160 );
1161
1162 if (this_chunk_position.x > 0 && this_chunk_position.x < CHUNK_WIDTH - 1 &&
1163 this_chunk_position.z > 0 && this_chunk_position.z < CHUNK_WIDTH - 1)
1164 {
1165 return;
1166 }
1167
1168 Chunk* chunk = GetChunk(x, z);
1169
1170 if (chunk == nullptr)
1171 {
1172 // This should not happen
1173 LOG_WARNING("Trying to propagate chunk updates from a non loaded chunk");
1174 return;
1175 }
1176
1177 const Blockstate* blockstate = chunk->GetBlock(this_chunk_position);
1178
1179 if (this_chunk_position.x == 0)
1180 {
1181 Chunk* neighbour_chunk = GetChunk(x - 1, z);
1182 if (neighbour_chunk != nullptr)
1183 {
1184 neighbour_chunk->SetBlock(Position(CHUNK_WIDTH, this_chunk_position.y, this_chunk_position.z), blockstate);
1185 }
1186 }
1187
1188 if (this_chunk_position.x == CHUNK_WIDTH - 1)
1189 {
1190 Chunk* neighbour_chunk = GetChunk(x + 1, z);
1191 if (neighbour_chunk != nullptr)
1192 {
1193 neighbour_chunk->SetBlock(Position(-1, this_chunk_position.y, this_chunk_position.z), blockstate);
1194 }
1195 }
1196
1197 if (this_chunk_position.z == 0)
1198 {
1199 Chunk* neighbour_chunk = GetChunk(x, z - 1);
1200 if (neighbour_chunk != nullptr)
1201 {
1202 neighbour_chunk->SetBlock(Position(this_chunk_position.x, this_chunk_position.y, CHUNK_WIDTH), blockstate);
1203 }
1204 }
1205
1206 if (this_chunk_position.z == CHUNK_WIDTH - 1)
1207 {
1208 Chunk* neighbour_chunk = GetChunk(x, z + 1);
1209 if (neighbour_chunk != nullptr)
1210 {
1211 neighbour_chunk->SetBlock(Position(this_chunk_position.x, this_chunk_position.y, -1), blockstate);
1212 }
1213 }
1214#endif
1215 }
1216
1217 void World::UpdateChunk(const int x, const int z)
1218 {
1219#if USE_GUI
1220 Chunk* chunk = GetChunk(x, z);
1221 // This means this chunk has just beend added
1222 // Copy data for all edges between its neighbours
1223 if (chunk != nullptr)
1224 {
1225 chunk->UpdateNeighbour(GetChunk(x - 1, z), Orientation::West);
1226 chunk->UpdateNeighbour(GetChunk(x + 1, z), Orientation::East);
1227 chunk->UpdateNeighbour(GetChunk(x, z - 1), Orientation::North);
1228 chunk->UpdateNeighbour(GetChunk(x, z + 1), Orientation::South);
1229 }
1230 // This means this chunk has just been removed
1231 // Update all existing neighbours to let them know
1232 else
1233 {
1234 Chunk* neighbour_chunk;
1235 neighbour_chunk = GetChunk(x - 1, z);
1236 if (neighbour_chunk != nullptr)
1237 {
1238 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::East);
1239 }
1240 neighbour_chunk = GetChunk(x + 1, z);
1241 if (neighbour_chunk != nullptr)
1242 {
1243 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::West);
1244 }
1245 neighbour_chunk = GetChunk(x, z - 1);
1246 if (neighbour_chunk != nullptr)
1247 {
1248 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::South);
1249 }
1250 neighbour_chunk = GetChunk(x, z + 1);
1251 if (neighbour_chunk != nullptr)
1252 {
1253 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::North);
1254 }
1255 }
1256#endif
1257 }
1258
1259 Chunk* World::GetChunk(const int x, const int z)
1260 {
1261 auto it = terrain.find({ x,z });
1262 if (it == terrain.end())
1263 {
1264 return nullptr;
1265 }
1266 return &it->second;
1267 }
1268
1269#if PROTOCOL_VERSION < 552 /* < 1.15 */
1270 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data,
1271 const int primary_bit_mask, const bool ground_up_continuous)
1272#elif PROTOCOL_VERSION < 755 /* < 1.17 */
1273 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data,
1274 const int primary_bit_mask)
1275#elif PROTOCOL_VERSION < 757 /* < 1.18 */
1276 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data,
1277 const std::vector<unsigned long long int>& primary_bit_mask)
1278#else
1279 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data)
1280#endif
1281 {
1282 auto it = terrain.find({ x,z });
1283 if (it != terrain.end())
1284 {
1285#if PROTOCOL_VERSION < 552 /* < 1.15 */
1286 it->second.LoadChunkData(data, primary_bit_mask, ground_up_continuous);
1287#elif PROTOCOL_VERSION < 757 /* < 1.18 */
1288 it->second.LoadChunkData(data, primary_bit_mask);
1289#else
1290 it->second.LoadChunkData(data);
1291#endif
1292#if USE_GUI
1293 UpdateChunk(x, z);
1294#endif
1295 }
1296 }
1297
1298#if PROTOCOL_VERSION < 757 /* < 1.18 */
1299 void World::LoadBlockEntityDataInChunk(const int x, const int z, const std::vector<ProtocolCraft::NBT::Value>& block_entities)
1300#else
1301 void World::LoadBlockEntityDataInChunk(const int x, const int z, const std::vector<ProtocolCraft::BlockEntityInfo>& block_entities)
1302#endif
1303 {
1304 auto it = terrain.find({ x,z });
1305 if (it != terrain.end())
1306 {
1307 it->second.LoadChunkBlockEntitiesData(block_entities);
1308 }
1309 }
1310
1311#if PROTOCOL_VERSION > 551 /* > 1.14.4 */ && PROTOCOL_VERSION < 757 /* < 1.18 */
1312 void World::LoadBiomesInChunk(const int x, const int z, const std::vector<int>& biomes)
1313 {
1314 auto it = terrain.find({ x,z });
1315 if (it != terrain.end())
1316 {
1317 it->second.SetBiomes(biomes);
1318 }
1319 }
1320#endif
1321
1322#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
1323#if PROTOCOL_VERSION < 719 /* < 1.16 */
1324 void World::UpdateChunkLight(const int x, const int z, const Dimension dim, const int light_mask, const int empty_light_mask,
1325 const std::vector<std::vector<char>>& data, const bool sky)
1326#elif PROTOCOL_VERSION < 755 /* < 1.17 */
1327 void World::UpdateChunkLight(const int x, const int z, const std::string& dim, const int light_mask, const int empty_light_mask,
1328 const std::vector<std::vector<char>>& data, const bool sky)
1329#else
1330 void World::UpdateChunkLight(const int x, const int z, const std::string& dim,
1331 const std::vector<unsigned long long int>& light_mask, const std::vector<unsigned long long int>& empty_light_mask,
1332 const std::vector<std::vector<char>>& data, const bool sky)
1333#endif
1334 {
1335 auto it = terrain.find({ x, z });
1336
1337 if (it == terrain.end())
1338 {
1339 LOG_WARNING("Trying to update lights in an unloaded chunk: (" << x << "," << z << ")");
1340 return;
1341 }
1342
1343 int counter_arrays = 0;
1344 Position pos1, pos2;
1345
1346 const int num_sections = GetHeightImpl() / 16 + 2;
1347
1348 for (int i = 0; i < num_sections; ++i)
1349 {
1350 const int section_Y = i - 1;
1351
1352 // Sky light
1353#if PROTOCOL_VERSION < 755 /* < 1.17 */
1354 if ((light_mask >> i) & 1)
1355#else
1356 if ((light_mask.size() > i / 64) && (light_mask[i / 64] >> (i % 64)) & 1)
1357#endif
1358 {
1359 if (i > 0 && i < num_sections - 1)
1360 {
1361 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
1362 {
1363 pos1.y = block_y + section_Y * SECTION_HEIGHT + GetMinYImpl();
1364 pos2.y = pos1.y;
1365 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
1366 {
1367 pos1.z = block_z;
1368 pos2.z = block_z;
1369 for (int block_x = 0; block_x < CHUNK_WIDTH; block_x += 2)
1370 {
1371 pos1.x = block_x;
1372 pos2.x = block_x + 1;
1373 const char two_light_values = data[counter_arrays][(block_y * CHUNK_WIDTH * CHUNK_WIDTH + block_z * CHUNK_WIDTH + block_x) / 2];
1374
1375 if (sky)
1376 {
1377 it->second.SetSkyLight(pos1, two_light_values & 0x0F);
1378 it->second.SetSkyLight(pos2, (two_light_values >> 4) & 0x0F);
1379 }
1380 else
1381 {
1382 it->second.SetBlockLight(pos1, two_light_values & 0x0F);
1383 it->second.SetBlockLight(pos2, (two_light_values >> 4) & 0x0F);
1384 }
1385 }
1386 }
1387 }
1388 }
1389 counter_arrays++;
1390 }
1391#if PROTOCOL_VERSION < 755 /* < 1.17 */
1392 else if ((empty_light_mask >> i) & 1)
1393#else
1394 else if ((empty_light_mask.size() > i / 64) && (empty_light_mask[i / 64] >> (i % 64)) & 1)
1395#endif
1396 {
1397 if (i > 0 && i < num_sections - 1)
1398 {
1399 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
1400 {
1401 pos1.y = block_y + section_Y * SECTION_HEIGHT + GetMinYImpl();
1402 pos2.y = pos1.y;
1403 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
1404 {
1405 pos1.z = block_z;
1406 pos2.z = block_z;
1407 for (int block_x = 0; block_x < CHUNK_WIDTH; block_x += 2)
1408 {
1409 pos1.x = block_x;
1410 pos2.x = block_x + 1;
1411 if (sky)
1412 {
1413 it->second.SetSkyLight(pos1, 0);
1414 it->second.SetSkyLight(pos2, 0);
1415 }
1416 else
1417 {
1418 it->second.SetBlockLight(pos1, 0);
1419 it->second.SetBlockLight(pos2, 0);
1420 }
1421 }
1422 }
1423 }
1424 }
1425 }
1426 }
1427 }
1428#endif
1429
1430#if PROTOCOL_VERSION < 719 /* < 1.16 */
1431 size_t World::GetDimIndex(const Dimension dim)
1432#else
1433 size_t World::GetDimIndex(const std::string& dim)
1434#endif
1435 {
1436 auto it = dimension_index_map.find(dim);
1437
1438 if (it == dimension_index_map.end())
1439 {
1440 index_dimension_map.insert({ dimension_index_map.size(), dim });
1441 it = dimension_index_map.insert({ dim, dimension_index_map.size() }).first;
1442 }
1443 return it->second;
1444 }
1445} // Botcraft
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
const Vector3< double > & GetHalfSize() const
Definition AABB.cpp:33
Vector3< double > GetMin() const
Definition AABB.cpp:18
const Vector3< double > & GetCenter() const
Definition AABB.cpp:28
bool Collide(const AABB &b) const
Definition AABB.cpp:60
Vector3< double > GetMax() const
Definition AABB.cpp:23
static AssetsManager & getInstance()
const Blockstate * GetBlockstate(const BlockstateId id) const
bool IsWaterOrWaterlogged() const
bool IsFluidOrWaterlogged() const
std::set< AABB > GetCollidersAtPos(const Position &pos) const
bool IsFluidFalling() const
float GetFluidHeight() const
Get fluid height for this block.
void UpdateNeighbour(Chunk *const neighbour, const Orientation direction)
Definition Chunk.cpp:798
void LoadChunkData(const std::vector< unsigned char > &data)
Definition Chunk.cpp:314
const Blockstate * GetBlock(const Position &pos) const
Definition Chunk.cpp:512
void SetBlock(const Position &pos, const Blockstate *block)
Definition Chunk.cpp:535
Mutex protected reference, will be locked until destroyed.
void SetBlockEntityData(const Position &pos, const ProtocolCraft::NBT::Value &data)
Set block entity data at pos.
Definition World.cpp:375
Vector3< double > GetFlow(const Position &pos)
Get the flow of fluid at a given position.
Definition World.cpp:192
void UnloadChunkImpl(const int x, const int z, const std::thread::id &loader_id)
Definition World.cpp:1024
void SetSkyLight(const Position &pos, const unsigned char skylight)
Set sky light value.
Definition World.cpp:309
std::unordered_map< std::pair< int, int >, Chunk > terrain
Definition World.hpp:375
void UnloadChunk(const int x, const int z, const std::thread::id &loader_id=std::this_thread::get_id())
Remove a chunk at given coordinates.
Definition World.cpp:113
unsigned char GetBlockLight(const Position &pos) const
Get block light value.
Definition World.cpp:358
const Blockstate * GetBlock(const Position &pos) const
Get the blockstate at a given position.
Definition World.cpp:142
bool IsFree(const AABB &aabb, const bool fluid_collide) const
Check if an AABB collides in the world.
Definition World.cpp:613
std::optional< Chunk > ResetChunkModificationState(const int x, const int z)
Reset a chunk modification state.
Definition World.cpp:87
void SetDimensionMinY(const std::string &dimension, const int min_y)
Set min block for given dimension.
Definition World.cpp:472
void LoadDataInChunk(const int x, const int z, const std::vector< unsigned char > &data)
Definition World.cpp:1279
void SetCurrentDimensionImpl(const std::string &dimension)
Definition World.cpp:1103
Chunk * GetChunk(const int x, const int z)
Get a pointer to a chunk.
Definition World.cpp:1259
void SetDimensionFastLava(const std::string &dimension, const bool fast_lava)
Set fast lava bool for given dimension.
Definition World.cpp:480
void UpdateChunkLight(const int x, const int z, const std::string &dim, const std::vector< unsigned long long int > &light_mask, const std::vector< unsigned long long int > &empty_light_mask, const std::vector< std::vector< char > > &data, const bool sky)
Definition World.cpp:1330
int GetNextWorldInteractionSequenceId()
Get a unique id used for server interactions.
Definition World.cpp:607
void LoadChunk(const int x, const int z, const std::string &dim, const std::thread::id &loader_id=std::this_thread::get_id())
Add a chunk at given coordinates.
Definition World.cpp:106
std::unordered_map< std::string, size_t > dimension_index_map
Definition World.hpp:389
bool IsInFastLavaDimension() const
Check if current dimension has fast lava.
Definition World.cpp:62
int GetMinYImpl() const
Definition World.cpp:1121
std::string current_dimension
Definition World.hpp:388
std::unordered_map< size_t, std::string > index_dimension_map
Definition World.hpp:390
bool HasChunkBeenModified(const int x, const int z)
Check if a chunk modification flag is set.
Definition World.cpp:72
void UpdateChunk(const int chunk_x, const int chunk_z, const Position &pos)
Progagate chunk update at pos to neighbouring chunks.
Definition World.cpp:1153
virtual void Handle(ProtocolCraft::ClientboundLoginPacket &packet) override
Definition World.cpp:708
void SetDimensionHeight(const std::string &dimension, const int height)
Set total height for given dimension.
Definition World.cpp:466
int GetHeight() const
Get height of the current dimension.
Definition World.cpp:42
std::unordered_map< std::string, unsigned int > dimension_height
Height of the chunks in a given dimension.
Definition World.hpp:399
const Biome * GetBiome(const Position &pos) const
Get the biome at a given position.
Definition World.cpp:287
const Blockstate * GetBlockImpl(const Position &pos) const
Definition World.cpp:1067
void SetCurrentDimension(const std::string &dimension)
Set current world dimension.
Definition World.cpp:458
void SetBlock(const Position &pos, const BlockstateId id)
Set block at given pos.
Definition World.cpp:136
std::unordered_map< std::string, bool > dimension_fast_lava
Definition World.hpp:404
void SetBlockImpl(const Position &pos, const BlockstateId id)
Definition World.cpp:1040
bool IsLoaded(const Position &pos) const
Check if a position is in a loaded chunk.
Definition World.cpp:27
ProtocolCraft::NBT::Value GetBlockEntityData(const Position &pos) const
Get the block entity data at a given position.
Definition World.cpp:403
World(const bool is_shared_)
Definition World.cpp:9
std::atomic< int > world_interaction_sequence_id
Definition World.hpp:394
void UnloadAllChunks(const std::thread::id &loader_id=std::this_thread::get_id())
Remove all chunks from memory.
Definition World.cpp:119
int GetHeightImpl() const
Definition World.cpp:1112
void SetBiomeImpl(const int x, const int y, const int z, const int biome)
Definition World.cpp:1135
std::vector< AABB > GetColliders(const AABB &aabb, const Vector3< double > &movement=Vector3< double >(0.0)) const
Get all colliders that could collide with a given AABB.
Definition World.cpp:160
void SetBlockLight(const Position &pos, const unsigned char blocklight)
Set block light value.
Definition World.cpp:325
std::unordered_map< std::string, int > dimension_min_y
Height of the lowest block in a given dimension.
Definition World.hpp:401
const Blockstate * Raycast(const Vector3< double > &origin, const Vector3< double > &direction, const float max_radius, Position &out_pos, Position &out_normal)
Perform a raycast in the voxel world and return position, normal and blockstate which are hit.
Definition World.cpp:487
void SetBiome(const int x, const int y, const int z, const int biome)
Set biome of given block.
Definition World.cpp:276
void LoadBlockEntityDataInChunk(const int x, const int z, const std::vector< ProtocolCraft::BlockEntityInfo > &block_entities)
Definition World.cpp:1301
unsigned char GetSkyLight(const Position &pos) const
Get sky light value.
Definition World.cpp:341
const bool is_shared
Definition World.hpp:382
size_t GetDimIndex(const std::string &dim)
Definition World.cpp:1433
bool IsShared() const
is_shared getter
Definition World.cpp:37
std::optional< Position > GetSupportingBlockPos(const AABB &aabb) const
Get the block position supporting an aabb.
Definition World.cpp:663
void LoadChunkImpl(const int x, const int z, const std::string &dim, const std::thread::id &loader_id)
Definition World.cpp:981
int GetMinY() const
Get min_y of the current dimension.
Definition World.cpp:52
std::shared_mutex world_mutex
Definition World.hpp:376
std::vector< const Blockstate * > GetBlocks(const std::vector< Position > &pos) const
Get blockstates for a set of positions.
Definition World.cpp:148
Utilities::ScopeLockedWrapper< const std::unordered_map< std::pair< int, int >, Chunk >, std::shared_mutex, std::shared_lock > GetChunks() const
Get a read-only locked version of all the loaded chunks.
Definition World.cpp:266
std::string GetCurrentDimension() const
Get current world dimension.
Definition World.cpp:448
std::string GetDimension(const int x, const int z) const
Get dimension of chunk at given coordinates.
Definition World.cpp:428
virtual int GetId() const override
bool contains(const std::string &s) const
Definition Tag.cpp:404
bool HasData() const
Definition NBT.cpp:63
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
double SqrDist(const Vector3 &v) const
Definition Vector3.hpp:204
Vector3 Abs() const
Definition Vector3.hpp:211