diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index a82334a00..a87fbdd85 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -276,7 +276,7 @@ class Player extends Human implements CommandSender, IPlayer{ $this->server->removeOp($this->getName()); } - $this->perm->recalculatePermissions(); + $this->recalculatePermissions(); } /** @@ -316,7 +316,17 @@ class Player extends Human implements CommandSender, IPlayer{ } public function recalculatePermissions(){ + $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); + $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); + $this->perm->recalculatePermissions(); + + if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ + $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); + } + if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ + $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); + } } /** @@ -344,10 +354,10 @@ class Player extends Human implements CommandSender, IPlayer{ $this->CID = $ip . ":" . $port; $this->ip = $ip; $this->port = $port; - $this->spawnPosition = Level::getDefault()->getSafeSpawn(); + $this->spawnPosition = $this->server->getDefaultLevel()->getSafeSpawn(); $this->timeout = microtime(true) + 20; $this->gamemode = $this->server->getGamemode(); - $this->level = Level::getDefault(); + $this->level = $this->server->getDefaultLevel(); $this->viewDistance = $this->server->getViewDistance(); $this->slot = 0; $this->hotbar = array(0, -1, -1, -1, -1, -1, -1, -1, -1); @@ -1285,8 +1295,13 @@ class Player extends Human implements CommandSender, IPlayer{ return; } - $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); - $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); + + if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ + $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); + } + if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ + $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); + } //TODO /*$u = $this->server->matchPlayer($this->username); @@ -1305,8 +1320,8 @@ class Player extends Human implements CommandSender, IPlayer{ $nbt["NameTag"] = $this->username; } $this->gamemode = $nbt["playerGameType"] & 0x03; - if(($this->level = Level::get($nbt["Level"])) === false){ - $this->level = Level::getDefault(); + if(($this->level = $this->server->getLevel($nbt["Level"])) === null){ + $this->level = $this->server->getDefaultLevel(); $nbt["Level"] = $this->level->getName(); $nbt["Pos"][0] = $this->level->getSpawn()->x; $nbt["Pos"][1] = $this->level->getSpawn()->y; @@ -1358,7 +1373,7 @@ class Player extends Human implements CommandSender, IPlayer{ $this->dataPacket($pk); - if(($level = Level::get($this->namedtag["SpawnLevel"])) !== false){ + if(($level = $this->server->getLevel($this->namedtag["SpawnLevel"])) instanceof Level){ $this->spawnPosition = new Position($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); $pk = new SetSpawnPositionPacket; @@ -2287,7 +2302,6 @@ class Player extends Human implements CommandSender, IPlayer{ if(isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != ""){ $this->server->broadcastMessage($ev->getQuitMessage()); } - $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); $this->spawned = false; console("[INFO] " . TextFormat::AQUA . $this->username . TextFormat::RESET . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . $reason); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 6781f025b..6cadddec6 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -26,6 +26,8 @@ namespace pocketmine; use pocketmine\block\Block; +use pocketmine\block\Chest; +use pocketmine\block\Furnace; use pocketmine\command\CommandReader; use pocketmine\command\CommandSender; use pocketmine\command\ConsoleCommandSender; @@ -37,8 +39,12 @@ use pocketmine\event\server\PacketReceiveEvent; use pocketmine\event\server\PacketSendEvent; use pocketmine\event\server\ServerCommandEvent; use pocketmine\item\Item; +use pocketmine\level\generator\Flat; use pocketmine\level\generator\Generator; +use pocketmine\level\generator\Normal; use pocketmine\level\Level; +use pocketmine\level\LevelImport; +use pocketmine\level\WorldGenerator; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Compound; @@ -61,16 +67,21 @@ use pocketmine\permission\DefaultPermissions; use pocketmine\plugin\Plugin; use pocketmine\plugin\PluginLoadOrder; use pocketmine\plugin\PluginManager; +use pocketmine\pmf\LevelFormat; use pocketmine\recipes\Crafting; use pocketmine\scheduler\CallbackTask; use pocketmine\scheduler\ServerScheduler; use pocketmine\scheduler\TickScheduler; +use pocketmine\tile\Sign; use pocketmine\tile\Tile; use pocketmine\utils\Config; use pocketmine\utils\TextFormat; use pocketmine\utils\Utils; use pocketmine\utils\VersionString; +/** + * The class that manages everything + */ class Server{ const BROADCAST_CHANNEL_ADMINISTRATIVE = "pocketmine.broadcast.admin"; const BROADCAST_CHANNEL_USERS = "pocketmine.broadcast.user"; @@ -141,6 +152,12 @@ class Server{ /** @var Player[] */ private $players = array(); + /** @var Level[] */ + private $levels = array(); + + /** @var Level */ + private $levelDefault = null; + /** * @return string */ @@ -477,7 +494,7 @@ class Server{ $name = strtolower($name); $path = $this->getDataPath() . "players/"; if(!file_exists($path . "$name.dat")){ - $spawn = Level::getDefault()->getSafeSpawn(); + $spawn = $this->getDefaultLevel()->getSafeSpawn(); $nbt = new Compound(false, array( new Long("firstPlayed", floor(microtime(true) * 1000)), new Long("lastPlayed", floor(microtime(true) * 1000)), @@ -486,8 +503,8 @@ class Server{ new Double(1, $spawn->y), new Double(2, $spawn->z) )), - new String("Level", Level::getDefault()->getName()), - new String("SpawnLevel", Level::getDefault()->getName()), + new String("Level", $this->getDefaultLevel()->getName()), + new String("SpawnLevel", $this->getDefaultLevel()->getName()), new Int("SpawnX", (int) $spawn->x), new Int("SpawnY", (int) $spawn->y), new Int("SpawnZ", (int) $spawn->z), @@ -659,6 +676,257 @@ class Server{ } } + public function saveLevels(){ + foreach($this->getLevels() as $level){ + $level->save(); + } + } + + /** + * @return Level[] + */ + public function getLevels(){ + return $this->levels; + } + + /** + * @return Level + */ + public function getDefaultLevel(){ + return $this->levelDefault; + } + + /** + * Sets the default level to a different level + * This won't change the level-name property, + * it only affects the server on runtime + * + * @param Level $level + */ + public function setDefaultLevel($level){ + if($level === null or ($this->isLevelLoaded($level->getName()) and $level !== $this->levelDefault)){ + $this->levelDefault = $level; + } + } + + /** + * @param string $name + * + * @return bool + */ + public function isLevelLoaded($name){ + return isset($this->levels[$name]); + } + + /** + * @param string $name + * + * @return Level + */ + public function getLevel($name){ + if(isset($this->levels[$name])){ + return $this->levels[$name]; + } + + return null; + } + + /** + * @param Level $level + * @param bool $forceUnload + */ + public function unloadLevel(Level $level, $forceUnload = false){ + if((!$level->isLoaded() or $level->unload($forceUnload) === true) and $this->isLevelLoaded($level->getName())){ + unset($this->levels[$level->getName()]); + } + } + + /** + * Loads a level from the data directory + * + * @param string $name + * + * @return bool + */ + public function loadLevel($name){ + if(trim($name) === ""){ + trigger_error("Invalid empty level name", E_USER_WARNING); + + return false; + } + if($this->isLevelLoaded($name)){ + return true; + }elseif(!$this->isLevelGenerated($name)){ + console("[NOTICE] Level \"" . $name . "\" not found"); + } + + $path = $this->getDataPath() . "worlds/" . $name . "/"; + console("[INFO] Preparing level \"" . $name . "\""); + $level = new LevelFormat($path . "level.pmf"); + if(!$level->isLoaded){ + console("[ERROR] Could not load level \"" . $name . "\""); + + return false; + } + //$entities = new Config($path."entities.yml", Config::YAML); + if(file_exists($path . "tileEntities.yml")){ + @rename($path . "tileEntities.yml", $path . "tiles.yml"); + } + + $level = new Level($this, $level, $name); + $this->levels[$level->getName()] = $level; + /*foreach($entities->getAll() as $entity){ + if(!isset($entity["id"])){ + break; + } + if($entity["id"] === 64){ //Item Drop + $e = $this->server->api->entity->add($this->levels[$name], ENTITY_ITEM, $entity["Item"]["id"], array( + "meta" => $entity["Item"]["Damage"], + "stack" => $entity["Item"]["Count"], + "x" => $entity["Pos"][0], + "y" => $entity["Pos"][1], + "z" => $entity["Pos"][2], + "yaw" => $entity["Rotation"][0], + "pitch" => $entity["Rotation"][1], + )); + }elseif($entity["id"] === FALLING_SAND){ + $e = $this->server->api->entity->add($this->levels[$name], ENTITY_FALLING, $entity["id"], $entity); + $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); + $e->setHealth($entity["Health"]); + }elseif($entity["id"] === OBJECT_PAINTING or $entity["id"] === OBJECT_ARROW){ //Painting + $e = $this->server->api->entity->add($this->levels[$name], ENTITY_OBJECT, $entity["id"], $entity); + $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); + $e->setHealth(1); + }else{ + $e = $this->server->api->entity->add($this->levels[$name], ENTITY_MOB, $entity["id"], $entity); + $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); + $e->setHealth($entity["Health"]); + } + }*/ + + if(file_exists($path . "tiles.yml")){ + $tiles = new Config($path . "tiles.yml", Config::YAML); + foreach($tiles->getAll() as $tile){ + if(!isset($tile["id"])){ + continue; + } + $level->loadChunk($tile["x"] >> 4, $tile["z"] >> 4); + + $nbt = new Compound(false, array()); + foreach($tile as $index => $data){ + switch($index){ + case "Items": + $tag = new Enum("Items", array()); + $tag->setTagType(NBT::TAG_Compound); + foreach($data as $slot => $fields){ + $tag[(int) $slot] = new Compound(false, array( + "Count" => new Byte("Count", $fields["Count"]), + "Slot" => new Short("Slot", $fields["Slot"]), + "Damage" => new Short("Damage", $fields["Damage"]), + "id" => new String("id", $fields["id"]) + )); + } + $nbt["Items"] = $tag; + break; + + case "id": + case "Text1": + case "Text2": + case "Text3": + case "Text4": + $nbt[$index] = new String($index, $data); + break; + + case "x": + case "y": + case "z": + case "pairx": + case "pairz": + $nbt[$index] = new Int($index, $data); + break; + + case "BurnTime": + case "CookTime": + case "MaxTime": + $nbt[$index] = new Short($index, $data); + break; + } + } + switch($tile["id"]){ + case Tile::FURNACE: + new Furnace($level, $nbt); + break; + case Tile::CHEST: + new Chest($level, $nbt); + break; + case Tile::SIGN: + new Sign($level, $nbt); + break; + } + } + unlink($path . "tiles.yml"); + $level->save(true, true); + } + + return true; + } + + /** + * Generates a new level if it does not exists + * + * @param string $name + * @param int $seed + * @param string $generator Class name that extends pocketmine\level\generator\Generator + * @param array $options + * + * @return bool + */ + public function generateLevel($name, $seed = null, $generator = null, array $options = array()){ + if(trim($name) === "" or $this->isLevelGenerated($name)){ + return false; + } + + if($generator !== false and class_exists($generator) and is_subclass_of($generator, "pocketmine\\level\\generator\\Generator")){ + $generator = new $generator($options); + }else{ + if(strtoupper($this->getLevelType()) == "FLAT"){ + $generator = new Flat($options); + $options["preset"] = $this->getConfigString("generator-settings", ""); + }else{ + $generator = new Normal($options); + } + } + $gen = new WorldGenerator($this, $generator, $name, $seed === null ? Utils::readInt(Utils::getRandomBytes(4, false)) : (int) $seed); + $gen->generate(); + $gen->close(); + + return true; + } + + /** + * @param string $name + * + * @return bool + */ + public function isLevelGenerated($name){ + if(trim($name) === ""){ + return false; + } + $path = $this->getDataPath() . "worlds/" . $name . "/"; + if($this->getLevel($name) === false and !file_exists($path . "level.pmf")){ + if(file_exists($path)){ + $level = new LevelImport($path); + if($level->import() === false){ //Try importing a world + return false; + } + }else{ + return false; + } + } + + return true; + } + /** * @param string $variable * @param string $defaultValue @@ -968,7 +1236,22 @@ class Server{ Generator::addGenerator("pocketmine\\level\\generator\\Flat", "flat"); Generator::addGenerator("pocketmine\\level\\generator\\Normal", "normal"); Generator::addGenerator("pocketmine\\level\\generator\\Normal", "default"); - Level::init(); + + if($this->getDefaultLevel() === null){ + $default = $this->getConfigString("level-name", "world"); + if(trim($default) == ""){ + trigger_error("level-name cannot be null", E_USER_WARNING); + $default = "world"; + $this->setConfigString("level-name", "world"); + } + if($this->loadLevel($default) === false){ + $this->generateLevel($default, $this->getConfigInt("level-seed", time())); + $this->loadLevel($default); + } + + $this->setDefaultLevel($this->getLevel($default)); + } + $this->properties->save(); //TODO @@ -1118,8 +1401,8 @@ class Server{ $player->kick("server stop"); } - foreach(Level::getAll() as $level){ - $level->unload(true); + foreach($this->getLevels() as $level){ + $this->unloadLevel($level, true); } HandlerList::unregisterAll(); @@ -1268,14 +1551,14 @@ class Server{ //TODO: Add level blocks //Do level ticks - foreach(Level::getAll() as $level){ + foreach($this->getLevels() as $level){ $level->doTick(); } } public function doAutoSave(){ $this->broadcast(TextFormat::GRAY . "Saving...", self::BROADCAST_CHANNEL_ADMINISTRATIVE); - Level::saveAll(); + $this->saveLevels(); } public function sendUsage(){ diff --git a/src/pocketmine/command/defaults/SeedCommand.php b/src/pocketmine/command/defaults/SeedCommand.php index 2cfd38f4e..a9cee952f 100644 --- a/src/pocketmine/command/defaults/SeedCommand.php +++ b/src/pocketmine/command/defaults/SeedCommand.php @@ -22,8 +22,8 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; -use pocketmine\level\Level; use pocketmine\Player; +use pocketmine\Server; class SeedCommand extends VanillaCommand{ @@ -44,7 +44,7 @@ class SeedCommand extends VanillaCommand{ if($sender instanceof Player){ $seed = $sender->getLevel()->getSeed(); }else{ - $seed = Level::getDefault()->getSeed(); + $seed = Server::getInstance()->getDefaultLevel()->getSeed(); } $sender->sendMessage("Seed: " . $seed); diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 99b1742f0..b955a2297 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -31,16 +31,12 @@ use pocketmine\event\block\BlockBreakEvent; use pocketmine\event\block\BlockPlaceEvent; use pocketmine\event\player\PlayerInteractEvent; use pocketmine\item\Item; -use pocketmine\level\generator\Flat; use pocketmine\level\generator\Generator; -use pocketmine\level\generator\Normal; use pocketmine\math\Vector3 as Vector3; use pocketmine\nbt\NBT; -use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\Int; -use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\String; use pocketmine\network\protocol\SetTimePacket; use pocketmine\network\protocol\UpdateBlockPacket; @@ -52,9 +48,7 @@ use pocketmine\tile\Furnace; use pocketmine\tile\Sign; use pocketmine\tile\Tile; use pocketmine\utils\Cache; -use pocketmine\utils\Config; use pocketmine\utils\Random; -use pocketmine\utils\Utils; /** * Main Level handling class, includes all the methods used on them. @@ -67,39 +61,19 @@ class Level{ const BLOCK_UPDATE_WEAK = 4; const BLOCK_UPDATE_TOUCH = 5; - /** - * @var Level[] - */ - public static $list = array(); - - /** @var Level */ - public static $default = null; - - public static $saveEnabled = true; - - /** - * @var Player[] - */ + /** @var Player[] */ public $players = array(); - /** - * @var Entity[] - */ + /** @var Entity[] */ public $entities = array(); - /** - * @var Entity[][] - */ + /** @var Entity[][] */ public $chunkEntities = array(); - /** - * @var Tile[] - */ + /** @var Tile[] */ public $tiles = array(); - /** - * @var Tile[][] - */ + /** @var Tile[][] */ public $chunkTiles = array(); public $nextSave; @@ -107,264 +81,27 @@ class Level{ /** @var LevelFormat */ public $level; public $stopTime; - private $time, $startCheck, $startTime; + private $time; + private $startCheck; + private $startTime; /** @var Server */ private $server; - private $name, $usedChunks, $changedBlocks, $changedCount, $generator; + private $name; + private $usedChunks; + private $changedBlocks; + private $changedCount; + /** @var Generator */ + private $generator; - public static function init(){ - if(self::$default === null){ - $default = Server::getInstance()->getConfigString("level-name", null); - if($default == ""){ - trigger_error("level-name cannot be null", E_USER_ERROR); - - return; - } - if(self::loadLevel($default) === false){ - self::generateLevel($default, 0); //TODO: Server->getSeed(); - self::loadLevel($default); - } - self::$default = self::get($default); - } - } + private $autoSave = true; /** - * Saves all the levels - * - * @return void + * @param Server $server + * @param LevelFormat $level + * @param string $name */ - public static function saveAll(){ - foreach(self::$list as $level){ - $level->save(); - } - } - - /** - * Returns an array of all the loaded Levels - * - * @return Level[] - */ - public static function getAll(){ - return self::$list; - } - - /** - * Gets the default Level on the Server - * - * @return Level - */ - public static function getDefault(){ - return self::$default; - } - - /** - * Gets a loaded Level - * - * @param $name string Level name - * - * @return bool|Level - */ - public static function get($name){ - if($name !== "" and isset(self::$list[$name])){ - return self::$list[$name]; - } - - return false; - } - - /** - * Loads a level from the data directory - * - * @param $name - * - * @return bool - */ - public static function loadLevel($name){ - if(self::get($name) !== false){ - return true; - }elseif(self::levelExists($name) === false){ - console("[NOTICE] Level \"" . $name . "\" not found"); - - return false; - } - $path = \pocketmine\DATA . "worlds/" . $name . "/"; - console("[INFO] Preparing level \"" . $name . "\""); - $level = new LevelFormat($path . "level.pmf"); - if(!$level->isLoaded){ - console("[ERROR] Could not load level \"" . $name . "\""); - - return false; - } - //$entities = new Config($path."entities.yml", Config::YAML); - if(file_exists($path . "tileEntities.yml")){ - @rename($path . "tileEntities.yml", $path . "tiles.yml"); - } - $blockUpdates = new Config($path . "bupdates.yml", Config::YAML); - $level = new Level($level, $name); - /*foreach($entities->getAll() as $entity){ - if(!isset($entity["id"])){ - break; - } - if($entity["id"] === 64){ //Item Drop - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_ITEM, $entity["Item"]["id"], array( - "meta" => $entity["Item"]["Damage"], - "stack" => $entity["Item"]["Count"], - "x" => $entity["Pos"][0], - "y" => $entity["Pos"][1], - "z" => $entity["Pos"][2], - "yaw" => $entity["Rotation"][0], - "pitch" => $entity["Rotation"][1], - )); - }elseif($entity["id"] === FALLING_SAND){ - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_FALLING, $entity["id"], $entity); - $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); - $e->setHealth($entity["Health"]); - }elseif($entity["id"] === OBJECT_PAINTING or $entity["id"] === OBJECT_ARROW){ //Painting - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_OBJECT, $entity["id"], $entity); - $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); - $e->setHealth(1); - }else{ - $e = $this->server->api->entity->add($this->levels[$name], ENTITY_MOB, $entity["id"], $entity); - $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]); - $e->setHealth($entity["Health"]); - } - }*/ - - if(file_exists($path . "tiles.yml")){ - $tiles = new Config($path . "tiles.yml", Config::YAML); - foreach($tiles->getAll() as $tile){ - if(!isset($tile["id"])){ - continue; - } - $level->loadChunk($tile["x"] >> 4, $tile["z"] >> 4); - - $nbt = new Compound(false, array()); - foreach($tile as $index => $data){ - switch($index){ - case "Items": - $tag = new Enum("Items", array()); - $tag->setTagType(NBT::TAG_Compound); - foreach($data as $slot => $fields){ - $tag[(int) $slot] = new Compound(false, array( - "Count" => new Byte("Count", $fields["Count"]), - "Slot" => new Short("Slot", $fields["Slot"]), - "Damage" => new Short("Damage", $fields["Damage"]), - "id" => new String("id", $fields["id"]) - )); - } - $nbt["Items"] = $tag; - break; - - case "id": - case "Text1": - case "Text2": - case "Text3": - case "Text4": - $nbt[$index] = new String($index, $data); - break; - - case "x": - case "y": - case "z": - case "pairx": - case "pairz": - $nbt[$index] = new Int($index, $data); - break; - - case "BurnTime": - case "CookTime": - case "MaxTime": - $nbt[$index] = new Short($index, $data); - break; - } - } - switch($tile["id"]){ - case Tile::FURNACE: - new Furnace($level, $nbt); - break; - case Tile::CHEST: - new Chest($level, $nbt); - break; - case Tile::SIGN: - new Sign($level, $nbt); - break; - } - } - unlink($path . "tiles.yml"); - $level->save(true, true); - } - - //TODO - /*foreach($blockUpdates->getAll() as $bupdate){ - Server::getInstance()->api->block->scheduleBlockUpdate(new Position((int) $bupdate["x"], (int) $bupdate["y"], (int) $bupdate["z"], $level), (float) $bupdate["delay"], (int) $bupdate["type"]); - }*/ - - return true; - } - - /** - * Generates a new level - * - * @param string $name - * @param bool $seed - * @param bool $generator - * @param bool|array $options - * - * @return bool - */ - public static function generateLevel($name, $seed = false, $generator = false, $options = false){ - if($name == "" or self::levelExists($name)){ - return false; - } - $options = array(); - if($options === false and trim(Server::getInstance()->getConfigString("generator-settings", "")) !== ""){ - $options["preset"] = Server::getInstance()->getConfigString("generator-settings", ""); - } - - if($generator !== false and class_exists($generator)){ - $generator = new $generator($options); - }else{ - if(strtoupper(Server::getInstance()->getLevelType()) == "FLAT"){ - $generator = new Flat($options); - }else{ - $generator = new Normal($options); - } - } - $gen = new WorldGenerator($generator, $name, $seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)) : (int) $seed); - $gen->generate(); - $gen->close(); - - return true; - } - - /** - * Searches if a level exists on file - * - * @param string $name - * - * @return bool - */ - public static function levelExists($name){ - if($name === ""){ - return false; - } - $path = \pocketmine\DATA . "worlds/" . $name . "/"; - if(self::get($name) === false and !file_exists($path . "level.pmf")){ - if(file_exists($path)){ - $level = new LevelImport($path); - if($level->import() === false){ - return false; - } - }else{ - return false; - } - } - - return true; - } - - public function __construct(LevelFormat $level, $name){ - $this->server = Server::getInstance(); + public function __construct(Server $server, LevelFormat $level, $name){ + $this->server = $server; $this->level = $level; $this->level->level = $this; $this->startTime = $this->time = (int) $this->level->getData("time"); @@ -378,7 +115,20 @@ class Level{ $gen = Generator::getGenerator($this->level->levelData["generator"]); $this->generator = new $gen((array) $this->level->levelData["generatorSettings"]); $this->generator->init($this, new Random($this->level->levelData["seed"])); - self::$list[$name] = $this; + } + + /** + * @return bool + */ + public function getAutoSave(){ + return $this->autoSave === true; + } + + /** + * @param bool $value + */ + public function setAutoSave($value){ + $this->autoSave = $value; } public function close(){ @@ -394,22 +144,23 @@ class Level{ * @return bool */ public function unload($force = false){ - if($this === self::getDefault() and $force !== true){ + if($this === $this->server->getDefaultLevel() and $force !== true){ return false; } console("[INFO] Unloading level \"" . $this->getName() . "\""); $this->nextSave = PHP_INT_MAX; $this->save(); + $defaultLevel = $this->server->getDefaultLevel(); foreach($this->getPlayers() as $player){ - if($this === self::getDefault()){ - $player->close($player->getName() . " has left the game", "forced level unload"); - }else{ - $player->teleport(Level::getDefault()->getSafeSpawn()); + if($this === $defaultLevel or $defaultLevel === null){ + $player->close($player->getName() . " has left the game", "forced default level unload"); + }elseif($defaultLevel instanceof Level){ + $player->teleport($this->server->getDefaultLevel()->getSafeSpawn()); } } $this->close(); - if($this === self::getDefault()){ - self::$default = null; + if($this === $defaultLevel){ + $this->server->setDefaultLevel(null); } return true; @@ -581,7 +332,7 @@ class Level{ unset($this->usedChunks[$i]); LevelFormat::getXZ($i, $X, $Z); if(!$this->isSpawnChunk($X, $Z)){ - $this->level->unloadChunk($X, $Z, self::$saveEnabled); + $this->level->unloadChunk($X, $Z, $this->getAutoSave()); } } } @@ -617,14 +368,23 @@ class Level{ } public function __destruct(){ - unset(self::$list[$this->getName()]); if(isset($this->level)){ $this->save(false, false); $this->level->closeLevel(); - unset($this->level); + if($this->isLoaded()){ + unset($this->level); + $this->server->unloadLevel($this, true); + } } } + /** + * @return bool + */ + public function isLoaded(){ + return $this->level instanceof LevelFormat; + } + /** * @param bool $force * @param bool $extra @@ -635,10 +395,10 @@ class Level{ if(!isset($this->level)){ return false; } - //TODO: save enabled/disabled - /*if(self::$saveEnabled === false and $force === false){ + + if($this->getAutoSave() === false and $force === false){ return; - }*/ + } if($extra !== false){ $this->doSaveRoundExtra(); @@ -648,6 +408,8 @@ class Level{ $this->level->doSaveRound($force); $this->level->saveData(); $this->nextSave = microtime(true) + 45; + + return true; } protected function doSaveRoundExtra(){ @@ -884,7 +646,7 @@ class Level{ if($player instanceof Player){ $this->server->getPluginManager()->callEvent($ev = new PlayerInteractEvent($player, $item, $target, $face)); if(!$ev->isCancelled()){ - $target->onUpdate(Level::BLOCK_UPDATE_TOUCH); + $target->onUpdate(self::BLOCK_UPDATE_TOUCH); } } @@ -1164,7 +926,7 @@ class Level{ unset($this->chunkTiles[$index]); Cache::remove("world:{$this->name}:$X:$Z"); - return $this->level->unloadChunk($X, $Z, self::$saveEnabled); + return $this->level->unloadChunk($X, $Z, $this->getAutoSave()); } /** diff --git a/src/pocketmine/level/WorldGenerator.php b/src/pocketmine/level/WorldGenerator.php index 2d8995d59..7bfcdeda6 100644 --- a/src/pocketmine/level/WorldGenerator.php +++ b/src/pocketmine/level/WorldGenerator.php @@ -23,18 +23,24 @@ namespace pocketmine\level; use pocketmine\level\generator\Generator; use pocketmine\pmf\LevelFormat; -use pocketmine\utils\Config; +use pocketmine\Server; use pocketmine\utils\Random; use pocketmine\utils\Utils; class WorldGenerator{ - private $seed, $level, $path, $random, $generator, $height; + private $seed, $level, $path, $random, $generator, $server; - public function __construct(Generator $generator, $name, $seed = false, $height = 8){ - $this->seed = $seed !== false ? (int) $seed : Utils::readInt(Utils::getRandomBytes(4, false)); + /** + * @param Server $server + * @param Generator $generator + * @param string $name + * @param int $seed + */ + public function __construct(Server $server, Generator $generator, $name, $seed = null){ + $this->seed = $seed !== null ? (int) $seed : Utils::readInt(Utils::getRandomBytes(4, false)); $this->random = new Random($this->seed); - $this->height = (int) $height; - $this->path = \pocketmine\DATA . "worlds/" . $name . "/"; + $this->server = $server; + $this->path = $this->server->getDataPath() . "worlds/" . $name . "/"; $this->generator = $generator; $level = new LevelFormat($this->path . "level.pmf", array( "name" => $name, @@ -43,13 +49,12 @@ class WorldGenerator{ "spawnX" => 128, "spawnY" => 128, "spawnZ" => 128, - "height" => $this->height, + "height" => 8, "generator" => $this->generator->getName(), "generatorSettings" => $this->generator->getSettings(), "extra" => "" )); - $blockUpdates = new Config($this->path . "bupdates.yml", Config::YAML); - $this->level = new Level($level, $name); + $this->level = new Level($this->server, $level, $name); } public function generate(){ diff --git a/src/pocketmine/network/query/QueryHandler.php b/src/pocketmine/network/query/QueryHandler.php index 57ac78a53..5fe9b09d9 100644 --- a/src/pocketmine/network/query/QueryHandler.php +++ b/src/pocketmine/network/query/QueryHandler.php @@ -25,7 +25,6 @@ */ namespace pocketmine\network\query; -use pocketmine\level\Level; use pocketmine\Server; use pocketmine\utils\Utils; @@ -73,7 +72,7 @@ class QueryHandler{ "version" => $this->server->getVersion(), "server_engine" => $this->server->getName() . " " . $this->server->getPocketMineVersion(), "plugins" => $plist, - "map" => Level::getDefault()->getName(), + "map" => $this->server->getDefaultLevel()->getName(), "numplayers" => count($this->server->getOnlinePlayers()), "maxplayers" => $this->server->getMaxPlayers(), "whitelist" => $this->server->hasWhitelist() === true ? "on" : "off", @@ -131,7 +130,7 @@ class QueryHandler{ } $pk->payload = $this->longData; }else{ - $pk->payload = $this->server->getServerName() . "\x00" . (($this->server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP") . "\x00" . Level::getDefault()->getName() . "\x00" . count($this->server->getOnlinePlayers()) . "\x00" . $this->server->getMaxPlayers() . "\x00" . Utils::writeLShort($this->server->getPort()) . $this->server->getIp() . "\x00"; + $pk->payload = $this->server->getServerName() . "\x00" . (($this->server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP") . "\x00" . $this->server->getDefaultLevel()->getName() . "\x00" . count($this->server->getOnlinePlayers()) . "\x00" . $this->server->getMaxPlayers() . "\x00" . Utils::writeLShort($this->server->getPort()) . $this->server->getIp() . "\x00"; } $pk->encode(); $this->server->sendPacket($pk); diff --git a/src/pocketmine/utils/Utils.php b/src/pocketmine/utils/Utils.php index a5daf896b..3ee320b8e 100644 --- a/src/pocketmine/utils/Utils.php +++ b/src/pocketmine/utils/Utils.php @@ -27,10 +27,7 @@ namespace pocketmine\utils; use pocketmine\item\Item; /** - * Class Utils * Big collection of functions - * - * @package PocketMine\Utils */ class Utils{ const BIG_ENDIAN = 0x00;