mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-29 14:49:59 +00:00
Proper recipe matching from network, bumped protocol, build 11, fixed entities not being killed on void (closes #3021), fixes achievement acquireIron not being possible (fixes #2600)
This commit is contained in:
parent
e61db8ad06
commit
76e6ccebd5
@ -70,6 +70,7 @@ use pocketmine\inventory\FurnaceInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\inventory\PlayerInventory;
|
||||
use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\inventory\StonecutterShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
@ -2383,6 +2384,139 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
unset($this->windowIndex[$packet->windowid]);
|
||||
}
|
||||
break;
|
||||
|
||||
case ProtocolInfo::CONTAINER_SET_CONTENT_PACKET:
|
||||
if($packet->windowid === ContainerSetContentPacket::SPECIAL_CRAFTING){
|
||||
if(count($packet->slots) < 9){
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($packet->slots as $i => $item){
|
||||
/** @var Item $item */
|
||||
if($item->getDamage() === -1 or $item->getDamage() === 0xffff){
|
||||
$item->setDamage(null);
|
||||
}
|
||||
|
||||
if($i < 9 and $item->getId() > 0){
|
||||
$item->setCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $packet->slots[9];
|
||||
|
||||
if($this->craftingType === 1 or $this->craftingType === 2){
|
||||
$recipe = new BigShapelessRecipe($result);
|
||||
}else{
|
||||
$recipe = new ShapelessRecipe($result);
|
||||
}
|
||||
|
||||
/** @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!Server::getInstance()->getCraftingManager()->matchRecipe($recipe)){
|
||||
$this->server->getLogger()->debug("Unmatched recipe from player ". $this->getName() .": " . $recipe->getResult().", using: " . implode(", ", $recipe->getIngredientList()));
|
||||
$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->equals($i, $checkDamage) and ($i->getCount() - $used[$index]) >= 1){
|
||||
$slot = $index;
|
||||
$used[$index]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($slot === -1){
|
||||
$canCraft = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$canCraft){
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($used as $slot => $count){
|
||||
if($count === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = $this->inventory->getItem($slot);
|
||||
|
||||
if($item->getCount() > $count){
|
||||
$newItem = clone $item;
|
||||
$newItem->setCount($item->getCount() - $count);
|
||||
}else{
|
||||
$newItem = Item::get(Item::AIR, 0, 0);
|
||||
}
|
||||
|
||||
$this->inventory->setItem($slot, $newItem);
|
||||
}
|
||||
|
||||
$extraItem = $this->inventory->addItem($recipe->getResult());
|
||||
if(count($extraItem) > 0){
|
||||
foreach($extraItem as $item){
|
||||
$this->level->dropItem($this, $item);
|
||||
}
|
||||
}
|
||||
|
||||
switch($recipe->getResult()->getId()){
|
||||
case Item::WORKBENCH:
|
||||
$this->awardAchievement("buildWorkBench");
|
||||
break;
|
||||
case Item::WOODEN_PICKAXE:
|
||||
$this->awardAchievement("buildPickaxe");
|
||||
break;
|
||||
case Item::FURNACE:
|
||||
$this->awardAchievement("buildFurnace");
|
||||
break;
|
||||
case Item::WOODEN_HOE:
|
||||
$this->awardAchievement("buildHoe");
|
||||
break;
|
||||
case Item::BREAD:
|
||||
$this->awardAchievement("makeBread");
|
||||
break;
|
||||
case Item::CAKE:
|
||||
//TODO: detect complex recipes like cake that leave remains
|
||||
$this->awardAchievement("bakeCake");
|
||||
$this->inventory->addItem(Item::get(Item::BUCKET, 0, 3));
|
||||
break;
|
||||
case Item::STONE_PICKAXE:
|
||||
case Item::GOLD_PICKAXE:
|
||||
case Item::IRON_PICKAXE:
|
||||
case Item::DIAMOND_PICKAXE:
|
||||
$this->awardAchievement("buildBetterPickaxe");
|
||||
break;
|
||||
case Item::WOODEN_SWORD:
|
||||
$this->awardAchievement("buildSword");
|
||||
break;
|
||||
case Item::DIAMOND:
|
||||
$this->awardAchievement("diamond");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ProtocolInfo::CONTAINER_SET_SLOT_PACKET:
|
||||
if($this->spawned === false or $this->blocked === true or !$this->isAlive()){
|
||||
break;
|
||||
@ -2424,7 +2558,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
|
||||
if($this->currentTransaction === null or $this->currentTransaction->getCreationTime() < (microtime(true) - 8)){
|
||||
if($this->currentTransaction instanceof SimpleTransactionGroup){
|
||||
if($this->currentTransaction !== null){
|
||||
foreach($this->currentTransaction->getInventories() as $inventory){
|
||||
if($inventory instanceof PlayerInventory){
|
||||
$inventory->sendArmorContents($this);
|
||||
@ -2438,77 +2572,29 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->currentTransaction->addTransaction($transaction);
|
||||
|
||||
if($this->currentTransaction->canExecute()){
|
||||
if($this->currentTransaction->execute()){
|
||||
$achievements = [];
|
||||
foreach($this->currentTransaction->getTransactions() as $ts){
|
||||
$inv = $ts->getInventory();
|
||||
if($inv instanceof FurnaceInventory){
|
||||
if($ts->getSlot() === 2){
|
||||
switch($inv->getResult()->getId()){
|
||||
case Item::IRON_INGOT:
|
||||
$this->awardAchievement("acquireIron");
|
||||
$achievements[] = "acquireIron";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->currentTransaction->execute()){
|
||||
foreach($achievements as $a){
|
||||
$this->awardAchievement($a);
|
||||
}
|
||||
}
|
||||
|
||||
$this->currentTransaction = null;
|
||||
}elseif($packet->windowid == 0){ //Try crafting
|
||||
$craftingGroup = new CraftingTransactionGroup($this->currentTransaction);
|
||||
if($craftingGroup->canExecute()){ //We can craft!
|
||||
$recipe = $craftingGroup->getMatchingRecipe();
|
||||
if($recipe instanceof BigShapelessRecipe and $this->craftingType !== 1){
|
||||
break;
|
||||
}elseif($recipe instanceof StonecutterShapelessRecipe and $this->craftingType !== 2){
|
||||
break;
|
||||
}
|
||||
|
||||
if($craftingGroup->execute()){
|
||||
switch($craftingGroup->getResult()->getId()){
|
||||
case Item::WORKBENCH:
|
||||
$this->awardAchievement("buildWorkBench");
|
||||
break;
|
||||
case Item::WOODEN_PICKAXE:
|
||||
$this->awardAchievement("buildPickaxe");
|
||||
break;
|
||||
case Item::FURNACE:
|
||||
$this->awardAchievement("buildFurnace");
|
||||
break;
|
||||
case Item::WOODEN_HOE:
|
||||
$this->awardAchievement("buildHoe");
|
||||
break;
|
||||
case Item::BREAD:
|
||||
$this->awardAchievement("makeBread");
|
||||
break;
|
||||
case Item::CAKE:
|
||||
//TODO: detect complex recipes like cake that leave remains
|
||||
$this->awardAchievement("bakeCake");
|
||||
$this->inventory->addItem(Item::get(Item::BUCKET, 0, 3));
|
||||
break;
|
||||
case Item::STONE_PICKAXE:
|
||||
case Item::GOLD_PICKAXE:
|
||||
case Item::IRON_PICKAXE:
|
||||
case Item::DIAMOND_PICKAXE:
|
||||
$this->awardAchievement("buildBetterPickaxe");
|
||||
break;
|
||||
case Item::WOODEN_SWORD:
|
||||
$this->awardAchievement("buildSword");
|
||||
break;
|
||||
case Item::DIAMOND:
|
||||
$this->awardAchievement("diamond");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->currentTransaction = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case ProtocolInfo::TILE_ENTITY_DATA_PACKET:
|
||||
if($this->spawned === false or $this->blocked === true or !$this->isAlive()){
|
||||
|
@ -74,8 +74,8 @@ namespace pocketmine {
|
||||
const VERSION = "1.5dev";
|
||||
const API_VERSION = "1.12.0";
|
||||
const CODENAME = "活発(Kappatsu)フグ(Fugu)";
|
||||
const MINECRAFT_VERSION = "v0.11.0 alpha build 10";
|
||||
const MINECRAFT_VERSION_NETWORK = "0.11.0.10";
|
||||
const MINECRAFT_VERSION = "v0.11.0 alpha build 11";
|
||||
const MINECRAFT_VERSION_NETWORK = "0.11.0.11";
|
||||
|
||||
/*
|
||||
* Startup code. Do not look at it, it may harm you.
|
||||
|
@ -744,7 +744,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$this->checkBlockCollision();
|
||||
|
||||
if($this->y < 0 and !$this->isAlive()){
|
||||
if($this->y < 0 and $this->isAlive()){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
$hasUpdate = true;
|
||||
|
@ -78,12 +78,12 @@ class CraftingManager{
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::JUNGLE, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::ACACIA, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE, Planks::DARK_OAK, 3)))->addIngredient(Item::get(Item::STICK, 0, 2))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::OAK, 2))->addIngredient(Item::get(Item::STICK, 0, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_SPRUCE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 2))->addIngredient(Item::get(Item::STICK, 0, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_BIRCH, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 2))->addIngredient(Item::get(Item::STICK, 0, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_JUNGLE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 2))->addIngredient(Item::get(Item::STICK, 0, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_DARK_OAK, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 2))->addIngredient(Item::get(Item::STICK, 0, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_ACACIA, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 2))->addIngredient(Item::get(Item::STICK, 0, 4)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::OAK, 2)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_SPRUCE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 2)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_BIRCH, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 2)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_JUNGLE, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 2)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_DARK_OAK, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 2)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FENCE_GATE_ACACIA, 0, 1)))->addIngredient(Item::get(Item::STICK, 0, 4))->addIngredient(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 2)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::FURNACE, 0, 1)))->addIngredient(Item::get(Item::COBBLESTONE, 0, 8)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::GLASS_PANE, 0, 16)))->addIngredient(Item::get(Item::GLASS, 0, 6)));
|
||||
$this->registerRecipe((new BigShapelessRecipe(Item::get(Item::LADDER, 0, 2)))->addIngredient(Item::get(Item::STICK, 0, 7)));
|
||||
@ -370,6 +370,25 @@ class CraftingManager{
|
||||
$this->furnaceRecipes[$input->getId() . ":" . ($input->getDamage() === null ? "?" : $input->getDamage())] = $recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShapelessRecipe $recipe
|
||||
* @return bool
|
||||
*/
|
||||
public function matchRecipe(ShapelessRecipe $recipe){
|
||||
if(!isset($this->recipeLookup[$idx = $recipe->getResult()->getId() . ":" . $recipe->getResult()->getDamage()])){
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = "";
|
||||
$ingredients = $recipe->getIngredientList();
|
||||
usort($ingredients, [$this, "sort"]);
|
||||
foreach($ingredients as $item){
|
||||
$hash .= $item->getId() . ":" . ($item->getDamage() === null ? "?" : $item->getDamage()) . "x" . $item->getCount() . ",";
|
||||
}
|
||||
|
||||
return isset($this->recipeLookup[$idx][$hash]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingTransactionGroup $ts
|
||||
*
|
||||
|
@ -31,6 +31,7 @@ class ContainerSetContentPacket extends DataPacket{
|
||||
const SPECIAL_INVENTORY = 0;
|
||||
const SPECIAL_ARMOR = 0x78;
|
||||
const SPECIAL_CREATIVE = 0x79;
|
||||
const SPECIAL_CRAFTING = 0x7a;
|
||||
|
||||
public $windowid;
|
||||
public $slots = [];
|
||||
|
@ -30,7 +30,7 @@ interface Info{
|
||||
/**
|
||||
* Actual Minecraft: PE protocol version
|
||||
*/
|
||||
const CURRENT_PROTOCOL = 25;
|
||||
const CURRENT_PROTOCOL = 26;
|
||||
|
||||
const LOGIN_PACKET = 0x82;
|
||||
const PLAY_STATUS_PACKET = 0x83;
|
||||
|
@ -79,7 +79,7 @@ class Furnace extends Tile implements InventoryHolder, Container{
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
$this->namedtag->Items = new Enum("Inventory", []);
|
||||
$this->namedtag->Items = new Enum("Items", []);
|
||||
$this->namedtag->Items->setTagType(NBT::TAG_Compound);
|
||||
for($index = 0; $index < $this->getSize(); ++$index){
|
||||
$this->setItem($index, $this->inventory->getItem($index));
|
||||
|
Loading…
x
Reference in New Issue
Block a user