Botcraft 1.21.4
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::SetDimensionUltrawarm(const std::string& dimension, const bool ultrawarm)
481 {
482 std::scoped_lock<std::shared_mutex> lock(world_mutex);
483 dimension_ultrawarm[dimension] = ultrawarm;
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>(msg.GetDimension()));
713#elif PROTOCOL_VERSION < 764 /* < 1.20.2 */
714 SetCurrentDimensionImpl(msg.GetDimension().GetFull());
715#else
716 SetCurrentDimensionImpl(msg.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 : msg.GetRegistryHolder()["dimension"].as_list_of<ProtocolCraft::NBT::TagCompound>())
722 {
723 const std::string& dim_name = d["name"].get<std::string>();
724 dimension_ultrawarm[dim_name] = static_cast<bool>(d["ultrawarm"].get<char>());
725 }
726#elif PROTOCOL_VERSION < 764 /* < 1.20.2 */
727 for (const auto& d : msg.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_ultrawarm[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>(msg.GetDimension()));
747#elif PROTOCOL_VERSION < 764 /* < 1.20.2 */
748 SetCurrentDimensionImpl(msg.GetDimension().GetFull());
749#else
750 SetCurrentDimensionImpl(msg.GetCommonPlayerSpawnInfo().GetDimension().GetFull());
751#endif
752
753#if PROTOCOL_VERSION > 747 /* > 1.16.1 */ && PROTOCOL_VERSION < 759 /* < 1.19 */
754 dimension_ultrawarm[current_dimension] = static_cast<bool>(msg.GetDimensionType()["ultrawarm"].get<char>());
755#if PROTOCOL_VERSION > 756 /* > 1.17.1 */
756 dimension_height[current_dimension] = msg.GetDimensionType()["height"].get<int>();
757 dimension_min_y[current_dimension] = msg.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(msg.GetBlockstate(), id, metadata);
769 SetBlockImpl(msg.GetPos(), { id, metadata });
770#else
771 SetBlockImpl(msg.GetPos(), msg.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 < msg.GetRecords().size(); ++i)
780 {
781 unsigned char x = (msg.GetRecords()[i].GetHorizontalPosition() >> 4) & 0x0F;
782 unsigned char z = msg.GetRecords()[i].GetHorizontalPosition() & 0x0F;
783
784 const int x_pos = CHUNK_WIDTH * msg.GetChunkX() + x;
785 const int y_pos = msg.GetRecords()[i].GetYCoordinate();
786 const int z_pos = CHUNK_WIDTH * msg.GetChunkZ() + z;
787#else
788 const int chunk_x = CHUNK_WIDTH * (msg.GetSectionPos() >> 42); // 22 bits
789 const int chunk_z = CHUNK_WIDTH * (msg.GetSectionPos() << 22 >> 42); // 22 bits
790 const int chunk_y = SECTION_HEIGHT * (msg.GetSectionPos() << 44 >> 44); // 20 bits
791
792 for (size_t i = 0; i < msg.GetPosState().size(); ++i)
793 {
794 const unsigned int block_id = msg.GetPosState()[i] >> 12;
795 const short position = msg.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(msg.GetRecords()[i].GetBlockId(), id, metadata);
808
809 SetBlockImpl(cube_pos, { id, metadata });
810#elif PROTOCOL_VERSION < 739 /* < 1.16.2 */
811 SetBlockImpl(cube_pos, msg.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(msg.GetX(), msg.GetZ(), std::this_thread::get_id());
823#else
824 UnloadChunk(msg.GetPos().GetX(), msg.GetPos().GetZ(), std::this_thread::get_id());
825#endif
826 }
827
828#if PROTOCOL_VERSION < 757 /* < 1.18 */
829 void World::Handle(ProtocolCraft::ClientboundLevelChunkPacket& msg)
830 {
831
832#if PROTOCOL_VERSION < 755 /* < 1.17 */
833 if (msg.GetFullChunk())
834 {
835#endif
836 LoadChunk(msg.GetX(), msg.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({ msg.GetX(), msg.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(msg.GetX(), msg.GetZ(), msg.GetBuffer(), msg.GetAvailableSections(), msg.GetFullChunk());
855#else
856 LoadDataInChunk(msg.GetX(), msg.GetZ(), msg.GetBuffer(), msg.GetAvailableSections());
857#if PROTOCOL_VERSION < 755 /* < 1.17 */
858 if (msg.GetBiomes().has_value())
859 {
860#if PROTOCOL_VERSION < 751 /* < 1.16.2 */
861 // Copy to convert the std::array to std::vector
862 LoadBiomesInChunk(msg.GetX(), msg.GetZ(), std::vector<int>(msg.GetBiomes().value().begin(), msg.GetBiomes().value().end()));
863#else
864 LoadBiomesInChunk(msg.GetX(), msg.GetZ(), msg.GetBiomes().value());
865#endif
866 }
867#else
868 LoadBiomesInChunk(msg.GetX(), msg.GetZ(), msg.GetBiomes());
869#endif
870#endif
871 LoadBlockEntityDataInChunk(msg.GetX(), msg.GetZ(), msg.GetBlockEntitiesTags());
872 }
873 }
874#else
876 {
877 std::scoped_lock<std::shared_mutex> lock(world_mutex);
878 LoadChunkImpl(msg.GetX(), msg.GetZ(), current_dimension, std::this_thread::get_id());
879 LoadDataInChunk(msg.GetX(), msg.GetZ(), msg.GetChunkData().GetBuffer());
880 LoadBlockEntityDataInChunk(msg.GetX(), msg.GetZ(), msg.GetChunkData().GetBlockEntitiesData());
881 UpdateChunkLight(msg.GetX(), msg.GetZ(), current_dimension,
882 msg.GetLightData().GetSkyYMask(), msg.GetLightData().GetEmptySkyYMask(), msg.GetLightData().GetSkyUpdates(), true);
883 UpdateChunkLight(msg.GetX(), msg.GetZ(), current_dimension,
884 msg.GetLightData().GetBlockYMask(), msg.GetLightData().GetEmptyBlockYMask(), msg.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({ msg.GetX(), msg.GetZ() }) == terrain.end())
894 {
895 delayed_light_updates[{msg.GetX(), msg.GetZ()}] = msg;
896 return;
897 }
898 UpdateChunkLight(msg.GetX(), msg.GetZ(), current_dimension,
899 msg.GetSkyYMask(), msg.GetEmptySkyYMask(), msg.GetSkyUpdates(), true);
900 UpdateChunkLight(msg.GetX(), msg.GetZ(), current_dimension,
901 msg.GetBlockYMask(), msg.GetEmptyBlockYMask(), msg.GetBlockUpdates(), false);
902#else
903 UpdateChunkLight(msg.GetX(), msg.GetZ(), current_dimension,
904 msg.GetLightData().GetSkyYMask(), msg.GetLightData().GetEmptySkyYMask(), msg.GetLightData().GetSkyUpdates(), true);
905 UpdateChunkLight(msg.GetX(), msg.GetZ(), current_dimension,
906 msg.GetLightData().GetBlockYMask(), msg.GetLightData().GetEmptyBlockYMask(), msg.GetLightData().GetBlockUpdates(), false);
907#endif
908 }
909#endif
910
912 {
913 SetBlockEntityData(msg.GetPos(), msg.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 : msg.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 : msg.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_ultrawarm[dim_name] = static_cast<bool>(d["element"]["ultrawarm"].get<char>());
946 }
947#else
948 if (msg.GetRegistry().GetFull() != "minecraft:dimension_type")
949 {
950 return;
951 }
952
953 const auto& entries = msg.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 dimension_ultrawarm[dim_name] = static_cast<bool>(data["ultrawarm"].get<char>());
968 }
969 }
970#endif
971 }
972#endif
973
974#if PROTOCOL_VERSION < 719 /* < 1.16 */
975 void World::LoadChunkImpl(const int x, const int z, const Dimension dim, const std::thread::id& loader_id)
976#else
977 void World::LoadChunkImpl(const int x, const int z, const std::string& dim, const std::thread::id& loader_id)
978#endif
979 {
980#if PROTOCOL_VERSION < 719 /* < 1.16 */
981 const bool has_sky_light = dim == Dimension::Overworld;
982#else
983 const bool has_sky_light = dim == "minecraft:overworld";
984#endif
985 const size_t dim_index = GetDimIndex(dim);
986 auto it = terrain.find({ x,z });
987 if (it == terrain.end())
988 {
989#if PROTOCOL_VERSION < 757 /* < 1.18 */
990 auto inserted = terrain.insert({ {x, z}, Chunk(dim_index, has_sky_light) });
991#else
992 auto inserted = terrain.insert({ { x, z }, Chunk(dimension_min_y.at(dim), dimension_height.at(dim), dim_index, has_sky_light)});
993#endif
994 inserted.first->second.AddLoader(loader_id);
995 }
996 // This may already exists in this dimension if this is a shared world
997 else if (it->second.GetDimensionIndex() != dim_index)
998 {
999 if (is_shared)
1000 {
1001 LOG_WARNING("Changing dimension with a shared world is not supported and can lead to wrong world data");
1002 }
1003 UnloadChunkImpl(x, z, loader_id);
1004#if PROTOCOL_VERSION < 757 /* < 1.18 */
1005 it->second = Chunk(dim_index, has_sky_light);
1006#else
1007 it->second = Chunk(dimension_min_y.at(dim), dimension_height.at(dim), dim_index, has_sky_light);
1008#endif
1009 it->second.AddLoader(loader_id);
1010 }
1011 else
1012 {
1013 it->second.AddLoader(loader_id);
1014 }
1015
1016 //Not necessary, from void to air, there is no difference
1017 //UpdateChunk(x, z);
1018 }
1019
1020 void World::UnloadChunkImpl(const int x, const int z, const std::thread::id& loader_id)
1021 {
1022 auto it = terrain.find({ x, z });
1023 if (it != terrain.end())
1024 {
1025 const size_t load_counter = it->second.RemoveLoader(loader_id);
1026 if (load_counter == 0)
1027 {
1028 terrain.erase(it);
1029#if USE_GUI
1030 UpdateChunk(x, z);
1031#endif
1032 }
1033 }
1034 }
1035
1036 void World::SetBlockImpl(const Position& pos, const BlockstateId id)
1037 {
1038 const int chunk_x = static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH)));
1039 const int chunk_z = static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)));
1040
1041 auto it = terrain.find({ chunk_x, chunk_z });
1042
1043 // Can't set block in unloaded chunk
1044 if (it == terrain.end())
1045 {
1046 return;
1047 }
1048
1049 const Position set_pos(
1051 pos.y,
1053 );
1054
1055 it->second.SetBlock(set_pos, id);
1056
1057#if USE_GUI
1058 // If this block is on the edge, update neighbours chunks
1059 UpdateChunk(chunk_x, chunk_z, pos);
1060#endif
1061 }
1062
1063 const Blockstate* World::GetBlockImpl(const Position& pos) const
1064 {
1065 const int chunk_x = static_cast<int>(std::floor(pos.x / static_cast<double>(CHUNK_WIDTH)));
1066 const int chunk_z = static_cast<int>(std::floor(pos.z / static_cast<double>(CHUNK_WIDTH)));
1067
1068 auto it = terrain.find({ chunk_x, chunk_z });
1069
1070 // Can't get block in unloaded chunk
1071 if (it == terrain.end())
1072 {
1073 return nullptr;
1074 }
1075
1076 const Position chunk_pos(
1078 pos.y,
1080 );
1081
1082 const Blockstate* output = it->second.GetBlock(chunk_pos);
1083
1084 // As we are in a loaded chunk, nullptr means it's in an empty section
1085 // (or the chunk position was invalid but we know it's valid given how it's constructed above)
1086 // --> return air block instead of nullptr
1087 return output != nullptr ?
1088 output :
1089#if PROTOCOL_VERSION < 347 /* < 1.13 */
1090 AssetsManager::getInstance().GetBlockstate(std::make_pair(0, 0));
1091#else
1093#endif
1094 }
1095
1096#if PROTOCOL_VERSION < 719 /* < 1.16 */
1097 void World::SetCurrentDimensionImpl(const Dimension dimension)
1098#else
1099 void World::SetCurrentDimensionImpl(const std::string& dimension)
1100#endif
1101 {
1102 current_dimension = dimension;
1103#if PROTOCOL_VERSION > 404 /* > 1.13.2 */ && PROTOCOL_VERSION < 757 /* < 1.18 */
1104 delayed_light_updates.clear();
1105#endif
1106 }
1107
1109 {
1110#if PROTOCOL_VERSION < 757 /* < 1.18 */
1111 return 256;
1112#else
1114#endif
1115 }
1116
1118 {
1119#if PROTOCOL_VERSION < 757 /* < 1.18 */
1120 return 0;
1121#else
1123#endif
1124 }
1125
1126#if PROTOCOL_VERSION < 358 /* < 1.13 */
1127 void World::SetBiomeImpl(const int x, const int z, const unsigned char biome)
1128#elif PROTOCOL_VERSION < 552 /* < 1.15 */
1129 void World::SetBiomeImpl(const int x, const int z, const int biome)
1130#else
1131 void World::SetBiomeImpl(const int x, const int y, const int z, const int biome)
1132#endif
1133 {
1134 auto it = terrain.find({
1135 static_cast<int>(std::floor(x / static_cast<double>(CHUNK_WIDTH))),
1136 static_cast<int>(std::floor(z / static_cast<double>(CHUNK_WIDTH)))
1137 });
1138
1139 if (it != terrain.end())
1140 {
1141#if PROTOCOL_VERSION < 552 /* < 1.15 */
1142 it->second.SetBiome((x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, (z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, biome);
1143#else
1144 it->second.SetBiome((x % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, y, (z % CHUNK_WIDTH + CHUNK_WIDTH) % CHUNK_WIDTH, biome);
1145#endif
1146 }
1147 }
1148
1149 void World::UpdateChunk(const int x, const int z, const Position& pos)
1150 {
1151#if USE_GUI
1152 const Position this_chunk_position(
1154 pos.y,
1156 );
1157
1158 if (this_chunk_position.x > 0 && this_chunk_position.x < CHUNK_WIDTH - 1 &&
1159 this_chunk_position.z > 0 && this_chunk_position.z < CHUNK_WIDTH - 1)
1160 {
1161 return;
1162 }
1163
1164 Chunk* chunk = GetChunk(x, z);
1165
1166 if (chunk == nullptr)
1167 {
1168 // This should not happen
1169 LOG_WARNING("Trying to propagate chunk updates from a non loaded chunk");
1170 return;
1171 }
1172
1173 const Blockstate* blockstate = chunk->GetBlock(this_chunk_position);
1174
1175 if (this_chunk_position.x == 0)
1176 {
1177 Chunk* neighbour_chunk = GetChunk(x - 1, z);
1178 if (neighbour_chunk != nullptr)
1179 {
1180 neighbour_chunk->SetBlock(Position(CHUNK_WIDTH, this_chunk_position.y, this_chunk_position.z), blockstate);
1181 }
1182 }
1183
1184 if (this_chunk_position.x == CHUNK_WIDTH - 1)
1185 {
1186 Chunk* neighbour_chunk = GetChunk(x + 1, z);
1187 if (neighbour_chunk != nullptr)
1188 {
1189 neighbour_chunk->SetBlock(Position(-1, this_chunk_position.y, this_chunk_position.z), blockstate);
1190 }
1191 }
1192
1193 if (this_chunk_position.z == 0)
1194 {
1195 Chunk* neighbour_chunk = GetChunk(x, z - 1);
1196 if (neighbour_chunk != nullptr)
1197 {
1198 neighbour_chunk->SetBlock(Position(this_chunk_position.x, this_chunk_position.y, CHUNK_WIDTH), blockstate);
1199 }
1200 }
1201
1202 if (this_chunk_position.z == CHUNK_WIDTH - 1)
1203 {
1204 Chunk* neighbour_chunk = GetChunk(x, z + 1);
1205 if (neighbour_chunk != nullptr)
1206 {
1207 neighbour_chunk->SetBlock(Position(this_chunk_position.x, this_chunk_position.y, -1), blockstate);
1208 }
1209 }
1210#endif
1211 }
1212
1213 void World::UpdateChunk(const int x, const int z)
1214 {
1215#if USE_GUI
1216 Chunk* chunk = GetChunk(x, z);
1217 // This means this chunk has just beend added
1218 // Copy data for all edges between its neighbours
1219 if (chunk != nullptr)
1220 {
1221 chunk->UpdateNeighbour(GetChunk(x - 1, z), Orientation::West);
1222 chunk->UpdateNeighbour(GetChunk(x + 1, z), Orientation::East);
1223 chunk->UpdateNeighbour(GetChunk(x, z - 1), Orientation::North);
1224 chunk->UpdateNeighbour(GetChunk(x, z + 1), Orientation::South);
1225 }
1226 // This means this chunk has just been removed
1227 // Update all existing neighbours to let them know
1228 else
1229 {
1230 Chunk* neighbour_chunk;
1231 neighbour_chunk = GetChunk(x - 1, z);
1232 if (neighbour_chunk != nullptr)
1233 {
1234 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::East);
1235 }
1236 neighbour_chunk = GetChunk(x + 1, z);
1237 if (neighbour_chunk != nullptr)
1238 {
1239 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::West);
1240 }
1241 neighbour_chunk = GetChunk(x, z - 1);
1242 if (neighbour_chunk != nullptr)
1243 {
1244 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::South);
1245 }
1246 neighbour_chunk = GetChunk(x, z + 1);
1247 if (neighbour_chunk != nullptr)
1248 {
1249 neighbour_chunk->UpdateNeighbour(nullptr, Orientation::North);
1250 }
1251 }
1252#endif
1253 }
1254
1255 Chunk* World::GetChunk(const int x, const int z)
1256 {
1257 auto it = terrain.find({ x,z });
1258 if (it == terrain.end())
1259 {
1260 return nullptr;
1261 }
1262 return &it->second;
1263 }
1264
1265#if PROTOCOL_VERSION < 552 /* < 1.15 */
1266 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data,
1267 const int primary_bit_mask, const bool ground_up_continuous)
1268#elif PROTOCOL_VERSION < 755 /* < 1.17 */
1269 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data,
1270 const int primary_bit_mask)
1271#elif PROTOCOL_VERSION < 757 /* < 1.18 */
1272 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data,
1273 const std::vector<unsigned long long int>& primary_bit_mask)
1274#else
1275 void World::LoadDataInChunk(const int x, const int z, const std::vector<unsigned char>& data)
1276#endif
1277 {
1278 auto it = terrain.find({ x,z });
1279 if (it != terrain.end())
1280 {
1281#if PROTOCOL_VERSION < 552 /* < 1.15 */
1282 it->second.LoadChunkData(data, primary_bit_mask, ground_up_continuous);
1283#elif PROTOCOL_VERSION < 757 /* < 1.18 */
1284 it->second.LoadChunkData(data, primary_bit_mask);
1285#else
1286 it->second.LoadChunkData(data);
1287#endif
1288#if USE_GUI
1289 UpdateChunk(x, z);
1290#endif
1291 }
1292 }
1293
1294#if PROTOCOL_VERSION < 757 /* < 1.18 */
1295 void World::LoadBlockEntityDataInChunk(const int x, const int z, const std::vector<ProtocolCraft::NBT::Value>& block_entities)
1296#else
1297 void World::LoadBlockEntityDataInChunk(const int x, const int z, const std::vector<ProtocolCraft::BlockEntityInfo>& block_entities)
1298#endif
1299 {
1300 auto it = terrain.find({ x,z });
1301 if (it != terrain.end())
1302 {
1303 it->second.LoadChunkBlockEntitiesData(block_entities);
1304 }
1305 }
1306
1307#if PROTOCOL_VERSION > 551 /* > 1.14.4 */ && PROTOCOL_VERSION < 757 /* < 1.18 */
1308 void World::LoadBiomesInChunk(const int x, const int z, const std::vector<int>& biomes)
1309 {
1310 auto it = terrain.find({ x,z });
1311 if (it != terrain.end())
1312 {
1313 it->second.SetBiomes(biomes);
1314 }
1315 }
1316#endif
1317
1318#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
1319#if PROTOCOL_VERSION < 719 /* < 1.16 */
1320 void World::UpdateChunkLight(const int x, const int z, const Dimension dim, const int light_mask, const int empty_light_mask,
1321 const std::vector<std::vector<char>>& data, const bool sky)
1322#elif PROTOCOL_VERSION < 755 /* < 1.17 */
1323 void World::UpdateChunkLight(const int x, const int z, const std::string& dim, const int light_mask, const int empty_light_mask,
1324 const std::vector<std::vector<char>>& data, const bool sky)
1325#else
1326 void World::UpdateChunkLight(const int x, const int z, const std::string& dim,
1327 const std::vector<unsigned long long int>& light_mask, const std::vector<unsigned long long int>& empty_light_mask,
1328 const std::vector<std::vector<char>>& data, const bool sky)
1329#endif
1330 {
1331 auto it = terrain.find({ x, z });
1332
1333 if (it == terrain.end())
1334 {
1335 LOG_WARNING("Trying to update lights in an unloaded chunk: (" << x << "," << z << ")");
1336 return;
1337 }
1338
1339 int counter_arrays = 0;
1340 Position pos1, pos2;
1341
1342 const int num_sections = GetHeightImpl() / 16 + 2;
1343
1344 for (int i = 0; i < num_sections; ++i)
1345 {
1346 const int section_Y = i - 1;
1347
1348 // Sky light
1349#if PROTOCOL_VERSION < 755 /* < 1.17 */
1350 if ((light_mask >> i) & 1)
1351#else
1352 if ((light_mask.size() > i / 64) && (light_mask[i / 64] >> (i % 64)) & 1)
1353#endif
1354 {
1355 if (i > 0 && i < num_sections - 1)
1356 {
1357 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
1358 {
1359 pos1.y = block_y + section_Y * SECTION_HEIGHT + GetMinYImpl();
1360 pos2.y = pos1.y;
1361 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
1362 {
1363 pos1.z = block_z;
1364 pos2.z = block_z;
1365 for (int block_x = 0; block_x < CHUNK_WIDTH; block_x += 2)
1366 {
1367 pos1.x = block_x;
1368 pos2.x = block_x + 1;
1369 const char two_light_values = data[counter_arrays][(block_y * CHUNK_WIDTH * CHUNK_WIDTH + block_z * CHUNK_WIDTH + block_x) / 2];
1370
1371 if (sky)
1372 {
1373 it->second.SetSkyLight(pos1, two_light_values & 0x0F);
1374 it->second.SetSkyLight(pos2, (two_light_values >> 4) & 0x0F);
1375 }
1376 else
1377 {
1378 it->second.SetBlockLight(pos1, two_light_values & 0x0F);
1379 it->second.SetBlockLight(pos2, (two_light_values >> 4) & 0x0F);
1380 }
1381 }
1382 }
1383 }
1384 }
1385 counter_arrays++;
1386 }
1387#if PROTOCOL_VERSION < 755 /* < 1.17 */
1388 else if ((empty_light_mask >> i) & 1)
1389#else
1390 else if ((empty_light_mask.size() > i / 64) && (empty_light_mask[i / 64] >> (i % 64)) & 1)
1391#endif
1392 {
1393 if (i > 0 && i < num_sections - 1)
1394 {
1395 for (int block_y = 0; block_y < SECTION_HEIGHT; ++block_y)
1396 {
1397 pos1.y = block_y + section_Y * SECTION_HEIGHT + GetMinYImpl();
1398 pos2.y = pos1.y;
1399 for (int block_z = 0; block_z < CHUNK_WIDTH; ++block_z)
1400 {
1401 pos1.z = block_z;
1402 pos2.z = block_z;
1403 for (int block_x = 0; block_x < CHUNK_WIDTH; block_x += 2)
1404 {
1405 pos1.x = block_x;
1406 pos2.x = block_x + 1;
1407 if (sky)
1408 {
1409 it->second.SetSkyLight(pos1, 0);
1410 it->second.SetSkyLight(pos2, 0);
1411 }
1412 else
1413 {
1414 it->second.SetBlockLight(pos1, 0);
1415 it->second.SetBlockLight(pos2, 0);
1416 }
1417 }
1418 }
1419 }
1420 }
1421 }
1422 }
1423 }
1424#endif
1425
1426#if PROTOCOL_VERSION < 719 /* < 1.16 */
1427 size_t World::GetDimIndex(const Dimension dim)
1428#else
1429 size_t World::GetDimIndex(const std::string& dim)
1430#endif
1431 {
1432 auto it = dimension_index_map.find(dim);
1433
1434 if (it == dimension_index_map.end())
1435 {
1436 index_dimension_map.insert({ dimension_index_map.size(), dim });
1437 it = dimension_index_map.insert({ dim, dimension_index_map.size() }).first;
1438 }
1439 return it->second;
1440 }
1441} // 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:793
void LoadChunkData(const std::vector< unsigned char > &data)
Definition Chunk.cpp:314
const Blockstate * GetBlock(const Position &pos) const
Definition Chunk.cpp:507
void SetBlock(const Position &pos, const Blockstate *block)
Definition Chunk.cpp:530
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:1020
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 SetDimensionUltrawarm(const std::string &dimension, const bool ultrawarm)
Set ultrawarm bool for given dimension.
Definition World.cpp:480
void LoadDataInChunk(const int x, const int z, const std::vector< unsigned char > &data)
Definition World.cpp:1275
void SetCurrentDimensionImpl(const std::string &dimension)
Definition World.cpp:1099
Chunk * GetChunk(const int x, const int z)
Get a pointer to a chunk.
Definition World.cpp:1255
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:1326
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
int GetMinYImpl() const
Definition World.cpp:1117
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
bool IsInUltraWarmDimension() const
Check if current dimension is Ultrawarm.
Definition World.cpp:62
void UpdateChunk(const int chunk_x, const int chunk_z, const Position &pos)
Progagate chunk update at pos to neighbouring chunks.
Definition World.cpp:1149
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:1063
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
void SetBlockImpl(const Position &pos, const BlockstateId id)
Definition World.cpp:1036
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:1108
virtual void Handle(ProtocolCraft::ClientboundLoginPacket &msg) override
Definition World.cpp:708
void SetBiomeImpl(const int x, const int y, const int z, const int biome)
Definition World.cpp:1131
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, bool > dimension_ultrawarm
Definition World.hpp:404
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:1297
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:1429
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:977
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 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:282
unsigned int BlockstateId
double SqrDist(const Vector3 &v) const
Definition Vector3.hpp:192
Vector3 Abs() const
Definition Vector3.hpp:199