Merge branch 'stable' into master

This commit is contained in:
Dylan K. Taylor 2020-08-03 20:14:46 +01:00
commit e1d80f05b1
5 changed files with 77 additions and 66 deletions

@ -1 +1 @@
Subproject commit 43edcfde6b9611b7c7d643be52332707cc10cd1b
Subproject commit 767676e2b97843072220bad719c076b169c28fd3

View File

@ -24,52 +24,52 @@ declare(strict_types=1);
namespace pocketmine\crafting;
use pocketmine\item\Item;
use pocketmine\utils\AssumptionFailedError;
use function array_map;
use function file_get_contents;
use function is_array;
use function json_decode;
final class CraftingManagerFromDataHelper{
public static function make(string $filePath) : CraftingManager{
$recipes = json_decode(file_get_contents($filePath), true);
if(!is_array($recipes)){
throw new AssumptionFailedError("recipes.json root should contain a map of recipe types");
}
$result = new CraftingManager();
$itemDeserializerFunc = \Closure::fromCallable([Item::class, 'jsonDeserialize']);
foreach($recipes as $recipe){
switch($recipe["type"]){
case "shapeless":
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
break;
}
$result->registerShapelessRecipe(new ShapelessRecipe(
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"])
));
break;
case "shaped":
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
break;
}
$result->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"],
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"])
));
break;
case "smelting":
if($recipe["block"] !== "furnace"){ //TODO: filter others out for now to avoid breaking economics
break;
}
$result->getFurnaceRecipeManager()->register(new FurnaceRecipe(
Item::jsonDeserialize($recipe["output"]),
Item::jsonDeserialize($recipe["input"]))
);
break;
default:
break;
foreach($recipes["shapeless"] as $recipe){
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
continue;
}
$result->registerShapelessRecipe(new ShapelessRecipe(
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"])
));
}
foreach($recipes["shaped"] as $recipe){
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
continue;
}
$result->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"],
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"])
));
}
foreach($recipes["smelting"] as $recipe){
if($recipe["block"] !== "furnace"){ //TODO: filter others out for now to avoid breaking economics
continue;
}
$result->getFurnaceRecipeManager()->register(new FurnaceRecipe(
Item::jsonDeserialize($recipe["output"]),
Item::jsonDeserialize($recipe["input"]))
);
}
return $result;
}
}
}

View File

@ -137,6 +137,8 @@ class InventoryTransaction{
* @throws TransactionValidationException
*/
protected function matchItems(array &$needItems, array &$haveItems) : void{
$needItems = [];
$haveItems = [];
foreach($this->actions as $key => $action){
if(!$action->getTargetItem()->isNull()){
$needItems[] = $action->getTargetItem();

View File

@ -34,6 +34,7 @@ use pocketmine\inventory\transaction\action\InventoryAction;
use pocketmine\inventory\transaction\CraftingTransaction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionException;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\VanillaItems;
use pocketmine\item\WritableBook;
use pocketmine\item\WrittenBook;
@ -82,10 +83,12 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes;
@ -215,19 +218,23 @@ class InGamePacketHandler extends PacketHandler{
$actions = [];
$isCraftingPart = false;
$isFinalCraftingPart = false;
$converter = TypeConverter::getInstance();
foreach($data->getActions() as $networkInventoryAction){
if(
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
(
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
)
) or (
$this->craftingTransaction !== null &&
!$networkInventoryAction->oldItem->equals($networkInventoryAction->newItem) &&
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER &&
$networkInventoryAction->windowId === ContainerIds::UI &&
$networkInventoryAction->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT
)
){
$isCraftingPart = true;
if($networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT){
$isFinalCraftingPart = true;
}
}
try{
@ -242,9 +249,6 @@ class InGamePacketHandler extends PacketHandler{
}
if($isCraftingPart){
//we get the actions for this in several packets, so we need to wait until we have all the pieces before
//trying to execute it
if($this->craftingTransaction === null){
$this->craftingTransaction = new CraftingTransaction($this->player, $this->player->getServer()->getCraftingManager(), $actions);
}else{
@ -253,29 +257,34 @@ class InGamePacketHandler extends PacketHandler{
}
}
if($isFinalCraftingPart){
try{
$this->session->getInvManager()->onTransactionStart($this->craftingTransaction);
$this->craftingTransaction->execute();
}catch(TransactionException $e){
$this->session->getLogger()->debug("Failed to execute crafting transaction: " . $e->getMessage());
try{
$this->craftingTransaction->validate();
}catch(TransactionValidationException $e){
//transaction is incomplete - crafting transaction comes in lots of little bits, so we have to collect
//all of the parts before we can execute it
return true;
}
try{
$this->session->getInvManager()->onTransactionStart($this->craftingTransaction);
$this->craftingTransaction->execute();
}catch(TransactionException $e){
$this->session->getLogger()->debug("Failed to execute crafting transaction: " . $e->getMessage());
//TODO: only sync slots that the client tried to change
foreach($this->craftingTransaction->getInventories() as $inventory){
$this->session->getInvManager()->syncContents($inventory);
}
/*
* TODO: HACK!
* we can't resend the contents of the crafting window, so we force the client to close it instead.
* So people don't whine about messy desync issues when someone cancels CraftItemEvent, or when a crafting
* transaction goes wrong.
*/
$this->session->sendDataPacket(ContainerClosePacket::create(InventoryManager::HARDCODED_CRAFTING_GRID_WINDOW_ID));
return false;
}finally{
$this->craftingTransaction = null;
//TODO: only sync slots that the client tried to change
foreach($this->craftingTransaction->getInventories() as $inventory){
$this->session->getInvManager()->syncContents($inventory);
}
/*
* TODO: HACK!
* we can't resend the contents of the crafting window, so we force the client to close it instead.
* So people don't whine about messy desync issues when someone cancels CraftItemEvent, or when a crafting
* transaction goes wrong.
*/
$this->session->sendDataPacket(ContainerClosePacket::create(InventoryManager::HARDCODED_CRAFTING_GRID_WINDOW_ID));
return false;
}finally{
$this->craftingTransaction = null;
}
}else{
//normal transaction fallthru

View File

@ -52,7 +52,7 @@ class VersionString{
$this->development = $isDevBuild;
$this->build = $buildNumber;
preg_match('/(\d+)\.(\d+)\.(\d+)(?:-(.*))?$/', $this->baseVersion, $matches);
preg_match('/^(\d+)\.(\d+)\.(\d+)(?:-(.*))?$/', $this->baseVersion, $matches);
if(count($matches) < 4){
throw new \InvalidArgumentException("Invalid base version \"$baseVersion\", should contain at least 3 version digits");
}