22declare(strict_types=1);
24namespace pocketmine\crafting;
45use Symfony\Component\Filesystem\Path;
46use
function base64_decode;
47use
function get_debug_type;
49use
function is_object;
50use
function json_decode;
55 if(isset($data->count) && $data->count !== 1){
60 if(isset($data->tag)){
64 $meta = $data->meta ??
null;
65 if($meta === RecipeIngredientData::WILDCARD_META_VALUE){
72 $itemStack = self::deserializeItemStackFromFields(
76 $data->block_states ??
null,
81 if($itemStack ===
null){
90 return self::deserializeItemStackFromFields(
94 $data->block_states ??
null,
96 $data->can_place_on ?? [],
97 $data->can_destroy ?? []
105 private static function deserializeItemStackFromFields(
string $name, ?
int $meta, ?
int $count, ?
string $blockStatesRaw, ?
string $nbtRaw, array $canPlaceOn, array $canDestroy) : ?
Item{
109 $blockName = BlockItemIdMap::getInstance()->lookupBlockId($name);
110 if($blockName !==
null){
114 $blockStatesTag = $blockStatesRaw ===
null ?
118 ->mustGetCompoundTag()
122 $blockStateData =
null;
127 ->mustGetCompoundTag();
144 return GlobalItemDataHandlers::getDeserializer()->deserializeStack($itemStackData);
159 $recipes = json_decode(
Filesystem::fileGetContents($filePath));
160 if(!is_array($recipes)){
164 $mapper = new \JsonMapper();
165 $mapper->bStrictObjectTypeChecking =
true;
166 $mapper->bExceptionOnUndefinedProperty =
true;
167 $mapper->bExceptionOnMissingData =
true;
169 return self::loadJsonObjectListIntoModel($mapper, $modelCLass, $recipes);
177 private static function loadJsonObjectIntoModel(\
JsonMapper $mapper,
string $modelClass,
object $data) : object{
180 return $mapper->map($data, (new \ReflectionClass($modelClass))->newInstanceWithoutConstructor());
182 throw new SavedDataLoadingException($e->getMessage(), 0, $e);
194 private static function loadJsonObjectListIntoModel(\
JsonMapper $mapper,
string $modelClass, array $data) : array{
196 foreach($data as $i => $item){
197 if(!is_object($item)){
198 throw new SavedDataLoadingException(
"Invalid entry at index $i: expected object, got " . get_debug_type($item));
201 $result[] = self::loadJsonObjectIntoModel($mapper, $modelClass, $item);
202 }
catch(SavedDataLoadingException $e){
203 throw new SavedDataLoadingException(
"Invalid entry at index $i: " . $e->getMessage(), 0, $e);
209 public static function make(
string $directoryPath) : CraftingManager{
210 $result = new CraftingManager();
212 foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath,
'shapeless_crafting.json'), ShapelessRecipeData::class) as $recipe){
213 $recipeType = match($recipe->block){
214 "crafting_table" => ShapelessRecipeType::CRAFTING,
215 "stonecutter" => ShapelessRecipeType::STONECUTTER,
216 "smithing_table" => ShapelessRecipeType::SMITHING,
217 "cartography_table" => ShapelessRecipeType::CARTOGRAPHY,
220 if($recipeType ===
null){
224 foreach($recipe->input as $inputData){
225 $input = self::deserializeIngredient($inputData);
232 foreach($recipe->output as $outputData){
233 $output = self::deserializeItemStack($outputData);
234 if($output ===
null){
237 $outputs[] = $output;
240 $result->registerShapelessRecipe(
new ShapelessRecipe(
246 foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath,
'shaped_crafting.json'), ShapedRecipeData::class) as $recipe){
247 if($recipe->block !==
"crafting_table"){
251 foreach(Utils::stringifyKeys($recipe->input) as $symbol => $inputData){
252 $input = self::deserializeIngredient($inputData);
256 $inputs[$symbol] = $input;
259 foreach($recipe->output as $outputData){
260 $output = self::deserializeItemStack($outputData);
261 if($output ===
null){
264 $outputs[] = $output;
267 $result->registerShapedRecipe(
new ShapedRecipe(
273 foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath,
'smelting.json'), FurnaceRecipeData::class) as $recipe){
274 $furnaceType = match ($recipe->block){
275 "furnace" => FurnaceType::FURNACE,
276 "blast_furnace" => FurnaceType::BLAST_FURNACE,
277 "smoker" => FurnaceType::SMOKER,
281 if($furnaceType ===
null){
284 $output = self::deserializeItemStack($recipe->output);
285 if($output ===
null){
288 $input = self::deserializeIngredient($recipe->input);
292 $result->getFurnaceRecipeManager($furnaceType)->register(
new FurnaceRecipe(
298 foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath,
'potion_type.json'), PotionTypeRecipeData::class) as $recipe){
299 $input = self::deserializeIngredient($recipe->input);
300 $ingredient = self::deserializeIngredient($recipe->ingredient);
301 $output = self::deserializeItemStack($recipe->output);
302 if($input ===
null || $ingredient ===
null || $output ===
null){
305 $result->registerPotionTypeRecipe(
new PotionTypeRecipe(
311 foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath,
'potion_container_change.json'), PotionContainerChangeRecipeData::class) as $recipe){
312 $ingredient = self::deserializeIngredient($recipe->ingredient);
313 if($ingredient ===
null){
317 $inputId = $recipe->input_item_name;
318 $outputId = $recipe->output_item_name;
322 self::deserializeItemStackFromFields($inputId,
null,
null,
null,
null, [], []) ===
null ||
323 self::deserializeItemStackFromFields($outputId,
null,
null,
null,
null, [], []) ===
null
328 $result->registerPotionContainerChangeRecipe(
new PotionContainerChangeRecipe(
static loadJsonArrayOfObjectsFile(string $filePath, string $modelCLass)
static current(string $name, array $states)
static trapAndRemoveFalse(\Closure $closure, int $levels=E_WARNING|E_NOTICE)