Updated creative and crafting data to 1.19

This commit is contained in:
Dylan K. Taylor 2022-07-04 20:28:07 +01:00
parent 59c5770cf2
commit 9f0b32e748
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
17 changed files with 592 additions and 129 deletions

8
composer.lock generated
View File

@ -280,12 +280,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockData.git", "url": "https://github.com/pmmp/BedrockData.git",
"reference": "a546e15f6a8d7498fb25d5a02ce16184a429bb78" "reference": "01948a627448395d9946c2e6a5e8332a8456eaa0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/a546e15f6a8d7498fb25d5a02ce16184a429bb78", "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/01948a627448395d9946c2e6a5e8332a8456eaa0",
"reference": "a546e15f6a8d7498fb25d5a02ce16184a429bb78", "reference": "01948a627448395d9946c2e6a5e8332a8456eaa0",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -298,7 +298,7 @@
"issues": "https://github.com/pmmp/BedrockData/issues", "issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/modern-world-support" "source": "https://github.com/pmmp/BedrockData/tree/modern-world-support"
}, },
"time": "2022-07-02T15:28:28+00:00" "time": "2022-07-04T16:59:39+00:00"
}, },
{ {
"name": "pocketmine/bedrock-item-upgrade-schema", "name": "pocketmine/bedrock-item-upgrade-schema",

View File

@ -963,7 +963,7 @@ class Server{
$this->commandMap = new SimpleCommandMap($this); $this->commandMap = new SimpleCommandMap($this);
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes.json")); $this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes"));
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger); $this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);

View File

@ -264,13 +264,13 @@ class CraftingManager{
} }
foreach($this->potionContainerChangeRecipes as $recipe){ foreach($this->potionContainerChangeRecipes as $recipe){
if($recipe->getIngredient()->equals($ingredient) && $recipe->getResultFor($input) !== null){ if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe; return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
} }
} }
foreach($this->potionTypeRecipes as $recipe){ foreach($this->potionTypeRecipes as $recipe){
if($recipe->getIngredient()->equals($ingredient) && $recipe->getResultFor($input) !== null){ if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe; return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
} }
} }

View File

