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