mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-17 03:08:58 +00:00
2436 lines
72 KiB
PHP
2436 lines
72 KiB
PHP
<?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/
|
||
*
|
||
*
|
||
*/
|
||
|
||
/***REM_START***/
|
||
require_once("Entity.php");
|
||
/***REM_END***/
|
||
|
||
class Player extends PlayerEntity{
|
||
private $recoveryQueue = array();
|
||
private $receiveQueue = array();
|
||
private $resendQueue = array();
|
||
private $ackQueue = array();
|
||
private $receiveCount = -1;
|
||
private $buffer;
|
||
private $bufferLen = 0;
|
||
private $nextBuffer = 0;
|
||
private $evid = array();
|
||
private $lastMovement = 0;
|
||
private $forceMovement = false;
|
||
private $timeout;
|
||
private $connected = true;
|
||
private $clientID;
|
||
private $ip;
|
||
private $port;
|
||
private $counter = array(0, 0, 0, 0);
|
||
private $username;
|
||
private $iusername;
|
||
private $startAction = false;
|
||
private $isSleeping = false;
|
||
public $auth = false;
|
||
public $CID;
|
||
public $MTU;
|
||
public $spawned = false;
|
||
public $inventory;
|
||
public $slot;
|
||
public $hotbar;
|
||
public $armor = array();
|
||
public $loggedIn = false;
|
||
public $gamemode;
|
||
public $lastBreak;
|
||
public $windowCnt = 2;
|
||
public $windows = array();
|
||
public $blocked = true;
|
||
public $achievements = array();
|
||
public $chunksLoaded = array();
|
||
private $viewDistance;
|
||
private $chunksOrder = array();
|
||
private $lastMeasure = 0;
|
||
private $bandwidthRaw = 0;
|
||
private $bandwidthStats = array(0, 0, 0);
|
||
private $lag = array();
|
||
private $lagStat = 0;
|
||
private $spawnPosition;
|
||
private $packetLoss = 0;
|
||
private $lastChunk = false;
|
||
public $lastCorrect;
|
||
private $bigCnt;
|
||
private $packetStats;
|
||
public $craftingItems = array();
|
||
public $toCraft = array();
|
||
public $lastCraft = 0;
|
||
private $chunkCount = array();
|
||
private $received = array();
|
||
public $loginData = array();
|
||
|
||
public function __get($name){
|
||
if(isset($this->{$name})){
|
||
return ($this->{$name});
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @param integer $clientID
|
||
* @param string $ip
|
||
* @param integer $port
|
||
* @param integer $MTU
|
||
*/
|
||
public function __construct($clientID, $ip, $port, $MTU){
|
||
$this->bigCnt = 0;
|
||
$this->MTU = $MTU;
|
||
$this->server = ServerAPI::request();
|
||
$this->lastBreak = microtime(true);
|
||
$this->clientID = $clientID;
|
||
$this->CID = MainServer::clientID($ip, $port);
|
||
$this->ip = $ip;
|
||
$this->port = $port;
|
||
$this->spawnPosition = $this->server->spawn;
|
||
$this->timeout = microtime(true) + 20;
|
||
$this->inventory = array();
|
||
$this->armor = array();
|
||
$this->gamemode = $this->server->gamemode;
|
||
$this->level = $this->server->api->level->getDefault();
|
||
$this->viewDistance = (int) $this->server->api->getProperty("view-distance");
|
||
$this->slot = 0;
|
||
$this->hotbar = array(0, -1, -1, -1, -1, -1, -1, -1, -1);
|
||
$this->packetStats = array(0,0);
|
||
$this->buffer = new RakNetPacket(RakNetInfo::DATA_PACKET_0);
|
||
$this->buffer->data = array();
|
||
$this->server->schedule(2, array($this, "handlePacketQueues"), array(), true);
|
||
$this->server->schedule(20 * 60, array($this, "clearQueue"), array(), true);
|
||
$this->evid[] = $this->server->event("server.close", array($this, "close"));
|
||
console("[DEBUG] New Session started with ".$ip.":".$port.". MTU ".$this->MTU.", Client ID ".$this->clientID, true, true, 2);
|
||
}
|
||
|
||
public function getSpawn(){
|
||
return $this->spawnPosition;
|
||
}
|
||
|
||
/**
|
||
* @param Vector3 $pos
|
||
*/
|
||
public function setSpawn(Vector3 $pos){
|
||
if(!($pos instanceof Position)){
|
||
$level = $this->level;
|
||
}else{
|
||
$level = $pos->level;
|
||
}
|
||
$this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level);
|
||
$pk = new SetSpawnPositionPacket;
|
||
$pk->x = (int) $this->spawnPosition->x;
|
||
$pk->y = (int) $this->spawnPosition->y;
|
||
$pk->z = (int) $this->spawnPosition->z;
|
||
$this->dataPacket($pk);
|
||
}
|
||
|
||
public function orderChunks(){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
|
||
$newOrder = array();
|
||
$lastLoaded = $this->chunksLoaded;
|
||
$centerX = intval(($this->x - 0.5) / 16);
|
||
$centerZ = intval(($this->z - 0.5) / 16);
|
||
$startX = $centerX - $this->viewDistance;
|
||
$startZ = $centerZ - $this->viewDistance;
|
||
$finalX = $centerX + $this->viewDistance;
|
||
$finalZ = $centerZ + $this->viewDistance;
|
||
for($X = $startX; $X <= $finalX; ++$X){
|
||
for($Z = $startZ; $Z <= $finalZ; ++$Z){
|
||
$distance = abs($X - $centerX) + abs($Z - $centerZ);
|
||
for($Y = 0; $Y < 8; ++$Y){
|
||
$index = "$X:$Y:$Z";
|
||
if(!isset($lastLoaded[$index])){
|
||
$newOrder[$index] = $distance;
|
||
}else{
|
||
unset($lastLoaded[$index]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
asort($newOrder);
|
||
$this->chunksOrder = $newOrder;
|
||
foreach($lastLoaded as $index => $distance){
|
||
$id = explode(":", $index);
|
||
$this->level->freeChunk($id[0], $id[2], $this);
|
||
unset($this->chunksLoaded[$index]);
|
||
}
|
||
}
|
||
|
||
public function getNextChunk(){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
|
||
foreach($this->chunkCount as $count => $t){
|
||
if(isset($this->recoveryQueue[$count]) or isset($this->resendQueue[$count])){
|
||
$this->server->schedule(MAX_CHUNK_RATE, array($this, "getNextChunk"));
|
||
return;
|
||
}else{
|
||
unset($this->chunkCount[$count]);
|
||
}
|
||
}
|
||
|
||
if(is_array($this->lastChunk)){
|
||
foreach($this->level->getChunkTiles($this->lastChunk[0], $this->lastChunk[1]) as $tile){
|
||
if($tile instanceof SpawnableTile){
|
||
$tile->spawnTo($this);
|
||
}
|
||
}
|
||
$this->lastChunk = false;
|
||
}
|
||
|
||
$c = key($this->chunksOrder);
|
||
$d = @$this->chunksOrder[$c];
|
||
if($c === null or $d === null){
|
||
$this->server->schedule(40, array($this, "getNextChunk"));
|
||
return false;
|
||
}
|
||
unset($this->chunksOrder[$c]);
|
||
$this->chunksLoaded[$c] = true;
|
||
$id = explode(":", $c);
|
||
$X = $id[0];
|
||
$Z = $id[2];
|
||
$Y = $id[1];
|
||
$this->level->useChunk($X, $Z, $this);
|
||
$Yndex = 1 << $Y;
|
||
for($iY = 0; $iY < 8; ++$iY){
|
||
if(isset($this->chunksOrder["$X:$iY:$Z"])){
|
||
unset($this->chunksOrder["$X:$iY:$Z"]);
|
||
$this->chunksLoaded["$X:$iY:$Z"] = true;
|
||
$Yndex |= 1 << $iY;
|
||
}
|
||
}
|
||
$pk = new ChunkDataPacket;
|
||
$pk->chunkX = $X;
|
||
$pk->chunkZ = $Z;
|
||
$pk->data = $this->level->getOrderedChunk($X, $Z, $Yndex);
|
||
$cnt = $this->dataPacket($pk);
|
||
if($cnt === false){
|
||
return false;
|
||
}
|
||
$this->chunkCount = array();
|
||
foreach($cnt as $i => $count){
|
||
$this->chunkCount[$count] = true;
|
||
}
|
||
|
||
$this->lastChunk = array($X, $Z);
|
||
|
||
$this->server->schedule(MAX_CHUNK_RATE, array($this, "getNextChunk"));
|
||
}
|
||
|
||
public function save(){
|
||
if($this->entity instanceof Entity){
|
||
$this->data->set("achievements", $this->achievements);
|
||
$this->data->set("position", array(
|
||
"level" => $this->entity->level->getName(),
|
||
"x" => $this->entity->x,
|
||
"y" => $this->entity->y,
|
||
"z" => $this->entity->z,
|
||
));
|
||
$this->data->set("spawn", array(
|
||
"level" => $this->spawnPosition->level->getName(),
|
||
"x" => $this->spawnPosition->x,
|
||
"y" => $this->spawnPosition->y,
|
||
"z" => $this->spawnPosition->z,
|
||
));
|
||
$inv = array();
|
||
foreach($this->inventory as $slot => $item){
|
||
if($item instanceof Item){
|
||
if($slot < (($this->gamemode & 0x01) === 0 ? PLAYER_SURVIVAL_SLOTS:PLAYER_CREATIVE_SLOTS)){
|
||
$inv[$slot] = array($item->getID(), $item->getMetadata(), $item->count);
|
||
}
|
||
}
|
||
}
|
||
$this->data->set("inventory", $inv);
|
||
$this->data->set("hotbar", $this->hotbar);
|
||
|
||
$armor = array();
|
||
foreach($this->armor as $slot => $item){
|
||
if($item instanceof Item){
|
||
$armor[$slot] = array($item->getID(), $item->getMetadata());
|
||
}
|
||
}
|
||
$this->data->set("armor", $armor);
|
||
if($this->entity instanceof Entity){
|
||
$this->data->set("health", $this->entity->getHealth());
|
||
}
|
||
$this->data->set("gamemode", $this->gamemode);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param string $reason Reason for closing connection
|
||
* @param boolean $msg Set to false to silently disconnect player. No broadcast.
|
||
*/
|
||
public function close($reason = "", $msg = true){
|
||
if($this->connected === true){
|
||
foreach($this->evid as $ev){
|
||
$this->server->deleteEvent($ev);
|
||
}
|
||
if($this->username != ""){
|
||
$this->server->api->handle("player.quit", $this);
|
||
$this->save();
|
||
}
|
||
$reason = $reason == "" ? "server stop":$reason;
|
||
$this->sendChat("You have been kicked. Reason: ".$reason."\n");
|
||
$this->sendBuffer();
|
||
$this->directDataPacket(new DisconnectPacket);
|
||
$this->connected = false;
|
||
$this->level->freeAllChunks($this);
|
||
$this->loggedIn = false;
|
||
$this->buffer = null;
|
||
unset($this->buffer);
|
||
$this->recoveryQueue = array();
|
||
$this->receiveQueue = array();
|
||
$this->resendQueue = array();
|
||
$this->ackQueue = array();
|
||
$this->server->api->player->remove($this->CID);
|
||
if($msg === true and $this->username != "" and $this->spawned !== false){
|
||
$this->server->api->chat->broadcast($this->username." left the game");
|
||
}
|
||
$this->spawned = false;
|
||
console("[INFO] ".TextFormat::AQUA.$this->username.TextFormat::RESET."[/".$this->ip.":".$this->port."] logged out due to ".$reason);
|
||
$this->windows = array();
|
||
$this->armor = array();
|
||
$this->inventory = array();
|
||
$this->chunksLoaded = array();
|
||
$this->chunksOrder = array();
|
||
$this->chunkCount = array();
|
||
$this->craftingItems = array();
|
||
$this->received = array();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param Vector3 $pos
|
||
*
|
||
* @return boolean
|
||
*/
|
||
public function sleepOn(Vector3 $pos){
|
||
foreach($this->server->api->player->getAll($this->level) as $p){
|
||
if($p->isSleeping instanceof Vector3){
|
||
if($pos->distance($p->isSleeping) <= 0.1){
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
$this->isSleeping = $pos;
|
||
$this->teleport(new Position($pos->x + 0.5, $pos->y + 1, $pos->z + 0.5, $this->level), false, false, false, false);
|
||
if($this->entity instanceof Entity){
|
||
$this->entity->updateMetadata();
|
||
}
|
||
$this->setSpawn($pos);
|
||
$this->server->schedule(60, array($this, "checkSleep"));
|
||
return true;
|
||
}
|
||
|
||
public function stopSleep(){
|
||
$this->isSleeping = false;
|
||
if($this->entity instanceof Entity){
|
||
$this->entity->updateMetadata();
|
||
}
|
||
}
|
||
|
||
public function checkSleep(){
|
||
if($this->isSleeping !== false){
|
||
if($this->server->api->time->getPhase($this->level) === "night"){
|
||
foreach($this->server->api->player->getAll($this->level) as $p){
|
||
if($p->isSleeping === false){
|
||
return false;
|
||
}
|
||
}
|
||
$this->server->api->time->set("day", $this->level);
|
||
foreach($this->server->api->player->getAll($this->level) as $p){
|
||
$p->stopSleep();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param $type
|
||
* @param $damage
|
||
* @param $count
|
||
*
|
||
* @return boolean
|
||
*/
|
||
public function hasSpace($type, $damage, $count){
|
||
$inv = $this->inventory;
|
||
while($count > 0){
|
||
$add = 0;
|
||
foreach($inv as $s => $item){
|
||
if($item->getID() === AIR){
|
||
$add = min($item->getMaxStackSize(), $count);
|
||
$inv[$s] = BlockAPI::getItem($type, $damage, $add);
|
||
break;
|
||
}elseif($item->getID() === $type and $item->getMetadata() === $damage){
|
||
$add = min($item->getMaxStackSize() - $item->count, $count);
|
||
if($add <= 0){
|
||
continue;
|
||
}
|
||
$inv[$s] = BlockAPI::getItem($type, $damage, $item->count + $add);
|
||
break;
|
||
}
|
||
}
|
||
if($add <= 0){
|
||
return false;
|
||
}
|
||
$count -= $add;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @param $type
|
||
* @param $damage
|
||
* @param integer $count
|
||
* @param boolean $send
|
||
*
|
||
* @return boolean
|
||
*/
|
||
public function addItem($type, $damage, $count, $send = true){
|
||
while($count > 0){
|
||
$add = 0;
|
||
foreach($this->inventory as $s => $item){
|
||
if($item->getID() === AIR){
|
||
$add = min($item->getMaxStackSize(), $count);
|
||
$this->inventory[$s] = BlockAPI::getItem($type, $damage, $add);
|
||
if($send === true){
|
||
$this->sendInventorySlot($s);
|
||
}
|
||
break;
|
||
}elseif($item->getID() === $type and $item->getMetadata() === $damage){
|
||
$add = min($item->getMaxStackSize() - $item->count, $count);
|
||
if($add <= 0){
|
||
continue;
|
||
}
|
||
$item->count += $add;
|
||
if($send === true){
|
||
$this->sendInventorySlot($s);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if($add <= 0){
|
||
return false;
|
||
}
|
||
$count -= $add;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public function removeItem($type, $damage, $count, $send = true){
|
||
while($count > 0){
|
||
$remove = 0;
|
||
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] = BlockAPI::getItem(AIR, 0, 0);
|
||
}
|
||
if($send === true){
|
||
$this->sendInventorySlot($s);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if($remove <= 0){
|
||
return false;
|
||
}
|
||
$count -= $remove;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @param integer $slot
|
||
* @param Item $item
|
||
* @param boolean $send
|
||
*
|
||
* @return boolean
|
||
*/
|
||
public function setSlot($slot, Item $item, $send = true){
|
||
$this->inventory[(int) $slot] = $item;
|
||
if($send === true){
|
||
$this->sendInventorySlot((int) $slot);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @param integer $slot
|
||
*
|
||
* @return Item
|
||
*/
|
||
public function getSlot($slot){
|
||
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])){
|
||
$pk = new ContainerSetSlotPacket;
|
||
$pk->windowid = 0;
|
||
$pk->slot = (int) $s;
|
||
$pk->item = BlockAPI::getItem(AIR, 0, 0);
|
||
$this->dataPacket($pk);
|
||
}
|
||
|
||
$slot = $this->inventory[$s];
|
||
$pk = new ContainerSetSlotPacket;
|
||
$pk->windowid = 0;
|
||
$pk->slot = (int) $s;
|
||
$pk->item = $slot;
|
||
$this->dataPacket($pk);
|
||
return true;
|
||
}
|
||
|
||
public function setArmor($slot, Item $armor, $send = true){
|
||
$this->armor[(int) $slot] = $armor;
|
||
if($send === true){
|
||
$this->sendArmor($this);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @param integer $slot
|
||
*
|
||
* @return Item
|
||
*/
|
||
public function getArmor($slot){
|
||
if(isset($this->armor[(int) $slot])){
|
||
return $this->armor[(int) $slot];
|
||
}else{
|
||
return BlockAPI::getItem(AIR, 0, 0);
|
||
}
|
||
}
|
||
|
||
public function hasItem($type, $damage = false){
|
||
foreach($this->inventory as $s => $item){
|
||
if($item->getID() === $type and ($item->getMetadata() === $damage or $damage === false) and $item->count > 0){
|
||
return $s;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @param mixed $data
|
||
* @param string $event
|
||
*/
|
||
public function eventHandler($data, $event){
|
||
switch($event){
|
||
case "tile.update":
|
||
if($data->level === $this->level){
|
||
if($data instanceof FurnaceTile){
|
||
foreach($this->windows as $id => $w){
|
||
if($w === $data){
|
||
$pk = new ContainerSetDataPacket;
|
||
$pk->windowid = $id;
|
||
$pk->property = 0; //Smelting
|
||
$pk->value = floor($data->namedtag->CookTime);
|
||
$this->dataPacket($pk);
|
||
|
||
$pk = new ContainerSetDataPacket;
|
||
$pk->windowid = $id;
|
||
$pk->property = 1; //Fire icon
|
||
$pk->value = $data->namedtag->BurnTicks;
|
||
$this->dataPacket($pk);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case "tile.container.slot":
|
||
if($data["tile"]->level === $this->level){
|
||
foreach($this->windows as $id => $w){
|
||
if($w === $data["tile"]){
|
||
$pk = new ContainerSetSlotPacket;
|
||
$pk->windowid = $id;
|
||
$pk->slot = $data["slot"] + (isset($data["offset"]) ? $data["offset"]:0);
|
||
$pk->item = $data["slotdata"];
|
||
$this->dataPacket($pk);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case "player.armor":
|
||
if($data["player"]->level === $this->level){
|
||
if($data["eid"] === $this->eid){
|
||
$this->sendArmor($this);
|
||
break;
|
||
}
|
||
$pk = new PlayerArmorEquipmentPacket;
|
||
$pk->eid = $data["eid"];
|
||
$pk->slots = $data["slots"];
|
||
$this->dataPacket($pk);
|
||
}
|
||
break;
|
||
case "player.pickup":
|
||
if($data["eid"] === $this->eid){
|
||
$data["eid"] = 0;
|
||
$pk = new TakeItemEntityPacket;
|
||
$pk->eid = 0;
|
||
$pk->target = $data["entity"]->eid;
|
||
$this->dataPacket($pk);
|
||
if(($this->gamemode & 0x01) === 0x00){
|
||
$this->addItem($data["entity"]->type, $data["entity"]->meta, $data["entity"]->stack, false);
|
||
}
|
||
switch($data["entity"]->type){
|
||
case WOOD:
|
||
AchievementAPI::grantAchievement($this, "mineWood");
|
||
break;
|
||
case DIAMOND:
|
||
AchievementAPI::grantAchievement($this, "diamond");
|
||
break;
|
||
}
|
||
}elseif($data["entity"]->level === $this->level){
|
||
$pk = new TakeItemEntityPacket;
|
||
$pk->eid = $data["eid"];
|
||
$pk->target = $data["entity"]->eid;
|
||
$this->dataPacket($pk);
|
||
}
|
||
break;
|
||
case "player.equipment.change":
|
||
if($data["eid"] === $this->eid or $data["player"]->level !== $this->level){
|
||
break;
|
||
}
|
||
$data["slot"] = 0;
|
||
|
||
$pk = new PlayerEquipmentPacket;
|
||
$pk->eid = $data["eid"];
|
||
$pk->item = $data["item"]->getID();
|
||
$pk->meta = $data["item"]->getMetadata();
|
||
$pk->slot = $data["slot"];
|
||
$this->dataPacket($pk);
|
||
|
||
break;
|
||
case "entity.motion":
|
||
if($data->eid === $this->eid or $data->level !== $this->level){
|
||
break;
|
||
}
|
||
$pk = new SetEntityMotionPacket;
|
||
$pk->eid = $data->eid;
|
||
$pk->speedX = $data->speedX;
|
||
$pk->speedY = $data->speedY;
|
||
$pk->speedZ = $data->speedZ;
|
||
$this->dataPacket($pk);
|
||
break;
|
||
case "entity.animate":
|
||
if($data["eid"] === $this->eid or $data["entity"]->level !== $this->level){
|
||
break;
|
||
}
|
||
$pk = new AnimatePacket;
|
||
$pk->eid = $data["eid"];
|
||
$pk->action = $data["action"]; //1 swing arm,
|
||
$this->dataPacket($pk);
|
||
break;
|
||
case "entity.metadata":
|
||
if($data->eid === $this->eid){
|
||
$eid = 0;
|
||
}else{
|
||
$eid = $data->eid;
|
||
}
|
||
if($data->level === $this->level){
|
||
$pk = new SetEntityDataPacket;
|
||
$pk->eid = $eid;
|
||
$pk->metadata = $data->getMetadata();
|
||
$this->dataPacket($pk);
|
||
}
|
||
break;
|
||
case "entity.event":
|
||
if($data["entity"]->eid === $this->eid){
|
||
$eid = 0;
|
||
}else{
|
||
$eid = $data["entity"]->eid;
|
||
}
|
||
if($data["entity"]->level === $this->level){
|
||
$pk = new EntityEventPacket;
|
||
$pk->eid = $eid;
|
||
$pk->event = $data["event"];
|
||
$this->dataPacket($pk);
|
||
}
|
||
break;
|
||
case "server.chat":
|
||
if(($data instanceof Container) === true){
|
||
if(!$data->check($this->username) and !$data->check($this->iusername)){
|
||
return;
|
||
}else{
|
||
$message = $data->get();
|
||
$this->sendChat($message["message"], $message["player"]);
|
||
}
|
||
}else{
|
||
$this->sendChat((string) $data);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param string $message
|
||
* @param string $author
|
||
*/
|
||
public function sendChat($message, $author = ""){
|
||
$mes = explode("\n", $message);
|
||
foreach($mes as $m){
|
||
if(preg_match_all('#@([@A-Za-z_]{1,})#', $m, $matches, PREG_OFFSET_CAPTURE) > 0){
|
||
$offsetshift = 0;
|
||
foreach($matches[1] as $selector){
|
||
if($selector[0]{0} === "@"){ //Escape!
|
||
$m = substr_replace($m, $selector[0], $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1);
|
||
--$offsetshift;
|
||
continue;
|
||
}
|
||
switch(strtolower($selector[0])){
|
||
case "player":
|
||
case "username":
|
||
$m = substr_replace($m, $this->username, $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1);
|
||
$offsetshift += strlen($selector[0]) - strlen($this->username) + 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if($m !== ""){
|
||
$pk = new MessagePacket;
|
||
$pk->source = ($author instanceof Player) ? $author->username:$author;
|
||
$pk->message = TextFormat::clean($m); //Colors not implemented :(
|
||
$this->dataPacket($pk);
|
||
}
|
||
}
|
||
}
|
||
|
||
public function sendSettings($nametags = true){
|
||
/*
|
||
bit mask | flag name
|
||
0x00000001 world_inmutable
|
||
0x00000002 -
|
||
0x00000004 -
|
||
0x00000008 - (autojump)
|
||
0x00000010 -
|
||
0x00000020 nametags_visible
|
||
0x00000040 ?
|
||
0x00000080 ?
|
||
0x00000100 ?
|
||
0x00000200 ?
|
||
0x00000400 ?
|
||
0x00000800 ?
|
||
0x00001000 ?
|
||
0x00002000 ?
|
||
0x00004000 ?
|
||
0x00008000 ?
|
||
0x00010000 ?
|
||
0x00020000 ?
|
||
0x00040000 ?
|
||
0x00080000 ?
|
||
0x00100000 ?
|
||
0x00200000 ?
|
||
0x00400000 ?
|
||
0x00800000 ?
|
||
0x01000000 ?
|
||
0x02000000 ?
|
||
0x04000000 ?
|
||
0x08000000 ?
|
||
0x10000000 ?
|
||
0x20000000 ?
|
||
0x40000000 ?
|
||
0x80000000 ?
|
||
*/
|
||
$flags = 0;
|
||
if(($this->gamemode & 0x02) === 0x02){
|
||
$flags |= 0x01; //Do not allow placing/breaking blocks, adventure mode
|
||
}
|
||
|
||
if($nametags !== false){
|
||
$flags |= 0x20; //Show Nametags
|
||
}
|
||
|
||
$pk = new AdventureSettingsPacket;
|
||
$pk->flags = $flags;
|
||
$this->dataPacket($pk);
|
||
}
|
||
|
||
/**
|
||
* @param array $craft
|
||
* @param array $recipe
|
||
* @param $type
|
||
*
|
||
* @return array|bool
|
||
*/
|
||
public function craftItems(array $craft, array $recipe, $type){
|
||
$craftItem = array(0, true, 0);
|
||
unset($craft[-1]);
|
||
foreach($craft as $slot => $item){
|
||
if($item instanceof Item){
|
||
$craftItem[0] = $item->getID();
|
||
if($item->getMetadata() !== $craftItem[1] and $craftItem[1] !== 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)){
|
||
|
||
if($this->server->api->dhandle("player.craft", array("player" => $this, "recipe" => $recipe, "craft" => $craft, "type" => $type)) === false){
|
||
return false;
|
||
}
|
||
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), false);
|
||
}
|
||
}
|
||
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), false);
|
||
}else{
|
||
$this->setSlot($slot, BlockAPI::getItem($item->getID(), $item->getMetadata(), $s->count + $item->count), false);
|
||
}
|
||
|
||
switch($item->getID()){
|
||
case WORKBENCH:
|
||
AchievementAPI::grantAchievement($this, "buildWorkBench");
|
||
break;
|
||
case WOODEN_PICKAXE:
|
||
AchievementAPI::grantAchievement($this, "buildPickaxe");
|
||
break;
|
||
case FURNACE:
|
||
AchievementAPI::grantAchievement($this, "buildFurnace");
|
||
break;
|
||
case WOODEN_HOE:
|
||
AchievementAPI::grantAchievement($this, "buildHoe");
|
||
break;
|
||
case BREAD:
|
||
AchievementAPI::grantAchievement($this, "makeBread");
|
||
break;
|
||
case CAKE:
|
||
AchievementAPI::grantAchievement($this, "bakeCake");
|
||
break;
|
||
case STONE_PICKAXE:
|
||
case GOLD_PICKAXE:
|
||
case IRON_PICKAXE:
|
||
case DIAMOND_PICKAXE:
|
||
AchievementAPI::grantAchievement($this, "buildBetterPickaxe");
|
||
break;
|
||
case WOODEN_SWORD:
|
||
AchievementAPI::grantAchievement($this, "buildSword");
|
||
break;
|
||
case DIAMOND:
|
||
AchievementAPI::grantAchievement($this, "diamond");
|
||
break;
|
||
case CAKE:
|
||
$this->addItem(BUCKET, 0, 3, false);
|
||
break;
|
||
|
||
}
|
||
}
|
||
}
|
||
return $res;
|
||
}
|
||
|
||
/**
|
||
* @param Vector3 $pos
|
||
* @param float|boolean $yaw
|
||
* @param float|boolean $pitch
|
||
* @param float|boolean $terrain
|
||
* @param float|boolean $force
|
||
*
|
||
* @return boolean
|
||
*/
|
||
public function teleport(Vector3 $pos, $yaw = false, $pitch = false, $terrain = true, $force = true){
|
||
if($this->level instanceof Level){
|
||
if($yaw === false){
|
||
$yaw = $this->yaw;
|
||
}
|
||
if($pitch === false){
|
||
$pitch = $this->pitch;
|
||
}
|
||
if($this->server->api->dhandle("player.teleport", array("player" => $this, "target" => $pos)) === false){
|
||
return false;
|
||
}
|
||
|
||
/*if($pos instanceof Position and $pos->level instanceof Level and $pos->level !== $this->level){
|
||
if($this->server->api->dhandle("player.teleport.level", array("player" => $this, "origin" => $this->level, "target" => $pos->level)) === false){
|
||
return false;
|
||
}
|
||
|
||
foreach($this->level->getEntities() as $e){
|
||
if($e !== $this->entity){
|
||
if($e->player instanceof Player){
|
||
$pk = new MoveEntityPacket_PosRot;
|
||
$pk->eid = $this->entity->eid;
|
||
$pk->x = -256;
|
||
$pk->y = 128;
|
||
$pk->z = -256;
|
||
$pk->yaw = 0;
|
||
$pk->pitch = 0;
|
||
$e->player->dataPacket($pk);
|
||
|
||
$pk = new MoveEntityPacket_PosRot;
|
||
$pk->eid = $e->eid;
|
||
$pk->x = -256;
|
||
$pk->y = 128;
|
||
$pk->z = -256;
|
||
$pk->yaw = 0;
|
||
$pk->pitch = 0;
|
||
$this->dataPacket($pk);
|
||
}else{
|
||
$pk = new RemoveEntityPacket;
|
||
$pk->eid = $e->eid;
|
||
$this->dataPacket($pk);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
$this->level->freeAllChunks($this);
|
||
$this->level = $pos->level;
|
||
$this->chunksLoaded = array();
|
||
$this->entity->spawnToAll();
|
||
//TODO
|
||
$this->server->api->entity->spawnAll($this);
|
||
|
||
$pk = new SetTimePacket;
|
||
$pk->time = $this->level->getTime();
|
||
$this->dataPacket($pk);
|
||
$terrain = true;
|
||
|
||
foreach($this->level->getPlayers() as $player){
|
||
if($player !== $this and $player->entity instanceof Entity){
|
||
$pk = new MoveEntityPacket_PosRot;
|
||
$pk->eid = $player->entity->eid;
|
||
$pk->x = $player->entity->x;
|
||
$pk->y = $player->entity->y;
|
||
$pk->z = $player->entity->z;
|
||
$pk->yaw = $player->entity->yaw;
|
||
$pk->pitch = $player->entity->pitch;
|
||
$this->dataPacket($pk);
|
||
|
||
$pk = new PlayerEquipmentPacket;
|
||
$pk->eid = $this->eid;
|
||
$pk->item = $this->getSlot($this->slot)->getID();
|
||
$pk->meta = $this->getSlot($this->slot)->getMetadata();
|
||
$pk->slot = 0;
|
||
$player->dataPacket($pk);
|
||
$this->sendArmor($player);
|
||
|
||
$pk = new PlayerEquipmentPacket;
|
||
$pk->eid = $player->eid;
|
||
$pk->item = $player->getSlot($player->slot)->getID();
|
||
$pk->meta = $player->getSlot($player->slot)->getMetadata();
|
||
$pk->slot = 0;
|
||
$this->dataPacket($pk);
|
||
$player->sendArmor($this);
|
||
}
|
||
}
|
||
}*/
|
||
|
||
$this->lastCorrect = $pos;
|
||
/*$this->entity->fallY = false;
|
||
$this->entity->fallStart = false;*/
|
||
$this->setPosition($pos, $yaw, $pitch);
|
||
/*$this->entity->resetSpeed();
|
||
$this->entity->updateLast();
|
||
$this->entity->calculateVelocity();*/
|
||
if($terrain === true){
|
||
$this->orderChunks();
|
||
$this->getNextChunk();
|
||
}
|
||
//$this->entity->check = true;
|
||
if($force === true){
|
||
$this->forceMovement = $pos;
|
||
}
|
||
}
|
||
|
||
$pk = new MovePlayerPacket;
|
||
$pk->eid = 0;
|
||
$pk->x = $pos->x;
|
||
$pk->y = $pos->y;
|
||
$pk->z = $pos->z;
|
||
$pk->bodyYaw = $yaw;
|
||
$pk->pitch = $pitch;
|
||
$pk->yaw = $yaw;
|
||
$this->dataPacket($pk);
|
||
}
|
||
|
||
public function getGamemode(){
|
||
switch($this->gamemode){
|
||
case SURVIVAL:
|
||
return "survival";
|
||
case CREATIVE:
|
||
return "creative";
|
||
case ADVENTURE:
|
||
return "adventure";
|
||
case VIEW:
|
||
return "view";
|
||
}
|
||
}
|
||
|
||
public function setGamemode($gm){
|
||
if($gm < 0 or $gm > 3 or $this->gamemode === $gm){
|
||
return false;
|
||
}
|
||
|
||
if($this->server->api->dhandle("player.gamemode.change", array("player" => $this, "gamemode" => $gm)) === false){
|
||
return false;
|
||
}
|
||
|
||
$inv =& $this->inventory;
|
||
if(($this->gamemode & 0x01) === ($gm & 0x01)){
|
||
if(($gm & 0x01) === 0x01 and ($gm & 0x02) === 0x02){
|
||
$inv = array();
|
||
foreach(BlockAPI::$creative as $item){
|
||
$inv[] = BlockAPI::getItem(DANDELION, 0, 1);
|
||
}
|
||
}elseif(($gm & 0x01) === 0x01){
|
||
$inv = array();
|
||
foreach(BlockAPI::$creative as $item){
|
||
$inv[] = BlockAPI::getItem($item[0], $item[1], 1);
|
||
}
|
||
}
|
||
$this->gamemode = $gm;
|
||
$this->sendChat("Your gamemode has been changed to ".$this->getGamemode().".\n");
|
||
}else{
|
||
foreach($this->inventory as $slot => $item){
|
||
$inv[$slot] = BlockAPI::getItem(AIR, 0, 0);
|
||
}
|
||
$this->blocked = true;
|
||
$this->gamemode = $gm;
|
||
$this->sendChat("Your gamemode has been changed to ".$this->getGamemode().", you've to do a forced reconnect.\n");
|
||
$this->server->schedule(30, array($this, "close"), "gamemode change"); //Forces a kick
|
||
}
|
||
$this->inventory = $inv;
|
||
$this->sendSettings();
|
||
$this->sendInventory();
|
||
return true;
|
||
}
|
||
|
||
public function measureLag(){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
if($this->packetStats[1] > 2){
|
||
$this->packetLoss = $this->packetStats[1] / max(1, $this->packetStats[0] + $this->packetStats[1]);
|
||
}else{
|
||
$this->packetLoss = 0;
|
||
}
|
||
$this->packetStats = array(0, 0);
|
||
array_shift($this->bandwidthStats);
|
||
$this->bandwidthStats[] = $this->bandwidthRaw / max(0.00001, microtime(true) - $this->lastMeasure);
|
||
$this->bandwidthRaw = 0;
|
||
$this->lagStat = array_sum($this->lag) / max(1, count($this->lag));
|
||
$this->lag = array();
|
||
$this->sendBuffer();
|
||
$this->lastMeasure = microtime(true);
|
||
}
|
||
|
||
public function getLag(){
|
||
return $this->lagStat * 1000;
|
||
}
|
||
|
||
public function getPacketLoss(){
|
||
return $this->packetLoss;
|
||
}
|
||
|
||
public function getBandwidth(){
|
||
return array_sum($this->bandwidthStats) / max(1, count($this->bandwidthStats));
|
||
}
|
||
|
||
public function clearQueue(){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
ksort($this->received);
|
||
if(($cnt = count($this->received)) > PLAYER_MAX_QUEUE){
|
||
foreach($this->received as $c => $t){
|
||
unset($this->received[$c]);
|
||
--$cnt;
|
||
if($cnt <= PLAYER_MAX_QUEUE){
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public function handlePacketQueues(){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
$time = microtime(true);
|
||
if($time > $this->timeout){
|
||
$this->close("timeout");
|
||
return false;
|
||
}
|
||
|
||
if(($ackCnt = count($this->ackQueue)) > 0){
|
||
rsort($this->ackQueue);
|
||
$safeCount = (int) (($this->MTU - 1) / 4);
|
||
$packetCnt = (int) ($ackCnt / $safeCount + 1);
|
||
for($p = 0; $p < $packetCnt; ++$p){
|
||
$pk = new RakNetPacket(RakNetInfo::ACK);
|
||
$pk->packets = array();
|
||
for($c = 0; $c < $safeCount; ++$c){
|
||
if(($k = array_pop($this->ackQueue)) === null){
|
||
break;
|
||
}
|
||
$pk->packets[] = $k;
|
||
}
|
||
$this->send($pk);
|
||
}
|
||
$this->ackQueue = array();
|
||
}
|
||
|
||
if(($receiveCnt = count($this->receiveQueue)) > 0){
|
||
ksort($this->receiveQueue);
|
||
foreach($this->receiveQueue as $count => $packets){
|
||
unset($this->receiveQueue[$count]);
|
||
foreach($packets as $p){
|
||
if($p instanceof RakNetDataPacket and $p->hasSplit === false){
|
||
if(isset($p->messageIndex) and $p->messageIndex !== false){
|
||
if($p->messageIndex > $this->receiveCount){
|
||
$this->receiveCount = $p->messageIndex;
|
||
}elseif($p->messageIndex !== 0){
|
||
if(isset($this->received[$p->messageIndex])){
|
||
continue;
|
||
}
|
||
switch($p->pid()){
|
||
case 0x01:
|
||
case ProtocolInfo::PING_PACKET:
|
||
case ProtocolInfo::PONG_PACKET:
|
||
case ProtocolInfo::MOVE_PLAYER_PACKET:
|
||
case ProtocolInfo::REQUEST_CHUNK_PACKET:
|
||
case ProtocolInfo::ANIMATE_PACKET:
|
||
case ProtocolInfo::SET_HEALTH_PACKET:
|
||
continue;
|
||
}
|
||
}
|
||
$this->received[$p->messageIndex] = true;
|
||
}
|
||
$p->decode();
|
||
$this->handleDataPacket($p);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if($this->nextBuffer <= $time and $this->bufferLen > 0){
|
||
$this->sendBuffer();
|
||
}
|
||
|
||
$limit = $time - 5; //max lag
|
||
foreach($this->recoveryQueue as $count => $data){
|
||
if($data->sendtime > $limit){
|
||
break;
|
||
}
|
||
unset($this->recoveryQueue[$count]);
|
||
$this->resendQueue[$count] = $data;
|
||
}
|
||
|
||
if(($resendCnt = count($this->resendQueue)) > 0){
|
||
foreach($this->resendQueue as $count => $data){
|
||
unset($this->resendQueue[$count]);
|
||
$this->packetStats[1]++;
|
||
$this->lag[] = microtime(true) - $data->sendtime;
|
||
$data->sendtime = microtime(true);
|
||
$cnt = $this->send($data);
|
||
if(isset($this->chunkCount[$count])){
|
||
unset($this->chunkCount[$count]);
|
||
$this->chunkCount[$cnt[0]] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public function handlePacket(RakNetPacket $packet){
|
||
if($this->connected === true){
|
||
$this->timeout = microtime(true) + 20;
|
||
switch($packet->pid()){
|
||
case RakNetInfo::NACK:
|
||
foreach($packet->packets as $count){
|
||
if(isset($this->recoveryQueue[$count])){
|
||
$this->resendQueue[$count] =& $this->recoveryQueue[$count];
|
||
$this->lag[] = microtime(true) - $this->recoveryQueue[$count]->sendtime;
|
||
unset($this->recoveryQueue[$count]);
|
||
}
|
||
++$this->packetStats[1];
|
||
}
|
||
break;
|
||
|
||
case RakNetInfo::ACK:
|
||
foreach($packet->packets as $count){
|
||
if(isset($this->recoveryQueue[$count])){
|
||
$this->lag[] = microtime(true) - $this->recoveryQueue[$count]->sendtime;
|
||
unset($this->recoveryQueue[$count]);
|
||
unset($this->resendQueue[$count]);
|
||
}
|
||
++$this->packetStats[0];
|
||
}
|
||
break;
|
||
|
||
case RakNetInfo::DATA_PACKET_0:
|
||
case RakNetInfo::DATA_PACKET_1:
|
||
case RakNetInfo::DATA_PACKET_2:
|
||
case RakNetInfo::DATA_PACKET_3:
|
||
case RakNetInfo::DATA_PACKET_4:
|
||
case RakNetInfo::DATA_PACKET_5:
|
||
case RakNetInfo::DATA_PACKET_6:
|
||
case RakNetInfo::DATA_PACKET_7:
|
||
case RakNetInfo::DATA_PACKET_8:
|
||
case RakNetInfo::DATA_PACKET_9:
|
||
case RakNetInfo::DATA_PACKET_A:
|
||
case RakNetInfo::DATA_PACKET_B:
|
||
case RakNetInfo::DATA_PACKET_C:
|
||
case RakNetInfo::DATA_PACKET_D:
|
||
case RakNetInfo::DATA_PACKET_E:
|
||
case RakNetInfo::DATA_PACKET_F:
|
||
$this->ackQueue[] = $packet->seqNumber;
|
||
$this->receiveQueue[$packet->seqNumber] = array();
|
||
foreach($packet->data as $pk){
|
||
$this->receiveQueue[$packet->seqNumber][] = $pk;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
public function handleDataPacket(RakNetDataPacket $packet){
|
||
if($this->connected === false){
|
||
return;
|
||
}
|
||
|
||
if(EventHandler::callEvent(new DataPacketReceiveEvent($this, $packet)) === BaseEvent::DENY){
|
||
return;
|
||
}
|
||
|
||
switch($packet->pid()){
|
||
case 0x01:
|
||
break;
|
||
case ProtocolInfo::PONG_PACKET:
|
||
break;
|
||
case ProtocolInfo::PING_PACKET:
|
||
$pk = new PongPacket;
|
||
$pk->ptime = $packet->time;
|
||
$pk->time = abs(microtime(true) * 1000);
|
||
$this->directDataPacket($pk);
|
||
break;
|
||
case ProtocolInfo::DISCONNECT_PACKET:
|
||
$this->close("client disconnect");
|
||
break;
|
||
case ProtocolInfo::CLIENT_CONNECT_PACKET:
|
||
if($this->loggedIn === true){
|
||
break;
|
||
}
|
||
$pk = new ServerHandshakePacket;
|
||
$pk->port = $this->port;
|
||
$pk->session = $packet->session;
|
||
$pk->session2 = Utils::readLong("\x00\x00\x00\x00\x04\x44\x0b\xa9");
|
||
$this->dataPacket($pk);
|
||
break;
|
||
case ProtocolInfo::CLIENT_HANDSHAKE_PACKET:
|
||
if($this->loggedIn === true){
|
||
break;
|
||
}
|
||
break;
|
||
case ProtocolInfo::LOGIN_PACKET:
|
||
if($this->loggedIn === true){
|
||
break;
|
||
}
|
||
$this->username = $packet->username;
|
||
$this->iusername = strtolower($this->username);
|
||
$this->loginData = array("clientId" => $packet->clientId, "loginData" => $packet->loginData);
|
||
if(count($this->server->clients) > $this->server->maxClients and !$this->server->api->ban->isOp($this->iusername)){
|
||
$this->close("server is full!", false);
|
||
return;
|
||
}
|
||
if($packet->protocol1 !== ProtocolInfo::CURRENT_PROTOCOL){
|
||
if($packet->protocol1 < ProtocolInfo::CURRENT_PROTOCOL){
|
||
$pk = new LoginStatusPacket;
|
||
$pk->status = 1;
|
||
$this->directDataPacket($pk);
|
||
}else{
|
||
$pk = new LoginStatusPacket;
|
||
$pk->status = 2;
|
||
$this->directDataPacket($pk);
|
||
}
|
||
$this->close("Incorrect protocol #".$packet->protocol1, false);
|
||
return;
|
||
}
|
||
if(preg_match('#^[a-zA-Z0-9_]{3,16}$#', $this->username) == 0 or $this->username === "" or $this->iusername === "rcon" or $this->iusername === "console"){
|
||
$this->close("Bad username", false);
|
||
return;
|
||
}
|
||
if($this->server->api->handle("player.connect", $this) === false){
|
||
$this->close("Unknown reason", false);
|
||
return;
|
||
}
|
||
|
||
if($this->server->whitelist === true and !$this->server->api->ban->inWhitelist($this->iusername)){
|
||
$this->close("Server is white-listed", false);
|
||
return;
|
||
}elseif($this->server->api->ban->isBanned($this->iusername) or $this->server->api->ban->isIPBanned($this->ip)){
|
||
$this->close("You are banned!", false);
|
||
return;
|
||
}
|
||
$this->loggedIn = true;
|
||
|
||
$u = $this->server->api->player->get($this->iusername, false);
|
||
if($u !== false){
|
||
$u->close("logged in from another location");
|
||
}
|
||
if(!isset($this->CID) or $this->CID == null){
|
||
console("[DEBUG] Player ".$this->username." does not have a CID", true, true, 2);
|
||
$this->CID = Utils::readLong(Utils::getRandomBytes(8, false));
|
||
}
|
||
|
||
$this->server->api->player->add($this->CID);
|
||
if($this->server->api->handle("player.join", $this) === false){
|
||
$this->close("join cancelled", false);
|
||
return;
|
||
}
|
||
|
||
if(!($this->data instanceof Config)){
|
||
$this->close("no config created", false);
|
||
return;
|
||
}
|
||
|
||
$this->auth = true;
|
||
if(!$this->data->exists("inventory") or ($this->gamemode & 0x01) === 0x01){
|
||
if(($this->gamemode & 0x01) === 0x01){
|
||
$inv = array();
|
||
if(($this->gamemode & 0x02) === 0x02){
|
||
foreach(BlockAPI::$creative as $item){
|
||
$inv[] = array(DANDELION, 0, 1);
|
||
}
|
||
}else{
|
||
foreach(BlockAPI::$creative as $item){
|
||
$inv[] = array($item[0], $item[1], 1);
|
||
}
|
||
}
|
||
$this->data->set("inventory", $inv);
|
||
}
|
||
}
|
||
$this->achievements = $this->data->get("achievements");
|
||
$this->data->set("caseusername", $this->username);
|
||
$this->inventory = array();
|
||
foreach($this->data->get("inventory") as $slot => $item){
|
||
if(!is_array($item) or count($item) < 3){
|
||
$item = array(AIR, 0, 0);
|
||
}
|
||
$this->inventory[$slot] = BlockAPI::getItem($item[0], $item[1], $item[2]);
|
||
}
|
||
|
||
$this->armor = array();
|
||
foreach($this->data->get("armor") as $slot => $item){
|
||
$this->armor[$slot] = BlockAPI::getItem($item[0], $item[1], $item[0] === 0 ? 0:1);
|
||
}
|
||
|
||
$this->data->set("lastIP", $this->ip);
|
||
$this->data->set("lastID", $this->clientID);
|
||
|
||
$this->server->api->player->saveOffline($this->data);
|
||
|
||
|
||
$pk = new LoginStatusPacket;
|
||
$pk->status = 0;
|
||
$this->dataPacket($pk);
|
||
|
||
$pk = new StartGamePacket;
|
||
$pk->seed = $this->level->getSeed();
|
||
$pk->x = $this->data->get("position")["x"];
|
||
$pk->y = $this->data->get("position")["y"];
|
||
$pk->z = $this->data->get("position")["z"];
|
||
$pk->generator = 0;
|
||
$pk->gamemode = $this->gamemode & 0x01;
|
||
$pk->eid = 0;
|
||
$this->dataPacket($pk);
|
||
|
||
if(($this->gamemode & 0x01) === 0x01){
|
||
$this->slot = 0;
|
||
$this->hotbar = array();
|
||
}elseif($this->data->exists("hotbar")){
|
||
$this->hotbar = $this->data->get("hotbar");
|
||
$this->slot = $this->hotbar[0];
|
||
}else{
|
||
$this->slot = -1;//0
|
||
$this->hotbar = array(-1, -1, -1, -1, -1, -1, -1, -1, -1);
|
||
}
|
||
|
||
parent::__construct($this->level, new NBTTag_Compound(false, array(
|
||
"Pos" => new NBTTag_List("Pos", array(
|
||
0 => new NBTTag_Double(0, $this->data->get("position")["x"]),
|
||
1 => new NBTTag_Double(1, $this->data->get("position")["y"]),
|
||
2 => new NBTTag_Double(2, $this->data->get("position")["z"])
|
||
)),
|
||
"NameTag" => new NBTTag_String("NameTag", $this->username),
|
||
)));
|
||
$this->namedtag->Pos->setTagType(NBTTag::TAG_Double);
|
||
if(($level = $this->server->api->level->get($this->data->get("spawn")["level"])) !== false){
|
||
$this->spawnPosition = new Position($this->data->get("spawn")["x"], $this->data->get("spawn")["y"], $this->data->get("spawn")["z"], $level);
|
||
|
||
$pk = new SetSpawnPositionPacket;
|
||
$pk->x = (int) $this->spawnPosition->x;
|
||
$pk->y = (int) $this->spawnPosition->y;
|
||
$pk->z = (int) $this->spawnPosition->z;
|
||
$this->dataPacket($pk);
|
||
}
|
||
|
||
$this->evid[] = $this->server->event("server.chat", array($this, "eventHandler"));
|
||
$this->evid[] = $this->server->event("entity.motion", array($this, "eventHandler"));
|
||
$this->evid[] = $this->server->event("entity.animate", array($this, "eventHandler"));
|
||
$this->evid[] = $this->server->event("entity.event", array($this, "eventHandler"));
|
||
$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.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"));
|
||
$this->lastMeasure = microtime(true);
|
||
$this->server->schedule(50, array($this, "measureLag"), array(), true);
|
||
console("[INFO] ".TextFormat::AQUA.$this->username.TextFormat::RESET."[/".$this->ip.":".$this->port."] logged in with entity id ".$this->id." at (".$this->level->getName().", ".round($this->x, 4).", ".round($this->y, 4).", ".round($this->z, 4).")");
|
||
break;
|
||
case ProtocolInfo::READY_PACKET:
|
||
if($this->loggedIn === false){
|
||
break;
|
||
}
|
||
switch($packet->status){
|
||
case 1: //Spawn!!
|
||
if($this->spawned !== false){
|
||
break;
|
||
}
|
||
$this->heal($this->data->get("health"), "spawn", true);
|
||
$this->spawned = true;
|
||
//TODO
|
||
//$this->server->api->player->spawnAllPlayers($this);
|
||
//TODO
|
||
//$this->server->api->player->spawnToAllPlayers($this);
|
||
//TODO
|
||
//$this->server->api->entity->spawnAll($this);
|
||
$this->spawnToAll();
|
||
//$this->sendArmor();
|
||
$this->sendChat($this->server->motd."\n");
|
||
|
||
if($this->iusername === "steve" or $this->iusername === "stevie"){
|
||
$this->sendChat("You're using the default username. Please change it on the Minecraft PE settings.\n");
|
||
}
|
||
$this->sendInventory();
|
||
$this->sendSettings();
|
||
$this->server->schedule(30, array($this, "orderChunks"), array(), true);
|
||
$this->blocked = false;
|
||
|
||
$pk = new SetTimePacket;
|
||
$pk->time = $this->level->getTime();
|
||
$this->dataPacket($pk);
|
||
|
||
$pos = new Position($this->x, $this->y, $this->z, $this->level);
|
||
$pos = $this->level->getSafeSpawn($pos);
|
||
$this->teleport($pos);
|
||
$this->server->handle("player.spawn", $this);
|
||
break;
|
||
case 2://Chunk loaded?
|
||
break;
|
||
}
|
||
break;
|
||
/*case ProtocolInfo::ROTATE_HEAD_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
if(($this->entity instanceof Entity)){
|
||
if($this->blocked === true or $this->server->api->handle("player.move", $this->entity) === false){
|
||
if($this->lastCorrect instanceof Vector3){
|
||
$this->teleport($this->lastCorrect, $this->entity->yaw, $this->entity->pitch, false);
|
||
}
|
||
}else{
|
||
$this->entity->setPosition($this->entity, $packet->yaw, $this->entity->pitch);
|
||
}
|
||
}
|
||
break;*/
|
||
case ProtocolInfo::MOVE_PLAYER_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
if($packet->messageIndex > $this->lastMovement){
|
||
$this->lastMovement = $packet->messageIndex;
|
||
$newPos = new Vector3($packet->x, $packet->y, $packet->z);
|
||
/*if($this->forceMovement instanceof Vector3){
|
||
if($this->forceMovement->distance($newPos) <= 0.7){
|
||
$this->forceMovement = false;
|
||
}else{
|
||
$this->teleport($this->forceMovement, $this->entity->yaw, $this->entity->pitch, false);
|
||
}
|
||
}*/
|
||
/*$speed = $this->entity->getSpeedMeasure();
|
||
if($this->blocked === true or ($this->server->api->getProperty("allow-flight") !== true and (($speed > 9 and ($this->gamemode & 0x01) === 0x00) or $speed > 20 or $this->entity->distance($newPos) > 7)) or $this->server->api->handle("player.move", $this->entity) === false){
|
||
if($this->lastCorrect instanceof Vector3){
|
||
$this->teleport($this->lastCorrect, $this->entity->yaw, $this->entity->pitch, false);
|
||
}
|
||
if($this->blocked !== true){
|
||
console("[WARNING] ".$this->username." moved too quickly!");
|
||
}
|
||
}else{*/
|
||
$this->setPosition($newPos, $packet->yaw, $packet->pitch);
|
||
//}
|
||
}
|
||
break;
|
||
/*case ProtocolInfo::PLAYER_EQUIPMENT_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
$packet->eid = $this->id;
|
||
|
||
$data = array();
|
||
$data["eid"] = $packet->eid;
|
||
$data["player"] = $this;
|
||
|
||
if($packet->slot === 0x28 or $packet->slot === 0){ //0 for 0.8.0 compatibility
|
||
$data["slot"] = -1;
|
||
$data["item"] = BlockAPI::getItem(AIR, 0, 0);
|
||
if($this->server->handle("player.equipment.change", $data) !== false){
|
||
$this->slot = -1;
|
||
}
|
||
break;
|
||
}else{
|
||
$packet->slot -= 9;
|
||
}
|
||
|
||
|
||
if(($this->gamemode & 0x01) === SURVIVAL){
|
||
$data["item"] = $this->getSlot($packet->slot);
|
||
if(!($data["item"] instanceof Item)){
|
||
break;
|
||
}
|
||
}elseif(($this->gamemode & 0x01) === CREATIVE){
|
||
$packet->slot = false;
|
||
foreach(BlockAPI::$creative as $i => $d){
|
||
if($d[0] === $packet->item and $d[1] === $packet->meta){
|
||
$packet->slot = $i;
|
||
}
|
||
}
|
||
if($packet->slot !== false){
|
||
$data["item"] = $this->getSlot($packet->slot);
|
||
}else{
|
||
break;
|
||
}
|
||
}else{
|
||
break;//?????
|
||
}
|
||
|
||
$data["slot"] = $packet->slot;
|
||
|
||
if($this->server->handle("player.equipment.change", $data) !== false){
|
||
$this->slot = $packet->slot;
|
||
if(($this->gamemode & 0x01) === SURVIVAL){
|
||
if(!in_array($this->slot, $this->hotbar)){
|
||
array_pop($this->hotbar);
|
||
array_unshift($this->hotbar, $this->slot);
|
||
}
|
||
}
|
||
}else{
|
||
//$this->sendInventorySlot($packet->slot);
|
||
$this->sendInventory();
|
||
}
|
||
if($this->entity->inAction === true){
|
||
$this->entity->inAction = false;
|
||
$this->entity->updateMetadata();
|
||
}
|
||
break;*/
|
||
case ProtocolInfo::REQUEST_CHUNK_PACKET:
|
||
break;
|
||
/*case ProtocolInfo::USE_ITEM_PACKET:
|
||
if(!($this->entity instanceof Entity)){
|
||
break;
|
||
}
|
||
|
||
$blockVector = new Vector3($packet->x, $packet->y, $packet->z);
|
||
|
||
if(($this->spawned === false or $this->blocked === true) and $packet->face >= 0 and $packet->face <= 5){
|
||
$target = $this->level->getBlock($blockVector);
|
||
$block = $target->getSide($packet->face);
|
||
|
||
$pk = new UpdateBlockPacket;
|
||
$pk->x = $target->x;
|
||
$pk->y = $target->y;
|
||
$pk->z = $target->z;
|
||
$pk->block = $target->getID();
|
||
$pk->meta = $target->getMetadata();
|
||
$this->dataPacket($pk);
|
||
|
||
$pk = new UpdateBlockPacket;
|
||
$pk->x = $block->x;
|
||
$pk->y = $block->y;
|
||
$pk->z = $block->z;
|
||
$pk->block = $block->getID();
|
||
$pk->meta = $block->getMetadata();
|
||
$this->dataPacket($pk);
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$packet->eid = $this->eid;
|
||
$data = array();
|
||
$data["eid"] = $packet->eid;
|
||
$data["player"] = $this;
|
||
$data["face"] = $packet->face;
|
||
$data["x"] = $packet->x;
|
||
$data["y"] = $packet->y;
|
||
$data["z"] = $packet->z;
|
||
$data["item"] = $packet->item;
|
||
$data["meta"] = $packet->meta;
|
||
$data["fx"] = $packet->fx;
|
||
$data["fy"] = $packet->fy;
|
||
$data["fz"] = $packet->fz;
|
||
$data["posX"] = $packet->posX;
|
||
$data["posY"] = $packet->posY;
|
||
$data["posZ"] = $packet->posZ;
|
||
|
||
if($packet->face >= 0 and $packet->face <= 5){ //Use Block, place
|
||
if($this->entity->inAction === true){
|
||
$this->entity->inAction = false;
|
||
$this->entity->updateMetadata();
|
||
}
|
||
|
||
if($this->blocked === true or ($this->entity instanceof Entity and $blockVector->distance($this->entity) > 10)){
|
||
|
||
}elseif($this->getSlot($this->slot)->getID() !== $packet->item or ($this->getSlot($this->slot)->isTool() === false and $this->getSlot($this->slot)->getMetadata() !== $packet->meta)){
|
||
$this->sendInventorySlot($this->slot);
|
||
}else{
|
||
$this->server->api->block->playerBlockAction($this, $blockVector, $packet->face, $packet->fx, $packet->fy, $packet->fz);
|
||
break;
|
||
}
|
||
$target = $this->level->getBlock($blockVector);
|
||
$block = $target->getSide($packet->face);
|
||
|
||
$pk = new UpdateBlockPacket;
|
||
$pk->x = $target->x;
|
||
$pk->y = $target->y;
|
||
$pk->z = $target->z;
|
||
$pk->block = $target->getID();
|
||
$pk->meta = $target->getMetadata();
|
||
$this->dataPacket($pk);
|
||
|
||
$pk = new UpdateBlockPacket;
|
||
$pk->x = $block->x;
|
||
$pk->y = $block->y;
|
||
$pk->z = $block->z;
|
||
$pk->block = $block->getID();
|
||
$pk->meta = $block->getMetadata();
|
||
$this->dataPacket($pk);
|
||
break;
|
||
}elseif($packet->face === 0xff and $this->server->handle("player.action", $data) !== false){
|
||
$this->entity->inAction = true;
|
||
$this->startAction = microtime(true);
|
||
$this->entity->updateMetadata();
|
||
}
|
||
break;*/
|
||
/*case ProtocolInfo::PLAYER_ACTION_PACKET:
|
||
if($this->spawned === false or $this->blocked === true){
|
||
break;
|
||
}
|
||
$packet->eid = $this->eid;
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
|
||
switch($packet->action){
|
||
case 5: //Shot arrow
|
||
if($this->entity->inAction === true){
|
||
if($this->getSlot($this->slot)->getID() === BOW){
|
||
if($this->startAction !== false){
|
||
$time = microtime(true) - $this->startAction;
|
||
$d = array(
|
||
"x" => $this->entity->x,
|
||
"y" => $this->entity->y + 1.6,
|
||
"z" => $this->entity->z,
|
||
);
|
||
$e = $this->server->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_ARROW, $d);
|
||
$e->yaw = $this->entity->yaw;
|
||
$e->pitch = $this->entity->pitch;
|
||
$rotation = ($this->entity->yaw - 90) % 360;
|
||
if($rotation < 0){
|
||
$rotation = (360 + $rotation);
|
||
}
|
||
$rotation = ($rotation + 180);
|
||
if($rotation >= 360){
|
||
$rotation = ($rotation - 360);
|
||
}
|
||
$X = 1;
|
||
$Z = 1;
|
||
$overturn = false;
|
||
if(0 <= $rotation and $rotation < 90){
|
||
|
||
}elseif(90 <= $rotation and $rotation < 180){
|
||
$rotation -= 90;
|
||
$X = (-1);
|
||
$overturn = true;
|
||
}elseif(180 <= $rotation and $rotation < 270){
|
||
$rotation -= 180;
|
||
$X = (-1);
|
||
$Z = (-1);
|
||
}elseif(270 <= $rotation and $rotation < 360){
|
||
$rotation -= 270;
|
||
$Z = (-1);
|
||
$overturn = true;
|
||
}
|
||
$rad = deg2rad($rotation);
|
||
$pitch = (-($this->entity->pitch));
|
||
$speed = 80;
|
||
$speedY = (sin(deg2rad($pitch)) * $speed);
|
||
$speedXZ = (cos(deg2rad($pitch)) * $speed);
|
||
if($overturn){
|
||
$speedX = (sin($rad) * $speedXZ * $X);
|
||
$speedZ = (cos($rad) * $speedXZ * $Z);
|
||
}
|
||
else{
|
||
$speedX = (cos($rad) * $speedXZ * $X);
|
||
$speedZ = (sin($rad) * $speedXZ * $Z);
|
||
}
|
||
$e->speedX = $speedX;
|
||
$e->speedZ = $speedZ;
|
||
$e->speedY = $speedY;
|
||
$e->spawnToAll();
|
||
}
|
||
}
|
||
}
|
||
$this->startAction = false;
|
||
$this->entity->inAction = false;
|
||
$this->entity->updateMetadata();
|
||
break;
|
||
case 6: //get out of the bed
|
||
$this->stopSleep();
|
||
}
|
||
break;*/
|
||
/*case ProtocolInfo::REMOVE_BLOCK_PACKET:
|
||
$blockVector = new Vector3($packet->x, $packet->y, $packet->z);
|
||
if($this->spawned === false or $this->blocked === true or $this->entity->distance($blockVector) > 8){
|
||
$target = $this->level->getBlock($blockVector);
|
||
|
||
$pk = new UpdateBlockPacket;
|
||
$pk->x = $target->x;
|
||
$pk->y = $target->y;
|
||
$pk->z = $target->z;
|
||
$pk->block = $target->getID();
|
||
$pk->meta = $target->getMetadata();
|
||
$this->dataPacket($pk);
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$this->server->api->block->playerBlockBreak($this, $blockVector);
|
||
break;*/
|
||
/*case ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET:
|
||
if($this->spawned === false or $this->blocked === true){
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
|
||
$packet->eid = $this->eid;
|
||
for($i = 0; $i < 4; ++$i){
|
||
$s = $packet->slots[$i];
|
||
if($s === 0 or $s === 255){
|
||
$s = BlockAPI::getItem(AIR, 0, 0);
|
||
}else{
|
||
$s = BlockAPI::getItem($s + 256, 0, 1);
|
||
}
|
||
$slot = $this->armor[$i];
|
||
if($slot->getID() !== AIR and $s->getID() === AIR){
|
||
$this->addItem($slot->getID(), $slot->getMetadata(), 1, false);
|
||
$this->armor[$i] = BlockAPI::getItem(AIR, 0, 0);
|
||
$packet->slots[$i] = 255;
|
||
}elseif($s->getID() !== AIR and $slot->getID() === AIR and ($sl = $this->hasItem($s->getID())) !== false){
|
||
$this->armor[$i] = $this->getSlot($sl);
|
||
$this->setSlot($sl, BlockAPI::getItem(AIR, 0, 0), false);
|
||
}elseif($s->getID() !== AIR and $slot->getID() !== AIR and ($slot->getID() !== $s->getID() or $slot->getMetadata() !== $s->getMetadata()) and ($sl = $this->hasItem($s->getID())) !== false){
|
||
$item = $this->armor[$i];
|
||
$this->armor[$i] = $this->getSlot($sl);
|
||
$this->setSlot($sl, $item, false);
|
||
}else{
|
||
$packet->slots[$i] = 255;
|
||
}
|
||
|
||
}
|
||
$this->sendArmor();
|
||
if($this->entity->inAction === true){
|
||
$this->entity->inAction = false;
|
||
$this->entity->updateMetadata();
|
||
}
|
||
break;*/
|
||
/*case ProtocolInfo::INTERACT_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
$packet->eid = $this->eid;
|
||
$data = array();
|
||
$data["target"] = $packet->target;
|
||
$data["eid"] = $packet->eid;
|
||
$data["action"] = $packet->action;
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$target = Entity::get($packet->target);
|
||
if($target instanceof Entity and $this->entity instanceof Entity and $this->gamemode !== VIEW and $this->blocked === false and ($target instanceof Entity) and $this->entity->distance($target) <= 8){
|
||
$data["targetentity"] = $target;
|
||
$data["entity"] = $this->entity;
|
||
if($target instanceof PlayerEntity 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){
|
||
$slot = $this->getSlot($this->slot);
|
||
switch($slot->getID()){
|
||
case WOODEN_SWORD:
|
||
case GOLD_SWORD:
|
||
$damage = 4;
|
||
break;
|
||
case STONE_SWORD:
|
||
$damage = 5;
|
||
break;
|
||
case IRON_SWORD:
|
||
$damage = 6;
|
||
break;
|
||
case DIAMOND_SWORD:
|
||
$damage = 7;
|
||
break;
|
||
|
||
case WOODEN_AXE:
|
||
case GOLD_AXE:
|
||
$damage = 3;
|
||
break;
|
||
case STONE_AXE:
|
||
$damage = 4;
|
||
break;
|
||
case IRON_AXE:
|
||
$damage = 5;
|
||
break;
|
||
case DIAMOND_AXE:
|
||
$damage = 6;
|
||
break;
|
||
|
||
case WOODEN_PICKAXE:
|
||
case GOLD_PICKAXE:
|
||
$damage = 2;
|
||
break;
|
||
case STONE_PICKAXE:
|
||
$damage = 3;
|
||
break;
|
||
case IRON_PICKAXE:
|
||
$damage = 4;
|
||
break;
|
||
case DIAMOND_PICKAXE:
|
||
$damage = 5;
|
||
break;
|
||
|
||
case WOODEN_SHOVEL:
|
||
case GOLD_SHOVEL:
|
||
$damage = 1;
|
||
break;
|
||
case STONE_SHOVEL:
|
||
$damage = 2;
|
||
break;
|
||
case IRON_SHOVEL:
|
||
$damage = 3;
|
||
break;
|
||
case DIAMOND_SHOVEL:
|
||
$damage = 4;
|
||
break;
|
||
|
||
default:
|
||
$damage = 1;//$this->server->difficulty;
|
||
}
|
||
$target->harm($damage, $this->eid);
|
||
if($slot->isTool() === true and ($this->gamemode & 0x01) === 0){
|
||
if($slot->useOn($target) and $slot->getMetadata() >= $slot->getMaxDurability()){
|
||
$this->setSlot($this->slot, new Item(AIR, 0, 0), false);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
break;*/
|
||
/*case ProtocolInfo::ANIMATE_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
$packet->eid = $this->eid;
|
||
$this->server->api->dhandle("entity.animate", array("eid" => $packet->eid, "entity" => $this->entity, "action" => $packet->action));
|
||
break;*/
|
||
case ProtocolInfo::RESPAWN_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
if(@$this->entity->dead === false){
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$this->teleport($this->spawnPosition);
|
||
if($this->entity instanceof Entity){
|
||
$this->entity->fire = 0;
|
||
$this->entity->air = 300;
|
||
$this->entity->setHealth(20, "respawn", true);
|
||
$this->entity->updateMetadata();
|
||
}else{
|
||
break;
|
||
}
|
||
$this->sendInventory();
|
||
$this->blocked = false;
|
||
$this->server->handle("player.respawn", $this);
|
||
break;
|
||
case ProtocolInfo::SET_HEALTH_PACKET: //Not used
|
||
break;
|
||
/*case ProtocolInfo::ENTITY_EVENT_PACKET:
|
||
if($this->spawned === false or $this->blocked === true){
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$packet->eid = $this->eid;
|
||
if($this->entity->inAction === true){
|
||
$this->entity->inAction = false;
|
||
$this->entity->updateMetadata();
|
||
}
|
||
switch($packet->event){
|
||
case 9: //Eating
|
||
$items = array(
|
||
APPLE => 4,
|
||
MUSHROOM_STEW => 10,
|
||
BEETROOT_SOUP => 10,
|
||
BREAD => 5,
|
||
RAW_PORKCHOP => 3,
|
||
COOKED_PORKCHOP => 8,
|
||
RAW_BEEF => 3,
|
||
STEAK => 8,
|
||
COOKED_CHICKEN => 6,
|
||
RAW_CHICKEN => 2,
|
||
MELON_SLICE => 2,
|
||
GOLDEN_APPLE => 10,
|
||
PUMPKIN_PIE => 8,
|
||
CARROT => 4,
|
||
POTATO => 1,
|
||
BAKED_POTATO => 6,
|
||
//COOKIE => 2,
|
||
//COOKED_FISH => 5,
|
||
//RAW_FISH => 2,
|
||
);
|
||
$slot = $this->getSlot($this->slot);
|
||
if($this->entity->getHealth() < 20 and isset($items[$slot->getID()])){
|
||
|
||
$pk = new EntityEventPacket;
|
||
$pk->eid = 0;
|
||
$pk->event = 9;
|
||
$this->dataPacket($pk);
|
||
|
||
$this->entity->heal($items[$slot->getID()], "eating");
|
||
--$slot->count;
|
||
if($slot->count <= 0){
|
||
$this->setSlot($this->slot, BlockAPI::getItem(AIR, 0, 0), false);
|
||
}
|
||
if($slot->getID() === MUSHROOM_STEW or $slot->getID() === BEETROOT_SOUP){
|
||
$this->addItem(BOWL, 0, 1, false);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
break;*/
|
||
/*case ProtocolInfo::DROP_ITEM_PACKET:
|
||
if($this->spawned === false or $this->blocked === true){
|
||
break;
|
||
}
|
||
$packet->eid = $this->eid;
|
||
$packet->item = $this->getSlot($this->slot);
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$data = array();
|
||
$data["eid"] = $packet->eid;
|
||
$data["unknown"] = $packet->unknown;
|
||
$data["item"] = $packet->item;
|
||
$data["player"] = $this;
|
||
if($this->blocked === false and $this->server->handle("player.drop", $data) !== false){
|
||
$this->server->api->entity->drop(new Position($this->entity->x - 0.5, $this->entity->y, $this->entity->z - 0.5, $this->level), $packet->item);
|
||
$this->setSlot($this->slot, BlockAPI::getItem(AIR, 0, 0), false);
|
||
}
|
||
if($this->entity->inAction === true){
|
||
$this->entity->inAction = false;
|
||
$this->entity->updateMetadata();
|
||
}
|
||
break;*/
|
||
case ProtocolInfo::MESSAGE_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
if(trim($packet->message) != "" and strlen($packet->message) <= 255){
|
||
$message = $packet->message;
|
||
if($message{0} === "/"){ //Command
|
||
$this->server->api->console->run(substr($message, 1), $this);
|
||
}else{
|
||
$data = array("player" => $this, "message" => $message);
|
||
if($this->server->api->handle("player.chat", $data) !== false){
|
||
if(isset($data["message"])){
|
||
$this->server->api->chat->send($this, $data["message"]);
|
||
}else{
|
||
$this->server->api->chat->send($this, $message);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case ProtocolInfo::CONTAINER_CLOSE_PACKET:
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
if(isset($this->windows[$packet->windowid])){
|
||
if(is_array($this->windows[$packet->windowid])){
|
||
foreach($this->windows[$packet->windowid] as $ob){
|
||
$pk = new TileEventPacket;
|
||
$pk->x = $ob->x;
|
||
$pk->y = $ob->y;
|
||
$pk->z = $ob->z;
|
||
$pk->case1 = 1;
|
||
$pk->case2 = 0;
|
||
$this->server->api->player->broadcastPacket($this->level->players, $pk);
|
||
}
|
||
}elseif($this->windows[$packet->windowid] instanceof ChestTile){
|
||
$pk = new TileEventPacket;
|
||
$pk->x = $this->windows[$packet->windowid]->x;
|
||
$pk->y = $this->windows[$packet->windowid]->y;
|
||
$pk->z = $this->windows[$packet->windowid]->z;
|
||
$pk->case1 = 1;
|
||
$pk->case2 = 0;
|
||
$this->server->api->player->broadcastPacket($this->level->players, $pk);
|
||
}
|
||
}
|
||
unset($this->windows[$packet->windowid]);
|
||
|
||
$pk = new ContainerClosePacket;
|
||
$pk->windowid = $packet->windowid;
|
||
$this->dataPacket($pk);
|
||
break;
|
||
case ProtocolInfo::CONTAINER_SET_SLOT_PACKET:
|
||
if($this->spawned === false or $this->blocked === true){
|
||
break;
|
||
}
|
||
|
||
if($this->lastCraft <= (microtime(true) - 1)){
|
||
if(isset($this->toCraft[-1])){
|
||
$this->toCraft = array(-1 => $this->toCraft[-1]);
|
||
}else{
|
||
$this->toCraft = array();
|
||
}
|
||
$this->craftingItems = array();
|
||
}
|
||
|
||
if($packet->windowid === 0){
|
||
$craft = false;
|
||
$slot = $this->getSlot($packet->slot);
|
||
if($slot->count >= $packet->item->count and (($slot->getID() === $packet->item->getID() and $slot->getMetadata() === $packet->item->getMetadata()) or ($packet->item->getID() === AIR and $packet->item->count === 0)) and !isset($this->craftingItems[$packet->slot])){ //Crafting recipe
|
||
$use = BlockAPI::getItem($slot->getID(), $slot->getMetadata(), $slot->count - $packet->item->count);
|
||
$this->craftingItems[$packet->slot] = $use;
|
||
$craft = true;
|
||
}elseif($slot->count <= $packet->item->count and ($slot->getID() === AIR or ($slot->getID() === $packet->item->getID() and $slot->getMetadata() === $packet->item->getMetadata()))){ //Crafting final
|
||
$craftItem = BlockAPI::getItem($packet->item->getID(), $packet->item->getMetadata(), $packet->item->count - $slot->count);
|
||
if(count($this->toCraft) === 0){
|
||
$this->toCraft[-1] = 0;
|
||
}
|
||
$this->toCraft[$packet->slot] = $craftItem;
|
||
$craft = true;
|
||
}elseif(((count($this->toCraft) === 1 and isset($this->toCraft[-1])) or count($this->toCraft) === 0) and $slot->count > 0 and $slot->getID() > AIR and ($slot->getID() !== $packet->item->getID() or $slot->getMetadata() !== $packet->item->getMetadata())){ //Crafting final
|
||
$craftItem = BlockAPI::getItem($packet->item->getID(), $packet->item->getMetadata(), $packet->item->count);
|
||
if(count($this->toCraft) === 0){
|
||
$this->toCraft[-1] = 0;
|
||
}
|
||
$use = BlockAPI::getItem($slot->getID(), $slot->getMetadata(), $slot->count);
|
||
$this->craftingItems[$packet->slot] = $use;
|
||
$this->toCraft[$packet->slot] = $craftItem;
|
||
$craft = true;
|
||
}
|
||
|
||
if($craft === true){
|
||
$this->lastCraft = microtime(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->sendInventory();
|
||
$this->toCraft = array();
|
||
}else{
|
||
$this->toCraft = array(-1 => $this->toCraft[-1]);
|
||
}
|
||
$this->craftingItems = array();
|
||
}
|
||
}else{
|
||
$this->toCraft = array();
|
||
$this->craftingItems = array();
|
||
}
|
||
if(!isset($this->windows[$packet->windowid])){
|
||
break;
|
||
}
|
||
|
||
if(is_array($this->windows[$packet->windowid])){
|
||
$tiles = $this->windows[$packet->windowid];
|
||
if($packet->slot >= 0 and $packet->slot < ChestTile::SLOTS){
|
||
$tile = $tiles[0];
|
||
$slotn = $packet->slot;
|
||
$offset = 0;
|
||
}elseif($packet->slot >= ChestTile::SLOTS and $packet->slot <= (ChestTile::SLOTS << 1)){
|
||
$tile = $tiles[1];
|
||
$slotn = $packet->slot - ChestTile::SLOTS;
|
||
$offset = ChestTile::SLOTS;
|
||
}else{
|
||
break;
|
||
}
|
||
|
||
$item = BlockAPI::getItem($packet->item->getID(), $packet->item->getMetadata(), $packet->item->count);
|
||
|
||
$slot = $tile->getSlot($slotn);
|
||
if($this->server->api->dhandle("player.container.slot", array(
|
||
"tile" => $tile,
|
||
"slot" => $packet->slot,
|
||
"offset" => $offset,
|
||
"slotdata" => $slot,
|
||
"itemdata" => $item,
|
||
"player" => $this,
|
||
)) === false){
|
||
$pk = new ContainerSetSlotPacket;
|
||
$pk->windowid = $packet->windowid;
|
||
$pk->slot = $packet->slot;
|
||
$pk->item = $slot;
|
||
$this->dataPacket($pk);
|
||
break;
|
||
}
|
||
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) === false){
|
||
$this->sendInventory();
|
||
break;
|
||
}
|
||
}elseif($slot->count > $item->count){
|
||
$this->addItem($item->getID(), $item->getMetadata(), $slot->count - $item->count, false);
|
||
}
|
||
}else{
|
||
if($this->removeItem($item->getID(), $item->getMetadata(), $item->count, false) === false){
|
||
$this->sendInventory();
|
||
break;
|
||
}
|
||
$this->addItem($slot->getID(), $slot->getMetadata(), $slot->count, false);
|
||
}
|
||
$tile->setSlot($slotn, $item, true, $offset);
|
||
}else{
|
||
$tile = $this->windows[$packet->windowid];
|
||
if(
|
||
!($tile instanceof ChestTile or $tile instanceof FurnaceTile)
|
||
or $packet->slot < 0
|
||
or (
|
||
$tile instanceof ChestTile
|
||
and $packet->slot >= ChestTile::SLOTS
|
||
) or (
|
||
$tile instanceof FurnaceTile and $packet->slot >= FurnaceTile::SLOTS
|
||
)
|
||
){
|
||
break;
|
||
}
|
||
$item = BlockAPI::getItem($packet->item->getID(), $packet->item->getMetadata(), $packet->item->count);
|
||
|
||
$slot = $tile->getSlot($packet->slot);
|
||
if($this->server->api->dhandle("player.container.slot", array(
|
||
"tile" => $tile,
|
||
"slot" => $packet->slot,
|
||
"slotdata" => $slot,
|
||
"itemdata" => $item,
|
||
"player" => $this,
|
||
)) === false){
|
||
$pk = new ContainerSetSlotPacket;
|
||
$pk->windowid = $packet->windowid;
|
||
$pk->slot = $packet->slot;
|
||
$pk->item = $slot;
|
||
$this->dataPacket($pk);
|
||
break;
|
||
}
|
||
|
||
if($tile instanceof FurnaceTile and $packet->slot == 2){
|
||
switch($slot->getID()){
|
||
case IRON_INGOT:
|
||
AchievementAPI::grantAchievement($this, "acquireIron");
|
||
break;
|
||
}
|
||
}
|
||
|
||
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) === false){
|
||
$this->sendInventory();
|
||
break;
|
||
}
|
||
}elseif($slot->count > $item->count){
|
||
$this->addItem($item->getID(), $item->getMetadata(), $slot->count - $item->count, false);
|
||
}
|
||
}else{
|
||
if($this->removeItem($item->getID(), $item->getMetadata(), $item->count, false) === false){
|
||
$this->sendInventory();
|
||
break;
|
||
}
|
||
$this->addItem($slot->getID(), $slot->getMetadata(), $slot->count, false);
|
||
}
|
||
$tile->setSlot($packet->slot, $item);
|
||
}
|
||
break;
|
||
case ProtocolInfo::SEND_INVENTORY_PACKET: //TODO, Mojang, enable this ´^_^`
|
||
if($this->spawned === false){
|
||
break;
|
||
}
|
||
break;
|
||
case ProtocolInfo::ENTITY_DATA_PACKET:
|
||
if($this->spawned === false or $this->blocked === true){
|
||
break;
|
||
}
|
||
$this->craftingItems = array();
|
||
$this->toCraft = array();
|
||
$t = $this->level->getTile(new Vector3($packet->x, $packet->y, $packet->z));
|
||
if($t instanceof SignTile){
|
||
if($t->namedtag->creator !== $this->username){
|
||
$t->spawnTo($this);
|
||
}else{
|
||
$nbt = new NBT();
|
||
$nbt->read($packet->namedtag);
|
||
if($nbt->id !== Tile::SIGN){
|
||
$t->spawnTo($this);
|
||
}else{
|
||
$t->setText($nbt->Text1, $nbt->Text2, $nbt->Text3, $nbt->Text4);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
console("[DEBUG] Unhandled 0x".dechex($packet->pid())." data packet for ".$this->username." (".$this->clientID."): ".print_r($packet, true), true, true, 2);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param Player|string|boolean|void $player
|
||
*/
|
||
public function sendArmor($player = false){
|
||
$data = array(
|
||
"player" => $this,
|
||
"eid" => $this->eid,
|
||
"slots" => array()
|
||
);
|
||
for($i = 0; $i < 4; ++$i){
|
||
if(isset($this->armor[$i]) and ($this->armor[$i] instanceof Item) and $this->armor[$i]->getID() > AIR){
|
||
$data["slots"][$i] = $this->armor[$i]->getID() !== AIR ? $this->armor[$i]->getID() - 256:0;
|
||
}else{
|
||
$this->armor[$i] = BlockAPI::getItem(AIR, 0, 0);
|
||
$data["slots"][$i] = 255;
|
||
}
|
||
}
|
||
if($player instanceof Player){
|
||
if($player === $this){
|
||
$pk = new ContainerSetContentPacket;
|
||
$pk->windowid = 0x78; //Armor window id
|
||
$pk->slots = $this->armor;
|
||
$this->dataPacket($pk);
|
||
}else{
|
||
$pk = new PlayerArmorEquipmentPacket;
|
||
$pk->eid = $this->eid;
|
||
$pk->slots = $data["slots"];
|
||
$player->dataPacket($pk);
|
||
}
|
||
}else{
|
||
$this->server->api->dhandle("player.armor", $data);
|
||
}
|
||
}
|
||
|
||
public function sendInventory(){
|
||
if(($this->gamemode & 0x01) === CREATIVE){
|
||
return;
|
||
}
|
||
$hotbar = array();
|
||
foreach($this->hotbar as $slot){
|
||
$hotbar[] = $slot <= -1 ? -1 : $slot + 9;
|
||
}
|
||
|
||
$pk = new ContainerSetContentPacket;
|
||
$pk->windowid = 0;
|
||
$pk->slots = $this->inventory;
|
||
$pk->hotbar = $hotbar;
|
||
$this->dataPacket($pk);
|
||
}
|
||
|
||
public function send(RakNetPacket $packet){
|
||
if($this->connected === true){
|
||
$packet->ip = $this->ip;
|
||
$packet->port = $this->port;
|
||
$this->bandwidthRaw += $this->server->send($packet);
|
||
}
|
||
}
|
||
|
||
public function sendBuffer(){
|
||
if($this->connected === true){
|
||
if($this->bufferLen > 0 and $this->buffer instanceof RakNetPacket){
|
||
$this->buffer->seqNumber = $this->counter[0]++;
|
||
$this->send($this->buffer);
|
||
}
|
||
$this->bufferLen = 0;
|
||
$this->buffer = new RakNetPacket(RakNetInfo::DATA_PACKET_0);
|
||
$this->buffer->data = array();
|
||
$this->nextBuffer = microtime(true) + 0.1;
|
||
}
|
||
}
|
||
|
||
private function directBigRawPacket(RakNetDataPacket $packet){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
|
||
$sendtime = microtime(true);
|
||
|
||
$size = $this->MTU - 34;
|
||
$buffer = str_split($packet->buffer, $size);
|
||
$bigCnt = $this->bigCnt;
|
||
$this->bigCnt = ($this->bigCnt + 1) % 0x10000;
|
||
$cnts = array();
|
||
$bufCount = count($buffer);
|
||
foreach($buffer as $i => $buf){
|
||
$cnts[] = $count = $this->counter[0]++;
|
||
|
||
$pk = new UnknownPacket;
|
||
$pk->packetID = $packet->pid();
|
||
$pk->reliability = 2;
|
||
$pk->hasSplit = true;
|
||
$pk->splitCount = $bufCount;
|
||
$pk->splitID = $bigCnt;
|
||
$pk->splitIndex = $i;
|
||
$pk->buffer = $buf;
|
||
$pk->messageIndex = $this->counter[3]++;
|
||
|
||
$rk = new RakNetPacket(RakNetInfo::DATA_PACKET_0);
|
||
$rk->data[] = $pk;
|
||
$rk->seqNumber = $count;
|
||
$rk->sendtime = $sendtime;
|
||
$this->recoveryQueue[$count] = $rk;
|
||
$this->send($rk);
|
||
}
|
||
return $cnts;
|
||
}
|
||
|
||
public function directDataPacket(RakNetDataPacket $packet, $recover = true){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
|
||
if(EventHandler::callEvent(new DataPacketSendEvent($this, $packet)) === BaseEvent::DENY){
|
||
return array();
|
||
}
|
||
$packet->encode();
|
||
$pk = new RakNetPacket(RakNetInfo::DATA_PACKET_0);
|
||
$pk->data[] = $packet;
|
||
$pk->seqNumber = $this->counter[0]++;
|
||
$pk->sendtime = microtime(true);
|
||
if($recover !== false){
|
||
$this->recoveryQueue[$pk->seqNumber] = $pk;
|
||
}
|
||
|
||
$this->send($pk);
|
||
return array($pk->seqNumber);
|
||
}
|
||
|
||
/**
|
||
* @param integer $id
|
||
* @param array $data
|
||
*
|
||
* @return array|bool
|
||
*/
|
||
public function dataPacket(RakNetDataPacket $packet){
|
||
if($this->connected === false){
|
||
return false;
|
||
}
|
||
|
||
if(EventHandler::callEvent(new DataPacketSendEvent($this, $packet)) === BaseEvent::DENY){
|
||
return;
|
||
}
|
||
|
||
$packet->encode();
|
||
$len = strlen($packet->buffer) + 1;
|
||
$MTU = $this->MTU - 24;
|
||
if($len > $MTU){
|
||
return $this->directBigRawPacket($packet);
|
||
}
|
||
|
||
if(($this->bufferLen + $len) >= $MTU){
|
||
$this->sendBuffer();
|
||
}
|
||
|
||
$packet->messageIndex = $this->counter[3]++;
|
||
$packet->reliability = 2;
|
||
@$this->buffer->data[] = $packet;
|
||
$this->bufferLen += 6 + $len;
|
||
return array();
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
function __toString(){
|
||
if($this->username != ""){
|
||
return $this->username;
|
||
}
|
||
return $this->clientID;
|
||
}
|
||
|
||
}
|