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": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "a546e15f6a8d7498fb25d5a02ce16184a429bb78"
"reference": "01948a627448395d9946c2e6a5e8332a8456eaa0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/a546e15f6a8d7498fb25d5a02ce16184a429bb78",
"reference": "a546e15f6a8d7498fb25d5a02ce16184a429bb78",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/01948a627448395d9946c2e6a5e8332a8456eaa0",
"reference": "01948a627448395d9946c2e6a5e8332a8456eaa0",
"shasum": ""
},
"type": "library",
@ -298,7 +298,7 @@
"issues": "https://github.com/pmmp/BedrockData/issues",
"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",

View File

@ -963,7 +963,7 @@ class Server{
$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);

View File

@ -264,13 +264,13 @@ class CraftingManager{
}
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;
}
}
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;
}
}

View File

@ -23,61 +23,205 @@ declare(strict_types=1);
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\upgrade\LegacyItemIdToStringIdMap;
use pocketmine\data\bedrock\item\SavedItemData;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\errorhandler\ErrorToExceptionHandler;
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\Utils;
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 get_debug_type;
use function is_array;
use function is_int;
use function is_object;
use function json_decode;
final class CraftingManagerFromDataHelper{
/**
* @param mixed[] $data
*/
private static function deserializeIngredient(array $data) : ?RecipeIngredient{
if(!isset($data["id"]) || !is_int($data["id"])){
throw new \InvalidArgumentException("Invalid input data, expected int ID");
}
if(isset($data["damage"]) && $data["damage"] === -1){
try{
$typeData = GlobalItemDataHandlers::getUpgrader()->upgradeItemTypeDataInt($data["id"], 0, 1, null);
}catch(ItemTypeDeserializeException){
//probably unknown item
return null;
private static function deserializeItemStackFromNameMeta(string $name, int $meta) : ?Item{
$blockName = BlockItemIdMap::getInstance()->lookupBlockId($name);
if($blockName !== null){
$blockStateDictionary = RuntimeBlockMapping::getInstance()->getBlockStateDictionary();
$blockRuntimeId = $blockStateDictionary->lookupStateIdFromIdMeta($name, $meta === RecipeIngredientData::WILDCARD_META_VALUE ? 0 : $meta);
if($blockRuntimeId === null){
throw new \InvalidArgumentException("$blockName with meta $meta doesn't map to any known blockstate");
}
return new MetaWildcardRecipeIngredient($typeData->getTypeData()->getName());
$blockStateData = $blockStateDictionary->getDataFromStateId($blockRuntimeId);
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{
$item = Item::legacyJsonDeserialize($data);
}catch(SavedDataLoadingException){
//unknown item
return GlobalItemDataHandlers::getDeserializer()->deserializeType($itemTypeData);
}catch(ItemTypeDeserializeException){
//probably unknown item
return null;
}
return new ExactRecipeIngredient($item);
}
public static function make(string $filePath) : CraftingManager{
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true);
if(!is_array($recipes)){
throw new AssumptionFailedError("recipes.json root should contain a map of recipe types");
private static function deserializeIngredient(RecipeIngredientData $data) : ?RecipeIngredient{
if(isset($data->count) && $data->count !== 1){
//every case we've seen so far where this isn't the case, it's been a bug and the count was ignored anyway
//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();
$ingredientDeserializerFunc = \Closure::fromCallable([self::class, "deserializeIngredient"]);
$itemDeserializerFunc = \Closure::fromCallable([Item::class, 'legacyJsonDeserialize']);
$itemDeserializerFunc = \Closure::fromCallable([self::class, 'deserializeItemStack']);
foreach($recipes["shapeless"] as $recipe){
$recipeType = match($recipe["block"]){
foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'shapeless_crafting.json'), ShapelessRecipeData::class) as $recipe){
$recipeType = match($recipe->block){
"crafting_table" => ShapelessRecipeType::CRAFTING(),
"stonecutter" => ShapelessRecipeType::STONECUTTER(),
//TODO: Cartography Table
@ -87,18 +231,20 @@ final class CraftingManagerFromDataHelper{
continue;
}
$inputs = [];
foreach($recipe["input"] as $inputData){
foreach($recipe->input as $inputData){
$input = $ingredientDeserializerFunc($inputData);
if($input === null){ //unknown input item
continue 2;
}
$inputs[] = $input;
}
try{
$outputs = array_map($itemDeserializerFunc, $recipe["output"]);
}catch(SavedDataLoadingException){
//unknown output item
continue;
$outputs = [];
foreach($recipe->output as $outputData){
$output = $itemDeserializerFunc($outputData);
if($output === null){ //unknown output item
continue 2;
}
$outputs[] = $output;
}
$result->registerShapelessRecipe(new ShapelessRecipe(
$inputs,
@ -106,32 +252,34 @@ final class CraftingManagerFromDataHelper{
$recipeType
));
}
foreach($recipes["shaped"] as $recipe){
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
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
continue;
}
$inputs = [];
foreach($recipe["input"] as $symbol => $inputData){
foreach(Utils::stringifyKeys($recipe->input) as $symbol => $inputData){
$input = $ingredientDeserializerFunc($inputData);
if($input === null){ //unknown input item
continue 2;
}
$inputs[$symbol] = $input;
}
try{
$outputs = array_map($itemDeserializerFunc, $recipe["output"]);
}catch(SavedDataLoadingException){
//unknown output item
continue;
$outputs = [];
foreach($recipe->output as $outputData){
$output = $itemDeserializerFunc($outputData);
if($output === null){ //unknown output item
continue 2;
}
$outputs[] = $output;
}
$result->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"],
$recipe->shape,
$inputs,
$outputs
));
}
foreach($recipes["smelting"] as $recipe){
$furnaceType = match ($recipe["block"]){
foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'smelting.json'), FurnaceRecipeData::class) as $recipe){
$furnaceType = match ($recipe->block){
"furnace" => FurnaceType::FURNACE(),
"blast_furnace" => FurnaceType::BLAST_FURNACE(),
"smoker" => FurnaceType::SMOKER(),
@ -141,12 +289,11 @@ final class CraftingManagerFromDataHelper{
if($furnaceType === null){
continue;
}
try{
$output = Item::legacyJsonDeserialize($recipe["output"]);
}catch(SavedDataLoadingException){
$output = self::deserializeItemStack($recipe->output);
if($output === null){
continue;
}
$input = self::deserializeIngredient($recipe["input"]);
$input = self::deserializeIngredient($recipe->input);
if($input === null){
continue;
}
@ -155,13 +302,12 @@ final class CraftingManagerFromDataHelper{
$input
));
}
foreach($recipes["potion_type"] as $recipe){
try{
$input = Item::legacyJsonDeserialize($recipe["input"]);
$ingredient = Item::legacyJsonDeserialize($recipe["ingredient"]);
$output = Item::legacyJsonDeserialize($recipe["output"]);
}catch(SavedDataLoadingException){
//unknown item
foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'potion_type.json'), PotionTypeRecipeData::class) as $recipe){
$input = self::deserializeIngredient($recipe->input);
$ingredient = self::deserializeIngredient($recipe->ingredient);
$output = self::deserializeItemStack($recipe->output);
if($input === null || $ingredient === null || $output === null){
continue;
}
$result->registerPotionTypeRecipe(new PotionTypeRecipe(
@ -170,18 +316,16 @@ final class CraftingManagerFromDataHelper{
$output
));
}
foreach($recipes["potion_container_change"] as $recipe){
try{
$ingredient = Item::legacyJsonDeserialize($recipe["ingredient"]);
}catch(SavedDataLoadingException){
//unknown item
foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'potion_container_change.json'), PotionContainerChangeRecipeData::class) as $recipe){
$ingredient = self::deserializeIngredient($recipe->ingredient);
if($ingredient === null){
continue;
}
//TODO: we'll be able to get rid of these conversions once the crafting data is updated
$inputId = LegacyItemIdToStringIdMap::getInstance()->legacyToString($recipe["input_item_id"]);
$outputId = LegacyItemIdToStringIdMap::getInstance()->legacyToString($recipe["output_item_id"]);
if($inputId === null || $outputId === null){
$inputId = $recipe->input_item_name;
$outputId = $recipe->output_item_name;
if(self::deserializeItemStackFromNameMeta($inputId, 0) === null || self::deserializeItemStackFromNameMeta($outputId, 0) === null){
//unknown item
continue;
}

View File

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

View File

@ -28,21 +28,19 @@ use pocketmine\item\Item;
class PotionTypeRecipe implements BrewingRecipe{
public function __construct(
private Item $input,
private Item $ingredient,
private RecipeIngredient $input,
private RecipeIngredient $ingredient,
private Item $output
){
$this->input = clone $input;
$this->ingredient = clone $ingredient;
$this->output = clone $output;
}
public function getInput() : Item{
return clone $this->input;
public function getInput() : RecipeIngredient{
return $this->input;
}
public function getIngredient() : Item{
return clone $this->ingredient;
public function getIngredient() : RecipeIngredient{
return $this->ingredient;
}
public function getOutput() : Item{
@ -50,6 +48,6 @@ class PotionTypeRecipe implements BrewingRecipe{
}
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\Utils;
use Webmozart\PathUtil\Path;
use function array_search;
use function array_flip;
use function file_get_contents;
use function is_array;
use function json_decode;
@ -52,20 +52,25 @@ final class BlockItemIdMap{
return new self($map);
}
/**
* @var string[]
* @phpstan-var array<string, string>
*/
private array $itemToBlockId;
/**
* @param 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{
return $this->blockToItemId[$blockId] ?? null;
}
public function lookupBlockId(string $itemId) : ?string{
//we don't need this for any functionality, so we're not concerned about performance here
//however, it might be nice to have for debugging
$blockId = array_search($itemId, $this->blockToItemId, true);
return $blockId !== false ? $blockId : null;
return $this->itemToBlockId[$itemId] ?? null;
}
}

View File

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

View File

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

View File

@ -575,21 +575,6 @@ parameters:
count: 1
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\\.$#"
count: 1