Improved world generation manager, UUIDs (some work for future usage)

This commit is contained in:
Shoghi Cervantes 2015-04-21 19:54:16 +02:00
parent 514ce0fb04
commit 1578fc3ddb
9 changed files with 255 additions and 106 deletions

View File

@ -122,6 +122,7 @@ use pocketmine\tile\Sign;
use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
/**
* Main class that handles networking, recovery, and packet sending to the server part
@ -175,6 +176,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
public $loginData = [];
protected $randomClientId;
protected $uuid;
protected $lastMovement = 0;
/** @var Vector3 */
@ -235,6 +237,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
return $this->randomClientId;
}
public function getUniqueId(){
return $this->uuid;
}
public function isBanned(){
return $this->server->getNameBans()->isBanned(strtolower($this->getName()));
}
@ -461,6 +467,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->viewDistance = $this->server->getViewDistance();
$this->newPosition = new Vector3(0, 0, 0);
$this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
$this->uuid = Utils::dataToUUID($ip, $port, $clientID);
}
/**
@ -1099,6 +1107,66 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->moveToSend[$entityId] = [$entityId, $x, $y, $z, $yaw, $headYaw === null ? $yaw : $headYaw, $pitch];
}
protected function checkNearEntities($currentTick){
foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){
if(($currentTick - $entity->lastUpdate) > 1){
$entity->scheduleUpdate();
}
if($entity instanceof Arrow and $entity->hadCollision){
if($entity->dead !== true){
$item = Item::get(Item::ARROW, 0, 1);
if($this->isSurvival() and !$this->inventory->canAddItem($item)){
continue;
}
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupArrowEvent($this->inventory, $entity));
if($ev->isCancelled()){
continue;
}
$pk = new TakeItemEntityPacket();
$pk->eid = $this->getId();
$pk->target = $entity->getId();
Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
$this->inventory->addItem(clone $item);
$entity->kill();
}
}elseif($entity instanceof DroppedItem){
if($entity->dead !== true and $entity->getPickupDelay() <= 0){
$item = $entity->getItem();
if($item instanceof Item){
if($this->isSurvival() and !$this->inventory->canAddItem($item)){
continue;
}
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($this->inventory, $entity));
if($ev->isCancelled()){
continue;
}
switch($item->getId()){
case Item::WOOD:
$this->awardAchievement("mineWood");
break;
case Item::DIAMOND:
$this->awardAchievement("diamond");
break;
}
$pk = new TakeItemEntityPacket();
$pk->eid = $this->getId();
$pk->target = $entity->getId();
Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
$this->inventory->addItem(clone $item);
$entity->kill();
}
}
}
}
}
protected function processMovement($currentTick){
if($this->dead or !$this->spawned or !($this->newPosition instanceof Vector3)){
return;
@ -1192,6 +1260,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
}
}
$this->checkNearEntities($currentTick);
$this->speed = $from->subtract($to);
}elseif($distanceSquared == 0){
$this->speed = new Vector3(0, 0, 0);
@ -1269,64 +1339,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
++$this->inAirTicks;
}
foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 0.5, 1), $this) as $entity){
if(($currentTick - $entity->lastUpdate) > 1){
$entity->scheduleUpdate();
}
if($entity instanceof Arrow and $entity->hadCollision){
if($entity->dead !== true){
$item = Item::get(Item::ARROW, 0, 1);
if($this->isSurvival() and !$this->inventory->canAddItem($item)){
continue;
}
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupArrowEvent($this->inventory, $entity));
if($ev->isCancelled()){
continue;
}
$pk = new TakeItemEntityPacket();
$pk->eid = $this->getId();
$pk->target = $entity->getId();
Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
$this->inventory->addItem(clone $item);
$entity->kill();
}
}elseif($entity instanceof DroppedItem){
if($entity->dead !== true and $entity->getPickupDelay() <= 0){
$item = $entity->getItem();
if($item instanceof Item){
if($this->isSurvival() and !$this->inventory->canAddItem($item)){
continue;
}
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($this->inventory, $entity));
if($ev->isCancelled()){
continue;
}
switch($item->getId()){
case Item::WOOD:
$this->awardAchievement("mineWood");
break;
case Item::DIAMOND:
$this->awardAchievement("diamond");
break;
}
$pk = new TakeItemEntityPacket();
$pk->eid = $this->getId();
$pk->target = $entity->getId();
Server::broadcastPacket($entity->getViewers(), $pk->setChannel(Network::CHANNEL_ENTITY_SPAWNING));
$this->inventory->addItem(clone $item);
$entity->kill();
}
}
}
}
}
if($this->nextChunkOrderRun-- <= 0 or $this->chunk === null){
@ -1441,6 +1453,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->setSkin($packet->skin, $packet->slim);
$this->uuid = Utils::dataToUUID($this->getClientId(), $this->iusername, $this->getAddress());
$this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason"));
if($ev->isCancelled()){
$this->close("", $ev->getKickMessage());
@ -2578,8 +2593,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
/**
* @param string $message Message to be broadcasted
* @param string $reason Reason showed in console
* @param bool $notify
*/
public function close($message = "", $reason = "generic reason"){
public function close($message = "", $reason = "generic reason", $notify = true){
foreach($this->tasks as $task){
$task->cancel();
@ -2587,7 +2603,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->tasks = [];
if($this->connected and !$this->closed){
if($reason != ""){
if($notify and $reason != ""){
$pk = new DisconnectPacket;
$pk->message = $reason;
$this->directDataPacket($pk->setChannel(Network::CHANNEL_PRIORITY));
@ -2612,7 +2628,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->removeWindow($window);
}
$this->interface->close($this, $reason);
$this->interface->close($this, $notify ? $reason : "");
$chunkX = $chunkZ = null;
foreach($this->usedChunks as $index => $d){

View File

@ -480,8 +480,13 @@ namespace pocketmine {
}
}
}elseif(!$thread->isJoined()){
$logger->debug("Joining " . (new \ReflectionClass($thread))->getShortName() . " thread");
$thread->join();
if(!$thread->isTerminated()){
$logger->debug("Joining " . (new \ReflectionClass($thread))->getShortName() . " thread");
$thread->join();
}else{
$logger->debug("Killing " . (new \ReflectionClass($thread))->getShortName() . " thread");
$thread->kill();
}
}
}

View File

@ -1602,7 +1602,7 @@ class Server{
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp() === "" ? "*" : $this->getIp(), $this->getPort()]));
define("BOOTUP_RANDOM", @Utils::getRandomBytes(16));
$this->serverID = Binary::readLong(substr(Utils::getUniqueID(true, $this->getIp() . $this->getPort()), 0, 8));
$this->serverID = Utils::getServerUniqueId($this->getIp() . $this->getPort());
$this->network = new Network($this);
$this->network->setName($this->getMotd());
@ -2234,7 +2234,7 @@ class Server{
$version = new VersionString();
$this->lastSendUsage = new SendUsageTask("https://stats.pocketmine.net/usage.php", [
"serverid" => $this->serverID,
"serverid" => Binary::readLong(substr(hex2bin(str_replace("-", "", $this->serverID)), 0, 8)),
"port" => $this->getPort(),
"os" => Utils::getOS(),
"name" => $this->getName(),

View File

@ -68,6 +68,8 @@ use pocketmine\level\format\generic\EmptyChunkSection;
use pocketmine\level\format\LevelProvider;
use pocketmine\level\generator\GenerationTask;
use pocketmine\level\generator\Generator;
use pocketmine\level\generator\GeneratorRegisterTask;
use pocketmine\level\generator\GeneratorUnregisterTask;
use pocketmine\level\generator\PopulationTask;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Math;
@ -341,6 +343,22 @@ class Level implements ChunkManager, Metadatable{
$generator = $this->generator;
$this->generatorInstance = new $generator($this->provider->getGeneratorOptions());
$this->generatorInstance->init($this, new Random($this->getSeed()));
$this->registerGenerator();
}
public function registerGenerator(){
$size = $this->server->getScheduler()->getAsyncTaskPoolSize();
for($i = 0; $i < $size; ++$i){
$this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorRegisterTask($this, $this->generatorInstance), $i);
}
}
public function unregisterGenerator(){
$size = $this->server->getScheduler()->getAsyncTaskPoolSize();
for($i = 0; $i < $size; ++$i){
$this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorUnregisterTask($this, $this->generatorInstance), $i);
}
}
/**
@ -383,6 +401,8 @@ class Level implements ChunkManager, Metadatable{
$this->unloadChunk($chunk->getX(), $chunk->getZ(), false);
}
$this->unregisterGenerator();
$this->provider->close();
$this->provider = null;
$this->blockMetadata = null;
@ -2378,7 +2398,7 @@ class Level implements ChunkManager, Metadatable{
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
}
}
$task = new PopulationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true));
$task = new PopulationTask($this, $this->getChunk($x, $z, true));
$this->server->getScheduler()->scheduleAsyncTask($task);
}
Timings::$generationTimer->stopTiming();
@ -2400,7 +2420,7 @@ class Level implements ChunkManager, Metadatable{
if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){
Timings::$generationTimer->startTiming();
$this->chunkGenerationQueue[$index] = true;
$task = new GenerationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true));
$task = new GenerationTask($this, $this->getChunk($x, $z, true));
$this->server->getScheduler()->scheduleAsyncTask($task);
Timings::$generationTimer->stopTiming();
}

View File

@ -32,17 +32,13 @@ use pocketmine\utils\Random;
class GenerationTask extends AsyncTask{
public $generator;
public $settings;
public $seed;
public $state;
public $levelId;
public $chunk;
public $chunkClass;
public function __construct(Level $level, Generator $generator, FullChunk $chunk){
$this->generator = get_class($generator);
$this->settings = $generator->getSettings();
$this->seed = $level->getSeed();
public function __construct(Level $level, FullChunk $chunk){
$this->state = true;
$this->levelId = $level->getId();
$this->chunk = $chunk->toFastBinary();
$this->chunkClass = get_class($chunk);
@ -50,19 +46,12 @@ class GenerationTask extends AsyncTask{
public function onRun(){
/** @var SimpleChunkManager $manager */
$manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager");
$manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager");
/** @var Generator $generator */
$generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator");
$generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator");
if($manager === null or $generator === null){
Block::init();
Biome::init();
$manager = new SimpleChunkManager($this->seed);
$this->saveToThreadStore($key, $manager);
/** @var Generator $generator */
$generator = $this->generator;
$generator = new $generator($this->settings);
$generator->init($manager, new Random($manager->getSeed()));
$this->saveToThreadStore($gKey, $generator);
$this->state = false;
return;
}
/** @var FullChunk $chunk */
@ -87,6 +76,10 @@ class GenerationTask extends AsyncTask{
public function onCompletion(Server $server){
$level = $server->getLevel($this->levelId);
if($level !== null){
if($this->state === false){
$level->registerGenerator();
return;
}
/** @var FullChunk $chunk */
$chunk = $this->chunkClass;
$chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider());

View File

@ -0,0 +1,58 @@
<?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/
*
*
*/
namespace pocketmine\level\generator;
use pocketmine\block\Block;
use pocketmine\level\format\FullChunk;
use pocketmine\level\generator\biome\Biome;
use pocketmine\level\Level;
use pocketmine\level\SimpleChunkManager;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
use pocketmine\utils\Random;
class GeneratorRegisterTask extends AsyncTask{
public $generator;
public $settings;
public $seed;
public $levelId;
public function __construct(Level $level, Generator $generator){
$this->generator = get_class($generator);
$this->settings = $generator->getSettings();
$this->seed = $level->getSeed();
$this->levelId = $level->getId();
}
public function onRun(){
Block::init();
Biome::init();
$manager = new SimpleChunkManager($this->seed);
$this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager);
/** @var Generator $generator */
$generator = $this->generator;
$generator = new $generator($this->settings);
$generator->init($manager, new Random($manager->getSeed()));
$this->saveToThreadStore("generation.level{$this->levelId}.generator", $generator);
}
}

View File

@ -0,0 +1,45 @@
<?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/
*
*
*/
namespace pocketmine\level\generator;
use pocketmine\block\Block;
use pocketmine\level\format\FullChunk;
use pocketmine\level\generator\biome\Biome;
use pocketmine\level\Level;
use pocketmine\level\SimpleChunkManager;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
use pocketmine\utils\Random;
class GeneratorUnregisterTask extends AsyncTask{
public $levelId;
public function __construct(Level $level){
$this->levelId = $level->getId();
}
public function onRun(){
$this->saveToThreadStore("generation.level{$this->levelId}.manager", null);
$this->saveToThreadStore("generation.level{$this->levelId}.generator", null);
}
}

View File

@ -32,9 +32,8 @@ use pocketmine\utils\Random;
class PopulationTask extends AsyncTask{
public $generator;
public $settings;
public $seed;
public $state;
public $levelId;
public $chunk;
public $chunkClass;
@ -49,10 +48,8 @@ class PopulationTask extends AsyncTask{
public $chunk21;
public $chunk22;
public function __construct(Level $level, Generator $generator, FullChunk $chunk){
$this->generator = get_class($generator);
$this->settings = $generator->getSettings();
$this->seed = $level->getSeed();
public function __construct(Level $level, FullChunk $chunk){
$this->state = true;
$this->levelId = $level->getId();
$this->chunk = $chunk->toFastBinary();
$this->chunkClass = get_class($chunk);
@ -70,19 +67,12 @@ class PopulationTask extends AsyncTask{
public function onRun(){
/** @var SimpleChunkManager $manager */
$manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager");
$manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager");
/** @var Generator $generator */
$generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator");
$generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator");
if($manager === null or $generator === null){
Block::init();
Biome::init();
$manager = new SimpleChunkManager($this->seed);
$this->saveToThreadStore($key, $manager);
/** @var Generator $generator */
$generator = $this->generator;
$generator = new $generator($this->settings);
$generator->init($manager, new Random($manager->getSeed()));
$this->saveToThreadStore($gKey, $generator);
$this->state = false;
return;
}
/** @var FullChunk[] $chunks */
@ -149,6 +139,10 @@ class PopulationTask extends AsyncTask{
public function onCompletion(Server $server){
$level = $server->getLevel($this->levelId);
if($level !== null){
if($this->state === false){
$level->registerGenerator();
return;
}
/** @var FullChunk[] $chunks */
$chunks = [];
/** @var FullChunk $chunkC */

View File

@ -48,18 +48,36 @@ class Utils{
}
}
public static function randomUUID(){
return Utils::toUUID(Binary::writeInt(time()) . Binary::writeShort(getmypid()) . Binary::writeShort(getmyuid()) . Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)) . Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), 2);
}
public static function dataToUUID(...$params){
return Utils::toUUID(hash("md5", implode($params), true), 3);
}
public static function toUUID($data, $version = 2, $fixed = "8"){
if(strlen($data) !== 16){
throw new \InvalidArgumentException("Data bust be 16 bytes");
}
$hex = bin2hex($data);
//xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 8-4-4-12
return substr($hex, 0, 8) . "-" . substr($hex, 8, 4) . "-" . hexdec($version) . substr($hex, 13, 3) . "-" . $fixed{0} . substr($hex, 17, 3) . "-" . substr($hex, 20, 12);
}
/**
* Gets this machine / server instance unique ID
* Returns a hash, the first 32 characters (or 16 if raw)
* will be an identifier that won't change frequently.
* The rest of the hash will change depending on other factors.
*
* @param bool $raw default false, if true, returns the raw identifier, not hexadecimal
* @param string $extra optional, additional data to identify the machine
*
* @return string
*/
public static function getUniqueID($raw = false, $extra = ""){
public static function getServerUniqueId($extra = ""){
$machine = php_uname("a");
$machine .= file_exists("/proc/cpuinfo") ? implode(preg_grep("/model name/", file("/proc/cpuinfo"))) : "";
$machine .= sys_get_temp_dir();
@ -96,7 +114,7 @@ class Utils{
$data .= $ext . ":" . phpversion($ext);
}
return hash("md5", $machine, $raw) . hash("sha512", $data, $raw);
return Utils::dataToUUID($machine, $data);
}
/**