Botcraft 1.21.5
Loading...
Searching...
No Matches
AssetsManager.cpp
Go to the documentation of this file.
1#include <fstream>
2#include <sstream>
3#include <filesystem>
4#include <set>
5
10
11#if USE_GUI
13#endif
14
16
17using namespace ProtocolCraft;
18
19namespace Botcraft
20{
22 {
23 static AssetsManager instance;
24
25 return instance;
26 }
27
29 {
30 std::filesystem::path expected_mc_path = ASSETS_PATH + std::string("/minecraft");
31 if (!std::filesystem::is_directory(expected_mc_path))
32 {
33 LOG_FATAL("Minecraft assets folder expected at " << std::filesystem::absolute(expected_mc_path) << " but not found");
34 throw std::runtime_error("Minecraft assets not found");
35 }
36 LOG_INFO("Loading blocks from file...");
38 LOG_INFO("Done!");
39 LOG_INFO("Loading biomes from file...");
41 LOG_INFO("Done!");
42 LOG_INFO("Loading items from file...");
44 LOG_INFO("Done!");
45#if USE_GUI
46 LOG_INFO("Loading textures...");
47 atlas = std::make_unique<Renderer::Atlas>();
49 LOG_INFO("Done!");
50 LOG_INFO("Updating models with Atlas data...");
52 LOG_INFO("Done!");
53#endif
54 LOG_INFO("Clearing cache from memory...");
56 LOG_INFO("Done!");
57 }
58
59#if PROTOCOL_VERSION < 347 /* < 1.13 */
60 const std::unordered_map<int, std::unordered_map<unsigned char, std::unique_ptr<Blockstate> > >& AssetsManager::Blockstates() const
61#else
62 const std::unordered_map<int, std::unique_ptr<Blockstate> >& AssetsManager::Blockstates() const
63#endif
64 {
65 return blockstates;
66 }
67
69 {
70#if PROTOCOL_VERSION < 347 /* < 1.13 */
71 auto it = blockstates.find(id.first);
72 if (it != blockstates.end())
73 {
74 auto it2 = it->second.find(id.second);
75 if (it2 != it->second.end())
76 {
77 return it2->second.get();
78 }
79 else
80 {
81 return it->second.at(0).get();
82 }
83 }
84 else
85 {
86 return blockstates.at(-1).at(0).get();
87 }
88#else
90 {
91 const Blockstate* block = flattened_blockstates[id];
92 if (block != nullptr)
93 {
94 return block;
95 }
96 }
97 auto it = blockstates.find(id);
98 if (it != blockstates.end())
99 {
100 return it->second.get();
101 }
102 else
103 {
104 return blockstates.at(-1).get();
105 }
106#endif
107 }
108
109 const Blockstate* AssetsManager::GetBlockstate(const std::string& name) const
110 {
111#if PROTOCOL_VERSION < 347 /* < 1.13 */
112 for (const auto& [id, m] : blockstates)
113 {
114 const Blockstate* block = m.at(0).get();
115 if (block->GetName() == name)
116 {
117 return block;
118 }
119 }
120 return blockstates.at(-1).at(0).get();
121#else
122 for (const auto& block : flattened_blockstates)
123 {
124 if (block->GetName() == name)
125 {
126 return block;
127 }
128 }
129 return blockstates.at(-1).get();
130#endif
131 }
132
133 std::vector<const Blockstate*> AssetsManager::GetBlockstates(const std::string& name) const
134 {
135 std::vector<const Blockstate*> output;
136#if PROTOCOL_VERSION < 347 /* < 1.13 */
137 for (const auto& [id, m] : blockstates)
138 {
139 for (const auto& [id2, block] : m)
140 {
141 if (block->GetName() == name)
142 {
143 output.push_back(block.get());
144 }
145 }
146 }
147#else
148 for (const auto& block : flattened_blockstates)
149 {
150 if (block->GetName() == name)
151 {
152 output.push_back(block);
153 }
154 }
155#endif
156 return output;
157 }
158
159#if PROTOCOL_VERSION < 358 /* < 1.13 */
160 const std::unordered_map<unsigned char, std::unique_ptr<Biome> >& AssetsManager::Biomes() const
161#else
162 const std::unordered_map<int, std::unique_ptr<Biome> >& AssetsManager::Biomes() const
163#endif
164 {
165 return biomes;
166 }
167
168#if PROTOCOL_VERSION < 358 /* < 1.13 */
169 const Biome* AssetsManager::GetBiome(const unsigned char id) const
170#else
171 const Biome* AssetsManager::GetBiome(const int id) const
172#endif
173 {
174 auto it = biomes.find(id);
175 if (it != biomes.end())
176 {
177 return it->second.get();
178 }
179 else
180 {
181 return nullptr;
182 }
183 }
184
185 const std::unordered_map<ItemId, std::unique_ptr<Item> >& AssetsManager::Items() const
186 {
187 return items;
188 }
189
190 const Item* AssetsManager::GetItem(const ItemId id) const
191 {
192 auto it = items.find(id);
193 if (it != items.end())
194 {
195 return it->second.get();
196 }
197 else
198 {
199#if PROTOCOL_VERSION < 347 /* < 1.13 */
200 it = items.find({ id.first, 0 });
201 if (it != items.end())
202 {
203 return it->second.get();
204 }
205#endif
206 return nullptr;
207 }
208 }
209
210 const Item* AssetsManager::GetItem(const std::string& item_name) const
211 {
212 for (const auto& p : items)
213 {
214 if (p.second->GetName() == item_name)
215 {
216 return p.second.get();
217 }
218 }
219 return nullptr;
220 }
221
222 ItemId AssetsManager::GetItemID(const std::string& item_name) const
223 {
224 for (const auto& p : items)
225 {
226 if (p.second->GetName() == item_name)
227 {
228 return p.first;
229 }
230 }
231
232#if PROTOCOL_VERSION < 347 /* < 1.13 */
233 return { -1, 0 };
234#else
235 return -1;
236#endif
237 }
238
239#if USE_GUI
241 {
242 return atlas.get();
243 }
244#endif
245
247 {
248 if (s == "wood")
249 {
250 return ToolMaterial::Wood;
251 }
252 else if (s == "gold")
253 {
254 return ToolMaterial::Gold;
255 }
256 else if (s == "stone")
257 {
258 return ToolMaterial::Stone;
259 }
260 else if (s == "iron")
261 {
262 return ToolMaterial::Iron;
263 }
264 else if (s == "diamond")
265 {
267 }
268#if PROTOCOL_VERSION > 578 /* > 1.15.2 */
269 else if (s == "netherite")
270 {
272 }
273#endif
274 return ToolMaterial::None;
275 }
276
277 ToolType GetToolTypeFromString(const std::string& s)
278 {
279 if (s == "axe")
280 {
281 return ToolType::Axe;
282 }
283 else if (s == "hoe")
284 {
285 return ToolType::Hoe;
286 }
287 else if (s == "pickaxe")
288 {
289 return ToolType::Pickaxe;
290 }
291 else if (s == "shears")
292 {
293 return ToolType::Shears;
294 }
295 else if (s == "shovel")
296 {
297 return ToolType::Shovel;
298 }
299 else if (s == "sword")
300 {
301 return ToolType::Sword;
302 }
303 return ToolType::None;
304 }
305
307 {
308 std::unordered_map<std::string, BlockstateProperties> blockstate_properties;
309 std::unordered_map<std::string, std::string> textures;
310 std::unordered_map<std::string, std::string> rendering;
311#if PROTOCOL_VERSION < 347 /* < 1.13 */
312 std::unordered_map<std::string, std::unordered_map<int, TintType> > tint_types;
313#else
314 std::unordered_map<std::string, TintType> tint_types;
315#endif
316 const std::string info_file_path = ASSETS_PATH + std::string("/custom/Blocks_info.json");
317
318 Json::Value json;
319 try
320 {
321 std::ifstream file(info_file_path);
322 file >> json;
323 file.close();
324 }
325 catch (const std::runtime_error& e)
326 {
327 LOG_ERROR("Error reading info block file at " << info_file_path << '\n' << e.what());
328 return;
329 }
330
331 if (!json.contains("colliders"))
332 {
333 LOG_ERROR("Error reading info block file at " << info_file_path << " (no colliders found)");
334 return;
335 }
336 const Json::Value& colliders = json["colliders"];
337
338 if (!json.contains("blocks"))
339 {
340 LOG_ERROR("Error reading info block file at " << info_file_path << " (no blocks found)");
341 return;
342 }
343 //Load all the info
344 for (const auto& info : json["blocks"].get_array())
345 {
346 std::string name = "";
347
348 if (!info.contains("name") || !info["name"].is_string())
349 {
350 LOG_ERROR("Error with an element of blockstates info: \n" << info.Dump());
351 continue;
352 }
353 else
354 {
355 name = info["name"].get_string();
356 blockstate_properties[name].name = name;
357 }
358
359 BlockstateProperties& current_block_properties = blockstate_properties[name];
360
361 if (info.contains("air") && info["air"].is_bool())
362 {
363 current_block_properties.air = info["air"].get<bool>();
364 }
365
366 current_block_properties.solid = info.contains("solid") ? info["solid"] : false;
367
368 if (info.contains("transparent") && info["transparent"].is_bool())
369 {
370 current_block_properties.transparent = info["transparent"].get<bool>();
371 }
372
373 if (info.contains("lava") && info["lava"].is_bool())
374 {
375 current_block_properties.lava = info["lava"].get<bool>();
376 }
377
378 if (info.contains("water") && info["water"].is_bool())
379 {
380 current_block_properties.water = info["water"].get<bool>();
381 }
382
383 if (info.contains("waterlogged") && info["waterlogged"].is_bool())
384 {
385 current_block_properties.waterlogged = info["waterlogged"].get<bool>();
386 }
387 else
388 {
389 current_block_properties.waterlogged = "waterlogged=true";
390 }
391
392 if (info.contains("climbable") && info["climbable"].is_bool())
393 {
394 current_block_properties.climbable = info["climbable"].get<bool>();
395 }
396
397 if (info.contains("hazardous") && info["hazardous"].is_bool())
398 {
399 current_block_properties.hazardous = info["hazardous"].get<bool>();
400 }
401
402#if PROTOCOL_VERSION < 393 /* < 1.13 */
403 current_block_properties.slime = name == "minecraft:slime";
404#else
405 current_block_properties.slime = name == "minecraft:slime_block";
406#endif
407
408#if PROTOCOL_VERSION < 393 /* < 1.13 */
409 current_block_properties.bed = name == "minecraft:bed";
410#else
411 current_block_properties.bed = Utilities::EndsWith(name, "_bed");
412#endif
413
414 current_block_properties.soul_sand = name == "minecraft:soul_sand";
415
416#if PROTOCOL_VERSION > 498 /* > 1.14.4 */
417 current_block_properties.honey = name == "minecraft:honey_block";
418#endif
419
420#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
421 current_block_properties.scaffolding = name == "minecraft:scaffolding";
422#endif
423
424#if PROTOCOL_VERSION < 393 /* < 1.13 */
425 current_block_properties.cobweb = name == "minecraft:web";
426#else
427 current_block_properties.cobweb = name == "minecraft:cobweb";
428#endif
429
430#if PROTOCOL_VERSION > 340 /* > 1.12.2 */
431 current_block_properties.up_bubble_column = info.contains("up_bubble_column") ? info["up_bubble_column"] : false;
432 current_block_properties.down_bubble_column = info.contains("down_bubble_column") ? info["down_bubble_column"] : false;
433#endif
434
435#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
436 current_block_properties.berry_bush = name == "minecraft:sweet_berry_bush";
437#endif
438
439#if PROTOCOL_VERSION > 754 /* > 1.16.5 */
440 current_block_properties.powder_snow = name == "minecraft:powder_snow";
441#endif
442
443 if (info.contains("horizontal_offset") && info["horizontal_offset"].is_number())
444 {
445 blockstate_properties[name].horizontal_offset = info["horizontal_offset"].get_number<float>();
446 }
447
448 if (info.contains("hardness") && info["hardness"].is_number())
449 {
450 blockstate_properties[name].hardness = info["hardness"].get_number<float>();
451 }
452
453 if (info.contains("friction") && info["friction"].is_number())
454 {
455 blockstate_properties[name].friction = info["friction"].get_number<float>();
456 }
457
458 if (info.contains("colliders") && info["colliders"].is_number() && info["colliders"].get<int>() < colliders.size())
459 {
460 blockstate_properties[name].colliders = colliders[info["colliders"].get<int>()];
461 }
462
463 if (!info.contains("render") || !info["render"].is_string())
464 {
465 rendering[name] = "block";
466 }
467 else
468 {
469 rendering[name] = info["render"].get_string();
470 }
471
472 // Get breaking tools info
473 if (info.contains("tools") && info["tools"].is_array())
474 {
475 for (const auto& tool : info["tools"].get_array())
476 {
477 if (tool.is_string())
478 {
479 const std::string tool_name = tool.get_string();
480 if (tool_name == "any")
481 {
482 blockstate_properties[name].any_tool_harvest = true;
483 }
484 else
485 {
486 ToolType tool_type = GetToolTypeFromString(tool_name);
487 ToolMaterial min_material = (tool_type == ToolType::Shears || tool_type == ToolType::Sword) ? ToolMaterial::None : ToolMaterial::Wood;
488 blockstate_properties[name].best_tools.push_back(
489 BestTool{
490 tool_type,
491 min_material,
492 1.0f
493 });
494 }
495 }
496 else if (tool.is_object())
497 {
498 if (tool.contains("tool") && tool["tool"].is_string())
499 {
500 BestTool best_tool;
501 best_tool.tool_type = GetToolTypeFromString(tool["tool"].get_string());
502
503 if (tool.contains("min_material") && tool["min_material"].is_string())
504 {
505 best_tool.min_material = GetToolMaterialFromString(tool["min_material"].get_string());
506 }
507 else
508 {
509 // Default min material
511 }
512
513 if (tool.contains("multiplier") && tool["multiplier"].is_number())
514 {
515 best_tool.multiplier = tool["multiplier"].get<float>();
516 }
517 else
518 {
519 // Default multiplier
520 best_tool.multiplier = 1.0f;
521 }
522 blockstate_properties[name].best_tools.push_back(best_tool);
523 }
524 }
525 }
526 }
527
528 // Get texture info (used for fluids)
529 if (info.contains("texture") && info["texture"].is_string())
530 {
531 textures[name] = info["texture"].get_string();
532 }
533
534 // Get the tint type info (for grass/leaves/water ...)
535 if (info.contains("tintType") && info["tintType"].is_string())
536 {
537 std::string tint_type_string;
538 TintType tint_type = TintType::None;
539 tint_type_string = info["tintType"].get_string();
540 if (tint_type_string == "grass")
541 {
542 tint_type = TintType::Grass;
543 }
544 else if (tint_type_string == "leaves")
545 {
546 tint_type = TintType::Leaves;
547 }
548 else if (tint_type_string == "water")
549 {
550 tint_type = TintType::Water;
551 }
552 else if (tint_type_string == "redstone")
553 {
554 tint_type = TintType::Redstone;
555 }
556
557#if PROTOCOL_VERSION < 347 /* < 1.13 */
558 tint_types[name] = std::unordered_map<int, TintType>({ { -1, tint_type } });
559#else
560 tint_types[name] = tint_type;
561#endif
562 }
563#if PROTOCOL_VERSION < 347 /* < 1.13 */
564 // Before the flattening, we could have different tints for different metadata
565 else if (info.contains("tintTypes") && info["tintType"].is_object())
566 {
567 tint_types[name] = std::unordered_map<int, TintType>({});
568 for (const auto& [key, val]: info["tintType"].get_object())
569 {
570 TintType tint_type = TintType::None;
571 std::string tint_type_string = val.get_string();
572
573 if (tint_type_string == "grass")
574 {
575 tint_type = TintType::Grass;
576 }
577 else if (tint_type_string == "leaves")
578 {
579 tint_type = TintType::Leaves;
580 }
581 else if (tint_type_string == "water")
582 {
583 tint_type = TintType::Water;
584 }
585 else if (tint_type_string == "redstone")
586 {
587 tint_type = TintType::Redstone;
588 }
589
590 tint_types[name][std::stoi(key)] = tint_type;
591 }
592 }
593#endif
594 else
595 {
596#if PROTOCOL_VERSION < 347 /* < 1.13 */
597 tint_types[name] = std::unordered_map<int, TintType>({ { -1, TintType::None } });
598#else
599 tint_types[name] = TintType::None;
600#endif
601 }
602 }
603
604 // Add a default block
605#if PROTOCOL_VERSION < 347 /* < 1.13 */
606 blockstates[-1];
607 blockstates[-1][0] = std::make_unique<Blockstate>(
609 -1, //id
610 0, //metadata
611#else
612 blockstates[-1] = std::make_unique<Blockstate>(
614 -1, //id
615#endif
616 false, //air
617 true, //solid
618 false, //transparent
619 false, //lava
620 false, //water
621 false, //waterlogged
622 false, //climbable
623 false, //hazardous
624 false, //any_tool_harvest
625 false, //slime
626 false, //bed
627 false, //soul_sand
628 false, //honey
629 false, //scaffolding
630 false, //cobweb
631 false, //up_bubble_column
632 false, //down_bubble_column
633 false, //berry_bush
634 false, //powder_snow
635 0.0f, //horizontal_offset
636 -2.0f, //hardness
637 0.6f, //friction
638 false, //custom
639 TintType::None, //tint_type
640 "default", //name
641 }
642 );
643
644 const std::string file_path = ASSETS_PATH + std::string("/custom/Blocks.json");
645
646 try
647 {
648 std::ifstream file(file_path);
649 file >> json;
650 file.close();
651 }
652 catch (const std::runtime_error& e)
653 {
654 LOG_ERROR("Error reading block file at " << file_path << '\n' << e.what());
655 return;
656 }
657
658#if PROTOCOL_VERSION < 347 /* < 1.13 */
659
660 if (!json.is_array())
661 {
662 LOG_ERROR("Error block file at " << file_path << " is not a json array as expected");
663 return;
664 }
665
666 //Load all the blockstates from JSON file
667 for (const auto& element : json.get_array())
668 {
669 const std::string& blockstate_name = element["name"].get_string();
670
671 if (rendering.find(blockstate_name) == rendering.end() ||
672 blockstate_properties.find(blockstate_name) == blockstate_properties.end() ||
673 tint_types.find(blockstate_name) == tint_types.end())
674 {
675 LOG_ERROR("Error trying to get information for blockstate " << blockstate_name);
676 continue;
677 }
678
679 BlockstateProperties& props = blockstate_properties[blockstate_name];
680 props.id = -1;
681 props.metadata = 0;
682 props.variables = std::vector<std::string>();
683
684 if (element.contains("id") && element["id"].is_number())
685 {
686 props.id = element["id"].get_number<int>();
687 }
688
689 const std::string& render = rendering[blockstate_name];
690
691 const bool fluid_falling = props.metadata & 0b1000;
692 const int fluid_level = 1 + (props.metadata & 0b111);
693
694 if (render == "none")
695 {
697 props.custom = false;
698 props.path = "none";
699 blockstates[props.id][0] = std::make_unique<Blockstate>(props);
700 }
701 else if (render == "block" || render == "fluid" || render == "other")
702 {
703 if (render == "fluid" && textures.find(blockstate_name) == textures.end())
704 {
705 LOG_ERROR("Error, blockstate " << blockstate_name << " is a fluid, but it does not have a texture file specified in Blocks_info.json");
706 }
707
708 if (!element.contains("metadata"))
709 {
710 LOG_ERROR("Error, no metadata found for block " << blockstate_name);
711 }
712
713 for (const auto& metadata_obj : element["metadata"].get_array())
714 {
715 props.metadata = metadata_obj["value"].get_number<int>();
716 props.path = metadata_obj["blockstate"].get_string();
717 props.variables = std::vector<std::string>();
718 if (metadata_obj.contains("variables"))
719 {
720 for (const auto& s : metadata_obj["variables"].get_array())
721 {
722 props.variables.push_back(s.get_string());
723 }
724 }
725
727 if (tint_types.find(blockstate_name) != tint_types.end())
728 {
729 if (tint_types[blockstate_name].find(-1) != tint_types[blockstate_name].end())
730 {
731 props.tint_type = tint_types[blockstate_name][-1];
732 }
733 else if (tint_types[blockstate_name].find(props.metadata) != tint_types[blockstate_name].end())
734 {
735 props.tint_type = tint_types[blockstate_name][props.metadata];
736 }
737 }
738
739 auto check_it = blockstates.find(props.id);
740 if (check_it != blockstates.end())
741 {
742 if (render == "fluid")
743 {
744 props.custom = false;
745 blockstates[props.id][props.metadata] = std::make_unique<Blockstate>(props, Model::GetModel(fluid_falling ? 1.0 : (1.0 - fluid_level / 9.0), textures[blockstate_name]));
746 }
747 else
748 {
749 props.custom = render == "other";
750 blockstates[props.id][props.metadata] = std::make_unique<Blockstate>(props);
751 }
752 }
753 else
754 {
755 // We want to be sure that blockstates[id][0] exists
756 if (render == "fluid")
757 {
758 blockstates[props.id];
759 blockstates[props.id][0] = std::make_unique<Blockstate>(props, Model::GetModel(fluid_falling ? 1.0 : (1.0 - fluid_level / 9.0), textures[blockstate_name]));
760 blockstates[props.id][props.metadata] = std::make_unique<Blockstate>(props, Model::GetModel(fluid_falling ? 1.0 : (1.0 - fluid_level / 9.0), textures[blockstate_name]));
761 }
762 else
763 {
764 props.custom = render == "other";
765 blockstates[props.id];
766 blockstates[props.id][0] = std::make_unique<Blockstate>(props);
767 blockstates[props.id][props.metadata] = std::make_unique<Blockstate>(props);
768 }
769 }
770 }
771 }
772 }
773#else
774 if (!json.is_object())
775 {
776 LOG_ERROR("Error block file at " << file_path << " is not a json object as expected");
777 return;
778 }
779
780 for (const auto& [blockstate_name, element]: json.get_object())
781 {
782 if (!element.contains("states") || !element["states"].is_array())
783 {
784 continue;
785 }
786
787 for (const auto& blockstate : element["states"].get_array())
788 {
789 if (rendering.find(blockstate_name) == rendering.end() ||
790 blockstate_properties.find(blockstate_name) == blockstate_properties.end() ||
791 tint_types.find(blockstate_name) == tint_types.end())
792 {
793 LOG_ERROR("Error trying to get information for blockstate " << blockstate_name);
794 continue;
795 }
796
797 BlockstateProperties& props = blockstate_properties[blockstate_name];
798 props.id = -1;
799 props.variables = std::vector<std::string>();
800
801 if (blockstate.contains("id") && blockstate["id"].is_number())
802 {
803 props.id = blockstate["id"].get_number<int>();
804 }
805 else
806 {
807 LOG_ERROR("Error trying to read the id of block " << blockstate_name);
808 continue;
809 }
810
811 const std::string& render = rendering[blockstate_name];
812
813 // Read the properties (if any)
814 int fluid_level = 0;
815 bool fluid_falling = false;
816 if (blockstate.contains("properties") && blockstate["properties"].is_object())
817 {
818 for (const auto& [key, val]: blockstate["properties"].get_object())
819 {
820 props.variables.push_back(key + "=" + val.get_string());
821 if (render == "fluid" && key == "level")
822 {
823 fluid_level = std::stoi(val.get_string());
824 fluid_falling = fluid_level & 0b1000;
825 fluid_level = 1 + (fluid_level & 0b111);
826 }
827 }
828 }
829
830 if (render == "none")
831 {
833 props.custom = false;
834 props.path = "none";
835 blockstates[props.id] = std::make_unique<Blockstate>(props);
836 }
837 else if (render == "fluid")
838 {
839 if (textures.find(blockstate_name) == textures.end())
840 {
841 LOG_ERROR("Error, blockstate " << blockstate_name << " is a fluid, but it does not have a texture file specified in Blocks_info.json");
842 }
843 else
844 {
845 props.tint_type = tint_types[blockstate_name];
846 blockstates[props.id] = std::make_unique<Blockstate>(props, Model::GetModel(fluid_falling ? 1.0 : (1.0 - fluid_level / 9.0), textures[blockstate_name]));
847 }
848 }
849 else if (render == "block" || render == "other")
850 {
851 props.custom = render == "other";
852 props.tint_type = tint_types[blockstate_name];
853 props.path = blockstate_name.substr(10);
854 blockstates[props.id] = std::make_unique<Blockstate>(props);
855 }
856 else
857 {
858 LOG_WARNING("No known rendering method defined for block: " << blockstate_name);
859 }
860 }
861 }
863#endif
864 }
865
866#if PROTOCOL_VERSION > 340 /* > 1.12.2 */
868 {
869 int max_id = -1;
870 for (const auto& [id, block] : blockstates)
871 {
872 max_id = std::max(id, max_id);
873 }
874 if (max_id > std::numeric_limits<unsigned short>::max())
875 {
876 LOG_ERROR("Too many blockstates, compact chunk representation will be broken");
877 }
878 flattened_blockstates = std::vector<const Blockstate*>(max_id + 1, nullptr);
880 for (const auto& [id, block] : blockstates)
881 {
882 if (id >= 0)
883 {
884 flattened_blockstates[id] = block.get();
885 }
886 }
887 }
888#endif
889
891 {
892 std::string file_path = ASSETS_PATH + std::string("/custom/Biomes.json");
893
894 Json::Value json;
895 try
896 {
897 std::ifstream file(file_path);
898 file >> json;
899 file.close();
900 }
901 catch (const std::runtime_error& e)
902 {
903 LOG_ERROR("Error reading biome file at " << file_path << '\n' << e.what());
904 return;
905 }
906
907 if (!json.is_array())
908 {
909 LOG_ERROR("Error biome file at " << file_path << " is not a json object as expected");
910 return;
911 }
912
913 //Load all the biomes from JSON file
914 int max_biome_id = 0;
915 for (const auto& element : json.get_array())
916 {
917 unsigned char id = 0;
918 std::string name = "";
919 float rainfall = 0.0f;
920 float temperature = 0.0f;
921 BiomeType biome_type = BiomeType::Classic;
922
923 if (element.contains("id"))
924 {
925 id = element["id"].get_number<unsigned char>();
926 }
927
928 if (element.contains("name"))
929 {
930 name = element["name"].get_string();
931 }
932
933 if (element.contains("rainfall"))
934 {
935 rainfall = element["rainfall"].get_number<float>();
936 }
937
938 if (element.contains("temperature"))
939 {
940 temperature = element["temperature"].get_number<float>();
941 }
942
943 if (element.contains("biomeType"))
944 {
945 const std::string& string_biome_type = element["biomeType"].get_string();
946 if (string_biome_type == "Swamp")
947 {
948 biome_type = BiomeType::Swamp;
949 }
950 else if (string_biome_type == "Badlands")
951 {
952 biome_type = BiomeType::Badlands;
953 }
954 else if (string_biome_type == "DarkForest")
955 {
956 biome_type = BiomeType::DarkForest;
957 }
958#if PROTOCOL_VERSION >= 393 /* >= 1.13 */
959 else if (string_biome_type == "WarmOcean")
960 {
961 biome_type = BiomeType::WarmOcean;
962 }
963 else if (string_biome_type == "LukewarmOcean")
964 {
965 biome_type = BiomeType::LukewarmOcean;
966 }
967 else if (string_biome_type == "ColdOcean")
968 {
969 biome_type = BiomeType::ColdOcean;
970 }
971 else if (string_biome_type == "FrozenOcean")
972 {
973 biome_type = BiomeType::FrozenOcean;
974 }
975#endif
976#if PROTOCOL_VERSION > 767 /* > 1.20.1 */
977 else if (string_biome_type == "PaleGarden")
978 {
979 biome_type = BiomeType::PaleGarden;
980 }
981#endif
982 }
983
984 biomes[id] = std::make_unique<Biome>(name, temperature, rainfall, biome_type);
985 }
986 }
987
989 {
990 std::string file_path = ASSETS_PATH + std::string("/custom/Items.json");
991
992 Json::Value json;
993 try
994 {
995 std::ifstream file(file_path);
996 file >> json;
997 file.close();
998 }
999 catch (const std::runtime_error& e)
1000 {
1001 LOG_ERROR("Error reading item file at " << file_path << '\n' << e.what());
1002 return;
1003 }
1004
1005 // Add a default item
1006 ItemProperties props{
1007#if PROTOCOL_VERSION < 347 /* < 1.13 */
1008 {-1, 0}, //id
1009#else
1010 -1, //id
1011#endif
1012 "default", //name
1013 64, //stack_size
1014 -1, // durability
1015 };
1016#if PROTOCOL_VERSION < 347 /* < 1.13 */
1017 items[{-1, 0}] = std::make_unique<Item>(props);
1018#else
1019 items[-1] = std::make_unique<Item>(props);
1020#endif
1021
1022 //Load all the items from JSON file
1023 for (const auto& [key, properties]: json.get_object())
1024 {
1025 // Read name
1026 props.name = key;
1027
1028 if (!properties.contains("id") || !properties["id"].is_number())
1029 {
1030 continue;
1031 }
1032#if PROTOCOL_VERSION < 347 /* < 1.13 */
1033 props.id.first = properties["id"].get_number<int>();
1034#else
1035 props.id = properties["id"].get_number<int>();
1036#endif
1037
1038 if (properties.contains("stack_size") && properties["stack_size"].is_number())
1039 {
1040 props.stack_size = properties["stack_size"].get_number<unsigned char>();
1041 }
1042 // Default value
1043 else
1044 {
1045 props.stack_size = 64;
1046 }
1047
1048 if (properties.contains("durability") && properties["durability"].is_number())
1049 {
1050 props.durability = properties["durability"].get_number<int>();
1051 }
1052 // Default value
1053 else
1054 {
1055 props.durability = -1;
1056 }
1057
1058#if PROTOCOL_VERSION < 347 /* < 1.13 */
1059 if (!properties.contains("damage_id") || !properties["damage_id"].is_number())
1060 {
1061 continue;
1062 }
1063 props.id.second = properties["damage_id"].get_number<unsigned char>();
1064#endif
1065 items[props.id] = std::make_unique<Item>(props);
1066 }
1067 }
1068
1069#if USE_GUI
1071 {
1072 std::set<std::string> unique_names;
1073
1074 for (auto it = blockstates.begin(); it != blockstates.end(); ++it)
1075 {
1076#if PROTOCOL_VERSION < 347 /* < 1.13 */
1077 for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
1078 {
1079 for (int n = 0; n < it2->second->GetNumModels(); ++n)
1080 {
1081 const auto& faces = it2->second->GetModel(n).GetFaces();
1082
1083 for (int i = 0; i < faces.size(); ++i)
1084 {
1085 for (int s = 0; s < faces[i].texture_names.size(); ++s)
1086 {
1087 unique_names.insert(faces[i].texture_names[s]);
1088 }
1089 }
1090 }
1091 }
1092#else
1093 for (int n = 0; n < it->second->GetNumModels(); ++n)
1094 {
1095 const auto& faces = it->second->GetModel(n).GetFaces();
1096
1097 for (int i = 0; i < faces.size(); ++i)
1098 {
1099 for (int s = 0; s < faces[i].texture_names.size(); ++s)
1100 {
1101 unique_names.insert(faces[i].texture_names[s]);
1102 }
1103 }
1104 }
1105#endif
1106 }
1107
1108 std::vector<std::pair<std::string, std::string> > paths;
1109 paths.reserve(unique_names.size());
1110 for (auto it = unique_names.begin(); it != unique_names.end(); ++it)
1111 {
1112 if ((*it).empty())
1113 {
1114 continue;
1115 }
1116 paths.push_back({ (ASSETS_PATH + std::string("/minecraft/textures/") + *it + ".png") , *it });
1117 }
1118
1119 atlas->LoadData(paths);
1120 }
1121#endif
1122
1128
1129#if USE_GUI
1134#endif
1135
1136} //Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
#define LOG_INFO(osstream)
Definition Logger.hpp:43
#define LOG_FATAL(osstream)
Definition Logger.hpp:46
const std::unordered_map< int, std::unique_ptr< Blockstate > > & Blockstates() const
std::unique_ptr< Renderer::Atlas > atlas
std::unordered_map< int, std::unique_ptr< Biome > > biomes
std::unordered_map< int, std::unique_ptr< Blockstate > > blockstates
const Biome * GetBiome(const int id) const
const std::unordered_map< ItemId, std::unique_ptr< Item > > & Items() const
std::vector< const Blockstate * > flattened_blockstates
std::vector< const Blockstate * > GetBlockstates(const std::string &name) const
Get all blockstates that match a given name.
const Renderer::Atlas * GetAtlas() const
const Item * GetItem(const ItemId id) const
static AssetsManager & getInstance()
std::unordered_map< ItemId, std::unique_ptr< Item > > items
ItemId GetItemID(const std::string &item_name) const
const std::unordered_map< int, std::unique_ptr< Biome > > & Biomes() const
const Blockstate * GetBlockstate(const BlockstateId id) const
const std::string & GetName() const
static void UpdateModelsWithAtlasData(const Renderer::Atlas *atlas)
static void ClearCache()
static void ClearCache()
Definition Model.cpp:595
static const Model & GetModel(const std::string &filepath, const bool custom)
Definition Model.cpp:23
Main class, basically a JsonVariant with extra utility functions it doesn't inherit JsonVariant direc...
Definition Json.hpp:45
size_t size() const
Definition Json.cpp:237
bool is_array() const
Definition Json.cpp:154
bool is_object() const
Definition Json.cpp:149
bool contains(const std::string &s) const
Definition Json.cpp:232
std::string & get_string()
Definition Json.cpp:119
bool EndsWith(const std::string &mainStr, const std::string &toMatch)
ToolType GetToolTypeFromString(const std::string &s)
ToolMaterial GetToolMaterialFromString(const std::string &s)
int ItemId
Definition Item.hpp:15
unsigned int BlockstateId
BiomeType
Enum for biomes with special color processing.
Definition Biome.hpp:9
ToolMaterial min_material
bool climbable
True if can be used as a ladder.
bool powder_snow
True if this block is powder_snow.
ProtocolCraft::Json::Value down_bubble_column
True if this block is a bubble column going down.
bool cobweb
True if this block is cobweb.
bool water
True for water.
std::vector< std::string > variables
bool honey
True if this block is honey.
bool slime
True if this block is slime.
bool berry_bush
True if this block is sweet_berry_bush.
bool transparent
True if not a full 1x1x1 block OR at least one face texture has transparency.
ProtocolCraft::Json::Value solid
True if can't go through it.
bool soul_sand
True if this block is soul_sand.
bool custom
True if the model is a custom one (chests/banners etc...)
ProtocolCraft::Json::Value waterlogged
True for blocks that are always waterlogged (kelp, seagrass...)
bool air
True if the block is air (air, cave_air, void and structure_void are counted as air)
bool hazardous
True if block can hurt when walking in/on it.
ProtocolCraft::Json::Value up_bubble_column
True if this block is a bubble column going up.
bool scaffolding
True if this block is scaffolding.
bool bed
True if this block has the BEDS tag.