Added CraftingGrid and BigCraftingGrid, WIP stuff for crafting

moving whole stacks in & out of the crafting grid works now, splitting stacks is fucked up because the transaction system can't handle the same slot changing multiple times in one transaction
This commit is contained in:
Dylan K. Taylor 2017-09-12 19:34:06 +01:00
parent 297172d111
commit 23a38400e2
7 changed files with 189 additions and 52 deletions

View File

@ -70,8 +70,10 @@ use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\event\TextContainer;
use pocketmine\event\Timings;
use pocketmine\event\TranslationContainer;
use pocketmine\inventory\BigCraftingGrid;
use pocketmine\inventory\BigShapedRecipe;
use pocketmine\inventory\BigShapelessRecipe;
use pocketmine\inventory\CraftingGrid;
use pocketmine\inventory\FurnaceInventory;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\PlayerCursorInventory;
@ -236,6 +238,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var PlayerCursorInventory */
protected $cursorInventory;
/** @var CraftingGrid */
protected $craftingGrid = null;
public $creationTime = 0;
protected $randomClientId;
@ -2071,7 +2076,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
$message = TextFormat::clean($message, $this->removeFormat);
foreach(explode("\n", $message) as $messagePart){
@ -2139,7 +2144,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($this->spawned === false or !$this->isAlive()){
return true;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
switch($packet->event){
default:
@ -2166,6 +2171,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @return bool
*/
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
if($this->isSpectator()){
$this->sendAllInventories();
return true;
}
/** @var InventoryAction[] $actions */
$actions = [];
foreach($packet->actions as $networkInventoryAction){
@ -2191,6 +2201,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
MainLogger::getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . " with actions: " . json_encode($packet->actions));
//TODO: check more stuff that might need reversion
return false; //oops!
}
@ -2251,7 +2263,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
$item = $this->inventory->getItemInHand();
$oldItem = clone $item;
@ -2500,7 +2512,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
$target = $this->level->getEntity($packet->target);
if($target === null){
@ -2594,7 +2606,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
break;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
$this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn()));
@ -2745,7 +2757,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
if(isset($this->windowIndex[$packet->windowId])){
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
@ -2972,7 +2984,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($this->spawned === false or !$this->isAlive()){
return true;
}
$this->craftingType = 0;
$this->resetCraftingGridType();
$pos = new Vector3($packet->x, $packet->y, $packet->z);
if($pos->distanceSquared($this) > 10000){
@ -3373,6 +3385,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->windows = null;
$this->windowIndex = [];
$this->cursorInventory = null;
$this->craftingGrid = null;
if($this->constructed){
parent::close();
@ -3701,6 +3714,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->cursorInventory = new PlayerCursorInventory($this);
$this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true);
$this->craftingGrid = new CraftingGrid($this);
//TODO: more windows
}
@ -3708,6 +3723,25 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return $this->cursorInventory;
}
public function getCraftingGrid() : CraftingGrid{
return $this->craftingGrid;
}
/**
* @param CraftingGrid $grid
*/
public function setCraftingGrid(CraftingGrid $grid) : void{
$this->craftingGrid = $grid;
}
public function resetCraftingGridType() : void{
if($this->craftingGrid instanceof BigCraftingGrid){
//TODO: this needs to drop anything left in the crafting grid otherwise our items might get deleted
$this->craftingGrid = new CraftingGrid($this);
$this->craftingType = 0;
}
}
/**
* Returns the window ID which the inventory has for this player, or -1 if the window is not open to the player.
*

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\inventory\BigCraftingGrid;
use pocketmine\item\Item;
use pocketmine\item\Tool;
use pocketmine\Player;
@ -49,6 +50,7 @@ class CraftingTable extends Solid{
public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){
$player->setCraftingGrid(new BigCraftingGrid($player));
$player->craftingType = 1;
}

View File

@ -30,6 +30,7 @@ use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\Player;
use pocketmine\Server;
@ -73,7 +74,9 @@ abstract class BaseInventory implements Inventory{
* Returns the Minecraft PE inventory type used to show the inventory window to clients.
* @return int
*/
abstract public function getNetworkType() : int;
public function getNetworkType() : int{
return ContainerIds::NONE;
}
/**
* Returns the size of the inventory.

View File

@ -0,0 +1,31 @@
<?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\inventory;
class BigCraftingGrid extends CraftingGrid{
public function getDefaultSize() : int{
return 9;
}
}

View File

@ -0,0 +1,53 @@
<?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\inventory;
use pocketmine\Player;
class CraftingGrid extends BaseInventory{
public function __construct(Player $holder){
parent::__construct($holder);
}
public function getDefaultSize() : int{
return 4;
}
public function setSize(int $size){
throw new \BadMethodCallException("Cannot change the size of a crafting grid");
}
public function getName() : string{
return "Crafting";
}
public function sendSlot(int $index, $target){
//we can't send a slot of a client-sided inventory window
}
public function sendContents($target){
//we can't send the contents of a client-sided inventory window
}
}

View File

@ -47,39 +47,7 @@ class InventoryTransactionPacket extends DataPacket{
const USE_ITEM_ON_ENTITY_ACTION_INTERACT = 0;
const USE_ITEM_ON_ENTITY_ACTION_ATTACK = 1;
const SOURCE_CONTAINER = 0;
const SOURCE_WORLD = 2; //drop/pickup item entity
const SOURCE_CREATIVE = 3;
const SOURCE_TODO = 99999;
/**
* These identifiers are used for special slot types for transaction/inventory types that are not yet implemented.
* Expect these to change in the future.
*/
const SOURCE_TYPE_CRAFTING_ADD_INGREDIENT = -2;
const SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT = -3;
const SOURCE_TYPE_CRAFTING_RESULT = -4;
const SOURCE_TYPE_CRAFTING_USE_INGREDIENT = -5;
const SOURCE_TYPE_ANVIL_INPUT = -10;
const SOURCE_TYPE_ANVIL_MATERIAL = -11;
const SOURCE_TYPE_ANVIL_RESULT = -12;
const SOURCE_TYPE_ANVIL_OUTPUT = -13;
const SOURCE_TYPE_ENCHANT_INPUT = -15;
const SOURCE_TYPE_ENCHANT_MATERIAL = -16;
const SOURCE_TYPE_ENCHANT_OUTPUT = -17;
const SOURCE_TYPE_TRADING_INPUT_1 = -20;
const SOURCE_TYPE_TRADING_INPUT_2 = -21;
const SOURCE_TYPE_TRADING_USE_INPUTS = -22;
const SOURCE_TYPE_TRADING_OUTPUT = -23;
const SOURCE_TYPE_BEACON = -24;
const SOURCE_TYPE_CONTAINER_DROP_CONTENTS = -100;
const ACTION_MAGIC_SLOT_DROP_ITEM = 0;

View File

@ -31,6 +31,46 @@ use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
use pocketmine\Player;
class NetworkInventoryAction{
const SOURCE_CONTAINER = 0;
const SOURCE_WORLD = 2; //drop/pickup item entity
const SOURCE_CREATIVE = 3;
const SOURCE_TODO = 99999;
/**
* Fake window IDs for the SOURCE_TODO type (99999)
*
* These identifiers are used for inventory source types which are not currently implemented server-side in MCPE.
* As a general rule of thumb, anything that doesn't have a permanent inventory is client-side. These types are
* to allow servers to track what is going on in client-side windows.
*
* Expect these to change in the future.
*/
const SOURCE_TYPE_CRAFTING_ADD_INGREDIENT = -2;
const SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT = -3;
const SOURCE_TYPE_CRAFTING_RESULT = -4;
const SOURCE_TYPE_CRAFTING_USE_INGREDIENT = -5;
const SOURCE_TYPE_ANVIL_INPUT = -10;
const SOURCE_TYPE_ANVIL_MATERIAL = -11;
const SOURCE_TYPE_ANVIL_RESULT = -12;
const SOURCE_TYPE_ANVIL_OUTPUT = -13;
const SOURCE_TYPE_ENCHANT_INPUT = -15;
const SOURCE_TYPE_ENCHANT_MATERIAL = -16;
const SOURCE_TYPE_ENCHANT_OUTPUT = -17;
const SOURCE_TYPE_TRADING_INPUT_1 = -20;
const SOURCE_TYPE_TRADING_INPUT_2 = -21;
const SOURCE_TYPE_TRADING_USE_INPUTS = -22;
const SOURCE_TYPE_TRADING_OUTPUT = -23;
const SOURCE_TYPE_BEACON = -24;
/** Any client-side window dropping its contents when the player closes it */
const SOURCE_TYPE_CONTAINER_DROP_CONTENTS = -100;
/** @var int */
public $sourceType;
/** @var int */
@ -51,15 +91,15 @@ class NetworkInventoryAction{
$this->sourceType = $packet->getUnsignedVarInt();
switch($this->sourceType){
case InventoryTransactionPacket::SOURCE_CONTAINER:
case self::SOURCE_CONTAINER:
$this->windowId = $packet->getVarInt();
break;
case InventoryTransactionPacket::SOURCE_WORLD:
case self::SOURCE_WORLD:
$this->unknown = $packet->getUnsignedVarInt();
break;
case InventoryTransactionPacket::SOURCE_CREATIVE:
case self::SOURCE_CREATIVE:
break;
case InventoryTransactionPacket::SOURCE_TODO:
case self::SOURCE_TODO:
$this->windowId = $packet->getVarInt();
break;
}
@ -76,15 +116,15 @@ class NetworkInventoryAction{
$packet->putUnsignedVarInt($this->sourceType);
switch($this->sourceType){
case InventoryTransactionPacket::SOURCE_CONTAINER:
case self::SOURCE_CONTAINER:
$packet->putVarInt($this->windowId);
break;
case InventoryTransactionPacket::SOURCE_WORLD:
case self::SOURCE_WORLD:
$packet->putUnsignedVarInt($this->unknown);
break;
case InventoryTransactionPacket::SOURCE_CREATIVE:
case self::SOURCE_CREATIVE:
break;
case InventoryTransactionPacket::SOURCE_TODO:
case self::SOURCE_TODO:
$packet->putVarInt($this->windowId);
break;
}
@ -96,7 +136,7 @@ class NetworkInventoryAction{
public function createInventoryAction(Player $player){
switch($this->sourceType){
case InventoryTransactionPacket::SOURCE_CONTAINER:
case self::SOURCE_CONTAINER:
if($this->windowId === ContainerIds::ARMOR){
//TODO: HACK!
$this->inventorySlot += 36;
@ -109,13 +149,13 @@ class NetworkInventoryAction{
}
throw new \InvalidStateException("Player " . $player->getName() . " has no open container with window ID $this->windowId");
case InventoryTransactionPacket::SOURCE_WORLD:
case self::SOURCE_WORLD:
if($this->inventorySlot !== InventoryTransactionPacket::ACTION_MAGIC_SLOT_DROP_ITEM){
throw new \UnexpectedValueException("Only expecting drop-item world actions from the client!");
}
return new DropItemAction($this->oldItem, $this->newItem);
case InventoryTransactionPacket::SOURCE_CREATIVE:
case self::SOURCE_CREATIVE:
switch($this->inventorySlot){
case InventoryTransactionPacket::ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM:
return new CreativeInventoryAction($this->oldItem, $this->newItem, CreativeInventoryAction::TYPE_DELETE_ITEM);
@ -124,7 +164,13 @@ class NetworkInventoryAction{
}
throw new \UnexpectedValueException("Unexpected creative action type $this->inventorySlot");
case InventoryTransactionPacket::SOURCE_TODO:
case self::SOURCE_TODO:
switch($this->windowId){
case self::SOURCE_TYPE_CRAFTING_ADD_INGREDIENT:
case self::SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT:
$window = $player->getCraftingGrid();
return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem);
}
//TODO
throw new \UnexpectedValueException("Player " . $player->getName() . " has no open container with window ID $this->windowId");
default: