Botcraft 26.3
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#if PROTOCOL_VERSION > 772 /* > 1.21.8 */
275 else if (s == "copper")
276 {
278 }
279#endif
280 return ToolMaterial::None;
281 }
282
283 ToolType GetToolTypeFromString(const std::string& s)
284 {
285 if (s == "axe")
286 {
287 return ToolType::Axe;
288 }
289 else if (s == "hoe")
290 {
291 return ToolType::Hoe;
292 }
293 else if (s == "pickaxe")
294 {
295 return ToolType::Pickaxe;
296 }
297 else if (s == "shears")
298 {
299 return ToolType::Shears;
300 }
301 else if (s == "shovel")
302 {
303 return ToolType::Shovel;
304 }
305 else if (s == "sword")
306 {
307 return ToolType::Sword;
308 }
309 return ToolType::None;
310 }
311
313 {
314 std::unordered_map<std::string, BlockstateProperties> blockstate_properties;
315 std::unordered_map<std::string, std::string> textures;
316 std::unordered_map<std::string, std::string> rendering;
317#if PROTOCOL_VERSION < 347 /* < 1.13 */
318 std::unordered_map<std::string, std::unordered_map<int, TintType> > tint_types;
319#else
320 std::unordered_map<std::string, TintType> tint_types;
321#endif
322 const std::string info_file_path = ASSETS_PATH + std::string("/custom/Blocks_info.json");
323
324 Json::Value json;
325 try
326 {
327 std::ifstream file(info_file_path);
328 file >> json;
329 file.close();
330 }
331 catch (const std::runtime_error& e)
332 {
333 LOG_ERROR("Error reading info block file at " << info_file_path << '\n' << e.what());
334 return;
335 }
336
337 if (!json.contains("colliders"))
338 {
339 LOG_ERROR("Error reading info block file at " << info_file_path << " (no colliders found)");
340 return;
341 }
342 const Json::Value& colliders = json["colliders"];
343
344 if (!json.contains("blocks"))
345 {
346 LOG_ERROR("Error reading info block file at " << info_file_path << " (no blocks found)");
347 return;
348 }
349 //Load all the info
350 for (const auto& info : json["blocks"].get_array())
351 {
352 std::string name = "";
353
354 if (!info.contains("name") || !info["name"].is_string())
355 {
356 LOG_ERROR("Error with an element of blockstates info: \n" << info.Dump());
357 continue;
358 }
359 else
360 {
361 name = info["name"].get_string();
362 blockstate_properties[name].name = name;
363 }
364
365 BlockstateProperties& current_block_properties = blockstate_properties[name];
366
367 if (info.contains("air") && info["air"].is_bool())
368 {
369 current_block_properties.air = info["air"].get<bool>();
370 }
371
372 current_block_properties.solid = info.contains("solid") ? info["solid"] : false;
373
374 if (info.contains("transparent") && info["transparent"].is_bool())
375 {
376 current_block_properties.transparent = info["transparent"].get<bool>();
377 }
378
379 if (info.contains("lava") && info["lava"].is_bool())
380 {
381 current_block_properties.lava = info["lava"].get<bool>();
382 }
383
384 if (info.contains("water") && info["water"].is_bool())
385 {
386 current_block_properties.water = info["water"].get<bool>();
387 }
388
389 if (info.contains("waterlogged") && info["waterlogged"].is_bool())
390 {
391 current_block_properties.waterlogged = info["waterlogged"].get<bool>();
392 }
393 else
394 {
395 current_block_properties.waterlogged = "waterlogged=true";
396 }
397
398 if (info.contains("climbable") && info["climbable"].is_bool())
399 {
400 current_block_properties.climbable = info["climbable"].get<bool>();
401 }
402
403 if (info.contains("hazardous") && info["hazardous"].is_bool())
404 {
405 current_block_properties.hazardous = info["hazardous"].get<bool>();
406 }
407
408#if PROTOCOL_VERSION < 393 /* < 1.13 */
409 current_block_properties.slime = name == "minecraft:slime";
410#else
411 current_block_properties.slime = name == "minecraft:slime_block";
412#endif
413
414#if PROTOCOL_VERSION < 393 /* < 1.13 */
415 current_block_properties.bed = name == "minecraft:bed";
416#else
417 current_block_properties.bed = Utilities::EndsWith(name, "_bed");
418#endif
419
420#if PROTOCOL_VERSION < 777 /* < 26.3 */
421 current_block_properties.shelf_mushroom = false;
422#else
423 current_block_properties.shelf_mushroom = name == "minecraft:shelf_mushroom";
424#endif
425
426 current_block_properties.soul_sand = name == "minecraft:soul_sand";
427
428#if PROTOCOL_VERSION > 498 /* > 1.14.4 */
429 current_block_properties.honey = name == "minecraft:honey_block";
430#endif
431
432#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
433 current_block_properties.scaffolding = name == "minecraft:scaffolding";
434#endif
435
436#if PROTOCOL_VERSION < 393 /* < 1.13 */
437 current_block_properties.cobweb = name == "minecraft:web";
438#else
439 current_block_properties.cobweb = name == "minecraft:cobweb";
440#endif
441
442#if PROTOCOL_VERSION > 340 /* > 1.12.2 */
443 current_block_properties.up_bubble_column = info.contains("up_bubble_column") ? info["up_bubble_column"] : false;
444 current_block_properties.down_bubble_column = info.contains("down_bubble_column") ? info["down_bubble_column"] : false;
445#endif
446
447#if PROTOCOL_VERSION > 404 /* > 1.13.2 */
448 current_block_properties.berry_bush = name == "minecraft:sweet_berry_bush";
449#endif
450
451#if PROTOCOL_VERSION > 754 /* > 1.16.5 */
452 current_block_properties.powder_snow = name == "minecraft:powder_snow";
453#endif
454
455 if (info.contains("horizontal_offset") && info["horizontal_offset"].is_number())
456 {
457 blockstate_properties[name].horizontal_offset = info["horizontal_offset"].get_number<float>();
458 }
459
460 if (info.contains("hardness") && info["hardness"].is_number())
461 {
462 blockstate_properties[name].hardness = info["hardness"].get_number<float>();
463 }
464
465 if (info.contains("friction") && info["friction"].is_number())
466 {
467 blockstate_properties[name].friction = info["friction"].get_number<float>();
468 }
469
470 if (info.contains("colliders") && info["colliders"].is_number() && info["colliders"].get<int>() < colliders.size())
471 {
472 blockstate_properties[name].colliders = colliders[info["colliders"].get<int>()];
473 }
474
475 if (!info.contains("render") || !info["render"].is_string())
476 {
477 rendering[name] = "block";
478 }
479 else
480 {
481 rendering[name] = info["render"].get_string();
482 }
483
484 // Get breaking tools info
485 if (info.contains("tools") && info["tools"].is_array())
486 {
487 for (const auto& tool : info["tools"].get_array())
488 {
489 if (tool.is_string())
490 {
491 const std::string tool_name = tool.get_string();
492 if (tool_name == "any")
493 {
494 blockstate_properties[name].any_tool_harvest = true;
495 }
496 else
497 {
498 ToolType tool_type = GetToolTypeFromString(tool_name);
499 ToolMaterial min_material = (tool_type == ToolType::Shears || tool_type == ToolType::Sword) ? ToolMaterial::None : ToolMaterial::Wood;
500 blockstate_properties[name].best_tools.push_back(
501 BestTool{
502 tool_type,
503 min_material,
504 1.0f
505 });
506 }
507 }
508 else if (tool.is_object())
509 {
510 if (tool.contains("tool") && tool["tool"].is_string())
511 {
512 BestTool best_tool;
513 best_tool.tool_type = GetToolTypeFromString(tool["tool"].get_string());
514
515 if (tool.contains("min_material") && tool["min_material"].is_string())
516 {
517 best_tool.min_material = GetToolMaterialFromString(tool["min_material"].get_string());
518 }
519 else
520 {
521 // Default min material
523 }
524
525 if (tool.contains("multiplier") && tool["multiplier"].is_number())
526 {
527 best_tool.multiplier = tool["multiplier"].get<float>();
528 }
529 else
530 {
531 // Default multiplier
532 best_tool.multiplier = 1.0f;
533 }
534 blockstate_properties[name].best_tools.push_back(best_tool);
535 }
536 }
537 }
538 }
539
540 // Get texture info (used for fluids)
541 if (info.contains("texture") && info["texture"].is_string())
542 {
543 textures[name] = info["texture"].get_string();
544 }
545
546 // Get the tint type info (for grass/leaves/water ...)
547 if (info.contains("tintType") && info["tintType"].is_string())
548 {
549 std::string tint_type_string;
550 TintType tint_type = TintType::None;
551 tint_type_string = info["tintType"].get_string();
552 if (tint_type_string == "grass")
553 {
554 tint_type = TintType::Grass;
555 }
556 else if (tint_type_string == "leaves")
557 {
558 tint_type = TintType::Leaves;
559 }
560 else if (tint_type_string == "water")
561 {
562 tint_type = TintType::Water;
563 }
564 else if (tint_type_string == "redstone")
565 {
566 tint_type = TintType::Redstone;
567 }
568
569#if PROTOCOL_VERSION < 347 /* < 1.13 */
570 tint_types[name] = std::unordered_map<int, TintType>({ { -1, tint_type } });
571#else
572 tint_types[name] = tint_type;
573#endif
574 }
575#if PROTOCOL_VERSION < 347 /* < 1.13 */
576 // Before the flattening, we could have different tints for different metadata
577 else if (info.contains("tintTypes") && info["tintType"].is_object())
578 {
579 tint_types[name] = std::unordered_map<int, TintType>({});
580 for (const auto& [key, val]: info["tintType"].get_object())
581 {
582 TintType tint_type = TintType::None;
583 std::string tint_type_string = val.get_string();
584
585 if (tint_type_string == "grass")
586 {
587 tint_type = TintType::Grass;
588 }
589 else if (tint_type_string == "leaves")
590 {
591 tint_type = TintType::Leaves;
592 }
593 else if (tint_type_string == "water")
594 {
595 tint_type = TintType::Water;
596 }
597 else if (tint_type_string == "redstone")
598 {
599 tint_type = TintType::Redstone;
600 }
601
602 tint_types[name][std::stoi(key)] = tint_type;
603 }
604 }
605#endif
606 else
607 {
608#if PROTOCOL_VERSION < 347 /* < 1.13 */
609 tint_types[name] = std::unordered_map<int, TintType>({ { -1, TintType::None } });
610#else
611 tint_types[name] = TintType::None;
612#endif
613 }
614 }
615
616 // Add a default block
617#if PROTOCOL_VERSION < 347 /* < 1.13 */
618 blockstates[-1];
619 blockstates[-1][0] = std::make_unique<Blockstate>(
621 -1, //id
622 0, //metadata
623#else
624 blockstates[-1] = std::make_unique<Blockstate>(
626 -1, //id
627#endif
628 false, //air
629 true, //solid
630 false, //transparent
631 false, //lava
632 false, //water
633 false, //waterlogged
634 false, //climbable
635 false, //hazardous
636 false, //any_tool_harvest
637 false, //slime
638 false, //bed
639 false, //shelf_mushroom
640 false, //soul_sand
641 false, //honey
642 false, //scaffolding
643 false, //cobweb
644 false, //up_bubble_column
645 false, //down_bubble_column
646 false, //berry_bush
647 false, //powder_snow
648 0.0f, //horizontal_offset
649 -2.0f, //hardness
650 0.6f, //friction
651 false, //custom
652 TintType::None, //tint_type
653 "default", //name
654 }
655 );
656
657 const std::string file_path = ASSETS_PATH + std::string("/custom/Blocks.json");
658
659 try
660 {
661 std::ifstream file(file_path);
662 file >> json;
663 file.close();
664 }
665 catch (const std::runtime_error& e)
666 {
667 LOG_ERROR("Error reading block file at " << file_path << '\n' << e.what());
668 return;
669 }
670
671#if PROTOCOL_VERSION < 347 /* < 1.13 */
672
673 if (!json.is_array())
674 {
675 LOG_ERROR("Error block file at " << file_path << " is not a json array as expected");
676 return;
677 }
678
679 //Load all the blockstates from JSON file
680 for (const auto& element : json.get_array())
681 {
682 const std::string& blockstate_name = element["name"].get_string();
683
684 if (rendering.find(blockstate_name) == rendering.end() ||
685 blockstate_properties.find(blockstate_name) == blockstate_properties.end() ||
686 tint_types.find(blockstate_name) == tint_types.end())
687 {
688 LOG_ERROR("Error trying to get information for blockstate " << blockstate_name);
689 continue;
690 }
691
692 BlockstateProperties& props = blockstate_properties[blockstate_name];
693 props.id = -1;
694 props.metadata = 0;
695 props.variables = std::vector<std::string>();
696
697 if (element.contains("id") && element["id"].is_number())
698 {
699 props.id = element["id"].get_number<int>();
700 }
701
702 const std::string& render = rendering[blockstate_name];
703
704 const bool fluid_falling = props.metadata & 0b1000;
705 const int fluid_level = 1 + (props.metadata & 0b111);
706
707 if (render == "none")
708 {
710 props.custom = false;
711 props.path = "none";
712 blockstates[props.id][0] = std::make_unique<Blockstate>(props);
713 }
714 else if (render == "block" || render == "fluid" || render == "other")
715 {
716 if (render == "fluid" && textures.find(blockstate_name) == textures.end())
717 {
718 LOG_ERROR("Error, blockstate " << blockstate_name << " is a fluid, but it does not have a texture file specified in Blocks_info.json");
719 }
720
721 if (!element.contains("metadata"))
722 {
723 LOG_ERROR("Error, no metadata found for block " << blockstate_name);
724 }
725
726 for (const auto& metadata_obj : element["metadata"].get_array())
727 {
728 props.metadata = metadata_obj["value"].get_number<int>();
729 props.path = metadata_obj["blockstate"].get_string();
730 props.variables = std::vector<std::string>();
731 if (metadata_obj.contains("variables"))
732 {
733 for (const auto& s : metadata_obj["variables"].get_array())
734 {
735 props.variables.push_back(s.get_string());
736 }
737 }
738
740 if (tint_types.find(blockstate_name) != tint_types.end())
741 {
742 if (tint_types[blockstate_name].find(-1) != tint_types[blockstate_name].end())
743 {
744 props.tint_type = tint_types[blockstate_name][-1];
745 }
746 else if (tint_types[blockstate_name].find(props.metadata) != tint_types[blockstate_name].end())
747 {
748 props.tint_type = tint_types[blockstate_name][props.metadata];
749 }
750 }
751
752 auto check_it = blockstates.find(props.id);
753 if (check_it != blockstates.end())
754 {
755 if (render == "fluid")
756 {
757 props.custom = false;
758 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]));
759 }
760 else
761 {
762 props.custom = render == "other";
763 blockstates[props.id][props.metadata] = std::make_unique<Blockstate>(props);
764 }
765 }
766 else
767 {
768 // We want to be sure that blockstates[id][0] exists
769 if (render == "fluid")
770 {
771 blockstates[props.id];
772 blockstates[props.id][0] = std::make_unique<Blockstate>(props, Model::GetModel(fluid_falling ? 1.0 : (1.0 - fluid_level / 9.0), textures[blockstate_name]));
773 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]));
774 }
775 else
776 {
777 props.custom = render == "other";
778 blockstates[props.id];
779 blockstates[props.id][0] = std::make_unique<Blockstate>(props);
780 blockstates[props.id][props.metadata] = std::make_unique<Blockstate>(props);
781 }
782 }
783 }
784 }
785 }
786#else
787 if (!json.is_object())
788 {
789 LOG_ERROR("Error block file at " << file_path << " is not a json object as expected");
790 return;
791 }
792
793 for (const auto& [blockstate_name, element]: json.get_object())
794 {
795 if (!element.contains("states") || !element["states"].is_array())
796 {
797 continue;
798 }
799
800 for (const auto& blockstate : element["states"].get_array())
801 {
802 if (rendering.find(blockstate_name) == rendering.end() ||
803 blockstate_properties.find(blockstate_name) == blockstate_properties.end() ||
804 tint_types.find(blockstate_name) == tint_types.end())
805 {
806 LOG_ERROR("Error trying to get information for blockstate " << blockstate_name);
807 continue;
808 }
809
810 BlockstateProperties& props = blockstate_properties[blockstate_name];
811 props.id = -1;
812 props.variables = std::vector<std::string>();
813
814 if (blockstate.contains("id") && blockstate["id"].is_number())
815 {
816 props.id = blockstate["id"].get_number<int>();
817 }
818 else
819 {
820 LOG_ERROR("Error trying to read the id of block " << blockstate_name);
821 continue;
822 }
823
824 const std::string& render = rendering[blockstate_name];
825
826 // Read the properties (if any)
827 int fluid_level = 0;
828 bool fluid_falling = false;
829 if (blockstate.contains("properties") && blockstate["properties"].is_object())
830 {
831 for (const auto& [key, val]: blockstate["properties"].get_object())
832 {
833 props.variables.push_back(key + "=" + val.get_string());
834 if (render == "fluid" && key == "level")
835 {
836 fluid_level = std::stoi(val.get_string());
837 fluid_falling = fluid_level & 0b1000;
838 fluid_level = 1 + (fluid_level & 0b111);
839 }
840 }
841 }
842
843 if (render == "none")
844 {
846 props.custom = false;
847 props.path = "none";
848 blockstates[props.id] = std::make_unique<Blockstate>(props);
849 }
850 else if (render == "fluid")
851 {
852 if (textures.find(blockstate_name) == textures.end())
853 {
854 LOG_ERROR("Error, blockstate " << blockstate_name << " is a fluid, but it does not have a texture file specified in Blocks_info.json");
855 }
856 else
857 {
858 props.tint_type = tint_types[blockstate_name];
859 blockstates[props.id] = std::make_unique<Blockstate>(props, Model::GetModel(fluid_falling ? 1.0 : (1.0 - fluid_level / 9.0), textures[blockstate_name]));
860 }
861 }
862 else if (render == "block" || render == "other")
863 {
864 props.custom = render == "other";
865 props.tint_type = tint_types[blockstate_name];
866 props.path = blockstate_name.substr(10);
867 blockstates[props.id] = std::make_unique<Blockstate>(props);
868 }
869 else
870 {
871 LOG_WARNING("No known rendering method defined for block: " << blockstate_name);
872 }
873 }
874 }
876#endif
877 }
878
879#if PROTOCOL_VERSION > 340 /* > 1.12.2 */
881 {
882 int max_id = -1;
883 for (const auto& [id, block] : blockstates)
884 {
885 max_id = std::max(id, max_id);
886 }
887 if (max_id > std::numeric_limits<unsigned short>::max())
888 {
889 LOG_ERROR("Too many blockstates, compact chunk representation will be broken");
890 }
891 flattened_blockstates = std::vector<const Blockstate*>(max_id + 1, nullptr);
893 for (const auto& [id, block] : blockstates)
894 {
895 if (id >= 0)
896 {
897 flattened_blockstates[id] = block.get();
898 }
899 }
900 }
901#endif
902
904 {
905 std::string file_path = ASSETS_PATH + std::string("/custom/Biomes.json");
906
907 Json::Value json;
908 try
909 {
910 std::ifstream file(file_path);
911 file >> json;
912 file.close();
913 }
914 catch (const std::runtime_error& e)
915 {
916 LOG_ERROR("Error reading biome file at " << file_path << '\n' << e.what());
917 return;
918 }
919
920 if (!json.is_array())
921 {
922 LOG_ERROR("Error biome file at " << file_path << " is not a json object as expected");
923 return;
924 }
925
926 //Load all the biomes from JSON file
927 int max_biome_id = 0;
928 for (const auto& element : json.get_array())
929 {
930 unsigned char id = 0;
931 std::string name = "";
932 float rainfall = 0.0f;
933 float temperature = 0.0f;
934 BiomeType biome_type = BiomeType::Classic;
935
936 if (element.contains("id"))
937 {
938 id = element["id"].get_number<unsigned char>();
939 }
940
941 if (element.contains("name"))
942 {
943 name = element["name"].get_string();
944 }
945
946 if (element.contains("rainfall"))
947 {
948 rainfall = element["rainfall"].get_number<float>();
949 }
950
951 if (element.contains("temperature"))
952 {
953 temperature = element["temperature"].get_number<float>();
954 }
955
956 if (element.contains("biomeType"))
957 {
958 const std::string& string_biome_type = element["biomeType"].get_string();
959 if (string_biome_type == "Swamp")
960 {
961 biome_type = BiomeType::Swamp;
962 }
963 else if (string_biome_type == "Badlands")
964 {
965 biome_type = BiomeType::Badlands;
966 }
967 else if (string_biome_type == "DarkForest")
968 {
969 biome_type = BiomeType::DarkForest;
970 }
971#if PROTOCOL_VERSION >= 393 /* >= 1.13 */
972 else if (string_biome_type == "WarmOcean")
973 {
974 biome_type = BiomeType::WarmOcean;
975 }
976 else if (string_biome_type == "LukewarmOcean")
977 {
978 biome_type = BiomeType::LukewarmOcean;
979 }
980 else if (string_biome_type == "ColdOcean")
981 {
982 biome_type = BiomeType::ColdOcean;
983 }
984 else if (string_biome_type == "FrozenOcean")
985 {
986 biome_type = BiomeType::FrozenOcean;
987 }
988#endif
989#if PROTOCOL_VERSION > 767 /* > 1.20.1 */
990 else if (string_biome_type == "PaleGarden")
991 {
992 biome_type = BiomeType::PaleGarden;
993 }
994#endif
995 }
996
997 biomes[id] = std::make_unique<Biome>(name, temperature, rainfall, biome_type);
998 }
999 }
1000
1002 {
1003 std::string file_path = ASSETS_PATH + std::string("/custom/Items.json");
1004
1005 Json::Value json;
1006 try
1007 {
1008 std::ifstream file(file_path);
1009 file >> json;
1010 file.close();
1011 }
1012 catch (const std::runtime_error& e)
1013 {
1014 LOG_ERROR("Error reading item file at " << file_path << '\n' << e.what());
1015 return;
1016 }
1017
1018 // Add a default item
1019 ItemProperties props{
1020#if PROTOCOL_VERSION < 347 /* < 1.13 */
1021 {-1, 0}, //id
1022#else
1023 -1, //id
1024#endif
1025 "default", //name
1026 64, //stack_size
1027 -1, // durability
1028 };
1029#if PROTOCOL_VERSION < 347 /* < 1.13 */
1030 items[{-1, 0}] = std::make_unique<Item>(props);
1031#else
1032 items[-1] = std::make_unique<Item>(props);
1033#endif
1034
1035 //Load all the items from JSON file
1036 for (const auto& [key, properties]: json.get_object())
1037 {
1038 // Read name
1039 props.name = key;
1040
1041 if (!properties.contains("id") || !properties["id"].is_number())
1042 {
1043 continue;
1044 }
1045#if PROTOCOL_VERSION < 347 /* < 1.13 */
1046 props.id.first = properties["id"].get_number<int>();
1047#else
1048 props.id = properties["id"].get_number<int>();
1049#endif
1050
1051 if (properties.contains("stack_size") && properties["stack_size"].is_number())
1052 {
1053 props.stack_size = properties["stack_size"].get_number<unsigned char>();
1054 }
1055 // Default value
1056 else
1057 {
1058 props.stack_size = 64;
1059 }
1060
1061 if (properties.contains("durability") && properties["durability"].is_number())
1062 {
1063 props.durability = properties["durability"].get_number<int>();
1064 }
1065 // Default value
1066 else
1067 {
1068 props.durability = -1;
1069 }
1070
1071#if PROTOCOL_VERSION < 347 /* < 1.13 */
1072 if (!properties.contains("damage_id") || !properties["damage_id"].is_number())
1073 {
1074 continue;
1075 }
1076 props.id.second = properties["damage_id"].get_number<unsigned char>();
1077#endif
1078 items[props.id] = std::make_unique<Item>(props);
1079 }
1080 }
1081
1082#if USE_GUI
1084 {
1085 std::set<std::string> unique_names;
1086
1087 for (auto it = blockstates.begin(); it != blockstates.end(); ++it)
1088 {
1089#if PROTOCOL_VERSION < 347 /* < 1.13 */
1090 for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
1091 {
1092 for (int n = 0; n < it2->second->GetNumModels(); ++n)
1093 {
1094 const auto& faces = it2->second->GetModel(n).GetFaces();
1095
1096 for (int i = 0; i < faces.size(); ++i)
1097 {
1098 for (int s = 0; s < faces[i].texture_names.size(); ++s)
1099 {
1100 unique_names.insert(faces[i].texture_names[s]);
1101 }
1102 }
1103 }
1104 }
1105#else
1106 for (int n = 0; n < it->second->GetNumModels(); ++n)
1107 {
1108 const auto& faces = it->second->GetModel(n).GetFaces();
1109
1110 for (int i = 0; i < faces.size(); ++i)
1111 {
1112 for (int s = 0; s < faces[i].texture_names.size(); ++s)
1113 {
1114 unique_names.insert(faces[i].texture_names[s]);
1115 }
1116 }
1117 }
1118#endif
1119 }
1120
1121 std::vector<std::pair<std::string, std::string> > paths;
1122 paths.reserve(unique_names.size());
1123 for (auto it = unique_names.begin(); it != unique_names.end(); ++it)
1124 {
1125 if ((*it).empty())
1126 {
1127 continue;
1128 }
1129 paths.push_back({ (ASSETS_PATH + std::string("/minecraft/textures/") + *it + ".png") , *it });
1130 }
1131
1132 atlas->LoadData(paths);
1133 }
1134#endif
1135
1141
1142#if USE_GUI
1147#endif
1148
1149} //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:612
static const Model & GetModel(const std::string &filepath, const bool custom)
Definition Model.cpp:24
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.
bool shelf_mushroom
True if this block is a shelf_mushroom.
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.