mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-27 05:40:01 +00:00
Implemented new crafting mechanism
This commit is contained in:
parent
696edfd31f
commit
d026e2ecf0
@ -37,6 +37,7 @@ use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\event\entity\EntityShootBowEvent;
|
||||
use pocketmine\event\entity\ProjectileLaunchEvent;
|
||||
use pocketmine\event\inventory\CraftItemEvent;
|
||||
use pocketmine\event\inventory\InventoryCloseEvent;
|
||||
use pocketmine\event\inventory\InventoryPickupArrowEvent;
|
||||
use pocketmine\event\inventory\InventoryPickupItemEvent;
|
||||
@ -64,12 +65,14 @@ use pocketmine\event\TextContainer;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\event\TranslationContainer;
|
||||
use pocketmine\inventory\BaseTransaction;
|
||||
use pocketmine\inventory\BigShapedRecipe;
|
||||
use pocketmine\inventory\BigShapelessRecipe;
|
||||
use pocketmine\inventory\CraftingTransactionGroup;
|
||||
use pocketmine\inventory\FurnaceInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\inventory\PlayerInventory;
|
||||
use pocketmine\inventory\ShapedRecipe;
|
||||
use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\inventory\StonecutterShapelessRecipe;
|
||||
@ -99,6 +102,7 @@ use pocketmine\network\Network;
|
||||
use pocketmine\network\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\protocol\AnimatePacket;
|
||||
use pocketmine\network\protocol\BatchPacket;
|
||||
use pocketmine\network\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\protocol\ContainerSetContentPacket;
|
||||
use pocketmine\network\protocol\DataPacket;
|
||||
use pocketmine\network\protocol\DisconnectPacket;
|
||||
@ -2556,16 +2560,24 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
break;
|
||||
|
||||
case ProtocolInfo::CRAFTING_EVENT_PACKET:
|
||||
//TODO HACK
|
||||
$this->server->getLogger()->warning("CRAFTING NOT YET IMPLEMENTED!");
|
||||
break;
|
||||
if(count($packet->slots) < 9){
|
||||
if($this->spawned === false or !$this->isAlive()){
|
||||
break;
|
||||
}elseif(!isset($this->windowIndex[$packet->windowId])){
|
||||
$this->inventory->sendContents($this);
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowid = $packet->windowId;
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
}
|
||||
|
||||
$recipe = $this->server->getCraftingManager()->getRecipe($packet->id);
|
||||
|
||||
if($recipe === null or (($recipe instanceof BigShapelessRecipe or $recipe instanceof BigShapedRecipe) and $this->craftingType === 0)){
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($packet->slots as $i => $item){
|
||||
/** @var Item $item */
|
||||
/*foreach($packet->input as $i => $item){
|
||||
if($item->getDamage() === -1 or $item->getDamage() === 0xffff){
|
||||
$item->setDamage(null);
|
||||
}
|
||||
@ -2573,57 +2585,98 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
if($i < 9 and $item->getId() > 0){
|
||||
$item->setCount(1);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
$result = $packet->slots[9];
|
||||
$canCraft = true;
|
||||
|
||||
if($this->craftingType === 1 or $this->craftingType === 2){
|
||||
$recipe = new BigShapelessRecipe($result);
|
||||
|
||||
if($recipe instanceof ShapedRecipe){
|
||||
for($x = 0; $x < 3 and $canCraft; ++$x){
|
||||
for($y = 0; $y < 3; ++$y){
|
||||
$item = $packet->input[$x * 3 + $y];
|
||||
$ingredient = $recipe->getIngredient($x, $y);
|
||||
if($item->getCount() > 0 and $item->getId() > 0){
|
||||
if($ingredient === null or !$ingredient->deepEquals($item, $ingredient->getDamage() === null, $ingredient->getCompoundTag() === null)){
|
||||
$canCraft = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}elseif($ingredient !== null and $ingredient->getId() !== 0){
|
||||
$canCraft = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}elseif($recipe instanceof ShapelessRecipe){
|
||||
$needed = $recipe->getIngredientList();
|
||||
|
||||
for($x = 0; $x < 3 and $canCraft; ++$x){
|
||||
for($y = 0; $y < 3; ++$y){
|
||||
$item = clone $packet->input[$x * 3 + $y];
|
||||
|
||||
foreach($needed as $k => $n){
|
||||
if($n->deepEquals($item, $n->getDamage() === null, $n->getCompoundTag() === null)){
|
||||
$remove = min($n->getCount(), $item->getCount());
|
||||
$n->setCount($n->getCount() - $remove);
|
||||
$item->setCount($item->getCount() - $remove);
|
||||
|
||||
if($n->getCount() === 0){
|
||||
unset($needed[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($item->getCount() > 0){
|
||||
$canCraft = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count($needed) > 0){
|
||||
$canCraft = false;
|
||||
}
|
||||
}else{
|
||||
$recipe = new ShapelessRecipe($result);
|
||||
$canCraft = false;
|
||||
}
|
||||
|
||||
/** @var Item[] $ingredients */
|
||||
$ingredients = [];
|
||||
for($x = 0; $x < 3; ++$x){
|
||||
for($y = 0; $y < 3; ++$y){
|
||||
$item = $packet->slots[$x * 3 + $y];
|
||||
if($item->getCount() > 0 and $item->getId() > 0){
|
||||
//TODO shaped
|
||||
$recipe->addIngredient($item);
|
||||
$ingredients[$x * 3 + $y] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
$ingredients = $packet->input;
|
||||
$result = $packet->output[0];
|
||||
|
||||
if(!Server::getInstance()->getCraftingManager()->matchRecipe($recipe)){
|
||||
$this->server->getLogger()->debug("Unmatched recipe from player ". $this->getName() .": " . $recipe->getResult().", using: " . implode(", ", $recipe->getIngredientList()));
|
||||
if(!$canCraft or !$recipe->getResult()->deepEquals($result)){
|
||||
$this->server->getLogger()->debug("Unmatched recipe ". $recipe->getId() ." from player ". $this->getName() .": expected " . $recipe->getResult() . ", got ". $result .", using: " . implode(", ", $ingredients));
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}
|
||||
|
||||
$canCraft = true;
|
||||
|
||||
$used = array_fill(0, $this->inventory->getSize(), 0);
|
||||
|
||||
foreach($ingredients as $ingredient){
|
||||
$slot = -1;
|
||||
$checkDamage = $ingredient->getDamage() === null ? false : true;
|
||||
foreach($this->inventory->getContents() as $index => $i){
|
||||
if($ingredient->deepEquals($i, $checkDamage) and ($i->getCount() - $used[$index]) >= 1){
|
||||
if($ingredient->getId() !== 0 and $ingredient->deepEquals($i) and ($i->getCount() - $used[$index]) >= 1){
|
||||
$slot = $index;
|
||||
$used[$index]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($slot === -1){
|
||||
if($ingredient->getId() !== 0 and $slot === -1){
|
||||
$canCraft = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$canCraft){
|
||||
$this->server->getLogger()->debug("Unmatched recipe ". $recipe->getId() ." from player ". $this->getName() .": client does not have enough items, using: " . implode(", ", $ingredients));
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($ingredients, $recipe));
|
||||
|
||||
if($ev->isCancelled()){
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}
|
||||
|
@ -1456,7 +1456,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
public function spawnToAll(){
|
||||
if($this->chunk === null){
|
||||
if($this->chunk === null or $this->closed){
|
||||
return;
|
||||
}
|
||||
foreach($this->level->getChunkPlayers($this->chunk->getX(), $this->chunk->getZ()) as $player){
|
||||
|
@ -23,31 +23,36 @@ namespace pocketmine\event\inventory;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\inventory\CraftingTransactionGroup;
|
||||
use pocketmine\inventory\Recipe;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class CraftItemEvent extends Event implements Cancellable{
|
||||
public static $handlerList = null;
|
||||
|
||||
/** @var CraftingTransactionGroup */
|
||||
private $ts;
|
||||
/** @var Item[] */
|
||||
private $input = [];
|
||||
/** @var Recipe */
|
||||
private $recipe;
|
||||
|
||||
/**
|
||||
* @param CraftingTransactionGroup $ts
|
||||
* @param Recipe $recipe
|
||||
* @param Item[] $input
|
||||
* @param Recipe $recipe
|
||||
*/
|
||||
public function __construct(CraftingTransactionGroup $ts, Recipe $recipe){
|
||||
$this->ts = $ts;
|
||||
public function __construct(array $input, Recipe $recipe){
|
||||
$this->input = $input;
|
||||
$this->recipe = $recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CraftingTransactionGroup
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getTransaction(){
|
||||
return $this->ts;
|
||||
public function getInput(){
|
||||
$items = [];
|
||||
foreach($items as $i => $item){
|
||||
$items[$i] = clone $item;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -340,6 +340,14 @@ class CraftingManager{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UUID $id
|
||||
* @return Recipe
|
||||
*/
|
||||
public function getRecipe(UUID $id){
|
||||
$index = $id->toBinary();
|
||||
return isset($this->recipes[$index]) ? $this->recipes[$index] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Recipe[]
|
||||
@ -375,7 +383,7 @@ class CraftingManager{
|
||||
*/
|
||||
public function registerShapedRecipe(ShapedRecipe $recipe){
|
||||
$result = $recipe->getResult();
|
||||
$this->recipes[spl_object_hash($recipe)] = $recipe;
|
||||
$this->recipes[$recipe->getId()->toBinary()] = $recipe;
|
||||
$ingredients = $recipe->getIngredientMap();
|
||||
$hash = "";
|
||||
foreach($ingredients as $v){
|
||||
@ -395,7 +403,7 @@ class CraftingManager{
|
||||
*/
|
||||
public function registerShapelessRecipe(ShapelessRecipe $recipe){
|
||||
$result = $recipe->getResult();
|
||||
$this->recipes[spl_object_hash($recipe)] = $recipe;
|
||||
$this->recipes[$recipe->getId()->toBinary()] = $recipe;
|
||||
$hash = "";
|
||||
$ingredients = $recipe->getIngredientList();
|
||||
usort($ingredients, [$this, "sort"]);
|
||||
@ -471,86 +479,12 @@ class CraftingManager{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingTransactionGroup $ts
|
||||
*
|
||||
* @return Recipe
|
||||
*/
|
||||
public function matchTransaction(CraftingTransactionGroup $ts){
|
||||
$result = $ts->getResult();
|
||||
|
||||
if(!($result instanceof Item)){
|
||||
return false;
|
||||
}
|
||||
$k = $result->getId() . ":" . $result->getDamage();
|
||||
|
||||
if(!isset($this->recipeLookup[$k])){
|
||||
return false;
|
||||
}
|
||||
$hash = "";
|
||||
$input = $ts->getRecipe();
|
||||
usort($input, [$this, "sort"]);
|
||||
$inputCount = 0;
|
||||
foreach($input as $item){
|
||||
$inputCount += $item->getCount();
|
||||
$hash .= $item->getId() . ":" . ($item->getDamage() === null ? "?" : $item->getDamage()) . "x" . $item->getCount() . ",";
|
||||
}
|
||||
if(!isset($this->recipeLookup[$k][$hash])){
|
||||
$hasRecipe = null;
|
||||
foreach($this->recipeLookup[$k] as $recipe){
|
||||
if($recipe instanceof ShapelessRecipe){
|
||||
if($recipe->getIngredientCount() !== $inputCount){
|
||||
continue;
|
||||
}
|
||||
$checkInput = $recipe->getIngredientList();
|
||||
foreach($input as $item){
|
||||
$amount = $item->getCount();
|
||||
foreach($checkInput as $k => $checkItem){
|
||||
if($checkItem->equals($item, $checkItem->getDamage() === null ? false : true, $checkItem->getCompoundTag() === null ? false : true)){
|
||||
$remove = min($checkItem->getCount(), $amount);
|
||||
$checkItem->setCount($checkItem->getCount() - $remove);
|
||||
if($checkItem->getCount() === 0){
|
||||
unset($checkInput[$k]);
|
||||
}
|
||||
$amount -= $remove;
|
||||
if($amount === 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count($checkInput) === 0){
|
||||
$hasRecipe = $recipe;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($hasRecipe instanceof Recipe){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($hasRecipe === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$recipe = $hasRecipe;
|
||||
}else{
|
||||
$recipe = $this->recipeLookup[$k][$hash];
|
||||
}
|
||||
|
||||
$checkResult = $recipe->getResult();
|
||||
if($checkResult->equals($result) and $checkResult->getCount() === $result->getCount()){
|
||||
return $recipe;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Recipe $recipe
|
||||
*/
|
||||
public function registerRecipe(Recipe $recipe){
|
||||
$recipe->setId(UUID::fromData(++self::$RECIPE_COUNT, $recipe->getResult()->getId(), $recipe->getResult()->getDamage(), $recipe->getResult()->getCount(), $recipe->getResult()->getCompoundTag()));
|
||||
|
||||
if($recipe instanceof ShapedRecipe){
|
||||
$this->registerShapedRecipe($recipe);
|
||||
}elseif($recipe instanceof ShapelessRecipe){
|
||||
@ -558,8 +492,6 @@ class CraftingManager{
|
||||
}elseif($recipe instanceof FurnaceRecipe){
|
||||
$this->registerFurnaceRecipe($recipe);
|
||||
}
|
||||
|
||||
$recipe->setId(UUID::fromData(++self::$RECIPE_COUNT, $recipe->getResult()->getId(), $recipe->getResult()->getDamage(), $recipe->getResult()->getCount(), $recipe->getResult()->getCompoundTag()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\event\inventory\CraftItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Server;
|
||||
|
||||
class CraftingTransactionGroup extends SimpleTransactionGroup{
|
||||
/** @var Item[] */
|
||||
protected $input = [];
|
||||
/** @var Item[] */
|
||||
protected $output = [];
|
||||
|
||||
/** @var Recipe */
|
||||
protected $recipe = null;
|
||||
|
||||
public function __construct(SimpleTransactionGroup $group){
|
||||
parent::__construct();
|
||||
$this->transactions = $group->getTransactions();
|
||||
$this->inventories = $group->getInventories();
|
||||
$this->source = $group->getSource();
|
||||
|
||||
$this->matchItems($this->output, $this->input);
|
||||
}
|
||||
|
||||
public function addTransaction(Transaction $transaction){
|
||||
parent::addTransaction($transaction);
|
||||
$this->input = [];
|
||||
$this->output = [];
|
||||
$this->matchItems($this->output, $this->input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Items that have been used
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getRecipe(){
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getResult(){
|
||||
reset($this->output);
|
||||
|
||||
return current($this->output);
|
||||
}
|
||||
|
||||
public function canExecute(){
|
||||
if(count($this->output) !== 1 or count($this->input) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getMatchingRecipe() instanceof Recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Recipe
|
||||
*/
|
||||
public function getMatchingRecipe(){
|
||||
if($this->recipe === null){
|
||||
$this->recipe = Server::getInstance()->getCraftingManager()->matchTransaction($this);
|
||||
}
|
||||
|
||||
return $this->recipe;
|
||||
}
|
||||
|
||||
public function execute(){
|
||||
if($this->hasExecuted() or !$this->canExecute()){
|
||||
return false;
|
||||
}
|
||||
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $this->getMatchingRecipe()));
|
||||
if($ev->isCancelled()){
|
||||
foreach($this->inventories as $inventory){
|
||||
$inventory->sendContents($inventory->getViewers());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->transactions as $transaction){
|
||||
$transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem(), $this->getSource());
|
||||
}
|
||||
$this->hasExecuted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1247,8 +1247,8 @@ class Item{
|
||||
return $this->id === $item->getId() and ($checkDamage === false or $this->getDamage() === $item->getDamage()) and ($checkCompound === false or $this->getCompoundTag() === $item->getCompoundTag());
|
||||
}
|
||||
|
||||
public final function deepEquals(Item $item){
|
||||
if($item->equals($item)){
|
||||
public final function deepEquals(Item $item, $checkDamage = true, $checkCompound = true){
|
||||
if($item->equals($item, $checkDamage, $checkCompound)){
|
||||
return true;
|
||||
}elseif($item->hasCompoundTag() or $this->hasCompoundTag()){
|
||||
return NBT::matchTree($this->getNamedTag(), $item->getNamedTag());
|
||||
|
Loading…
x
Reference in New Issue
Block a user