New player chunk load queues with ACK notification

This commit is contained in:
Shoghi Cervantes 2014-06-21 19:41:06 +02:00
parent be0a31697a
commit e381313747
4 changed files with 119 additions and 112 deletions

View File

@ -119,7 +119,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
public $blocked = true; public $blocked = true;
public $achievements = []; public $achievements = [];
public $chunksLoaded = [];
public $lastCorrect; public $lastCorrect;
/** @var SimpleTransactionGroup */ /** @var SimpleTransactionGroup */
protected $currentTransaction = null; protected $currentTransaction = null;
@ -139,17 +138,22 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
protected $displayName; protected $displayName;
protected $startAction = false; protected $startAction = false;
protected $sleeping = false; protected $sleeping = false;
protected $chunksOrder = [];
protected $usedChunks = [];
protected $loadQueue = [];
protected $chunkACK = [];
/** @var \pocketmine\scheduler\TaskHandler */
protected $chunkLoadTask;
/** @var Player[] */ /** @var Player[] */
protected $hiddenPlayers = []; protected $hiddenPlayers = [];
private $viewDistance; private $viewDistance;
private $spawnPosition; private $spawnPosition;
private $lastChunk = false;
private $chunkScheduled = 0;
private $inAction = false; private $inAction = false;
private $needACK = []; private $needACK = [];
/** /**
@ -480,20 +484,23 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
return $this->sleeping instanceof Vector3; return $this->sleeping instanceof Vector3;
} }
/** public function unloadChunk($x, $z){
* Sets the chunk send flags for a specific index $index = Level::chunkHash($x, $z);
* if(isset($this->usedChunks[$index])){
* WARNING: Do not use this, it's only for internal use. foreach($this->getLevel()->getChunkEntities($x, $z) as $entity){
* Changes to this function won't be recorded on the version. if($entity !== $this){
* $entity->despawnFrom($this);
* @param int $index
* @param int $flags
*/
public function setChunkIndex($index, $flags){
if(isset($this->chunksLoaded[$index])){
$this->chunksLoaded[$index] |= $flags;
} }
} }
$pk = new UnloadChunkPacket();
$pk->chunkX = $x;
$pk->chunkZ = $z;
$this->dataPacket($pk);
unset($this->usedChunks[$index]);
}
unset($this->loadQueue[$index]);
$this->orderChunks();
}
/** /**
* @return Position * @return Position
@ -513,6 +520,28 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
public function handleACK($identifier){ public function handleACK($identifier){
unset($this->needACK[$identifier]); unset($this->needACK[$identifier]);
if(isset($this->chunkACK[$identifier])){
$index = $this->chunkACK[$identifier];
unset($this->chunkACK[$identifier]);
if(isset($this->usedChunks[$index])){
$this->usedChunks[$index][0] = true;
if($this->spawned === true){
$X = null;
$Z = null;
Level::getXZ($index, $X, $Z);
foreach($this->getLevel()->getChunkEntities($X, $Z) as $entity){
if($entity !== $this){
$entity->spawnTo($this);
}
}
foreach($this->getLevel()->getChunkTiles($X, $Z) as $tile){
if($tile instanceof Spawnable){
$tile->spawnTo($this);
}
}
}
}
}
} }
/** /**
@ -521,89 +550,72 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
* WARNING: Do not use this, it's only for internal use. * WARNING: Do not use this, it's only for internal use.
* Changes to this function won't be recorded on the version. * Changes to this function won't be recorded on the version.
* *
* @param bool $force
* @param bool $ev
*
* @return void|bool
*/ */
public function getNextChunk($force = false, $ev = null){
if($this->connected === false){ public function sendNextChunk(){
if($this->connected === false or !isset($this->chunkLoadTask)){
return; return;
} }
if($ev === true){ $index = key($this->loadQueue);
--$this->chunkScheduled; $distance = @$this->loadQueue[$index];
if($this->chunkScheduled < 0){
$this->chunkScheduled = 0;
}
}
if(is_array($this->lastChunk)){
/*$identifier = $this->lastChunk[2];
if(!$this->checkACK($identifier)){
if((microtime(true) - $this->lastChunk[3]) < 1.5){
$this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "getNextChunk"), array(false, true)), MAX_CHUNK_RATE);
return false;
}else{
$index = null;
Level::getXZ($index, $this->lastChunk[0], $this->lastChunk[1]);
unset($this->chunksLoaded[$index]);
}
}else{*/
foreach($this->getLevel()->getChunkEntities($this->lastChunk[0], $this->lastChunk[1]) as $entity){
if($entity !== $this){
$entity->spawnTo($this);
}
}
foreach($this->getLevel()->getChunkTiles($this->lastChunk[0], $this->lastChunk[1]) as $tile){
if($tile instanceof Spawnable){
$tile->spawnTo($this);
}
}
//}
$this->lastChunk = false;
}
$index = key($this->chunksOrder);
$distance = @$this->chunksOrder[$index];
if($index === null or $distance === null){ if($index === null or $distance === null){
$this->orderChunks(); $this->chunkLoadTask->setNextRun($this->chunkLoadTask->getNextRun() + 30);
if($this->chunkScheduled === 0){ return;
$this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "getNextChunk"), array(false, true)), 60);
}
return false;
} }
$X = null; $X = null;
$Z = null; $Z = null;
Level::getXZ($index, $X, $Z); Level::getXZ($index, $X, $Z);
if(!$this->getLevel()->isChunkPopulated($X, $Z)){ if(!$this->getLevel()->isChunkPopulated($X, $Z)){
$this->orderChunks(); return;
if($this->chunkScheduled === 0 or $force === true){
$this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "getNextChunk"), array(false, true)), MAX_CHUNK_RATE);
++$this->chunkScheduled;
} }
unset($this->loadQueue[$index]);
$this->usedChunks[$index] = [false, 0];
return false;
}
unset($this->chunksOrder[$index]);
if(!isset($this->chunksLoaded[$index])){
$this->chunksLoaded[$index] = 0xff;
}
$Yndex = $this->chunksLoaded[$index];
$this->chunksLoaded[$index] = 0; //Load them all
$this->getLevel()->useChunk($X, $Z, $this); $this->getLevel()->useChunk($X, $Z, $this);
$pk = new FullChunkDataPacket; $pk = new FullChunkDataPacket;
$pk->chunkX = $X; $pk->chunkX = $X;
$pk->chunkZ = $Z; $pk->chunkZ = $Z;
$pk->data = $this->getLevel()->getNetworkChunk($X, $Z, $Yndex); $pk->data = $this->getLevel()->getNetworkChunk($X, $Z, 0xff);
$cnt = $this->dataPacket($pk, true); $cnt = $this->dataPacket($pk, true);
if($cnt === false){ if($cnt === false or $cnt === true){
return false; return;
}
$this->chunkACK[$cnt] = $index;
if(count($this->usedChunks) >= 56 and $this->spawned === false){
$spawned = 0;
foreach($this->usedChunks as $d){
if($d[0] === true){
$spawned++;
}
}
if($spawned < 56){
return;
}
foreach($this->usedChunks as $index => $d){
if($d[0] === false){
continue;
}
$X = null;
$Z = null;
Level::getXZ($index, $X, $Z);
foreach($this->getLevel()->getChunkEntities($X, $Z) as $entity){
if($entity !== $this){
$entity->spawnTo($this);
}
}
foreach($this->getLevel()->getChunkTiles($X, $Z) as $tile){
if($tile instanceof Spawnable){
$tile->spawnTo($this);
}
}
} }
if(count($this->chunksLoaded) >= 56 and $this->spawned === false){
//TODO //TODO
//$this->heal($this->data->get("health"), "spawn", true); //$this->heal($this->data->get("health"), "spawn", true);
$this->spawned = true; $this->spawned = true;
@ -635,13 +647,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->server->getUpdater()->showPlayerUpdate($this); $this->server->getUpdater()->showPlayerUpdate($this);
} }
} }
$this->lastChunk = array($X, $Z, $cnt, microtime(true));
if($this->chunkScheduled === 0 or $force === true){
$this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "getNextChunk"), array(false, true)), MAX_CHUNK_RATE);
++$this->chunkScheduled;
}
} }
/** /**
@ -657,7 +662,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
} }
$newOrder = []; $newOrder = [];
$lastChunk = $this->chunksLoaded; $lastChunk = $this->usedChunks;
$centerX = $this->x >> 4; $centerX = $this->x >> 4;
$centerZ = $this->z >> 4; $centerZ = $this->z >> 4;
$startX = $centerX - $this->viewDistance; $startX = $centerX - $this->viewDistance;
@ -669,7 +674,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
for($Z = $startZ; $Z <= $finalZ; ++$Z){ for($Z = $startZ; $Z <= $finalZ; ++$Z){
$distance = abs($X - $centerX) + abs($Z - $centerZ); $distance = abs($X - $centerX) + abs($Z - $centerZ);
$index = Level::chunkHash($X, $Z); $index = Level::chunkHash($X, $Z);
if(!isset($this->chunksLoaded[$index]) or $this->chunksLoaded[$index] !== 0){ if(!isset($this->usedChunks[$index])){
if($this->getLevel()->isChunkPopulated($X, $Z)){ if($this->getLevel()->isChunkPopulated($X, $Z)){
$newOrder[$index] = $distance; $newOrder[$index] = $distance;
}else{ }else{
@ -681,10 +686,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
} }
asort($newOrder); asort($newOrder);
$this->chunksOrder = $newOrder; $this->loadQueue = $newOrder;
$i = 0; $i = 0;
while(count($this->chunksOrder) < 3 and $generateQueue->count() > 0 and $i < 16){ while(count($this->loadQueue) < 3 and $generateQueue->count() > 0 and $i < 16){
$d = $generateQueue->extract(); $d = $generateQueue->extract();
$this->getLevel()->generateChunk($d[0], $d[1]); $this->getLevel()->generateChunk($d[0], $d[1]);
++$i; ++$i;
@ -692,7 +697,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
foreach($lastChunk as $index => $Yndex){ foreach($lastChunk as $index => $Yndex){
if($Yndex === 0){
$X = null; $X = null;
$Z = null; $Z = null;
Level::getXZ($index, $X, $Z); Level::getXZ($index, $X, $Z);
@ -705,8 +709,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$pk->chunkX = $X; $pk->chunkX = $X;
$pk->chunkZ = $Z; $pk->chunkZ = $Z;
$this->dataPacket($pk); $this->dataPacket($pk);
} unset($this->usedChunks[$index]);
unset($this->chunksLoaded[$index]);
} }
} }
@ -1230,8 +1233,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->orderChunks(); $this->orderChunks();
$this->tasks[] = $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask(array($this, "orderChunks")), 30); $this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "orderChunks")), 10, 40);
$this->getNextChunk(); $this->sendNextChunk();
$this->tasks[] = $this->chunkLoadTask = $this->server->getScheduler()->scheduleRepeatingTask(new CallbackTask(array($this, "sendNextChunk")), MAX_CHUNK_RATE);
$pk = new ReadyPacket(); $pk = new ReadyPacket();
$pk->x = $this->x; $pk->x = $this->x;
@ -2039,8 +2043,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . $reason); $this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . $reason);
$this->windows = new \SplObjectStorage(); $this->windows = new \SplObjectStorage();
$this->windowIndex = []; $this->windowIndex = [];
$this->chunksLoaded = []; $this->usedChunks = [];
$this->chunksOrder = []; $this->loadQueue = [];
unset($this->buffer); unset($this->buffer);
} }
} }

View File

@ -1387,7 +1387,7 @@ class Server{
$this->logger->setLogDebug(\pocketmine\DEBUG > 1); $this->logger->setLogDebug(\pocketmine\DEBUG > 1);
} }
define("ADVANCED_CACHE", $this->getProperty("settings.advanced-cache", false)); define("ADVANCED_CACHE", $this->getProperty("settings.advanced-cache", false));
define("MAX_CHUNK_RATE", 20 / $this->getProperty("chunk-sending.per-second", 20)); define("MAX_CHUNK_RATE", ceil(20 / $this->getProperty("chunk-sending.per-second", 20)));
if(ADVANCED_CACHE == true){ if(ADVANCED_CACHE == true){
$this->logger->info("Advanced cache enabled"); $this->logger->info("Advanced cache enabled");
} }

View File

@ -931,7 +931,7 @@ abstract class Entity extends Position implements Metadatable{
$this->airTicks = 300; $this->airTicks = 300;
$this->fallDistance = 0; $this->fallDistance = 0;
$this->orderChunks(); $this->orderChunks();
$this->getNextChunk(true); $this->sendNextChunk();
$this->forceMovement = $pos; $this->forceMovement = $pos;
$pk = new MovePlayerPacket; $pk = new MovePlayerPacket;

View File

@ -345,8 +345,11 @@ class Level implements ChunkManager, Metadatable{
if(count($this->changedBlocks[$index][$Y]) < 582){ //Optimal value, calculated using the relation between minichunks and single packets if(count($this->changedBlocks[$index][$Y]) < 582){ //Optimal value, calculated using the relation between minichunks and single packets
continue; continue;
}else{ }else{
$X = null;
$Z = null;
Level::getXZ($index, $X, $Z);
foreach($this->players as $p){ foreach($this->players as $p){
$p->setChunkIndex($index, $mini); $p->unloadChunk($X, $Z);
} }
unset($this->changedBlocks[$index][$Y]); unset($this->changedBlocks[$index][$Y]);
} }
@ -1147,7 +1150,7 @@ class Level implements ChunkManager, Metadatable{
public function setChunk($x, $z, SimpleChunk $chunk){ public function setChunk($x, $z, SimpleChunk $chunk){
$index = Level::chunkHash($x, $z); $index = Level::chunkHash($x, $z);
foreach($this->getUsingChunk($x, $z) as $player){ foreach($this->getUsingChunk($x, $z) as $player){
$player->setChunkIndex($index, 0xff); $player->unloadChunk($x, $z);
} }
$this->provider->setChunk($x, $z, $chunk); $this->provider->setChunk($x, $z, $chunk);
} }