@ -23,61 +23,205 @@ declare(strict_types=1);
namespace pocketmine\crafting; namespace pocketmine\crafting;
use pocketmine\crafting\json\FurnaceRecipeData;
use pocketmine\crafting\json\ItemStackData;
use pocketmine\crafting\json\PotionContainerChangeRecipeData;
use pocketmine\crafting\json\PotionTypeRecipeData;
use pocketmine\crafting\json\RecipeIngredientData;
use pocketmine\crafting\json\ShapedRecipeData;
use pocketmine\crafting\json\ShapelessRecipeData;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\item\BlockItemIdMap;
use pocketmine\data\bedrock\item\ItemTypeDeserializeException; use pocketmine\data\bedrock\item\ItemTypeDeserializeException;
use pocketmine\data\bedrock\item\upgrade\LegacyItemIdToStringIdMap; use pocketmine\data\bedrock\item\SavedItemData;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException; use pocketmine\data\SavedDataLoadingException;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use pocketmine\world\format\io\GlobalItemDataHandlers; use pocketmine\world\format\io\GlobalItemDataHandlers;
use function array_map; use Webmozart\PathUtil\Path;
use function base64_decode;
use function file_get_contents; use function file_get_contents;
use function get_debug_type;
use function is_array; use function is_array;
use function is_int; use function is_object;
use function json_decode; use function json_decode;
final class CraftingManagerFromDataHelper{ final class CraftingManagerFromDataHelper{
/**
* @param mixed[] $data private static function deserializeItemStackFromNameMeta(string $name, int $meta) : ?Item{
*/ $blockName = BlockItemIdMap::getInstance()->lookupBlockId($name);
private static function deserializeIngredient(array $data) : ?RecipeIngredient{ if($blockName !== null){
if(!isset($data["id"]) || !is_int($data["id"])){ $blockStateDictionary = RuntimeBlockMapping::getInstance()->getBlockStateDictionary();
throw new \InvalidArgumentException("Invalid input data, expected int ID"); $blockRuntimeId = $blockStateDictionary->lookupStateIdFromIdMeta($name, $meta === RecipeIngredientData::WILDCARD_META_VALUE ? 0 : $meta);
} if($blockRuntimeId === null){
if(isset($data["damage"]) && $data["damage"] === -1){ throw new \InvalidArgumentException("$blockName with meta $meta doesn't map to any known blockstate");
try{
$typeData = GlobalItemDataHandlers::getUpgrader()->upgradeItemTypeDataInt($data["id"], 0, 1, null);
}catch(ItemTypeDeserializeException){
//probably unknown item
return null;
} }
$blockStateData = $blockStateDictionary->getDataFromStateId($blockRuntimeId);
return new MetaWildcardRecipeIngredient($typeData->getTypeData()->getName()); if($blockStateData === null){
throw new AssumptionFailedError("We just looked up the runtime ID for this state, so it can't possibly be null");
}
}else{
$blockStateData = null;
} }
//TODO: we need to stop using jsonDeserialize for this //TODO: for wildcards, we only need a way to check if the item serializer recognizes the ID; we don't need to
//deserialize the whole itemstack, which might give bogus results anyway if meta 0 isn't recognized
$itemTypeData = new SavedItemData(
$name,
$meta === RecipeIngredientData::WILDCARD_META_VALUE ? 0 : $meta,
$blockStateData,
null
);
try{ try{
$item = Item::legacyJsonDeserialize($data); return GlobalItemDataHandlers::getDeserializer()->deserializeType($itemTypeData);
}catch(SavedDataLoadingException){ }catch(ItemTypeDeserializeException){
//unknown item //probably unknown item
return null; return null;
} }
return new ExactRecipeIngredient($item);
} }
public static function make(string $filePath) : CraftingManager{ private static function deserializeIngredient(RecipeIngredientData $data) : ?RecipeIngredient{
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true); if(isset($data->count) && $data->count !== 1){
if(!is_array($recipes)){ //every case we've seen so far where this isn't the case, it's been a bug and the count was ignored anyway
throw new AssumptionFailedError("recipes.json root should contain a map of recipe types"); //e.g. gold blocks crafted from 9 ingots, but each input item individually had a count of 9
throw new SavedDataLoadingException("Recipe inputs should have a count of exactly 1");
} }
$itemStack = self::deserializeItemStackFromNameMeta($data->name, $data->meta);
if($itemStack === null){
//probably unknown item
return null;
}
return $data->meta === RecipeIngredientData::WILDCARD_META_VALUE ?
new MetaWildcardRecipeIngredient($data->name) :
new ExactRecipeIngredient($itemStack);
}
public static function deserializeItemStack(ItemStackData $data) : ?Item{
//count, name, block_name, block_states, meta, nbt, can_place_on, can_destroy
$name = $data->name;
$meta = $data->meta ?? 0;
$count = $data->count ?? 1;
$blockStatesRaw = $data->block_states ?? null;
$nbtRaw = $data->nbt ?? null;
$canPlaceOn = $data->can_place_on ?? [];
$canDestroy = $data->can_destroy ?? [];
$blockName = BlockItemIdMap::getInstance()->lookupBlockId($name);
if($blockName !== null){
if($meta !== 0){
throw new \InvalidArgumentException("Meta should not be specified for blockitems");
}
$blockStatesTag = $blockStatesRaw === null ?
CompoundTag::create() :
(new LittleEndianNbtSerializer())
->read(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => base64_decode($blockStatesRaw, true)))
->mustGetCompoundTag();
$blockStateData = new BlockStateData($blockName, $blockStatesTag, BlockStateData::CURRENT_VERSION);
}else{
$blockStateData = null;
}
$nbt = $nbtRaw === null ? null : (new LittleEndianNbtSerializer())
->read(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => base64_decode($nbtRaw, true)))
->mustGetCompoundTag();
$itemStackData = new SavedItemStackData(
new SavedItemData(
$name,
$meta,
$blockStateData,
$nbt
),
$count,
null,
null,
$canPlaceOn,
$canDestroy,
);
try{
return GlobalItemDataHandlers::getDeserializer()->deserializeStack($itemStackData);
}catch(ItemTypeDeserializeException){
//probably unknown item
return null;
}
}
/**
* @return mixed[]
*
* @phpstan-template TData of object
* @phpstan-param class-string<TData> $modelCLass
* @phpstan-return list<TData>
*/
public static function loadJsonArrayOfObjectsFile(string $filePath, string $modelCLass) : array{
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"));
if(!is_array($recipes)){
throw new AssumptionFailedError("$filePath root should be an array, got " . get_debug_type($recipes));
}
$mapper = new \JsonMapper();
$mapper->bStrictObjectTypeChecking = true;
$mapper->bExceptionOnUndefinedProperty = true;
$mapper->bExceptionOnMissingData = true;
return self::loadJsonObjectListIntoModel($mapper, $modelCLass, $recipes);
}
/**
* @phpstan-template TRecipeData of object
* @phpstan-param class-string<TRecipeData> $modelClass
* @phpstan-return TRecipeData
*/
private static function loadJsonObjectIntoModel(\JsonMapper $mapper, string $modelClass, object $data) : object{
//JsonMapper does this for subtypes, but not for the base type :(
try{
return $mapper->map($data, (new \ReflectionClass($modelClass))->newInstanceWithoutConstructor());
}catch(\JsonMapper_Exception $e){
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
}
}
/**
* @param mixed[] $data
* @return object[]
*
* @phpstan-template TRecipeData of object
* @phpstan-param class-string<TRecipeData> $modelClass
* @phpstan-return list<TRecipeData>
*/
private static function loadJsonObjectListIntoModel(\JsonMapper $mapper, string $modelClass, array $data) : array{
$result = [];
foreach($data as $i => $item){
if(!is_object($item)){
throw new SavedDataLoadingException("Invalid entry at index $i: expected object, got " . get_debug_type($item));
}
try{
$result[] = self::loadJsonObjectIntoModel($mapper, $modelClass, $item);
}catch(SavedDataLoadingException $e){
throw new SavedDataLoadingException("Invalid entry at index $i: " . $e->getMessage(), 0, $e);
}
}
return $result;
}
public static function make(string $directoryPath) : CraftingManager{
$result = new CraftingManager(); $result = new CraftingManager();
$ingredientDeserializerFunc = \Closure::fromCallable([self::class, "deserializeIngredient"]); $ingredientDeserializerFunc = \Closure::fromCallable([self::class, "deserializeIngredient"]);
$itemDeserializerFunc = \Closure::fromCallable([Item::class, 'legacyJsonDeserialize']); $itemDeserializerFunc = \Closure::fromCallable([self::class, 'deserializeItemStack']);
foreach($recipes["shapeless"] as $recipe){ foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'shapeless_crafting.json'), ShapelessRecipeData::class) as $recipe){
$recipeType = match($recipe["block"]){ $recipeType = match($recipe->block){
"crafting_table" => ShapelessRecipeType::CRAFTING(), "crafting_table" => ShapelessRecipeType::CRAFTING(),
"stonecutter" => ShapelessRecipeType::STONECUTTER(), "stonecutter" => ShapelessRecipeType::STONECUTTER(),
//TODO: Cartography Table //TODO: Cartography Table
@ -87,18 +231,20 @@ final class CraftingManagerFromDataHelper{
continue; continue;
} }
$inputs = []; $inputs = [];
foreach($recipe["input"] as $inputData){ foreach($recipe->input as $inputData){
$input = $ingredientDeserializerFunc($inputData); $input = $ingredientDeserializerFunc($inputData);
if($input === null){ //unknown input item if($input === null){ //unknown input item
continue 2; continue 2;
} }
$inputs[] = $input; $inputs[] = $input;
} }
try{ $outputs = [];
$outputs = array_map($itemDeserializerFunc, $recipe["output"]); foreach($recipe->output as $outputData){
}catch(SavedDataLoadingException){ $output = $itemDeserializerFunc($outputData);
//unknown output item if($output === null){ //unknown output item
continue; continue 2;
}
$outputs[] = $output;
} }
$result->registerShapelessRecipe(new ShapelessRecipe( $result->registerShapelessRecipe(new ShapelessRecipe(
$inputs, $inputs,
@ -106,32 +252,34 @@ final class CraftingManagerFromDataHelper{
$recipeType $recipeType
)); ));
} }
foreach($recipes["shaped"] as $recipe){ foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'shaped_crafting.json'), ShapedRecipeData::class) as $recipe){
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics if($recipe->block !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
continue; continue;
} }
$inputs = []; $inputs = [];
foreach($recipe["input"] as $symbol => $inputData){ foreach(Utils::stringifyKeys($recipe->input) as $symbol => $inputData){
$input = $ingredientDeserializerFunc($inputData); $input = $ingredientDeserializerFunc($inputData);
if($input === null){ //unknown input item if($input === null){ //unknown input item
continue 2; continue 2;
} }
$inputs[$symbol] = $input; $inputs[$symbol] = $input;
} }
try{ $outputs = [];
$outputs = array_map($itemDeserializerFunc, $recipe["output"]); foreach($recipe->output as $outputData){
}catch(SavedDataLoadingException){ $output = $itemDeserializerFunc($outputData);
//unknown output item if($output === null){ //unknown output item
continue; continue 2;
}
$outputs[] = $output;
} }
$result->registerShapedRecipe(new ShapedRecipe( $result->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"], $recipe->shape,
$inputs, $inputs,
$outputs $outputs
)); ));
} }
foreach($recipes["smelting"] as $recipe){ foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'smelting.json'), FurnaceRecipeData::class) as $recipe){
$furnaceType = match ($recipe["block"]){ $furnaceType = match ($recipe->block){
"furnace" => FurnaceType::FURNACE(), "furnace" => FurnaceType::FURNACE(),
"blast_furnace" => FurnaceType::BLAST_FURNACE(), "blast_furnace" => FurnaceType::BLAST_FURNACE(),
"smoker" => FurnaceType::SMOKER(), "smoker" => FurnaceType::SMOKER(),
@ -141,12 +289,11 @@ final class CraftingManagerFromDataHelper{
if($furnaceType === null){ if($furnaceType === null){
continue; continue;
} }
try{ $output = self::deserializeItemStack($recipe->output);
$output = Item::legacyJsonDeserialize($recipe["output"]); if($output === null){
}catch(SavedDataLoadingException){
continue; continue;
} }
$input = self::deserializeIngredient($recipe["input"]); $input = self::deserializeIngredient($recipe->input);
if($input === null){ if($input === null){
continue; continue;
} }
@ -155,13 +302,12 @@ final class CraftingManagerFromDataHelper{
$input $input
)); ));
} }
foreach($recipes["potion_type"] as $recipe){
try{ foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'potion_type.json'), PotionTypeRecipeData::class) as $recipe){
$input = Item::legacyJsonDeserialize($recipe["input"]); $input = self::deserializeIngredient($recipe->input);
$ingredient = Item::legacyJsonDeserialize($recipe["ingredient"]); $ingredient = self::deserializeIngredient($recipe->ingredient);
$output = Item::legacyJsonDeserialize($recipe["output"]); $output = self::deserializeItemStack($recipe->output);
}catch(SavedDataLoadingException){ if($input === null || $ingredient === null || $output === null){
//unknown item
continue; continue;
} }
$result->registerPotionTypeRecipe(new PotionTypeRecipe( $result->registerPotionTypeRecipe(new PotionTypeRecipe(
@ -170,18 +316,16 @@ final class CraftingManagerFromDataHelper{
$output $output
)); ));
} }
foreach($recipes["potion_container_change"] as $recipe){ foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'potion_container_change.json'), PotionContainerChangeRecipeData::class) as $recipe){
try{ $ingredient = self::deserializeIngredient($recipe->ingredient);
$ingredient = Item::legacyJsonDeserialize($recipe["ingredient"]); if($ingredient === null){
}catch(SavedDataLoadingException){
//unknown item
continue; continue;
} }
//TODO: we'll be able to get rid of these conversions once the crafting data is updated $inputId = $recipe->input_item_name;
$inputId = LegacyItemIdToStringIdMap::getInstance()->legacyToString($recipe["input_item_id"]); $outputId = $recipe->output_item_name;
$outputId = LegacyItemIdToStringIdMap::getInstance()->legacyToString($recipe["output_item_id"]);
if($inputId === null || $outputId === null){ if(self::deserializeItemStackFromNameMeta($inputId, 0) === null || self::deserializeItemStackFromNameMeta($outputId, 0) === null){
//unknown item //unknown item
continue; continue;
} }

View File

@ -31,18 +31,16 @@ class PotionContainerChangeRecipe implements BrewingRecipe{
public function __construct( public function __construct(
private string $inputItemId, private string $inputItemId,
private Item $ingredient, private RecipeIngredient $ingredient,
private string $outputItemId private string $outputItemId
){ ){}
$this->ingredient = clone $ingredient;
}
public function getInputItemId() : string{ public function getInputItemId() : string{
return $this->inputItemId; return $this->inputItemId;
} }
public function getIngredient() : Item{ public function getIngredient() : RecipeIngredient{
return clone $this->ingredient; return $this->ingredient;
} }
public function getOutputItemId() : string{ public function getOutputItemId() : string{

View File

@ -28,21 +28,19 @@ use pocketmine\item\Item;
class PotionTypeRecipe implements BrewingRecipe{ class PotionTypeRecipe implements BrewingRecipe{
public function __construct( public function __construct(
private Item $input, private RecipeIngredient $input,
private Item $ingredient, private RecipeIngredient $ingredient,
private Item $output private Item $output
){ ){
$this->input = clone $input;
$this->ingredient = clone $ingredient;
$this->output = clone $output; $this->output = clone $output;
} }
public function getInput() : Item{ public function getInput() : RecipeIngredient{
return clone $this->input; return $this->input;
} }
public function getIngredient() : Item{ public function getIngredient() : RecipeIngredient{
return clone $this->ingredient; return $this->ingredient;
} }
public function getOutput() : Item{ public function getOutput() : Item{
@ -50,6 +48,6 @@ class PotionTypeRecipe implements BrewingRecipe{
} }
public function getResultFor(Item $input) : ?Item{ public function getResultFor(Item $input) : ?Item{
return $input->equals($this->input, true, false) ? $this->getOutput() : null; return $this->input->accepts($input) ? $this->getOutput() : null;
} }
} }

View File

@ -0,0 +1,42 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class FurnaceRecipeData{
/** @required */
public RecipeIngredientData $input;
/** @required */
public ItemStackData $output;
/** @required */
public string $block;
public function __construct(RecipeIngredientData $input, ItemStackData $output, string $block){
$this->input = $input;
$this->output = $output;
$this->block = $block;
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class ItemStackData{
/** @required */
public string $name;
public int $count;
public string $block_states;
public int $meta;
public string $nbt;
/** @var string[] */
public array $can_place_on;
/** @var string[] */
public array $can_destroy;
public function __construct(string $name){
$this->name = $name;
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class PotionContainerChangeRecipeData{
/** @required */
public string $input_item_name;
/** @required */
public RecipeIngredientData $ingredient;
/** @required */
public string $output_item_name;
public function __construct(string $input_item_name, RecipeIngredientData $ingredient, string $output_item_name){
$this->input_item_name = $input_item_name;
$this->ingredient = $ingredient;
$this->output_item_name = $output_item_name;
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class PotionTypeRecipeData{
/** @required */
public RecipeIngredientData $input;
/** @required */
public RecipeIngredientData $ingredient;
/** @required */
public ItemStackData $output;
public function __construct(RecipeIngredientData $input, RecipeIngredientData $ingredient, ItemStackData $output){
$this->input = $input;
$this->ingredient = $ingredient;
$this->output = $output;
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class RecipeIngredientData{
public const WILDCARD_META_VALUE = 32767;
/** @required */
public string $name;
/** @required */
public int $meta;
public int $count;
public function __construct(string $name, int $meta){
$this->name = $name;
$this->meta = $meta;
}
}

View File

@ -0,0 +1,72 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class ShapedRecipeData{
/**
* @required
* @var string[]
* @phpstan-var list<string>
*/
public array $shape;
/**
* @required
* @var RecipeIngredientData[]
* @phpstan-var array<string, RecipeIngredientData>
*/
public array $input;
/**
* @required
* @var ItemStackData[]
* @phpstan-var list<ItemStackData>
*/
public array $output;
/** @required */
public string $block;
/** @required */
public int $priority;
/**
* TODO: convert this to use promoted properties - avoiding them for now since it would break JsonMapper
*
* @param string[] $shape
* @param RecipeIngredientData[] $input
* @param ItemStackData[] $output
*
* @phpstan-param list<string> $shape
* @phpstan-param array<string, RecipeIngredientData> $input
* @phpstan-param list<ItemStackData> $output
*/
public function __construct(array $shape, array $input, array $output, string $block, int $priority){
$this->block = $block;
$this->priority = $priority;
$this->shape = $shape;
$this->input = $input;
$this->output = $output;
}
}

View File

@ -0,0 +1,61 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crafting\json;
final class ShapelessRecipeData{
/**
* @required
* @var RecipeIngredientData[]
* @phpstan-var list<RecipeIngredientData>
*/
public array $input;
/**
* @required
* @var ItemStackData[]
* @phpstan-var list<ItemStackData>
*/
public array $output;
/** @required */
public string $block;
/** @required */
public int $priority;
/**
* @param RecipeIngredientData[] $input
* @param ItemStackData[] $output
*
* @phpstan-param list<RecipeIngredientData> $input
* @phpstan-param list<ItemStackData> $output
*/
public function __construct(array $input, array $output, string $block, int $priority){
$this->block = $block;
$this->priority = $priority;
$this->input = $input;
$this->output = $output;
}
}

View File

@ -27,7 +27,7 @@ use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait; use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use Webmozart\PathUtil\Path; use Webmozart\PathUtil\Path;
use function array_search; use function array_flip;
use function file_get_contents; use function file_get_contents;
use function is_array; use function is_array;
use function json_decode; use function json_decode;
@ -52,20 +52,25 @@ final class BlockItemIdMap{
return new self($map); return new self($map);
} }
/**
* @var string[]
* @phpstan-var array<string, string>
*/
private array $itemToBlockId;
/** /**
* @param string[] $blockToItemId * @param string[] $blockToItemId
* @phpstan-param array<string, string> $blockToItemId * @phpstan-param array<string, string> $blockToItemId
*/ */
public function __construct(private array $blockToItemId){} public function __construct(private array $blockToItemId){
$this->itemToBlockId = array_flip($this->blockToItemId);
}
public function lookupItemId(string $blockId) : ?string{ public function lookupItemId(string $blockId) : ?string{
return $this->blockToItemId[$blockId] ?? null; return $this->blockToItemId[$blockId] ?? null;
} }
public function lookupBlockId(string $itemId) : ?string{ public function lookupBlockId(string $itemId) : ?string{
//we don't need this for any functionality, so we're not concerned about performance here return $this->itemToBlockId[$itemId] ?? null;
//however, it might be nice to have for debugging
$blockId = array_search($itemId, $this->blockToItemId, true);
return $blockId !== false ? $blockId : null;
} }
} }

View File

@ -23,13 +23,11 @@ declare(strict_types=1);
namespace pocketmine\inventory; namespace pocketmine\inventory;
use pocketmine\data\SavedDataLoadingException; use pocketmine\crafting\CraftingManagerFromDataHelper;
use pocketmine\crafting\json\ItemStackData;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\utils\SingletonTrait; use pocketmine\utils\SingletonTrait;
use Webmozart\PathUtil\Path; use Webmozart\PathUtil\Path;
use function file_get_contents;
use function is_array;
use function json_decode;
final class CreativeInventory{ final class CreativeInventory{
use SingletonTrait; use SingletonTrait;
@ -38,18 +36,13 @@ final class CreativeInventory{
private array $creative = []; private array $creative = [];
private function __construct(){ private function __construct(){
$creativeItems = json_decode(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "creativeitems.json")), true); $creativeItems = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile(
if(!is_array($creativeItems)){ Path::join(\pocketmine\BEDROCK_DATA_PATH, "creativeitems.json"),
throw new SavedDataLoadingException("Invalid creative items file, expected array as root type"); ItemStackData::class
} );
foreach($creativeItems as $data){ foreach($creativeItems as $data){
if(!is_array($data)){ $item = CraftingManagerFromDataHelper::deserializeItemStack($data);
throw new SavedDataLoadingException("Invalid creative items file, expected array as item type"); if($item === null){
}
try{
$item = Item::legacyJsonDeserialize($data);
}catch(SavedDataLoadingException){
//unknown item //unknown item
continue; continue;
} }

View File

@ -149,8 +149,8 @@ final class CraftingDataCache{
$potionTypeRecipes = []; $potionTypeRecipes = [];
foreach($manager->getPotionTypeRecipes() as $recipe){ foreach($manager->getPotionTypeRecipes() as $recipe){
$input = $converter->coreItemStackToNet($recipe->getInput()); $input = $converter->coreRecipeIngredientToNet($recipe->getInput());
$ingredient = $converter->coreItemStackToNet($recipe->getIngredient()); $ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient());
$output = $converter->coreItemStackToNet($recipe->getOutput()); $output = $converter->coreItemStackToNet($recipe->getOutput());
$potionTypeRecipes[] = new ProtocolPotionTypeRecipe( $potionTypeRecipes[] = new ProtocolPotionTypeRecipe(
$input->getId(), $input->getId(),
@ -166,7 +166,7 @@ final class CraftingDataCache{
$itemTypeDictionary = GlobalItemTypeDictionary::getInstance()->getDictionary(); $itemTypeDictionary = GlobalItemTypeDictionary::getInstance()->getDictionary();
foreach($manager->getPotionContainerChangeRecipes() as $recipe){ foreach($manager->getPotionContainerChangeRecipes() as $recipe){
$input = $itemTypeDictionary->fromStringId($recipe->getInputItemId()); $input = $itemTypeDictionary->fromStringId($recipe->getInputItemId());
$ingredient = $converter->coreItemStackToNet($recipe->getIngredient()); $ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient());
$output = $itemTypeDictionary->fromStringId($recipe->getOutputItemId()); $output = $itemTypeDictionary->fromStringId($recipe->getOutputItemId());
$potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe( $potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe(
$input, $input,

View File

@ -575,21 +575,6 @@ parameters:
count: 1 count: 1
path: ../../../src/entity/projectile/Projectile.php path: ../../../src/entity/projectile/Projectile.php
-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 1
path: ../../../src/inventory/CreativeInventory.php
-
message: "#^Parameter \\#1 \\$data of static method pocketmine\\\\item\\\\Item\\:\\:jsonDeserialize\\(\\) expects array, mixed given\\.$#"
count: 1
path: ../../../src/inventory/CreativeInventory.php
-
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 1
path: ../../../src/inventory/CreativeInventory.php
- -
message: "#^Parameter \\#2 \\$recipe of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects pocketmine\\\\crafting\\\\CraftingRecipe, pocketmine\\\\crafting\\\\CraftingRecipe\\|null given\\.$#" message: "#^Parameter \\#2 \\$recipe of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects pocketmine\\\\crafting\\\\CraftingRecipe, pocketmine\\\\crafting\\\\CraftingRecipe\\|null given\\.$#"
count: 1 count: 1