Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions generated/item/VanillaItems.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ final class VanillaItems{
private static Item $_mBLAZE_POWDER;
private static BlazeRod $_mBLAZE_ROD;
private static Item $_mBLEACH;
private static Item $_mBOLT_ARMOR_TRIM_SMITHING_TEMPLATE;
private static Item $_mBONE;
private static Fertilizer $_mBONE_MEAL;
private static Book $_mBOOK;
Expand Down Expand Up @@ -184,6 +185,7 @@ final class VanillaItems{
private static FishingRod $_mFISHING_ROD;
private static Item $_mFLINT;
private static FlintSteel $_mFLINT_AND_STEEL;
private static Item $_mFLOW_ARMOR_TRIM_SMITHING_TEMPLATE;
private static Item $_mGHAST_TEAR;
private static GlassBottle $_mGLASS_BOTTLE;
private static Item $_mGLISTERING_MELON;
Expand Down Expand Up @@ -437,6 +439,7 @@ private static function getInitAssigners() : array{
"blaze_powder" => fn(Item $v) => self::$_mBLAZE_POWDER = $v,
"blaze_rod" => fn(BlazeRod $v) => self::$_mBLAZE_ROD = $v,
"bleach" => fn(Item $v) => self::$_mBLEACH = $v,
"bolt_armor_trim_smithing_template" => fn(Item $v) => self::$_mBOLT_ARMOR_TRIM_SMITHING_TEMPLATE = $v,
"bone" => fn(Item $v) => self::$_mBONE = $v,
"bone_meal" => fn(Fertilizer $v) => self::$_mBONE_MEAL = $v,
"book" => fn(Book $v) => self::$_mBOOK = $v,
Expand Down Expand Up @@ -558,6 +561,7 @@ private static function getInitAssigners() : array{
"fishing_rod" => fn(FishingRod $v) => self::$_mFISHING_ROD = $v,
"flint" => fn(Item $v) => self::$_mFLINT = $v,
"flint_and_steel" => fn(FlintSteel $v) => self::$_mFLINT_AND_STEEL = $v,
"flow_armor_trim_smithing_template" => fn(Item $v) => self::$_mFLOW_ARMOR_TRIM_SMITHING_TEMPLATE = $v,
"ghast_tear" => fn(Item $v) => self::$_mGHAST_TEAR = $v,
"glass_bottle" => fn(GlassBottle $v) => self::$_mGLASS_BOTTLE = $v,
"glistering_melon" => fn(Item $v) => self::$_mGLISTERING_MELON = $v,
Expand Down Expand Up @@ -907,6 +911,11 @@ public static function BLEACH() : Item{
return clone self::$_mBLEACH;
}

public static function BOLT_ARMOR_TRIM_SMITHING_TEMPLATE() : Item{
if(!isset(self::$_mBOLT_ARMOR_TRIM_SMITHING_TEMPLATE)){ self::init(); }
return clone self::$_mBOLT_ARMOR_TRIM_SMITHING_TEMPLATE;
}

public static function BONE() : Item{
if(!isset(self::$_mBONE)){ self::init(); }
return clone self::$_mBONE;
Expand Down Expand Up @@ -1512,6 +1521,11 @@ public static function FLINT_AND_STEEL() : FlintSteel{
return clone self::$_mFLINT_AND_STEEL;
}

public static function FLOW_ARMOR_TRIM_SMITHING_TEMPLATE() : Item{
if(!isset(self::$_mFLOW_ARMOR_TRIM_SMITHING_TEMPLATE)){ self::init(); }
return clone self::$_mFLOW_ARMOR_TRIM_SMITHING_TEMPLATE;
}

public static function GHAST_TEAR() : Item{
if(!isset(self::$_mGHAST_TEAR)){ self::init(); }
return clone self::$_mGHAST_TEAR;
Expand Down
13 changes: 13 additions & 0 deletions src/crafting/CraftingManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
}
}

/**
* Registers a smithing trim recipe. Unlike shaped/shapeless recipes, this isn't indexed by its results, since
* the actual output depends on which specific pattern and material items are provided - it's not something that
* can be matched generically, so it's handled as a special case in {@link SmithingTrimRecipe}'s consumer.
*/
public function registerSmithingTrimRecipe(SmithingTrimRecipe $recipe) : void{
$this->craftingRecipeIndex[] = $recipe;

foreach($this->recipeRegisteredCallbacks as $callback){
$callback();
}
}

public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
$this->potionTypeRecipes[] = $recipe;

Expand Down
58 changes: 57 additions & 1 deletion src/crafting/CraftingManagerFromDataHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,58 @@ private static function loadShapelessRecipe(CraftingManager $manager, array $rec
}
}

/**
* @param mixed[] $recipe
*/
private static function loadSmithingTransformRecipe(CraftingManager $manager, array $recipe) : void{
if(!isset($recipe["base"], $recipe["addition"], $recipe["template"], $recipe["result"]) ||
!is_array($recipe["base"]) || !is_array($recipe["addition"]) || !is_array($recipe["template"]) || !is_array($recipe["result"])
){
throw new SavedDataLoadingException("Smithing transform recipe should have base, addition, template and result objects");
}

$base = self::deserializeNetworkIngredient($recipe["base"]);
$addition = self::deserializeNetworkIngredient($recipe["addition"]);
$template = self::deserializeNetworkIngredient($recipe["template"]);
if($base === null || $addition === null || $template === null){
//unknown ingredient item
return;
}

$result = self::deserializeNetworkItemStack($recipe["result"]);
if($result === null){
//unknown result item
return;
}

$manager->registerShapelessRecipe(new ShapelessRecipe(
[$base, $addition, $template],
[$result],
ShapelessRecipeType::SMITHING
));
Comment on lines +345 to +349

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve base item data in smithing transforms

When upgrading damaged, enchanted, named, unbreakable, or trimmed diamond equipment, this static ShapelessRecipe produces the pristine JSON result instead of inheriting the base stack's data. CraftingTransaction::getExpectedResultsFor() only invokes this recipe and the container-specific transfer helper, so the client's correctly inherited netherite output will fail the exact-output validation and the upgrade will be rejected; smithing transforms need a dynamic result derived from the base item.

Useful? React with 👍 / 👎.

}

/**
* @param mixed[] $recipe
*/
private static function loadSmithingTrimRecipe(CraftingManager $manager, array $recipe) : void{
if(!isset($recipe["base"], $recipe["addition"], $recipe["template"]) ||
!is_array($recipe["base"]) || !is_array($recipe["addition"]) || !is_array($recipe["template"])
){
throw new SavedDataLoadingException("Smithing trim recipe should have base, addition and template objects");
}

$base = self::deserializeNetworkIngredient($recipe["base"]);
$addition = self::deserializeNetworkIngredient($recipe["addition"]);
$template = self::deserializeNetworkIngredient($recipe["template"]);
if($base === null || $addition === null || $template === null){
//unknown ingredient item
return;
}

$manager->registerSmithingTrimRecipe(new SmithingTrimRecipe($base, $addition, $template));
}

/**
* @param mixed[] $recipe
*/
Expand Down Expand Up @@ -425,9 +477,13 @@ public static function make(string $filePath) : CraftingManager{
case self::NETWORK_RECIPE_TYPE_SHAPED:
self::loadShapedRecipe($result, $recipe);
break;
case self::NETWORK_RECIPE_TYPE_MULTI:
case self::NETWORK_RECIPE_TYPE_SMITHING_TRANSFORM:
self::loadSmithingTransformRecipe($result, $recipe);
break;
case self::NETWORK_RECIPE_TYPE_SMITHING_TRIM:
self::loadSmithingTrimRecipe($result, $recipe);
break;
case self::NETWORK_RECIPE_TYPE_MULTI:
//TODO: not supported by the crafting system yet
break;
}
Expand Down
67 changes: 67 additions & 0 deletions src/crafting/SmithingTrimRecipe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?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.
*
* Original work by the PocketMine Team.
* https://www.pocketmine.net/
*
* @author Altay Team
* @link https://github.com/altayofficial
*/

declare(strict_types=1);

namespace pocketmine\crafting;

use pocketmine\utils\AssumptionFailedError;

/**
* Represents the smithing table's armor trim recipe. This only exists so the client knows which items are valid in
* each smithing table slot and to advertise the recipe in the recipe book - the actual result item (which depends on
* the specific pattern and material used) is computed by {@link \pocketmine\inventory\transaction\SmithingTrimTransaction}
* rather than through the generic recipe matching system, since getResultsFor()/matchesCraftingGrid() aren't called.
*/
final class SmithingTrimRecipe implements CraftingRecipe{

public function __construct(
private RecipeIngredient $base,
private RecipeIngredient $addition,
private RecipeIngredient $template
){}

public function getBase() : RecipeIngredient{
return $this->base;
}

public function getAddition() : RecipeIngredient{
return $this->addition;
}

public function getTemplate() : RecipeIngredient{
return $this->template;
}

public function getIngredientList() : array{
return [$this->base, $this->addition, $this->template];
}

public function getResultsFor(CraftingGrid $grid) : array{
throw new AssumptionFailedError("Armor trim results are computed by SmithingTrimTransaction, not through the generic recipe system");
}

public function matchesCraftingGrid(CraftingGrid $grid) : bool{
return false;
}
}
84 changes: 84 additions & 0 deletions src/data/bedrock/item/ArmorTrimIdMap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?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.
*
* Original work by the PocketMine Team.
* https://www.pocketmine.net/
*
* @author Altay Team
* @link https://github.com/altayofficial
*/

declare(strict_types=1);

namespace pocketmine\data\bedrock\item;

use pocketmine\crafting\CraftingManagerFromDataHelper;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\item\Item;
use pocketmine\utils\Filesystem;
use pocketmine\utils\SingletonTrait;
use function is_array;
use function is_string;
use function json_decode;

/**
* Maps smithing templates to trim pattern IDs, and trim-eligible ingredients (ingots, crystals, etc.) to trim material
* IDs, as used by the smithing table's armor trim recipe.
*/
final class ArmorTrimIdMap{
use SingletonTrait;

/** @var array<int, string> typeId => patternId */
private array $patterns = [];
/** @var array<int, string> typeId => materialId */
private array $materials = [];

private function __construct(){
$data = json_decode(Filesystem::fileGetContents(BedrockDataFiles::TRIM_DATA_JSON), true);
if(!is_array($data) || !isset($data["patterns"], $data["materials"]) || !is_array($data["patterns"]) || !is_array($data["materials"])){
throw new SavedDataLoadingException(BedrockDataFiles::TRIM_DATA_JSON . " should contain patterns and materials lists");
}

foreach($data["patterns"] as $pattern){
if(!is_array($pattern) || !isset($pattern["itemName"], $pattern["patternId"]) || !is_string($pattern["itemName"]) || !is_string($pattern["patternId"])){
throw new SavedDataLoadingException("Invalid trim pattern entry");
}
$item = CraftingManagerFromDataHelper::deserializeItemStackFromFields($pattern["itemName"], null, 1, null, null);
if($item !== null){
$this->patterns[$item->getTypeId()] = $pattern["patternId"];
}
}

foreach($data["materials"] as $material){
if(!is_array($material) || !isset($material["itemName"], $material["materialId"]) || !is_string($material["itemName"]) || !is_string($material["materialId"])){
throw new SavedDataLoadingException("Invalid trim material entry");
}
$item = CraftingManagerFromDataHelper::deserializeItemStackFromFields($material["itemName"], null, 1, null, null);
if($item !== null){
$this->materials[$item->getTypeId()] = $material["materialId"];
}
}
}

public function getPatternId(Item $template) : ?string{
return $this->patterns[$template->getTypeId()] ?? null;
}

public function getMaterialId(Item $ingredient) : ?string{
return $this->materials[$ingredient->getTypeId()] ?? null;
}
}
2 changes: 2 additions & 0 deletions src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ private function register1to1ItemMappings() : void{
$this->map1to1Item(Ids::BLAZE_POWDER, Items::BLAZE_POWDER());
$this->map1to1Item(Ids::BLAZE_ROD, Items::BLAZE_ROD());
$this->map1to1Item(Ids::BLEACH, Items::BLEACH());
$this->map1to1Item(Ids::BOLT_ARMOR_TRIM_SMITHING_TEMPLATE, Items::BOLT_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::BONE, Items::BONE());
$this->map1to1Item(Ids::BONE_MEAL, Items::BONE_MEAL());
$this->map1to1Item(Ids::BOOK, Items::BOOK());
Expand Down Expand Up @@ -275,6 +276,7 @@ private function register1to1ItemMappings() : void{
$this->map1to1Item(Ids::FISHING_ROD, Items::FISHING_ROD());
$this->map1to1Item(Ids::FLINT, Items::FLINT());
$this->map1to1Item(Ids::FLINT_AND_STEEL, Items::FLINT_AND_STEEL());
$this->map1to1Item(Ids::FLOW_ARMOR_TRIM_SMITHING_TEMPLATE, Items::FLOW_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::GHAST_TEAR, Items::GHAST_TEAR());
$this->map1to1Item(Ids::GLASS_BOTTLE, Items::GLASS_BOTTLE());
$this->map1to1Item(Ids::GLISTERING_MELON_SLICE, Items::GLISTERING_MELON());
Expand Down
Loading
Loading