New chunk indexing and sending algorithm

This commit is contained in:
Shoghi Cervantes 2014-03-01 17:35:44 +01:00
parent 8403a34eb3
commit 9d02ed9a28
6 changed files with 106 additions and 87 deletions

View File

@ -41,13 +41,12 @@ class PlayerAPI{
$this->server->api->ban->cmdWhitelist("list"); $this->server->api->ban->cmdWhitelist("list");
$this->server->api->ban->cmdWhitelist("ping"); $this->server->api->ban->cmdWhitelist("ping");
$this->server->api->ban->cmdWhitelist("spawn"); $this->server->api->ban->cmdWhitelist("spawn");
$this->server->preparedSQL->selectPlayersToHeal = $this->server->database->prepare("SELECT EID FROM entities WHERE class = ".ENTITY_PLAYER." AND health < 20;");
} }
public function handle($data, $event){ public function handle($data, $event){
switch($event){ switch($event){
case "server.regeneration": case "server.regeneration":
if($this->server->difficulty === 0){ /*if($this->server->difficulty === 0){
$result = $this->server->preparedSQL->selectPlayersToHeal->execute(); $result = $this->server->preparedSQL->selectPlayersToHeal->execute();
if($result !== false){ if($result !== false){
while(($player = $result->fetchArray()) !== false){ while(($player = $result->fetchArray()) !== false){
@ -60,7 +59,7 @@ class PlayerAPI{
} }
return true; return true;
} }
} }*/
break; break;
case "player.death": case "player.death":
if(is_numeric($data["cause"])){ if(is_numeric($data["cause"])){
@ -267,7 +266,7 @@ class PlayerAPI{
if(!isset($params[0]) and ($issuer instanceof Player)){ if(!isset($params[0]) and ($issuer instanceof Player)){
$player = $issuer; $player = $issuer;
}else{ }else{
$player = $this->get($params[0]); $player = Player::get($params[0]);
} }
if($player instanceof Player){ if($player instanceof Player){
$player->entity->harm(1000, "console", true); $player->entity->harm(1000, "console", true);
@ -294,7 +293,7 @@ class PlayerAPI{
if(substr($target, 0, 2) === "w:"){ if(substr($target, 0, 2) === "w:"){
$lv = $this->server->api->level->get(substr($target, 2)); $lv = $this->server->api->level->get(substr($target, 2));
if($lv instanceof Level){ if($lv instanceof Level){
$origin = $this->get($name); $origin = Player::get($name);
if($origin instanceof Player){ if($origin instanceof Player){
$name = $origin->username; $name = $origin->username;
return $origin->teleport($lv->getSafeSpawn()); return $origin->teleport($lv->getSafeSpawn());
@ -303,10 +302,10 @@ class PlayerAPI{
return false; return false;
} }
} }
$player = $this->get($target); $player = Player::get($target);
if(($player instanceof Player) and ($player->entity instanceof Entity)){ if(($player instanceof Player) and ($player->entity instanceof Entity)){
$target = $player->username; $target = $player->username;
$origin = $this->get($name); $origin = Player::get($name);
if($origin instanceof Player){ if($origin instanceof Player){
$name = $origin->username; $name = $origin->username;
return $origin->teleport($player->entity); return $origin->teleport($player->entity);
@ -316,7 +315,7 @@ class PlayerAPI{
} }
public function tppos(&$name, &$x, &$y, &$z){ public function tppos(&$name, &$x, &$y, &$z){
$player = $this->get($name); $player = Player::get($name);
if(($player instanceof Player) and ($player->entity instanceof Entity)){ if(($player instanceof Player) and ($player->entity instanceof Entity)){
$name = $player->username; $name = $player->username;
$x = $x{0} === "~" ? $player->entity->x + floatval(substr($x, 1)):floatval($x); $x = $x{0} === "~" ? $player->entity->x + floatval(substr($x, 1)):floatval($x);

View File

@ -25,8 +25,8 @@ abstract class Entity extends Position{
public static $needUpdate = array(); public static $needUpdate = array();
private $id; private $id;
//public $passenger = null; public $passenger = null;
//public $vehicle = null; public $vehicle = null;
public $chunkIndex; public $chunkIndex;
@ -258,12 +258,14 @@ abstract class Entity extends Position{
unset($this->level->chunkEntities[$this->chunkIndex][$this->id]); unset($this->level->chunkEntities[$this->chunkIndex][$this->id]);
$this->despawnFromAll(); $this->despawnFromAll();
if($this instanceof Player){ if($this instanceof Player){
foreach($this->chunksLoaded as $index => $boolean){ foreach($this->chunksLoaded as $index => $Yndex){
$X = null; if($Yndex !== 0xff){
$Z = null; $X = null;
PMFLevel::getXZ($index, $X, $Z); $Z = null;
foreach($this->level->getChunkEntities($X, $Z) as $entity){ PMFLevel::getXZ($index, $X, $Z);
$entity->despawnFrom($this); foreach($this->level->getChunkEntities($X, $Z) as $entity){
$entity->despawnFrom($this);
}
} }
} }
$this->level->freeAllChunks($this); $this->level->freeAllChunks($this);

View File

@ -137,8 +137,6 @@ class MainServer{
$this->preparedSQL->selectHandlers = $this->database->prepare("SELECT DISTINCT ID FROM handlers WHERE name = :name ORDER BY priority DESC;"); $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);"); $this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);");
$this->preparedSQL->updateAction = $this->database->prepare("UPDATE actions SET last = :time WHERE ID = :id;"); $this->preparedSQL->updateAction = $this->database->prepare("UPDATE actions SET last = :time WHERE ID = :id;");
$this->preparedSQL->entity->setPosition = $this->database->prepare("UPDATE entities SET x = :x, y = :y, z = :z, pitch = :pitch, yaw = :yaw WHERE EID = :eid ;");
$this->preparedSQL->entity->setLevel = $this->database->prepare("UPDATE entities SET level = :level WHERE EID = :eid ;");
} }
public function query($sql, $fetch = false){ public function query($sql, $fetch = false){
@ -154,10 +152,8 @@ class MainServer{
$info["tps"] = $this->getTPS(); $info["tps"] = $this->getTPS();
$info["memory_usage"] = round((memory_get_usage() / 1024) / 1024, 2)."MB"; $info["memory_usage"] = round((memory_get_usage() / 1024) / 1024, 2)."MB";
$info["memory_peak_usage"] = round((memory_get_peak_usage() / 1024) / 1024, 2)."MB"; $info["memory_peak_usage"] = round((memory_get_peak_usage() / 1024) / 1024, 2)."MB";
$info["entities"] = $this->query("SELECT count(EID) as count FROM entities;", true); $info["entities"] = count(Entity::$list);
$info["entities"] = $info["entities"]["count"]; $info["players"] = count(Player::$list);
$info["players"] = $this->query("SELECT count(CID) as count FROM players;", true);
$info["players"] = $info["players"]["count"];
$info["events"] = count($this->eventsID); $info["events"] = count($this->eventsID);
$info["handlers"] = $this->query("SELECT count(ID) as count FROM handlers;", true); $info["handlers"] = $this->query("SELECT count(ID) as count FROM handlers;", true);
$info["handlers"] = $info["handlers"]["count"]; $info["handlers"] = $info["handlers"]["count"];

View File

@ -181,7 +181,7 @@ class Player extends PlayerEntity{
} }
$newOrder = array(); $newOrder = array();
$lastLoaded = $this->chunksLoaded; $lastChunk = $this->chunksLoaded;
$centerX = intval(($this->x - 0.5) / 16); $centerX = intval(($this->x - 0.5) / 16);
$centerZ = intval(($this->z - 0.5) / 16); $centerZ = intval(($this->z - 0.5) / 16);
$startX = $centerX - $this->viewDistance; $startX = $centerX - $this->viewDistance;
@ -191,21 +191,26 @@ class Player extends PlayerEntity{
for($X = $startX; $X <= $finalX; ++$X){ for($X = $startX; $X <= $finalX; ++$X){
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);
for($Y = 0; $Y < 8; ++$Y){ $index = PMFLevel::getIndex($X, $Z);
$index = "$X:$Y:$Z"; if(!isset($this->chunksLoaded[$index]) or $this->chunksLoaded[$index] !== 0){
if(!isset($lastLoaded[$index])){ $newOrder[$index] = $distance;
$newOrder[$index] = $distance;
}else{
unset($lastLoaded[$index]);
}
} }
unset($lastChunk[$index]);
} }
} }
asort($newOrder); asort($newOrder);
$this->chunksOrder = $newOrder; $this->chunksOrder = $newOrder;
foreach($lastLoaded as $index => $distance){ foreach($lastChunk as $index => $Yndex){
$id = explode(":", $index); if($Yndex !== 0xff){
$this->level->freeChunk($id[0], $id[2], $this); $X = null;
$Z = null;
PMFLevel::getXZ($index, $X, $Z);
foreach($this->level->getChunkEntities($X, $Z) as $entity){
if($entity !== $this){
$entity->despawnFrom($this);
}
}
}
unset($this->chunksLoaded[$index]); unset($this->chunksLoaded[$index]);
} }
} }
@ -235,6 +240,11 @@ class Player extends PlayerEntity{
} }
if(is_array($this->lastChunk)){ if(is_array($this->lastChunk)){
foreach($this->level->getChunkEntities($this->lastChunk[0], $this->lastChunk[1]) as $entity){
if($entity !== $this){
$entity->spawnTo($this);
}
}
foreach($this->level->getChunkTiles($this->lastChunk[0], $this->lastChunk[1]) as $tile){ foreach($this->level->getChunkTiles($this->lastChunk[0], $this->lastChunk[1]) as $tile){
if($tile instanceof SpawnableTile){ if($tile instanceof SpawnableTile){
$tile->spawnTo($this); $tile->spawnTo($this);
@ -243,29 +253,24 @@ class Player extends PlayerEntity{
$this->lastChunk = false; $this->lastChunk = false;
} }
$c = key($this->chunksOrder); $index = key($this->chunksOrder);
$d = @$this->chunksOrder[$c]; $distance = @$this->chunksOrder[$index];
if($c === null or $d === null){ if($index === null or $distance === null){
if($this->chunkScheduled === 0){ if($this->chunkScheduled === 0){
$this->server->schedule(40, array($this, "getNextChunk")); $this->server->schedule(40, array($this, "getNextChunk"));
} }
return false; return false;
} }
unset($this->chunksOrder[$c]); unset($this->chunksOrder[$index]);
$this->chunksLoaded[$c] = true; if(!isset($this->chunksLoaded[$index])){
$id = explode(":", $c); $this->chunksLoaded[$index] = 0xff;
$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;
}
} }
$X = null;
$Z = null;
PMFLevel::getXZ($index, $X, $Z);
$this->level->useChunk($X, $Z, $this);
$Yndex = $this->chunksLoaded[$index];
$this->chunksLoaded[$index] = 0; //Load them all
$pk = new ChunkDataPacket; $pk = new ChunkDataPacket;
$pk->chunkX = $X; $pk->chunkX = $X;
$pk->chunkZ = $Z; $pk->chunkZ = $Z;

View File

@ -34,8 +34,8 @@ class UDPSocket{
}else{ }else{
if(socket_bind($this->sock, $serverip, $port) === true){ if(socket_bind($this->sock, $serverip, $port) === true){
socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 0); socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 0);
socket_set_option($this->sock, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2); //2MB @socket_set_option($this->sock, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2); //2MB
socket_set_option($this->sock, SOL_SOCKET, SO_RCVBUF, 1024 * 1024); //1MB @socket_set_option($this->sock, SOL_SOCKET, SO_RCVBUF, 1024 * 1024); //1MB
$this->unblock(); $this->unblock();
$this->connected = true; $this->connected = true;
}else{ }else{

View File

@ -109,28 +109,37 @@ class Level{
$now = microtime(true); $now = microtime(true);
if($this->level->isGenerating === 0 and count($this->changedCount) > 0){ if($this->level->isGenerating === 0 and count($this->changedCount) > 0){
arsort($this->changedCount); foreach($this->changedCount as $index => $mini){
foreach($this->changedCount as $index => $count){ for($Y = 0; $Y < 8; ++$Y){
if($count < 582){//Optimal value, calculated using the relation between minichunks and single packets if(($mini & (1 << $Y)) === 0){
break; continue;
}
if(count($this->changedBlocks[$index][$Y]) < 582){//Optimal value, calculated using the relation between minichunks and single packets
continue;
}else{
foreach($this->players as $p){
if(isset($p->chunksLoaded[$index])){
$p->chunksLoaded[$index] |= $mini;
}
}
unset($this->changedBlocks[$index][$Y]);
}
} }
foreach($this->players as $p){
unset($p->chunksLoaded[$index]);
}
unset($this->changedBlocks[$index]);
} }
$this->changedCount = array(); $this->changedCount = array();
if(count($this->changedBlocks) > 0){ if(count($this->changedBlocks) > 0){
foreach($this->changedBlocks as $blocks){ foreach($this->changedBlocks as $index => $mini){
foreach($blocks as $b){ foreach($mini as $blocks){
$pk = new UpdateBlockPacket; foreach($blocks as $b){
$pk->x = $b->x; $pk = new UpdateBlockPacket;
$pk->y = $b->y; $pk->x = $b->x;
$pk->z = $b->z; $pk->y = $b->y;
$pk->block = $b->getID(); $pk->z = $b->z;
$pk->meta = $b->getMetadata(); $pk->block = $b->getID();
$this->server->api->player->broadcastPacket($this->players, $pk); $pk->meta = $b->getMetadata();
$this->server->api->player->broadcastPacket($this->players, $pk);
}
} }
} }
$this->changedBlocks = array(); $this->changedBlocks = array();
@ -251,16 +260,20 @@ class Level{
$pos = new Position($pos->x, $pos->y, $pos->z, $this); $pos = new Position($pos->x, $pos->y, $pos->z, $this);
} }
$block->position($pos); $block->position($pos);
$i = ($pos->x >> 4).":".($pos->y >> 4).":".($pos->z >> 4); $index = PMFLevel::getIndex($pos->x >> 4, $pos->z >> 4);
if(ADVANCED_CACHE == true){ if(ADVANCED_CACHE == true){
Cache::remove("world:{$this->name}:".($pos->x >> 4).":".($pos->z >> 4)); Cache::remove("world:{$this->name}:{$index}");
} }
if(!isset($this->changedBlocks[$i])){ if(!isset($this->changedBlocks[$index])){
$this->changedBlocks[$i] = array(); $this->changedBlocks[$index] = array();
$this->changedCount[$i] = 0; $this->changedCount[$index] = 0;
} }
$this->changedBlocks[$i][] = clone $block; $Y = $pos->y >> 4;
++$this->changedCount[$i]; if(!isset($this->changedBlocks[$index][$Y])){
$this->changedBlocks[$index][$Y] = array();
$this->changedCount[$index] |= 1 << $Y;
}
$this->changedBlocks[$index][$Y][] = clone $block;
} }
} }
return $ret; return $ret;
@ -287,16 +300,20 @@ class Level{
$pk->meta = $block->getMetadata(); $pk->meta = $block->getMetadata();
$this->server->api->player->broadcastPacket($this->players, $pk); $this->server->api->player->broadcastPacket($this->players, $pk);
}else{ }else{
$i = ($pos->x >> 4).":".($pos->y >> 4).":".($pos->z >> 4); $index = PMFLevel::getIndex($pos->x >> 4, $pos->z >> 4);
if(!isset($this->changedBlocks[$i])){
$this->changedBlocks[$i] = array();
$this->changedCount[$i] = 0;
}
if(ADVANCED_CACHE == true){ if(ADVANCED_CACHE == true){
Cache::remove("world:{$this->name}:".($pos->x >> 4).":".($pos->z >> 4)); Cache::remove("world:{$this->name}:{$index}");
} }
$this->changedBlocks[$i][] = clone $block; if(!isset($this->changedBlocks[$index])){
++$this->changedCount[$i]; $this->changedBlocks[$index] = array();
$this->changedCount[$index] = 0;
}
$Y = $pos->y >> 4;
if(!isset($this->changedBlocks[$index][$Y])){
$this->changedBlocks[$index][$Y] = array();
$this->changedCount[$index] |= 1 << $Y;
}
$this->changedBlocks[$index][$Y][] = clone $block;
} }
if($update === true){ if($update === true){
@ -440,8 +457,8 @@ class Level{
if(!isset($this->level)){ if(!isset($this->level)){
return false; return false;
} }
if(ADVANCED_CACHE == true and $Yndex == 0xff){ if(ADVANCED_CACHE == true and $Yndex === 0xff){
$identifier = "world:{$this->name}:$X:$Z"; $identifier = "world:{$this->name}:".PMFLevel::getIndex($X, $Z);
if(($cache = Cache::get($identifier)) !== false){ if(($cache = Cache::get($identifier)) !== false){
return $cache; return $cache;
} }
@ -450,7 +467,7 @@ class Level{
$raw = array(); $raw = array();
for($Y = 0; $Y < 8; ++$Y){ for($Y = 0; $Y < 8; ++$Y){
if(($Yndex & (1 << $Y)) > 0){ if(($Yndex & (1 << $Y)) !== 0){
$raw[$Y] = $this->level->getMiniChunk($X, $Z, $Y); $raw[$Y] = $this->level->getMiniChunk($X, $Z, $Y);
} }
} }