Thanks to visit codestin.com
Credit goes to adepierre.github.io

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