From 4b408675cf135a1f681af97d2742e75845264964 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Thu, 6 Jun 2013 17:14:09 +0200 Subject: [PATCH] Minecraft: Pocket Edition 0.7.0 dev. release * Crafting enabled, report bugs * Chat handler --- src/API/BlockAPI.php | 28 +- src/API/PlayerAPI.php | 2 +- src/API/ServerAPI.php | 3 +- src/API/{Tile.php => TileAPI.php} | 0 src/API/TileEntityAPI.php | 122 ------ src/Player.php | 395 ++++++++++++-------- src/PocketMinecraftServer.php | 1 + src/config.php | 4 +- src/constants/BlockIDs.php | 1 + src/constants/GeneralConstants.php | 3 + src/constants/ItemIDs.php | 18 +- src/constants/ProtocolInfo.php | 35 +- src/material/Item.php | 2 - src/material/block/solid/BurningFurnace.php | 2 +- src/material/block/solid/Chest.php | 2 +- src/material/block/solid/Stonecutter.php | 5 + src/material/block/solid/Workbench.php | 7 +- src/material/item/generic/Bucket.php | 26 +- src/material/item/generic/Coal.php | 2 +- src/material/item/generic/LavaBucket.php | 44 --- src/material/item/generic/WaterBucket.php | 44 --- src/network/CustomPacketHandler.php | 19 +- src/recipes/CraftingRecipes.php | 289 ++++++++++++++ src/recipes/SmeltingData.php | 1 + src/world/Entity.php | 17 +- 25 files changed, 652 insertions(+), 420 deletions(-) rename src/API/{Tile.php => TileAPI.php} (100%) delete mode 100644 src/API/TileEntityAPI.php delete mode 100644 src/material/item/generic/LavaBucket.php delete mode 100644 src/material/item/generic/WaterBucket.php create mode 100644 src/recipes/CraftingRecipes.php diff --git a/src/API/BlockAPI.php b/src/API/BlockAPI.php index 54751d96d..c12455acc 100644 --- a/src/API/BlockAPI.php +++ b/src/API/BlockAPI.php @@ -100,6 +100,9 @@ class BlockAPI{ array(LADDER, 0), array(TORCH, 0), array(GLASS_PANE, 0), + array(BUCKET, 0), + array(BUCKET, 8), + array(BUCKET, 10), array(WOODEN_DOOR, 0), array(TRAPDOOR, 0), array(FENCE, 0), @@ -129,9 +132,15 @@ class BlockAPI{ array(MELON_SEEDS, 0), array(DYE, 15), //Bonemeal array(IRON_HOE, 0), + array(CAKE, 0), + array(EGG, 0), array(IRON_SWORD, 0), array(BOW, 0), array(SIGN, 0), + array(SPAWN_EGG, MOB_CHICKEN), + array(SPAWN_EGG, MOB_COW), + array(SPAWN_EGG, MOB_PIG), + array(SPAWN_EGG, MOB_SHEEP), ); public static function fromString($str, $multiple = false){ @@ -217,11 +226,7 @@ class BlockAPI{ $output .= "Player is in creative mode.\n"; break; } - if($this->server->api->getProperty("item-enforcement") === false){ - $this->server->api->entity->drop(new Position($player->entity->x - 0.5, $player->entity->y, $player->entity->z - 0.5, $player->level), $item, true); - }else{ - $player->addItem($item->getID(), $item->getMetadata(), $item->count); - } + $player->addItem($item->getID(), $item->getMetadata(), $item->count); $output .= "Giving ".$item->count." of ".$item->getName()." (".$item->getID().":".$item->getMetadata().") to ".$player->username."\n"; }else{ $output .= "Unknown player.\n"; @@ -240,16 +245,14 @@ class BlockAPI{ "block" => $block->getID(), "meta" => $block->getMetadata() )); - if($player->itemEnforcement === true){ - $player->sendInventory(); - } + $player->sendInventorySlot($player->slot); return false; } public function playerBlockBreak(Player $player, Vector3 $vector){ $target = $player->level->getBlock($vector); - $item = $player->equipment; + $item = $player->getSlot($player->slot); if($this->server->api->dhandle("player.block.touch", array("type" => "break", "player" => $player, "target" => $target, "item" => $item)) === false){ return $this->cancelAction($target, $player); @@ -287,7 +290,7 @@ class BlockAPI{ $target = $player->level->getBlock($vector); $block = $target->getSide($face); - $item = $player->equipment; + $item = $player->getSlot($player->slot); if($target->getID() === AIR and $this->server->api->dhandle("player.block.place.invalid", array("player" => $player, "block" => $block, "target" => $target, "item" => $item)) !== true){ //If no block exists or not allowed in CREATIVE $this->cancelAction($target, $player); @@ -343,7 +346,10 @@ class BlockAPI{ } if(($player->gamemode & 0x01) === 0x00){ - $player->removeItem($item->getID(), $item->getMetadata(), 1); + --$item->count; + if($item->count <= 0){ + $player->setSlot($player->slot, BlockAPI::getItem(AIR, 0, 0)); + } } return false; diff --git a/src/API/PlayerAPI.php b/src/API/PlayerAPI.php index e93a22a1d..3100ecad4 100644 --- a/src/API/PlayerAPI.php +++ b/src/API/PlayerAPI.php @@ -419,7 +419,7 @@ class PlayerAPI{ "y" => $this->server->spawn->y, "z" => $this->server->spawn->z, ), - "inventory" => array_fill(0, 36, array(AIR, 0, 0)), + "inventory" => array_fill(0, PLAYER_SURVIVAL_SLOTS, array(AIR, 0, 0)), "armor" => array_fill(0, 4, array(AIR, 0, 0)), "gamemode" => $this->server->gamemode, "health" => 20, diff --git a/src/API/ServerAPI.php b/src/API/ServerAPI.php index 38367bfba..ab0024ad5 100644 --- a/src/API/ServerAPI.php +++ b/src/API/ServerAPI.php @@ -72,7 +72,6 @@ class ServerAPI{ "view-distance" => 10, "max-players" => 20, "allow-flight" => false, - "item-enforcement" => false, "gamemode" => SURVIVAL, "hardcore" => false, "pvp" => true, @@ -289,7 +288,7 @@ class ServerAPI{ if($this->getProperty("enable-query") === true){ $this->query = new Query(); } - + CraftingRecipes::init(); $this->server->init(); unregister_tick_function(array($this->server, "tick")); $this->console->__destruct(); diff --git a/src/API/Tile.php b/src/API/TileAPI.php similarity index 100% rename from src/API/Tile.php rename to src/API/TileAPI.php diff --git a/src/API/TileEntityAPI.php b/src/API/TileEntityAPI.php deleted file mode 100644 index 6d02f2214..000000000 --- a/src/API/TileEntityAPI.php +++ /dev/null @@ -1,122 +0,0 @@ -tileEntities = array(); - $this->server = ServerAPI::request(); - } - - public function get(Position $pos){ - $tile = $this->server->query("SELECT * FROM tileentities WHERE level = '".$pos->level->getName()."' AND x = {$pos->x} AND y = {$pos->y} AND z = {$pos->z};", true); - if($tile !== false and $tile !== true and ($tile = $this->getByID($tile["ID"])) !== false){ - return $tile; - } - return false; - } - - public function getByID($id){ - if($id instanceof TileEntity){ - return $id; - }elseif(isset($this->tileEntities[$id])){ - return $this->tileEntities[$id]; - } - return false; - } - - public function init(){ - - } - - public function getAll($level = null){ - if($level instanceof Level){ - $tileEntities = array(); - $l = $this->server->query("SELECT ID FROM tileentities WHERE level = '".$level->getName()."';"); - if($l !== false and $l !== true){ - while(($t = $l->fetchArray(SQLITE3_ASSOC)) !== false){ - $t = $this->getByID($t["ID"]); - if($t instanceof TileEntity){ - $tileEntities[$t->id] = $t; - } - } - } - return $tileEntities; - } - return $this->tileEntities; - } - - public function add(Level $level, $class, $x, $y, $z, $data = array()){ - $id = $this->tCnt++; - $this->tileEntities[$id] = new TileEntity($level, $id, $class, $x, $y, $z, $data); - $this->spawnToAll($this->tileEntities[$id]); - return $this->tileEntities[$id]; - } - - public function addSign(Level $level, $x, $y, $z, $lines = array("", "", "", "")){ - return $this->add($level, TILE_SIGN, $x, $y, $z, $data = array( - "id" => "Sign", - "x" => $x, - "y" => $y, - "z" => $z, - "Text1" => $lines[0], - "Text2" => $lines[1], - "Text3" => $lines[2], - "Text4" => $lines[3], - )); - } - - public function spawnToAll(TileEntity $t){ - foreach($this->server->api->player->getAll($t->level) as $player){ - if($player->eid !== false){ - $t->spawn($player); - } - } - } - - public function spawnAll(Player $player){ - foreach($this->getAll($player->level) as $t){ - $t->spawn($player); - } - } - - public function remove($id){ - if(isset($this->tileEntities[$id])){ - $t = $this->tileEntities[$id]; - $this->tileEntities[$id] = null; - unset($this->tileEntities[$id]); - $t->closed = true; - $t->close(); - $this->server->query("DELETE FROM tileentities WHERE ID = ".$id.";"); - $this->server->api->dhandle("tile.remove", $t); - $t = null; - unset($t); - } - } -} \ No newline at end of file diff --git a/src/Player.php b/src/Player.php index 2a9e43fcb..3ee634838 100644 --- a/src/Player.php +++ b/src/Player.php @@ -43,6 +43,8 @@ class Player{ private $iusername; private $eid = false; private $startAction = false; + private $queue = array(); + private $need = array(); public $data; public $entity = false; public $auth = false; @@ -50,12 +52,12 @@ class Player{ public $MTU; public $spawned = false; public $inventory; - public $equipment; + public $slot; public $armor; public $loggedIn = false; public $gamemode; public $lastBreak; - public $windowCnt = 0; + public $windowCnt = 2; public $windows = array(); public $blocked = true; public $chunksLoaded = array(); @@ -67,10 +69,11 @@ class Player{ private $lagStat = 0; private $spawnPosition; private $packetLoss = 0; - public $itemEnforcement; public $lastCorrect; private $bigCnt; private $packetStats; + public $craftingItems = array(); + public $toCraft = array(); public function __get($name){ if(isset($this->{$name})){ @@ -88,14 +91,13 @@ class Player{ $this->CID = $this->server->clientID($ip, $port); $this->ip = $ip; $this->port = $port; - $this->itemEnforcement = $this->server->api->getProperty("item-enforcement"); $this->spawnPosition = $this->server->spawn; $this->timeout = microtime(true) + 20; - $this->inventory = array_fill(0, 36, array(AIR, 0, 0)); + $this->inventory = array(); $this->armor = array_fill(0, 4, array(AIR, 0, 0)); $this->gamemode = $this->server->gamemode; $this->level = $this->server->api->level->getDefault(); - $this->equipment = BlockAPI::getItem(AIR); + $this->slot = 0; $this->packetStats = array(0,0); $this->server->schedule(2, array($this, "onTick"), array(), true); $this->evid[] = $this->server->event("server.close", array($this, "close")); @@ -217,7 +219,12 @@ class Player{ "y" => $this->spawnPosition->y, "z" => $this->spawnPosition->z, )); - $this->data->set("inventory", $this->inventory); + $inv = array(); + + foreach($this->inventory as $slot => $item){ + $inv[$slot] = array($item->getID(), $item->getMetadata(), $item->count); + } + $this->data->set("inventory", $inv); $this->data->set("armor", $this->armor); $this->data->set("gamemode", $this->gamemode); } @@ -254,17 +261,17 @@ class Player{ $inv = $this->inventory; while($count > 0){ $add = 0; - foreach($inv as $s => $data){ - if($data[0] === AIR){ + foreach($inv as $s => $item){ + if($item->getID() === AIR){ $add = min(64, $count); - $inv[$s] = array($type, $damage, $add); + $inv[$s] = BlockAPI::getItem($type, $damage, $add); break; - }elseif($data[0] === $type and $data[1] === $damage){ - $add = min(64 - $data[2], $count); + }elseif($item->getID() === $type and $item->getMetadata() === $damage){ + $add = min(64 - $item->count, $count); if($add <= 0){ continue; } - $inv[$s] = array($type, $damage, $data[2] + $add); + $inv[$s] = BlockAPI::getItem($type, $damage, $item->count + $add); break; } } @@ -276,22 +283,26 @@ class Player{ return true; } - public function addItem($type, $damage, $count){ + public function addItem($type, $damage, $count, $send = true){ while($count > 0){ $add = 0; - foreach($this->inventory as $s => $data){ - if($data[0] === AIR){ + foreach($this->inventory as $s => $item){ + if($item->getID() === AIR){ $add = min(64, $count); - $this->inventory[$s] = array($type, $damage, $add); - $this->sendInventorySlot($s); + $this->inventory[$s] = BlockAPI::getItem($type, $damage, $add); + if($send === true){ + $this->sendInventorySlot($s); + } break; - }elseif($data[0] === $type and $data[1] === $damage){ - $add = min(64 - $data[2], $count); + }elseif($item->getID() === $type and $item->getMetadata() === $damage){ + $add = min(64 - $item->count, $count); if($add <= 0){ continue; } - $this->inventory[$s] = array($type, $damage, $data[2] + $add); - $this->sendInventorySlot($s); + $item->count += $add; + if($send === true){ + $this->sendInventorySlot($s); + } break; } } @@ -303,18 +314,20 @@ class Player{ return true; } - public function removeItem($type, $damage, $count){ + public function removeItem($type, $damage, $count, $send = true){ while($count > 0){ $remove = 0; - foreach($this->inventory as $s => $data){ - if($data[0] === $type and $data[1] === $damage){ - $remove = min($count, $data[2]); - if($remove < $data[2]){ - $this->inventory[$s][2] -= $remove; + foreach($this->inventory as $s => $item){ + if($item->getID() === $type and $item->getMetadata() === $damage){ + $remove = min($count, $item->count); + if($remove < $item->count){ + $item->count -= $remove; }else{ - $this->inventory[$s] = array(AIR, 0, 0); + $this->inventory[$s] = BlockAPI::getItem(AIR, 0, 0); + } + if($send === true){ + $this->sendInventorySlot($s); } - $this->sendInventorySlot($s); break; } } @@ -327,45 +340,41 @@ class Player{ } public function setSlot($slot, Item $item){ - $this->inventory[(int) $slot] = array($item->getID(), $item->getMetadata(), $item->count); + $this->inventory[(int) $slot] = $item; $this->sendInventorySlot((int) $slot); return true; } public function getSlot($slot){ - $slot = $this->inventory[(int) $slot]; - return BlockAPI::getItem($slot[0], $slot[1], $slot[2]); + if(isset($this->inventory[(int) $slot])){ + return $this->inventory[(int) $slot]; + }else{ + return BlockAPI::getItem(AIR, 0, 0); + } } public function sendInventorySlot($s){ + $this->sendInventory(); + return; $s = (int) $s; - if(!isset($this->inventory[$s]) or $this->itemEnforcement === false){ + if(!isset($this->inventory[$s])){ return false; } - - $this->sendInventory(); //Fallback - return true; - - //Can't do this :( - - /*$slot = BlockAPI::getItem($this->inventory[$s][0], $this->inventory[$s][1], $this->inventory[$s][2]); + $slot = $this->inventory[$s]; $this->dataPacket(MC_CONTAINER_SET_SLOT, array( "windowid" => 0, - "slot" => (int) $s, + "slot" => ((int) $s) + 9, "block" => $slot->getID(), "stack" => $slot->count, "meta" => $slot->getMetadata(), )); - return true;*/ + return true; } public function hasItem($type, $damage = false){ - if($type === AIR){ - return true; - } - foreach($this->inventory as $s => $data){ - if($data[0] === $type and ($data[1] === $damage or $damage === false) and $data[2] > 0){ + foreach($this->inventory as $s => $item){ + if($item->getID() === $type and ($item->getMetadata() === $damage or $damage === false) and $item->count > 0){ return true; } } @@ -432,7 +441,7 @@ class Player{ $data["eid"] = 0; $this->dataPacket(MC_TAKE_ITEM_ENTITY, $data); if(($this->gamemode & 0x01) === 0x00){ - $this->addItem($data["entity"]->type, $data["entity"]->meta, $data["entity"]->stack); + $this->addItem($data["entity"]->type, $data["entity"]->meta, $data["entity"]->stack, false); } }elseif($data["entity"]->level === $this->level){ $this->dataPacket(MC_TAKE_ITEM_ENTITY, $data); @@ -444,12 +453,6 @@ class Player{ } $this->dataPacket(MC_PLAYER_EQUIPMENT, $data); - break; - case "player.action": - if($data["eid"] === $this->eid or $data["player"]->level !== $this->level){ - break; - } - $this->dataPacket(MC_USE_ITEM, $data); break; case "entity.move": if($data->eid === $this->eid or $data->level !== $this->level){ @@ -609,6 +612,59 @@ class Player{ )); } + public function craftItems(array $craft, array $recipe, $type){ + $craftItem = array(0, true, 0); + unset($craft[-1]); + foreach($craft as $slot => $item){ + $craftItem[0] = $item->getID(); + if($item->getMetadata() !== $craftItem[1] and $craftItem[0] !== true){ + $craftItem[1] = false; + }else{ + $craftItem[1] = $item->getMetadata(); + } + $craftItem[2] += $item->count; + } + + $recipeItems = array(); + foreach($recipe as $slot => $item){ + if(!isset($recipeItems[$item->getID()])){ + $recipeItems[$item->getID()] = array($item->getID(), $item->getMetadata(), $item->count); + }else{ + if($item->getMetadata() !== $recipeItems[$item->getID()][1]){ + $recipeItems[$item->getID()][1] = false; + } + $recipeItems[$item->getID()][2] += $item->count; + } + } + + $res = CraftingRecipes::canCraft($craftItem, $recipeItems, $type); + if(!is_array($res) and $type === 1){ + $res2 = CraftingRecipes::canCraft($craftItem, $recipeItems, 0); + if(is_array($res2)){ + $res = $res2; + } + } + + if(is_array($res)){ + foreach($craft as $slot => $item){ + $s = $this->getSlot($slot); + if($s->count <= 0 or $s->getID() === AIR){ + $this->setSlot($slot, BlockAPI::getItem($item->getID(), $item->getMetadata(), $item->count)); + }else{ + $s->count += $item->count; + } + } + foreach($recipe as $slot => $item){ + $s = $this->getSlot($slot); + $s->count -= $item->count; + if($s->count <= 0){ + $this->setSlot($slot, BlockAPI::getItem(AIR, 0, 0)); + } + } + } + return $res; + } + public function teleport(Vector3 $pos, $yaw = false, $pitch = false, $terrain = true){ if($this->entity instanceof Entity){ $this->entity->check = false; @@ -656,7 +712,7 @@ class Player{ $this->entity->calculateVelocity(); if($terrain === true){ $this->orderChunks(); - $this->getNextChunk(false); + $this->getNextChunk(); } $this->entity->check = true; } @@ -708,20 +764,15 @@ class Player{ $this->gamemode = $gm; $this->eventHandler("Your gamemode has been changed to ".$this->getGamemode().".", "server.chat"); }else{ - $inv = array_fill(0, 36, array(AIR, 0, 0)); + $inv = array_fill(0, ($gamemode & 0x01) > 0 ? PLAYER_CREATIVE_SLOTS:PLAYER_SURVIVAL_SLOTS, array(AIR, 0, 0)); $this->blocked = true; $this->gamemode = $gm; $this->eventHandler("Your gamemode has been changed to ".$this->getGamemode().", you've to do a forced reconnect.", "server.chat"); $this->server->schedule(30, array($this, "close"), "gamemode change"); //Forces a kick - if(($gm & 0x01) === 0x01){ - $this->itemEnforcement = true; - } } $this->inventory = $inv; $this->sendSettings(); - if($this->itemEnforcement === true){ - $this->sendInventory(); - } + $this->sendInventory(); return true; } @@ -824,20 +875,24 @@ class Player{ case 0x8e: case 0x8f: if(isset($data[0])){ + $this->send(0xc0, array(array($data[0]))); $diff = $data[0] - $this->counter[1]; + unset($this->need[$data[0]]); if($diff > 1){ //Packet recovery $arr = array(); for($i = $this->counter[1]; $i < $data[0]; ++$i){ $arr[] = $i; - ++$this->packetStats[1]; + $this->need[$i] = true; } - $this->send(0xa0, array($arr)); - $this->counter[1] = $data[0]; + $this->send(0xa0, $arr); + $this->queue[$data[0]] = $data; + break; }elseif($diff === 1){ - $this->counter[1] = $data[0]; + $this->counter[1] = $data[0]; + ++$this->packetStats[0]; + }else{ + break; } - ++$this->packetStats[0]; - $this->send(0xc0, array(array($data[0]))); } if(!isset($data["id"])){ @@ -892,7 +947,7 @@ class Player{ "status" => 2, )); } - $this->close("Incorrect protocol", false); + $this->close("Incorrect protocol #".$data["protocol1"], false); break; } if(preg_match('#[^a-zA-Z0-9_]#', $data["username"]) == 0){ @@ -935,22 +990,25 @@ class Player{ $this->auth = true; if(!$this->data->exists("inventory") or ($this->gamemode & 0x01) === 0x01){ if(($this->gamemode & 0x01) === 0x01){ - $this->itemEnforcement = true; - $this->inventory = array(); + $inv = array(); if(($this->gamemode & 0x02) === 0x02){ foreach(BlockAPI::$creative as $item){ - $this->inventory[] = array(DANDELION, 0, 1); + $inv[] = array(DANDELION, 0, 1); } }else{ foreach(BlockAPI::$creative as $item){ - $this->inventory[] = array($item[0], $item[1], 1); + $inv[] = array($item[0], $item[1], 1); } } } - $this->data->set("inventory", $this->inventory); + $this->data->set("inventory", $inv); } $this->data->set("caseusername", $this->username); - $this->inventory = $this->data->get("inventory"); + $this->inventory = array(); + foreach($this->data->get("inventory") as $slot => $item){ + $this->inventory[$slot] = BlockAPI::getItem($item[0], $item[1], $item[2]); + } + $this->armor = $this->data->get("armor"); $this->data->set("lastIP", $this->ip); @@ -972,7 +1030,9 @@ class Player{ "eid" => 0, )); if(($this->gamemode & 0x01) === 0x01){ - $this->equipment = BlockAPI::getItem($this->inventory[7][0], $this->inventory[7][1], $this->inventory[7][2]); + $this->slot = 7; + }else{ + $this->slot = 0; } $this->entity = $this->server->api->entity->add($this->level, ENTITY_PLAYER, 0, array("player" => $this)); $this->eid = $this->entity->eid; @@ -992,7 +1052,6 @@ class Player{ $this->evid[] = $this->server->event("entity.metadata", array($this, "eventHandler")); $this->evid[] = $this->server->event("player.equipment.change", array($this, "eventHandler")); $this->evid[] = $this->server->event("player.armor", array($this, "eventHandler")); - $this->evid[] = $this->server->event("player.action", array($this, "eventHandler")); $this->evid[] = $this->server->event("player.pickup", array($this, "eventHandler")); $this->evid[] = $this->server->event("tile.container.slot", array($this, "eventHandler")); $this->evid[] = $this->server->event("tile.update", array($this, "eventHandler")); @@ -1056,13 +1115,26 @@ class Player{ if($this->spawned === false){ break; } + + if($data["slot"] === 0){ + $this->slot = -1; + break; + }else{ + $data["slot"] -= 9; + } + $data["eid"] = $this->eid; $data["player"] = $this; - $data["item"] = BlockAPI::getItem($data["block"], $data["meta"]); - if(($this->hasItem($data["block"], $data["meta"]) or $this->itemEnforcement !== true) and $this->server->handle("player.equipment.change", $data) !== false){ - $this->equipment = $data["item"]; - }elseif($this->itemEnforcement === true){ - $this->sendInventory(); + $data["item"] = $this->getSlot($data["slot"]); + if(!($data["item"] instanceof Item)){ + break; + } + $data["block"] = $data["item"]->getID(); + $data["meta"] = $data["item"]->getMetadata(); + if($this->server->handle("player.equipment.change", $data) !== false){ + $this->slot = $data["slot"]; + }else{ + $this->sendInventorySlot($data["slot"]); } if($this->entity->inAction === true){ $this->entity->inAction = false; @@ -1078,6 +1150,8 @@ class Player{ if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $data["eid"] = $this->eid; $data["player"] = $this; if($data["face"] >= 0 and $data["face"] <= 5){ //Use Block, place @@ -1086,12 +1160,29 @@ class Player{ $this->entity->updateMetadata(); } if($this->blocked === true or Utils::distance($this->entity->position, $data) > 10){ - break; - }elseif(!$this->hasItem($data["block"], $data["meta"]) and $this->itemEnforcement === true){ - $this->sendInventory(); + }elseif($this->getSlot($this->slot)->getID() !== $data["block"] or $this->getSlot($this->slot)->getMetadata() !== $data["meta"]){ + $this->sendInventorySlot($this->slot); + }else{ + $this->server->api->block->playerBlockAction($this, new Vector3($data["x"], $data["y"], $data["z"]), $data["face"], $data["fx"], $data["fy"], $data["fz"]); break; } - $this->server->api->block->playerBlockAction($this, new Vector3($data["x"], $data["y"], $data["z"]), $data["face"], $data["fx"], $data["fy"], $data["fz"]); + $target = $this->level->getBlock(new Vector3($data["x"], $data["y"], $data["z"])); + $block = $target->getSide($data["face"]); + $this->dataPacket(MC_UPDATE_BLOCK, array( + "x" => $target->x, + "y" => $target->y, + "z" => $target->z, + "block" => $target->getID(), + "meta" => $target->getMetadata() + )); + $this->dataPacket(MC_UPDATE_BLOCK, array( + "x" => $block->x, + "y" => $block->y, + "z" => $block->z, + "block" => $block->getID(), + "meta" => $block->getMetadata() + )); + break; }elseif($data["face"] === 0xFF and $this->server->handle("player.action", $data) !== false){ $this->entity->inAction = true; $this->startAction = microtime(true); @@ -1102,10 +1193,12 @@ class Player{ if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); if($this->entity->inAction === true){ switch($data["action"]){ case 5: //Shot arrow - if($this->equipment->getID() === BOW){ + if($this->getSlot($this->slot)->getID() === BOW){ if($this->startAction !== false){ $time = microtime(true) - $this->startAction; $d = array( @@ -1131,12 +1224,16 @@ class Player{ if($this->blocked === true or $this->entity->distance(new Vector3($data["x"], $data["y"], $data["z"])) > 8){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $this->server->api->block->playerBlockBreak($this, new Vector3($data["x"], $data["y"], $data["z"])); break; case MC_PLAYER_ARMOR_EQUIPMENT: if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $data["eid"] = $this->eid; $data["player"] = $this; $this->server->handle("player.armor", $data); @@ -1149,6 +1246,8 @@ class Player{ if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $target = $this->server->api->entity->get($data["target"]); if($this->gamemode !== VIEW and $this->blocked === false and ($target instanceof Entity) and $this->entity->distance($target) <= 8){ $data["targetentity"] = $target; @@ -1158,7 +1257,7 @@ class Player{ }elseif($target->class === ENTITY_PLAYER and ($this->server->api->getProperty("pvp") == false or $this->server->difficulty <= 0 or ($target->player->gamemode & 0x01) === 0x01)){ break; }elseif($this->server->handle("player.interact", $data) !== false){ - switch($this->equipment->getID()){ + switch($this->getSlot($this->slot)->getID()){ case WOODEN_SWORD: case GOLD_SWORD: $damage = 4; @@ -1235,6 +1334,8 @@ class Player{ if($this->entity->dead === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $this->entity->fire = 0; $this->entity->air = 300; $this->entity->setHealth(20, "respawn"); @@ -1250,6 +1351,8 @@ class Player{ if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $data["eid"] = $this->eid; if($this->entity->inAction === true){ $this->entity->inAction = false; @@ -1274,13 +1377,13 @@ class Player{ RAW_FISH => 2, ); - if(isset($items[$this->equipment->getID()])){ - if($this->removeItem($this->equipment->getID(), $this->equipment->getMetadata(), 1) === true or $this->itemEnforcement !== true){ + if(isset($items[$this->getSlot($this->slot)->getID()])){ + if($this->removeItem($this->getSlot($this->slot)->getID(), $this->getSlot($this->slot)->getMetadata(), 1, false) === true){ $this->dataPacket(MC_ENTITY_EVENT, array( "eid" => 0, "event" => 9, )); - $this->entity->heal($items[$this->equipment->getID()], "eating"); + $this->entity->heal($items[$this->getSlot($this->slot)->getID()], "eating"); } } break; @@ -1290,12 +1393,12 @@ class Player{ if($this->spawned === false){ break; } - $item = BlockAPI::getItem($data["block"], $data["meta"], $data["stack"]); - $data["item"] = $item; + $this->craftingItems = array(); + $this->toCraft = array(); + $data["item"] = $this->getSlot($this->slot); if($this->blocked === false and $this->server->handle("player.drop", $data) !== false){ - if($this->removeItem($item->getID(), $item->getMetadata(), $item->count) === true or $this->itemEnforcement !== true){ - $this->server->api->entity->drop(new Position($this->entity->x - 0.5, $this->entity->y, $this->entity->z - 0.5, $this->level), $item); - } + $this->server->api->entity->drop(new Position($this->entity->x - 0.5, $this->entity->y, $this->entity->z - 0.5, $this->level), $data["item"]); + $this->setSlot($this->slot, BlockAPI::getItem(AIR, 0, 0)); } if($this->entity->inAction === true){ $this->entity->inAction = false; @@ -1306,6 +1409,8 @@ class Player{ if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); $t = $this->server->api->tile->get(new Position($data["x"], $data["y"], $data["z"], $this->level)); if(($t instanceof Tile) and $t->class === TILE_SIGN){ if($t->data["creator"] !== $this->username){ @@ -1319,7 +1424,9 @@ class Player{ if($this->spawned === false){ break; } - $message = $data["message"]; + $this->craftingItems = array(); + $this->toCraft = array(); + $message = preg_replace('#^<.*> #', "", $data["message"]); if(trim($data["message"]) != "" and strlen($data["message"]) <= 100 and preg_match('#[^\\x20-\\xff]#', $message) == 0){ if($message{0} === "/"){ //Command $this->server->api->console->run(substr($message, 1), $this); @@ -1334,6 +1441,8 @@ class Player{ if($this->spawned === false){ break; } + $this->craftingItems = array(); + $this->toCraft = array(); unset($this->windows[$data["windowid"]]); $this->dataPacket(MC_CONTAINER_CLOSE, array( "windowid" => $data["windowid"], @@ -1343,6 +1452,35 @@ class Player{ if($this->spawned === false){ break; } + + if($data["windowid"] === 0){ + $craft = false; + $slot = $this->getSlot($data["slot"]); + if($slot->count >= $data["stack"] and $slot->getID() === $data["block"] and $slot->getMetadata() === $data["meta"] and !isset($this->craftingItems[$data["slot"]])){ //Crafting recipe + $use = BlockAPI::getItem($slot->getID(), $slot->getMetadata(), $slot->count - $data["stack"]); + $this->craftingItems[$data["slot"]] = $use; + $craft = true; + }elseif($slot->count <= $data["stack"] and ($slot->getID() === AIR or $slot->getID() === $data["block"])){ //Crafting final + $craftItem = BlockAPI::getItem($data["block"], $data["meta"], $data["stack"] - $slot->count); + if(count($this->toCraft) === 0){ + $this->toCraft[-1] = 0; + } + $this->toCraft[$data["slot"]] = $craftItem; + $craft = true; + } + + if($craft === true and count($this->craftingItems) > 0 and count($this->toCraft) > 0 and ($recipe = $this->craftItems($this->toCraft, $this->craftingItems, $this->toCraft[-1])) !== true){ + if($recipe === false){ + $this->dataPacket(MC_CONTAINER_CLOSE, array( + "windowid" => 0, + )); + $this->sendInventory(); + $this->toCraft[-1] = 0; + } + $this->toCraft = array(-1 => $this->toCraft[-1]); + $this->craftingItems = array(); + } + } if(!isset($this->windows[$data["windowid"]])){ break; } @@ -1373,14 +1511,14 @@ class Player{ } if($item->getID() !== AIR and $slot->getID() == $item->getID()){ if($slot->count < $item->count){ - if($this->removeItem($item->getID(), $item->getMetadata(), $item->count - $slot->count) === false and $this->itemEnforcement === true){ + if($this->removeItem($item->getID(), $item->getMetadata(), $item->count - $slot->count) === false){ break; } }elseif($slot->count > $item->count){ $this->addItem($item->getID(), $item->getMetadata(), $slot->count - $item->count); } }else{ - if($this->removeItem($item->getID(), $item->getMetadata(), $item->count) === false and $this->itemEnforcement === true){ + if($this->removeItem($item->getID(), $item->getMetadata(), $item->count) === false){ break; } $this->addItem($slot->getID(), $slot->getMetadata(), $slot->count); @@ -1396,6 +1534,12 @@ class Player{ console("[DEBUG] Unhandled 0x".dechex($data["id"])." Data Packet for Client ID ".$this->clientID.": ".print_r($data, true), true, true, 2); break; } + + if(isset($this->queue[$this->counter[1] + 1])){ + $d = $this->queue[$this->counter[1] + 1]; + unset($this->queue[$this->counter[1] + 1]); + $this->handle($d["pid"], $d); + } break; } } @@ -1406,58 +1550,11 @@ class Player{ } public function sendInventory(){ - /* - //OLD WAY - foreach($this->inventory as $s => $data){ - if($data[0] > 0 and $data[2] >= 0){ - $e = $this->server->api->entity->add(ENTITY_ITEM, $data[0], array( - "x" => $this->entity->x + 0.5, - "y" => $this->entity->y + 0.19, - "z" => $this->entity->z + 0.5, - "meta" => $data[1], - "stack" => $data[2], - )); - $e->spawn($this); - } - $this->inventory[$s] = array(AIR, 0, 0); - }*/ - $inv = array(); - foreach($this->inventory as $s => $data){ - if($data[0] > AIR and $data[2] >= 0){ - $inv[] = BlockAPI::getItem($data[0], $data[1], $data[2]); - }else{ - $inv[] = BlockAPI::getItem(AIR, 0, 0); - $this->inventory[$s] = array(AIR, 0, 0); - } - } $this->dataPacket(MC_CONTAINER_SET_CONTENT, array( "windowid" => 0, - "count" => count($inv), - "slots" => $inv + "count" => count($this->inventory), + "slots" => $this->inventory )); - /* - //Future - $inv = array(); - foreach($this->inventory as $s => $data){ - if($data[0] > 0 and $data[2] >= 0){ - $inv[] = BlockAPI::getItem($data[0], $data[1], $data[2]); - }else{ - $inv[] = BlockAPI::getItem(AIR, 0, 0); - $this->inventory[$s] = array(AIR, 0, 0); - } - } - $this->dataPacket(MC_SEND_INVENTORY, array( - "eid" => 0, - "windowid" => 0, - "slots" => $inv, - "armor" => array( - 0 => BlockAPI::getItem($this->armor[0][0], $this->armor[0][1], $this->armor[0][2], $this->armor[0][3]), - 1 => BlockAPI::getItem($this->armor[1][0], $this->armor[1][1], $this->armor[1][2], $this->armor[1][3]), - 2 => BlockAPI::getItem($this->armor[2][0], $this->armor[2][1], $this->armor[2][2], $this->armor[2][3]), - 3 => BlockAPI::getItem($this->armor[3][0], $this->armor[3][1], $this->armor[3][2], $this->armor[3][3]), - ), - )); - */ } public function send($pid, $data = array(), $raw = false){ diff --git a/src/PocketMinecraftServer.php b/src/PocketMinecraftServer.php index 9a3af7bcb..192b2bfc8 100644 --- a/src/PocketMinecraftServer.php +++ b/src/PocketMinecraftServer.php @@ -123,6 +123,7 @@ class PocketMinecraftServer{ $this->query("CREATE TABLE actions (ID INTEGER PRIMARY KEY, interval NUMERIC, last NUMERIC, code TEXT, repeat NUMERIC);"); $this->query("CREATE TABLE handlers (ID INTEGER PRIMARY KEY, name TEXT, priority NUMERIC);"); $this->query("CREATE TABLE blockUpdates (level TEXT, x INTEGER, y INTEGER, z INTEGER, type INTEGER, delay NUMERIC);"); + $this->query("CREATE TABLE recipes (id INTEGER PRIMARY KEY, type NUMERIC, recipe TEXT);"); //$this->query("PRAGMA synchronous = OFF;"); $this->preparedSQL->selectHandlers = $this->database->prepare("SELECT DISTINCT ID FROM handlers WHERE name = :name ORDER BY priority DESC;"); $this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);"); diff --git a/src/config.php b/src/config.php index 56f0798ea..cf71e6716 100644 --- a/src/config.php +++ b/src/config.php @@ -66,9 +66,7 @@ ini_set("memory_limit", "128M"); //Default define("LOG", true); define("START_TIME", microtime(true)); define("MAJOR_VERSION", "Alpha_1.3.1dev"); -define("CURRENT_STRUCTURE", 5); -define("CURRENT_PROTOCOL", 9); -define("CURRENT_MINECRAFT_VERSION", "0.6.1 alpha"); +define("CURRENT_MINECRAFT_VERSION", "0.7.0 alpha"); define("CURRENT_API_VERSION", 8); define("CURRENT_PHP_VERSION", "5.5"); $gitsha1 = false; diff --git a/src/constants/BlockIDs.php b/src/constants/BlockIDs.php index 6f9ba9076..d5920a872 100644 --- a/src/constants/BlockIDs.php +++ b/src/constants/BlockIDs.php @@ -150,6 +150,7 @@ define("BRICK_STAIRS", 108); define("STONE_BRICK_STAIRS", 109); define("NETHER_BRICKS", 112); +define("NETHER_BRICK_BLOCK", 112); define("NETHER_BRICKS_STAIRS", 114); diff --git a/src/constants/GeneralConstants.php b/src/constants/GeneralConstants.php index cb64209c3..f459a0b0e 100644 --- a/src/constants/GeneralConstants.php +++ b/src/constants/GeneralConstants.php @@ -37,6 +37,9 @@ define("VIEWER", 3); define("PLAYER_RECOVERY_BUFFER", 2048); define("PLAYER_MAX_PACKET_LOSS", 0.20); +define("PLAYER_SURVIVAL_SLOTS", 36); +define("PLAYER_CREATIVE_SLOTS", 111); + //Block Updates define("BLOCK_UPDATE_NORMAL", 1); diff --git a/src/constants/ItemIDs.php b/src/constants/ItemIDs.php index 0ca6b13a2..ca8e3a476 100644 --- a/src/constants/ItemIDs.php +++ b/src/constants/ItemIDs.php @@ -50,12 +50,17 @@ define("DIAMOND_SHOVEL", 277); define("DIAMOND_PICKAXE", 278); define("DIAMOND_AXE", 279); define("STICK", 280);//Implemented +define("STICKS", 280); define("BOWL", 281);//Implemented define("MUSHROOM_STEW", 282); define("GOLD_SWORD", 283); define("GOLD_SHOVEL", 284); define("GOLD_PICKAXE", 285); define("GOLD_AXE", 286); +define("GOLDEN_SWORD", 283); +define("GOLDEN_SHOVEL", 284); +define("GOLDEN_PICKAXE", 285); +define("GOLDEN_AXE", 286); define("STRING", 287); define("FEATHER", 288);//Implemented define("GUNPOWDER", 289); @@ -64,6 +69,7 @@ define("STONE_HOE", 291); define("IRON_HOE", 292);//Implemented define("DIAMOND_HOE", 293); define("GOLD_HOE", 294); +define("GOLDEN_HOE", 294); define("SEEDS", 295); define("WHEAT_SEEDS", 295); define("WHEAT", 296); @@ -74,19 +80,19 @@ define("LEATHER_PANTS", 300); define("LEATHER_BOOTS", 301); define("CHAIN_HELMET", 302); define("CHAIN_CHESTPLATE", 303); -define("CHAIN_LEGGINS", 304); +define("CHAIN_LEGGINGS", 304); define("CHAIN_BOOTS", 305); define("IRON_HELMET", 306); define("IRON_CHESTPLATE", 307); -define("IRON_LEGGINS", 308); +define("IRON_LEGGINGS", 308); define("IRON_BOOTS", 309); define("DIAMOND_HELMET", 310); define("DIAMOND_CHESTPLATE", 311); -define("DIAMOND_LEGGINS", 312); +define("DIAMOND_LEGGINGS", 312); define("DIAMOND_BOOTS", 313); define("GOLD_HELMET", 314); define("GOLD_CHESTPLATE", 315); -define("GOLD_LEGGINS", 316); +define("GOLD_LEGGINGS", 316); define("GOLD_BOOTS", 317); define("FLINT", 318); define("RAW_PORKCHOP", 319); @@ -96,8 +102,6 @@ define("GOLDEN_APPLE", 322); define("SIGN", 323); define("WOODEN_DOOR", 324); define("BUCKET", 325); -define("WATER_BUCKET", 326); -define("LAVA_BUCKET", 327); define("IRON_DOOR", 330); @@ -142,6 +146,8 @@ define("COOKED_BEEF", 364); define("RAW_CHICKEN", 365); define("COOKED_CHICKEN", 366); +define("SPAWN_EGG", 383); + define("NETHER_BRICK", 405); define("QUARTZ", 406); define("NETHER_QUARTZ", 406); diff --git a/src/constants/ProtocolInfo.php b/src/constants/ProtocolInfo.php index d13f94a34..12e225ce2 100644 --- a/src/constants/ProtocolInfo.php +++ b/src/constants/ProtocolInfo.php @@ -25,6 +25,10 @@ the Free Software Foundation, either version 3 of the License, or */ + +define("CURRENT_STRUCTURE", 5); +define("CURRENT_PROTOCOL", 11); + define("RAKNET_MAGIC", "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"); define("MC_PING", 0x00); @@ -78,21 +82,22 @@ define("MC_PLAYER_ACTION", 0xa3); define("MC_HURT_ARMOR", 0xa5); define("MC_SET_ENTITY_DATA", 0xa6); define("MC_SET_ENTITY_MOTION", 0xa7); -define("MC_SET_HEALTH", 0xa8); -define("MC_SET_SPAWN_POSITION", 0xa9); -define("MC_ANIMATE", 0xaa); -define("MC_RESPAWN", 0xab); -define("MC_SEND_INVENTORY", 0xac); -define("MC_DROP_ITEM", 0xad); -define("MC_CONTAINER_OPEN", 0xae); -define("MC_CONTAINER_CLOSE", 0xaf); -define("MC_CONTAINER_SET_SLOT", 0xb0); -define("MC_CONTAINER_SET_DATA", 0xb1); -define("MC_CONTAINER_SET_CONTENT", 0xb2); -//define("MC_CONTAINER_ACK", 0xb3); -define("MC_CLIENT_MESSAGE", 0xb4); -define("MC_SIGN_UPDATE", 0xb5); -define("MC_ADVENTURE_SETTINGS", 0xb6); +//define("MC_SET_RIDING_PACKET", 0xa8); +define("MC_SET_HEALTH", 0xa9); +define("MC_SET_SPAWN_POSITION", 0xaa); +define("MC_ANIMATE", 0xab); +define("MC_RESPAWN", 0xac); +define("MC_SEND_INVENTORY", 0xad); +define("MC_DROP_ITEM", 0xae); +define("MC_CONTAINER_OPEN", 0xaf); +define("MC_CONTAINER_CLOSE", 0xb0); +define("MC_CONTAINER_SET_SLOT", 0xb1); +define("MC_CONTAINER_SET_DATA", 0xb2); +define("MC_CONTAINER_SET_CONTENT", 0xb3); +//define("MC_CONTAINER_ACK", 0xb4); +define("MC_CLIENT_MESSAGE", 0xb5); +define("MC_SIGN_UPDATE", 0xb6); +define("MC_ADVENTURE_SETTINGS", 0xb7); class Protocol{ diff --git a/src/material/Item.php b/src/material/Item.php index aebc68562..492e03428 100644 --- a/src/material/Item.php +++ b/src/material/Item.php @@ -33,8 +33,6 @@ class Item{ SIGN => "SignItem", WOODEN_DOOR => "WoodenDoorItem", BUCKET => "BucketItem", - WATER_BUCKET => "WaterBucketItem", - LAVA_BUCKET => "LavaBucketItem", IRON_DOOR => "IronDoorItem", CAKE => "CakeItem", BED => "BedItem", diff --git a/src/material/block/solid/BurningFurnace.php b/src/material/block/solid/BurningFurnace.php index 2f7cecbc2..f885d486e 100644 --- a/src/material/block/solid/BurningFurnace.php +++ b/src/material/block/solid/BurningFurnace.php @@ -70,7 +70,7 @@ class BurningFurnaceBlock extends SolidBlock{ return true; } $player->windowCnt++; - $player->windowCnt = $id = max(1, $player->windowCnt % 255); + $player->windowCnt = $id = max(2, $player->windowCnt % 255); $player->windows[$id] = $furnace; $player->dataPacket(MC_CONTAINER_OPEN, array( "windowid" => $id, diff --git a/src/material/block/solid/Chest.php b/src/material/block/solid/Chest.php index 559d3dbb1..d4e8876f6 100644 --- a/src/material/block/solid/Chest.php +++ b/src/material/block/solid/Chest.php @@ -88,7 +88,7 @@ class ChestBlock extends TransparentBlock{ return true; } $player->windowCnt++; - $player->windowCnt = $id = max(1, $player->windowCnt % 255); + $player->windowCnt = $id = max(2, $player->windowCnt % 255); $player->windows[$id] = $chest; $player->dataPacket(MC_CONTAINER_OPEN, array( "windowid" => $id, diff --git a/src/material/block/solid/Stonecutter.php b/src/material/block/solid/Stonecutter.php index 80c7c6781..0b1c60fb8 100644 --- a/src/material/block/solid/Stonecutter.php +++ b/src/material/block/solid/Stonecutter.php @@ -30,6 +30,11 @@ class StonecutterBlock extends SolidBlock{ parent::__construct(STONECUTTER, $meta, "Stonecutter"); $this->isActivable = true; } + + public function onActivate(Item $item, Player $player){ + $player->toCraft[-1] = 2; + return true; + } public function getDrops(Item $item, Player $player){ return array( diff --git a/src/material/block/solid/Workbench.php b/src/material/block/solid/Workbench.php index a2b8a75a0..4c46ee0e4 100644 --- a/src/material/block/solid/Workbench.php +++ b/src/material/block/solid/Workbench.php @@ -30,10 +30,15 @@ class WorkbenchBlock extends SolidBlock{ parent::__construct(WORKBENCH, $meta, "Crafting Table"); $this->isActivable = true; } + + public function onActivate(Item $item, Player $player){ + $player->toCraft[-1] = 1; + return true; + } public function getDrops(Item $item, Player $player){ return array( array($this->id, 0, 1), ); - } + } } \ No newline at end of file diff --git a/src/material/item/generic/Bucket.php b/src/material/item/generic/Bucket.php index 79f518227..dff13815e 100644 --- a/src/material/item/generic/Bucket.php +++ b/src/material/item/generic/Bucket.php @@ -27,17 +27,31 @@ the Free Software Foundation, either version 3 of the License, or class BucketItem extends Item{ public function __construct($meta = 0, $count = 1){ - parent::__construct(BUCKET, 0, $count, "Empty Bucket"); + parent::__construct(BUCKET, 0, $count, "Bucket"); + $this->meta = $meta; $this->isActivable = true; $this->maxStackSize = 1; } public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ - if($target->getID() === STILL_WATER or $target->getID() === STILL_LAVA){ - $level->setBlock($target, new AirBlock()); - $player->removeItem($this->getID(), $this->getMetadata(), $this->count); - $player->addItem(($target->getID() === STILL_LAVA ? LAVA_BUCKET:WATER_BUCKET), 0, 1); - return true; + if($this->meta === AIR){ + if($block->getID() === STILL_WATER or $block->getID() === STILL_LAVA){ + $level->setBlock($block, new AirBlock()); + $this->meta = $block->getID(); + return true; + } + }elseif($this->meta === STILL_WATER){ + if($block->getID() === AIR){ + $level->setBlock($block, new StillWaterBLock()); + $this->meta = 0; + return true; + } + }elseif($this->meta === STILL_LAVA){ + if($block->getID() === AIR){ + $level->setBlock($block, new StillLavaBlock()); + $this->meta = 0; + return true; + } } return false; } diff --git a/src/material/item/generic/Coal.php b/src/material/item/generic/Coal.php index e8e3d0bf2..7b80aca66 100644 --- a/src/material/item/generic/Coal.php +++ b/src/material/item/generic/Coal.php @@ -28,7 +28,7 @@ the Free Software Foundation, either version 3 of the License, or class CoalItem extends Item{ public function __construct($meta = 0, $count = 1){ parent::__construct(COAL, $meta & 0x01, $count, "Coal"); - if($this->metadata === 1){ + if($this->meta === 1){ $this->name = "Charcoal"; } } diff --git a/src/material/item/generic/LavaBucket.php b/src/material/item/generic/LavaBucket.php deleted file mode 100644 index 64be8f63a..000000000 --- a/src/material/item/generic/LavaBucket.php +++ /dev/null @@ -1,44 +0,0 @@ -isActivable = true; - $this->maxStackSize = 1; - } - - public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ - if($target->getID() === AIR){ - $level->setBlock($target, new StillLavaBlock()); - $player->removeItem($this->getID(), $this->getMetadata(), $this->count); - $player->addItem(BUCKET, 0, 1); - return true; - } - return false; - } -} \ No newline at end of file diff --git a/src/material/item/generic/WaterBucket.php b/src/material/item/generic/WaterBucket.php deleted file mode 100644 index 1c999da46..000000000 --- a/src/material/item/generic/WaterBucket.php +++ /dev/null @@ -1,44 +0,0 @@ -isActivable = true; - $this->maxStackSize = 1; - } - - public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ - if($target->getID() === AIR){ - $level->setBlock($target, new StillWaterBLock()); - $player->removeItem($this->getID(), $this->getMetadata(), $this->count); - $player->addItem(BUCKET, 0, 1); - return true; - } - return false; - } -} \ No newline at end of file diff --git a/src/network/CustomPacketHandler.php b/src/network/CustomPacketHandler.php index ac705bdc4..81cf29af0 100644 --- a/src/network/CustomPacketHandler.php +++ b/src/network/CustomPacketHandler.php @@ -159,9 +159,14 @@ class CustomPacketHandler{ $this->data["username"] = $this->get(Utils::readShort($this->get(2), false)); $this->data["protocol1"] = Utils::readInt($this->get(4)); $this->data["protocol2"] = Utils::readInt($this->get(4)); + $this->data["unknown1"] = Utils::readInt($this->get(4)); + $this->data["unknown2"] = $this->get(Utils::readShort($this->get(2), false)); }else{ $this->raw .= Utils::writeShort(strlen($this->data["username"])).$this->data["username"]; - $this->raw .= Utils::writeInt(CURRENT_PROTOCOL).Utils::writeInt(CURRENT_PROTOCOL); + $this->raw .= Utils::writeInt(CURRENT_PROTOCOL). + Utils::writeInt(CURRENT_PROTOCOL). + Utils::writeInt($this->data["unknown1"]); + $this->raw .= Utils::writeShort(strlen($this->data["unknown2"])).$this->data["unknown2"]; } break; case MC_LOGIN_STATUS: @@ -187,9 +192,9 @@ class CustomPacketHandler{ break; case MC_SET_TIME: if($this->c === false){ - $this->data["time"] = Utils::readInt($this->get(4)); + $this->data["time"] = Utils::readLong($this->get(4)); }else{ - $this->raw .= Utils::writeInt($this->data["time"]); + $this->raw .= Utils::writeLong($this->data["time"]); } break; case MC_START_GAME: @@ -509,10 +514,12 @@ class CustomPacketHandler{ $this->data["eid"] = Utils::readInt($this->get(4)); $this->data["block"] = Utils::readShort($this->get(2), false); $this->data["meta"] = Utils::readShort($this->get(2), false); + $this->data["slot"] = ord($this->get(1)); }else{ $this->raw .= Utils::writeInt($this->data["eid"]); $this->raw .= Utils::writeShort($this->data["block"]); $this->raw .= Utils::writeShort($this->data["meta"]); + $this->raw .= chr($this->data["slot"]); } break; case MC_PLAYER_ARMOR_EQUIPMENT: @@ -553,6 +560,9 @@ class CustomPacketHandler{ $this->data["fx"] = Utils::readFloat($this->get(4)); $this->data["fy"] = Utils::readFloat($this->get(4)); $this->data["fz"] = Utils::readFloat($this->get(4)); + $this->data["posX"] = Utils::readFloat($this->get(4)); + $this->data["posY"] = Utils::readFloat($this->get(4)); + $this->data["posZ"] = Utils::readFloat($this->get(4)); }else{ $this->raw .= Utils::writeInt($this->data["x"]); $this->raw .= Utils::writeInt($this->data["y"]); @@ -564,6 +574,9 @@ class CustomPacketHandler{ $this->raw .= Utils::writeFloat($this->data["fx"]); $this->raw .= Utils::writeFloat($this->data["fy"]); $this->raw .= Utils::writeFloat($this->data["fz"]); + $this->raw .= Utils::writeFloat($this->data["posX"]); + $this->raw .= Utils::writeFloat($this->data["posY"]); + $this->raw .= Utils::writeFloat($this->data["posZ"]); } break; case MC_PLAYER_ACTION: diff --git a/src/recipes/CraftingRecipes.php b/src/recipes/CraftingRecipes.php new file mode 100644 index 000000000..87dc5ee49 --- /dev/null +++ b/src/recipes/CraftingRecipes.php @@ -0,0 +1,289 @@ +CLAY_BLOCK:0x1", + "WOODEN_PLANKS:?x4=>WORKBENCH:0x1", + "GLOWSTONE_DUST:?x4=>GLOWSTONE_BLOCK:0x1", + "SNOWBALL:?x4=>SNOW_BLOCK:0x1", + "WOODEN_PLANKS:?x2=>STICK:0x4", + "COBBLESTONE:?x4=>STONECUTTER:0x1", + "WOOD:?x1=>WOODEN_PLANKS:0x4", + "WOOL:0x1,DYE:0x1=>WOOL:15x1", + "WOOL:0x1,DYE:1x1=>WOOL:14x1", + "WOOL:0x1,DYE:2x1=>WOOL:13x1", + "WOOL:0x1,DYE:3x1=>WOOL:12x1", + "WOOL:0x1,DYE:4x1=>WOOL:11x1", + "WOOL:0x1,DYE:5x1=>WOOL:10x1", + "WOOL:0x1,DYE:6x1=>WOOL:9x1", + "WOOL:0x1,DYE:7x1=>WOOL:8x1", + "WOOL:0x1,DYE:8x1=>WOOL:7x1", + "WOOL:0x1,DYE:9x1=>WOOL:6x1", + "WOOL:0x1,DYE:10x1=>WOOL:5x1", + "WOOL:0x1,DYE:11x1=>WOOL:4x1", + "WOOL:0x1,DYE:12x1=>WOOL:3x1", + "WOOL:0x1,DYE:13x1=>WOOL:2x1", + "WOOL:0x1,DYE:14x1=>WOOL:1x1", + "STRING:?x4=>WOOL:0x1", + + //Tools + "IRON_INGOT:?x1,FLINT:?x1=>FLINT_STEEL:0x1", + "IRON_INGOT:?x2=>SHEARS:0x1", + "COAL:0x1,STICK:?x1=>TORCH:0x4", + "COAL:1x1,STICK:?x1=>TORCH:0x4", + + //Food & protection + "BROWN_MUSHROOM:?x1,RED_MUSHROOM:?x1,BOWL:?x1=>MUSHROOM_STEW:0x1", + "SUGARCANE:?x1=>SUGAR:0x1", + "MELON_SLICE:?x1=>MELON_SEEDS:0x1", + + //Items + "DIAMOND_BLOCK:?x1=>DIAMOND:0x9", + "GOLD_BLOCK:?x1=>GOLD_INGOT:0x9", + "IRON_BLOCK:?x1=>IRON_INGOT:0x9", + "LAPIS_BLOCK:?x1=>DYE:4x9", + "DANDELION:?x1=>DYE:11x2", + "BONE:?x1=>DYE:15x3", + "DYE:1x1,DYE:15x1=>DYE:9x2", + "DYE:1x1,DYE:11x1=>DYE:14x2", + "DYE:2x1,DYE:15x1=>DYE:10x2", + "DYE:4x1,DYE:15x1=>DYE:12x2", + "DYE:2x1,DYE:4x1=>DYE:6x2", + "DYE:1x1,DYE:4x1=>DYE:5x2", + "DYE:1x1,DYE:4x1,DYE:15x1=>DYE:13x3", + "DYE:15x1,DYE:1x2,DYE:4x1=>DYE:13x4",// + "DYE:5x1,DYE:9x1=>DYE:13x2",// + "DYE:0x1,DYE:15x1=>DYE:8x2",// + "DYE:0x1,DYE:15x2=>DYE:7x3",// + "DYE:0x1,DYE:8x1=>DYE:7x2",// + ); + + private static $big = array( + //Building + "WOOL:?x3,WOODEN_PLANKS:?x3=>BED:0x1", + "WOODEN_PLANKS:?x8=>CHEST:0x1", + "STICK:?x6=>FENCE:0x2", + "STICK:?x4,WOODEN_PLANKS:?x2=>FENCE_GATE:0x1", + "COBBLESTONE:?x8=>FURNACE:0x1", + "GLASS:?x6=>GLASS_PANE:0x16", + "STICK:?x7=>LADDER:0x2", + "DIAMOND:?x3,IRON_INGOT:?x6=>NETHER_REACTOR:0x1", + "WOODEN_PLANKS:?x6=>TRAPDOOR:0x2", + "WOODEN_PLANKS:?x6=>WOODEN_DOOR:0x1", + "WOODEN_PLANKS:?x6=>WOODEN_STAIRS:0x4", + "WOODEN_PLANKS:?x3=>SLAB:2x6", + + //Tools + "STICK:?x1,FEATHER:?x1,FLINT:?x1=>ARROW:0x4", + "STICK:?x3,STRING:?x3=>BOW:0x1", + "IRON_INGOT:?x3=>BUCKET:0x1", + "DIAMOND:?x3,STICK:?x2=>DIAMOND_AXE:0x1", + "DIAMOND:?x2,STICK:?x2=>DIAMOND_HOE:0x1", + "DIAMOND:?x3,STICK:?x2=>DIAMOND_PICKAXE:0x1", + "DIAMOND:?x1,STICK:?x2=>DIAMOND_SHOVEL:0x1", + "DIAMOND:?x2,STICK:?x1=>DIAMOND_SWORD:0x1", + "GOLD_INGOT:?x3,STICK:?x2=>GOLD_AXE:0x1", + "GOLD_INGOT:?x2,STICK:?x2=>GOLD_HOE:0x1", + "GOLD_INGOT:?x3,STICK:?x2=>GOLD_PICKAXE:0x1", + "GOLD_INGOT:?x1,STICK:?x2=>GOLD_SHOVEL:0x1", + "GOLD_INGOT:?x2,STICK:?x1=>GOLD_SWORD:0x1", + "IRON_INGOT:?x3,STICK:?x2=>IRON_AXE:0x1", + "IRON_INGOT:?x2,STICK:?x2=>IRON_HOE:0x1", + "IRON_INGOT:?x3,STICK:?x2=>IRON_PICKAXE:0x1", + "IRON_INGOT:?x1,STICK:?x2=>IRON_SHOVEL:0x1", + "IRON_INGOT:?x2,STICK:?x1=>IRON_SWORD:0x1", + "COBBLESTONE:?x3,STICK:?x2=>STONE_AXE:0x1", + "COBBLESTONE:?x2,STICK:?x2=>STONE_HOE:0x1", + "COBBLESTONE:?x3,STICK:?x2=>STONE_PICKAXE:0x1", + "COBBLESTONE:?x1,STICK:?x2=>STONE_SHOVEL:0x1", + "COBBLESTONE:?x2,STICK:?x1=>STONE_SWORD:0x1", + "SAND:?x4,GUNPOWDER:?x5=>TNT:0x1", + "WOODEN_PLANKS:?x3,STICK:?x2=>WOODEN_AXE:0x1", + "WOODEN_PLANKS:?x2,STICK:?x2=>WOODEN_HOE:0x1", + "WOODEN_PLANKS:?x3,STICK:?x2=>WOODEN_PICKAXE:0x1", + "WOODEN_PLANKS:?x1,STICK:?x2=>WOODEN_SHOVEL:0x1", + "WOODEN_PLANKS:?x2,STICK:?x1=>WOODEN_SWORD:0x1", + + //Food & protection + "WEATH:?x3=>BREAD:0x1", + "WHEATH:?x3,BUCKET:1x3,EGG:?x1,SUGAR:?x2=>CAKE:0x1", + "DIAMOND:?x4=>DIAMOND_BOOTS:0x1", + "DIAMOND:?x8=>DIAMOND_CHESTPLATE:0x1", + "DIAMOND:?x5=>DIAMOND_HELMET:0x1", + "DIAMOND:?x8=>DIAMOND_LEGGINGS:0x1", + "GOLD_INGOT:?x4=>GOLD_BOOTS:0x1", + "GOLD_INGOT:?x8=>GOLD_CHESTPLATE:0x1", + "GOLD_INGOT:?x5=>GOLD_HELMET:0x1", + "GOLD_INGOT:?x8=>GOLD_LEGGINGS:0x1", + "IRON_INGOT:?x4=>IRON_BOOTS:0x1", + "IRON_INGOT:?x8=>IRON_CHESTPLATE:0x1", + "IRON_INGOT:?x5=>IRON_HELMET:0x1", + "IRON_INGOT:?x8=>IRON_LEGGINGS:0x1", + "LEATHER:?x4=>LEATHER_BOOTS:0x1", + "LEATHER:?x8=>LEATHER_TUNIC:0x1", + "LEATHER:?x5=>LEATHER_CAP:0x1", + "LEATHER:?x8=>LEATHER_PANTS:0x1", + "FIRE:?x4=>CHAIN_BOOTS:0x1", + "FIRE:?x8=>CHAIN_CHESTPLATE:0x1", + "FIRE:?x5=>CHAIN_HELMET:0x1", + "FIRE:?x8=>CHAIN_LEGGINGS:0x1", + + //Items + "DIAMOND:?x9=>DIAMOND_BLOCK:0x1", + "GOLD_INGOT:?x9=>GOLD_BLOCK:0x1", + "IRON_INGOT:?x9=>IRON_BLOCK:0x1", + "PAPER:?x3=>BOOK:0x1", + "WOODEN_PLANKS:?x6,BOOK:?x3=>BOOKSHELF:0x1", + "DYE:4x9=>LAPIS_BLOCK:0x1", + "WOOL:?x1,STICK:?x8=>PAINTING:0x1", + "SUGARCANE:?x3=>PAPER:0x1", + "WOODEN_PLANKS:?x6,STICK:?x1=>SIGN:0x1", + ); + + private static $stone = array( + "QUARTZ:?x4=>QUARTZ_BLOCK:0x1", + "BRICKS_BLOCK:?x6=>BRICK_STAIRS:0x4", + "BRICK:?x4=>BRICKS_BLOCK:0x1", + "NETHER_BRICK:?x4=>NETHER_BRICKS:0x1", + "NETHER_BRICKS:?x6=>NETHER_BRICK_STAIRS:0x4", + "SAND:?x4=>SANDSTONE:0x1", + "SANDSTONE:0x4=>SANDSTONE:2x1", + "SLAB:1x2=>SANDSTONE:1x1", + "STONE_BRICK:?x6=>STONE_BRICK_STAIRS:0x4", + "STONE:?x4=>STONE_BRICK:0x1", + "COBBLESTONE:?x6=>COBBLESTONE_STAIRS:0x4", + "COBBLESTONE:?x3=>SLAB:3x6", + "STONE:?x3=>SLAB:0x6", + "SANDSTONE:2x3=>SLAB:1x6", + "BRICKS_BLOCK:?x3=>SLAB:4x6", + "STONE_BRICKS:?x3=>SLAB:5x6", + ); + + private static $recipes = array(); + + private static function parseRecipe($recipe){ + $recipe = explode("=>", $recipe); + $recipeItems = array(); + foreach(explode(",", $recipe[0]) as $item){ + $item = explode("x", $item); + $id = explode(":", $item[0]); + $meta = array_pop($id); + $id = $id[0]; + + $it = BlockAPI::fromString($id); + $recipeItems[$it->getID()] = array($it->getID(), $meta === "?" ? false:intval($meta)&0xFFFF, intval($item[1])); + } + ksort($recipeItems); + $item = explode("x", $recipe[1]); + $id = explode(":", $item[0]); + $meta = array_pop($id); + $id = $id[0]; + + $it = BlockAPI::fromString($id); + + $craftItem = array($it->getID(), intval($meta)&0xFFFF, intval($item[1])); + + $recipeString = ""; + foreach($recipeItems as $item){ + $recipeString .= $item[0]."x".$item[2].","; + } + $recipeString = substr($recipeString, 0, -1)."=>".$craftItem[0]."x".$craftItem[2]; + + return array($recipeItems, $craftItem, $recipeString); + } + + public static function init(){ + $server = ServerAPI::request(); + $id = 1; + foreach(CraftingRecipes::$small as $recipe){ + $recipe = CraftingRecipes::parseRecipe($recipe); + $recipe[3] = 0; //Type + CraftingRecipes::$recipes[$id] = $recipe; + ++$id; + } + foreach(CraftingRecipes::$big as $recipe){ + $recipe = CraftingRecipes::parseRecipe($recipe); + $recipe[3] = 1; + CraftingRecipes::$recipes[$id] = $recipe; + ++$id; + } + foreach(CraftingRecipes::$stone as $recipe){ + $recipe = CraftingRecipes::parseRecipe($recipe); + $recipe[3] = 2; + CraftingRecipes::$recipes[$id] = $recipe; + ++$id; + } + + foreach(CraftingRecipes::$recipes as $id => $recipe){ + + $server->query("INSERT INTO recipes (id, type, recipe) VALUES (".$id.", ".$recipe[3].", '".$recipe[2]."');"); + } + + } + + public static function canCraft(array $craftItem, array $recipeItems, $type){ + ksort($recipeItems); + $recipeString = ""; + foreach($recipeItems as $item){ + $recipeString .= $item[0]."x".$item[2].","; + } + $recipeString = substr($recipeString, 0, -1)."=>".$craftItem[0]."x".$craftItem[2]; + $server = ServerAPI::request(); + + $result = $server->query("SELECT id FROM recipes WHERE type == ".$type." AND recipe == '".$recipeString."';"); + if($result instanceof SQLite3Result){ + $continue = true; + while(($r = $result->fetchArray(SQLITE3_NUM)) !== false){ + $continue = true; + $recipe = CraftingRecipes::$recipes[$r[0]]; + foreach($recipe[0] as $item){ + if(!isset($recipeItems[$item[0]])){ + $continue = false; + break; + } + $oitem = $recipeItems[$item[0]]; + if(($oitem[1] != $item[1] and $item[2] !== false) or $oitem[2] != $item[2]){ + $continue = false; + break; + } + } + if($continue === false or $craftItem[0] != $recipe[1][0] or $recipe[1][1] != $recipe[1][1] or $recipe[1][2] != $recipe[1][2]){ + $continue = false; + continue; + } + $continue = $recipe; + break; + } + }else{ + return true; + } + return $continue; + } + +} \ No newline at end of file diff --git a/src/recipes/SmeltingData.php b/src/recipes/SmeltingData.php index 0198f6e96..f97f27640 100644 --- a/src/recipes/SmeltingData.php +++ b/src/recipes/SmeltingData.php @@ -41,6 +41,7 @@ class SmeltingData{ RED_MUSHROOM => array(DYE, 1), RAW_BEEF => array(STEAK, 0), RAW_CHICKEN => array(COOKED_CHICKEN, 0), + RED_MUSHROOM => array(DYE, 1), ); } \ No newline at end of file diff --git a/src/world/Entity.php b/src/world/Entity.php index 2f1df5992..0fcb9d747 100644 --- a/src/world/Entity.php +++ b/src/world/Entity.php @@ -143,10 +143,10 @@ class Entity extends Position{ } public function getDrops(){ - if($this->class === ENTITY_PLAYER){ + if($this->class === ENTITY_PLAYER and ($this->player->gamemode & 0x00) === 0){ $inv = array(); $air = BlockAPI::getItem(AIR, 0, 0); - for($i = 0; $i < 36; ++$i){ + for($i = 0; $i < PLAYER_SURVIVAL_SLOTS; ++$i){ $slot = $this->player->getSlot($i); $this->player->setSlot($i, $air); if($slot->getID() !== AIR and $slot->count > 0){ @@ -562,8 +562,9 @@ class Entity extends Position{ )); $player->dataPacket(MC_PLAYER_EQUIPMENT, array( "eid" => $this->eid, - "block" => $this->player->equipment->getID(), - "meta" => $this->player->equipment->getMetadata(), + "block" => $this->player->getSlot($this->player->slot)->getID(), + "meta" => $this->player->getSlot($this->player->slot)->getMetadata(), + "slot" => 0, )); break; case ENTITY_ITEM: @@ -799,19 +800,19 @@ class Entity extends Position{ LEATHER_BOOTS => 1, CHAIN_HELMET => 1, CHAIN_CHESTPLATE => 5, - CHAIN_LEGGINS => 4, + CHAIN_LEGGINGS => 4, CHAIN_BOOTS => 1, GOLD_HELMET => 1, GOLD_CHESTPLATE => 5, - GOLD_LEGGINS => 3, + GOLD_LEGGINGS => 3, GOLD_BOOTS => 1, IRON_HELMET => 2, IRON_CHESTPLATE => 6, - IRON_LEGGINS => 5, + IRON_LEGGINGS => 5, IRON_BOOTS => 2, DIAMOND_HELMET => 3, DIAMOND_CHESTPLATE => 8, - DIAMOND_LEGGINS => 6, + DIAMOND_LEGGINGS => 6, DIAMOND_BOOTS => 3, ); foreach($this->player->armor as $part){