mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 08:17:34 +00:00
Re-implemented chunk sending (#304)
Re-implement chunk sending, send chunks inside a radius instead of below a count This sends chunks in concentric squares around players. When the radius is hit, it will pad out the radius until a full circle of chunks is loaded around the player. TODO: implement radius-per-tick, send chunks in concentric circles, use radius for player spawning. To set your server chunk radius, change `view-distance` in server.properties. Values are intended to be the same as MCPE render distance values. With matching client and server render distances the chunks should reach the horizon. NOTE: You may notice significantly increased memory usage per player when increasing these values to something respectable. This is normal and expected. A player with render distance 14 for example will cause loading of 600+ chunks. A player cannot however exceed the render distance limit set in server.properties - the server will simply not send any more chunks. Render distance of 8 chunks is approximately 200 chunks. This is roughly equivalent to the original default max-chunks of 192 in pocketmine.yml, but sent in a circle instead of a square. Wait for client to request a chunk radius before ordering chunks Use 8 for default maximum radius (roughly matches old setting of 192) Calculate spawn chunk count from chunk-sending.spawn-radius
This commit is contained in:
parent
d588222e84
commit
4fbc5738e3
@ -47,7 +47,7 @@ class MemoryManager{
|
||||
private $garbageCollectionTrigger;
|
||||
private $garbageCollectionAsync;
|
||||
|
||||
private $chunkLimit;
|
||||
private $chunkRadiusOverride;
|
||||
private $chunkCollect;
|
||||
private $chunkTrigger;
|
||||
|
||||
@ -104,7 +104,7 @@ class MemoryManager{
|
||||
$this->garbageCollectionTrigger = (bool) $this->server->getProperty("memory.garbage-collection.low-memory-trigger", true);
|
||||
$this->garbageCollectionAsync = (bool) $this->server->getProperty("memory.garbage-collection.collect-async-worker", true);
|
||||
|
||||
$this->chunkLimit = (int) $this->server->getProperty("memory.max-chunks.trigger-limit", 96);
|
||||
$this->chunkRadiusOverride = (int) $this->server->getProperty("memory.max-chunks.chunk-radius", 4);
|
||||
$this->chunkCollect = (bool) $this->server->getProperty("memory.max-chunks.trigger-chunk-collect", true);
|
||||
$this->chunkTrigger = (bool) $this->server->getProperty("memory.max-chunks.low-memory-trigger", true);
|
||||
|
||||
@ -122,8 +122,15 @@ class MemoryManager{
|
||||
return !($this->lowMemory and $this->chunkTrigger);
|
||||
}
|
||||
|
||||
public function getViewDistance($distance){
|
||||
return $this->lowMemory ? min($this->chunkLimit, $distance) : $distance;
|
||||
/**
|
||||
* Returns the allowed chunk radius based on the current memory usage.
|
||||
*
|
||||
* @param int $distance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getViewDistance(int $distance) : int{
|
||||
return $this->lowMemory ? min($this->chunkRadiusOverride, $distance) : $distance;
|
||||
}
|
||||
|
||||
public function trigger($memory, $limit, $global = false, $triggerCount = 0){
|
||||
|
@ -216,7 +216,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
/** @var Vector3 */
|
||||
protected $newPosition;
|
||||
|
||||
protected $viewDistance;
|
||||
protected $viewDistance = -1;
|
||||
protected $chunksPerTick;
|
||||
protected $spawnThreshold;
|
||||
/** @var null|WeakPosition */
|
||||
@ -421,6 +421,17 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
$this->inAirTicks = 0;
|
||||
}
|
||||
|
||||
public function getViewDistance() : int{
|
||||
return $this->viewDistance;
|
||||
}
|
||||
|
||||
public function setViewDistance(int $distance){
|
||||
$this->viewDistance = $this->server->getAllowedViewDistance($distance);
|
||||
$pk = new ChunkRadiusUpdatedPacket();
|
||||
$pk->radius = $this->viewDistance;
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@ -556,11 +567,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
$this->clientID = $clientID;
|
||||
$this->loaderId = Level::generateChunkLoaderId($this);
|
||||
$this->chunksPerTick = (int) $this->server->getProperty("chunk-sending.per-tick", 4);
|
||||
$this->spawnThreshold = (int) $this->server->getProperty("chunk-sending.spawn-threshold", 56);
|
||||
$this->spawnThreshold = (int) (($this->server->getProperty("chunk-sending.spawn-radius", 4) ** 2) * M_PI);
|
||||
$this->spawnPosition = null;
|
||||
$this->gamemode = $this->server->getGamemode();
|
||||
$this->setLevel($this->server->getDefaultLevel());
|
||||
$this->viewDistance = $this->server->getViewDistance();
|
||||
$this->newPosition = new Vector3(0, 0, 0);
|
||||
$this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
|
||||
@ -859,7 +869,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
}
|
||||
|
||||
protected function orderChunks(){
|
||||
if($this->connected === false){
|
||||
if($this->connected === false or $this->viewDistance === -1){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -867,59 +877,77 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
|
||||
$this->nextChunkOrderRun = 200;
|
||||
|
||||
$viewDistance = $this->server->getMemoryManager()->getViewDistance($this->viewDistance);
|
||||
$radius = $this->server->getAllowedViewDistance($this->viewDistance);
|
||||
$radiusSquared = $radius ** 2;
|
||||
|
||||
$newOrder = [];
|
||||
$lastChunk = $this->usedChunks;
|
||||
$unloadChunks = $this->usedChunks;
|
||||
|
||||
$centerX = $this->x >> 4;
|
||||
$centerZ = $this->z >> 4;
|
||||
|
||||
$layer = 1;
|
||||
$leg = 0;
|
||||
$x = 0;
|
||||
$z = 0;
|
||||
for($x = 0; $x < $radius; ++$x){
|
||||
for($z = 0; $z <= $x; ++$z){
|
||||
if(($x ** 2 + $z ** 2) > $radiusSquared){
|
||||
break; //skip to next band
|
||||
}
|
||||
|
||||
for($i = 0; $i < $viewDistance; ++$i){
|
||||
//If the chunk is in the radius, others at the same offsets in different quadrants are also guaranteed to be.
|
||||
|
||||
$chunkX = $x + $centerX;
|
||||
$chunkZ = $z + $centerZ;
|
||||
/* Top right quadrant */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX + $x, $centerZ + $z)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($chunkX, $chunkZ)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
unset($lastChunk[$index]);
|
||||
/* Top left quadrant */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX - $x - 1, $centerZ + $z)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
switch($leg){
|
||||
case 0:
|
||||
++$x;
|
||||
if($x === $layer){
|
||||
++$leg;
|
||||
/* Bottom right quadrant */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX + $x, $centerZ - $z - 1)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
|
||||
/* Bottom left quadrant */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX - $x - 1, $centerZ - $z - 1)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
if($x !== $z){
|
||||
/* Top right quadrant mirror */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX + $z, $centerZ + $x)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
++$z;
|
||||
if($z === $layer){
|
||||
++$leg;
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
/* Top left quadrant mirror */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX - $z - 1, $centerZ + $x)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
--$x;
|
||||
if(-$x === $layer){
|
||||
++$leg;
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
/* Bottom right quadrant mirror */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX + $z, $centerZ - $x - 1)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
--$z;
|
||||
if(-$z === $layer){
|
||||
$leg = 0;
|
||||
++$layer;
|
||||
unset($unloadChunks[$index]);
|
||||
|
||||
/* Bottom left quadrant mirror */
|
||||
if(!isset($this->usedChunks[$index = Level::chunkHash($centerX - $z - 1, $centerZ - $x - 1)]) or $this->usedChunks[$index] === false){
|
||||
$newOrder[$index] = true;
|
||||
}
|
||||
break;
|
||||
unset($unloadChunks[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($lastChunk as $index => $bool){
|
||||
foreach($unloadChunks as $index => $bool){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
$this->unloadChunk($X, $Z);
|
||||
}
|
||||
@ -2932,12 +2960,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
}
|
||||
break;
|
||||
case ProtocolInfo::REQUEST_CHUNK_RADIUS_PACKET:
|
||||
if($this->spawned){
|
||||
$this->viewDistance = $packet->radius ** 2;
|
||||
}
|
||||
$pk = new ChunkRadiusUpdatedPacket();
|
||||
$pk->radius = $packet->radius;
|
||||
$this->dataPacket($pk);
|
||||
$this->setViewDistance($packet->radius);
|
||||
break;
|
||||
case ProtocolInfo::SET_PLAYER_GAME_TYPE_PACKET:
|
||||
if($packet->gamemode !== ($this->gamemode & 0x01)){
|
||||
|
@ -333,8 +333,19 @@ class Server{
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getViewDistance(){
|
||||
return max(56, $this->getProperty("chunk-sending.max-chunks", 256));
|
||||
public function getViewDistance() : int{
|
||||
return max(2, $this->getConfigInt("view-distance", 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view distance up to the currently-allowed limit.
|
||||
*
|
||||
* @param int $distance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAllowedViewDistance(int $distance) : int{
|
||||
return max(2, min($distance, $this->memoryManager->getViewDistance($this->getViewDistance())));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1384,6 +1395,7 @@ class Server{
|
||||
"enable-rcon" => false,
|
||||
"rcon.password" => substr(base64_encode(random_bytes(20)), 3, 10),
|
||||
"auto-save" => true,
|
||||
"view-distance" => 8
|
||||
]);
|
||||
|
||||
$this->forceLanguage = $this->getProperty("settings.force-language", false);
|
||||
|
@ -58,8 +58,8 @@ memory:
|
||||
low-memory-trigger: true
|
||||
|
||||
max-chunks:
|
||||
#Limit of chunks to load per player, overrides chunk-sending.max-chunks
|
||||
trigger-limit: 96
|
||||
#Maximum render distance per player when low memory is triggered
|
||||
chunk-radius: 4
|
||||
|
||||
#Do chunk garbage collection on trigger
|
||||
trigger-chunk-collect: true
|
||||
@ -117,12 +117,11 @@ level-settings:
|
||||
always-tick-players: false
|
||||
|
||||
chunk-sending:
|
||||
#To change server normal render distance, change view-distance in server.properties.
|
||||
#Amount of chunks sent to players per tick
|
||||
per-tick: 4
|
||||
#Amount of chunks sent around each player
|
||||
max-chunks: 192
|
||||
#Amount of chunks that need to be sent before spawning the player
|
||||
spawn-threshold: 56
|
||||
#Radius of chunks that need to be sent before spawning the player
|
||||
spawn-radius: 4
|
||||
#Save a serialized copy of the chunk in memory for faster sending
|
||||
#Useful in mostly-static worlds where lots of players join at the same time
|
||||
cache-chunks: false
|
||||
|
Loading…
x
Reference in New Issue
Block a user