PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/src/pocketmine/Server.php

https://github.com/MCMrARM/PocketMine-MP
PHP | 1972 lines | 1208 code | 242 blank | 522 comment | 83 complexity | b726a69da08ed4637682bd3160c8e8ae MD5 | raw file
Possible License(s): LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. *
  4. * ____ _ _ __ __ _ __ __ ____
  5. * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
  6. * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
  7. * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
  8. * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * @author PocketMine Team
  16. * @link http://www.pocketmine.net/
  17. *
  18. *
  19. */
  20. /**
  21. * PocketMine-MP is the Minecraft: PE multiplayer server software
  22. * Homepage: http://www.pocketmine.net/
  23. */
  24. namespace pocketmine;
  25. use pocketmine\block\Block;
  26. use pocketmine\command\CommandReader;
  27. use pocketmine\command\CommandSender;
  28. use pocketmine\command\ConsoleCommandSender;
  29. use pocketmine\command\PluginIdentifiableCommand;
  30. use pocketmine\command\SimpleCommandMap;
  31. use pocketmine\entity\Entity;
  32. use pocketmine\event\HandlerList;
  33. use pocketmine\event\server\ServerCommandEvent;
  34. use pocketmine\inventory\CraftingManager;
  35. use pocketmine\inventory\InventoryType;
  36. use pocketmine\inventory\Recipe;
  37. use pocketmine\item\Item;
  38. use pocketmine\level\format\LevelProviderManager;
  39. use pocketmine\level\generator\GenerationRequestManager;
  40. use pocketmine\level\generator\Generator;
  41. use pocketmine\level\Level;
  42. use pocketmine\metadata\EntityMetadataStore;
  43. use pocketmine\metadata\LevelMetadataStore;
  44. use pocketmine\metadata\PlayerMetadataStore;
  45. use pocketmine\nbt\NBT;
  46. use pocketmine\nbt\tag\Byte;
  47. use pocketmine\nbt\tag\Compound;
  48. use pocketmine\nbt\tag\Double;
  49. use pocketmine\nbt\tag\Enum;
  50. use pocketmine\nbt\tag\Float;
  51. use pocketmine\nbt\tag\Int;
  52. use pocketmine\nbt\tag\Long;
  53. use pocketmine\nbt\tag\Short;
  54. use pocketmine\nbt\tag\String;
  55. use pocketmine\network\protocol\DataPacket;
  56. use pocketmine\network\protocol\Info;
  57. use pocketmine\network\query\QueryHandler;
  58. use pocketmine\network\RakLibInterface;
  59. use pocketmine\network\rcon\RCON;
  60. use pocketmine\network\SourceInterface;
  61. use pocketmine\network\upnp\UPnP;
  62. use pocketmine\permission\BanList;
  63. use pocketmine\permission\DefaultPermissions;
  64. use pocketmine\plugin\Plugin;
  65. use pocketmine\plugin\PluginLoadOrder;
  66. use pocketmine\plugin\PluginManager;
  67. use pocketmine\scheduler\CallbackTask;
  68. use pocketmine\scheduler\SendUsageTask;
  69. use pocketmine\scheduler\ServerScheduler;
  70. use pocketmine\tile\Tile;
  71. use pocketmine\updater\AutoUpdater;
  72. use pocketmine\utils\Binary;
  73. use pocketmine\utils\Config;
  74. use pocketmine\utils\MainLogger;
  75. use pocketmine\utils\TextFormat;
  76. use pocketmine\utils\Utils;
  77. use pocketmine\utils\VersionString;
  78. /**
  79. * The class that manages everything
  80. */
  81. class Server{
  82. const BROADCAST_CHANNEL_ADMINISTRATIVE = "pocketmine.broadcast.admin";
  83. const BROADCAST_CHANNEL_USERS = "pocketmine.broadcast.user";
  84. /** @var Server */
  85. private static $instance = null;
  86. /** @var BanList */
  87. private $banByName = null;
  88. /** @var BanList */
  89. private $banByIP = null;
  90. /** @var Config */
  91. private $operators = null;
  92. /** @var Config */
  93. private $whitelist = null;
  94. /** @var bool */
  95. private $isRunning = true;
  96. /** @var PluginManager */
  97. private $pluginManager = null;
  98. /** @var AutoUpdater */
  99. private $updater = null;
  100. /** @var ServerScheduler */
  101. private $scheduler = null;
  102. /** @var GenerationRequestManager */
  103. private $generationManager = null;
  104. /**
  105. * Counts the ticks since the server start
  106. *
  107. * @var int
  108. */
  109. private $tickCounter;
  110. private $nextTick = 0;
  111. private $tickMeasure = 20;
  112. private $tickTime = 0;
  113. private $inTick = false;
  114. /** @var \Logger */
  115. private $logger;
  116. /** @var CommandReader */
  117. private $console = null;
  118. /** @var SimpleCommandMap */
  119. private $commandMap = null;
  120. /** @var CraftingManager */
  121. private $craftingManager;
  122. /** @var ConsoleCommandSender */
  123. private $consoleSender;
  124. /** @var int */
  125. private $maxPlayers;
  126. /** @var RCON */
  127. private $rcon;
  128. /** @var EntityMetadataStore */
  129. private $entityMetadata;
  130. /** @var PlayerMetadataStore */
  131. private $playerMetadata;
  132. /** @var LevelMetadataStore */
  133. private $levelMetadata;
  134. /** @var SourceInterface[] */
  135. private $interfaces = [];
  136. private $serverID;
  137. private $autoloader;
  138. private $filePath;
  139. private $dataPath;
  140. private $pluginPath;
  141. private $lastSendUsage = null;
  142. /** @var QueryHandler */
  143. private $queryHandler;
  144. /** @var Config */
  145. private $properties;
  146. /** @var Config */
  147. private $config;
  148. /** @var Player[] */
  149. private $players = [];
  150. /** @var Level[] */
  151. private $levels = [];
  152. /** @var Level */
  153. private $levelDefault = null;
  154. /**
  155. * @return string
  156. */
  157. public function getName(){
  158. return "PocketMine-MP";
  159. }
  160. /**
  161. * @return bool
  162. */
  163. public function isRunning(){
  164. return $this->isRunning === true;
  165. }
  166. /**
  167. * @return string
  168. */
  169. public function getPocketMineVersion(){
  170. return \pocketmine\VERSION;
  171. }
  172. /**
  173. * @return string
  174. */
  175. public function getCodename(){
  176. return \pocketmine\CODENAME;
  177. }
  178. /**
  179. * @return string
  180. */
  181. public function getVersion(){
  182. return \pocketmine\MINECRAFT_VERSION;
  183. }
  184. /**
  185. * @return string
  186. */
  187. public function getApiVersion(){
  188. return \pocketmine\API_VERSION;
  189. }
  190. /**
  191. * @return string
  192. */
  193. public function getFilePath(){
  194. return $this->filePath;
  195. }
  196. /**
  197. * @return string
  198. */
  199. public function getDataPath(){
  200. return $this->dataPath;
  201. }
  202. /**
  203. * @return string
  204. */
  205. public function getPluginPath(){
  206. return $this->pluginPath;
  207. }
  208. /**
  209. * @return int
  210. */
  211. public function getMaxPlayers(){
  212. return $this->maxPlayers;
  213. }
  214. /**
  215. * @return int
  216. */
  217. public function getPort(){
  218. return $this->getConfigInt("server-port", 19132);
  219. }
  220. /**
  221. * @return int
  222. */
  223. public function getViewDistance(){
  224. return min(11, max($this->getConfigInt("view-distance", 7), 4));
  225. }
  226. /**
  227. * @return string
  228. */
  229. public function getIp(){
  230. return $this->getConfigString("server-ip", "");
  231. }
  232. /**
  233. * @return string
  234. */
  235. public function getServerName(){
  236. return $this->getConfigString("server-name", "Unknown server");
  237. }
  238. /**
  239. * @return string
  240. */
  241. public function getLevelType(){
  242. return $this->getConfigString("level-type", "DEFAULT");
  243. }
  244. /**
  245. * @return bool
  246. */
  247. public function getGenerateStructures(){
  248. return $this->getConfigBoolean("generate-structures", true);
  249. }
  250. /**
  251. * @return int
  252. */
  253. public function getGamemode(){
  254. return $this->getConfigInt("gamemode", 0) & 0b11;
  255. }
  256. /**
  257. * @return bool
  258. */
  259. public function getForceGamemode(){
  260. return $this->getConfigBoolean("force-gamemode", false);
  261. }
  262. /**
  263. * Returns the gamemode text name
  264. *
  265. * @param int $mode
  266. *
  267. * @return string
  268. */
  269. public static function getGamemodeString($mode){
  270. switch((int) $mode){
  271. case Player::SURVIVAL:
  272. return "SURVIVAL";
  273. case Player::CREATIVE:
  274. return "CREATIVE";
  275. case Player::ADVENTURE:
  276. return "ADVENTURE";
  277. case Player::SPECTATOR:
  278. return "SPECTATOR";
  279. }
  280. return "UNKNOWN";
  281. }
  282. /**
  283. * Parses a string and returns a gamemode integer, -1 if not found
  284. *
  285. * @param string $str
  286. *
  287. * @return int
  288. */
  289. public static function getGamemodeFromString($str){
  290. switch(strtolower(trim($str))){
  291. case (string) Player::SURVIVAL:
  292. case "survival":
  293. case "s":
  294. return Player::SURVIVAL;
  295. case (string) Player::CREATIVE:
  296. case "creative":
  297. case "c":
  298. return Player::CREATIVE;
  299. case (string) Player::ADVENTURE:
  300. case "adventure":
  301. case "a":
  302. return Player::ADVENTURE;
  303. case (string) Player::SPECTATOR:
  304. case "spectator":
  305. case "view":
  306. case "v":
  307. return Player::SPECTATOR;
  308. }
  309. return -1;
  310. }
  311. /**
  312. * @param string $str
  313. *
  314. * @return int
  315. */
  316. public static function getDifficultyFromString($str){
  317. switch(strtolower(trim($str))){
  318. case "0":
  319. case "peaceful":
  320. case "p":
  321. return 0;
  322. case "1":
  323. case "easy":
  324. case "e":
  325. return 1;
  326. case "2":
  327. case "normal":
  328. case "n":
  329. return 2;
  330. case "3":
  331. case "hard":
  332. case "h":
  333. return 3;
  334. }
  335. return -1;
  336. }
  337. /**
  338. * @return int
  339. */
  340. public function getDifficulty(){
  341. return $this->getConfigInt("difficulty", 1);
  342. }
  343. /**
  344. * @return bool
  345. */
  346. public function hasWhitelist(){
  347. return $this->getConfigBoolean("white-list", false);
  348. }
  349. /**
  350. * @return int
  351. */
  352. public function getSpawnRadius(){
  353. return $this->getConfigInt("spawn-protection", 16);
  354. }
  355. /**
  356. * @return bool
  357. */
  358. public function getAllowFlight(){
  359. return $this->getConfigBoolean("allow-flight", false);
  360. }
  361. /**
  362. * @return bool
  363. */
  364. public function isHardcore(){
  365. return $this->getConfigBoolean("hardcore", false);
  366. }
  367. /**
  368. * @return int
  369. */
  370. public function getDefaultGamemode(){
  371. return $this->getConfigInt("gamemode", 0) & 0b11;
  372. }
  373. /**
  374. * @return string
  375. */
  376. public function getMotd(){
  377. return $this->getConfigString("motd", "Minecraft: PE Server");
  378. }
  379. /**
  380. * @return \SplClassLoader
  381. */
  382. public function getLoader(){
  383. return $this->autoloader;
  384. }
  385. /**
  386. * @return \ThreadedLogger
  387. */
  388. public function getLogger(){
  389. return $this->logger;
  390. }
  391. /**
  392. * @return EntityMetadataStore
  393. */
  394. public function getEntityMetadata(){
  395. return $this->entityMetadata;
  396. }
  397. /**
  398. * @return PlayerMetadataStore
  399. */
  400. public function getPlayerMetadata(){
  401. return $this->playerMetadata;
  402. }
  403. /**
  404. * @return LevelMetadataStore
  405. */
  406. public function getLevelMetadata(){
  407. return $this->levelMetadata;
  408. }
  409. /**
  410. * @return AutoUpdater
  411. */
  412. public function getUpdater(){
  413. return $this->updater;
  414. }
  415. /**
  416. * @return PluginManager
  417. */
  418. public function getPluginManager(){
  419. return $this->pluginManager;
  420. }
  421. /**
  422. * @return CraftingManager
  423. */
  424. public function getCraftingManager(){
  425. return $this->craftingManager;
  426. }
  427. /**
  428. * @return ServerScheduler
  429. */
  430. public function getScheduler(){
  431. return $this->scheduler;
  432. }
  433. /**
  434. * @return GenerationRequestManager
  435. */
  436. public function getGenerationManager(){
  437. return $this->generationManager;
  438. }
  439. /**
  440. * @return int
  441. */
  442. public function getTick(){
  443. return $this->tickCounter;
  444. }
  445. /**
  446. * Returns the last server TPS measure
  447. *
  448. * @return float
  449. */
  450. public function getTicksPerSecond(){
  451. return round((0.05 / $this->tickMeasure) * 20, 2);
  452. }
  453. /**
  454. * @return SourceInterface[]
  455. */
  456. public function getInterfaces(){
  457. return $this->interfaces;
  458. }
  459. /**
  460. * @param SourceInterface $interface
  461. */
  462. public function addInterface(SourceInterface $interface){
  463. $this->interfaces[] = $interface;
  464. }
  465. /**
  466. * @return SimpleCommandMap
  467. */
  468. public function getCommandMap(){
  469. return $this->commandMap;
  470. }
  471. /**
  472. * @return Player[]
  473. */
  474. public function getOnlinePlayers(){
  475. return $this->players;
  476. }
  477. public function addRecipe(Recipe $recipe){
  478. $this->craftingManager->registerRecipe($recipe);
  479. }
  480. /**
  481. * @param string $name
  482. *
  483. * @return OfflinePlayer|Player
  484. */
  485. public function getOfflinePlayer($name){
  486. $name = strtolower($name);
  487. $result = $this->getPlayerExact($name);
  488. if($result === null){
  489. $result = new OfflinePlayer($this, $name);
  490. }
  491. return $result;
  492. }
  493. /**
  494. * @param string $name
  495. *
  496. * @return Compound
  497. */
  498. public function getOfflinePlayerData($name){
  499. $name = strtolower($name);
  500. $path = $this->getDataPath() . "players/";
  501. if(!file_exists($path . "$name.dat")){
  502. $spawn = $this->getDefaultLevel()->getSafeSpawn();
  503. $nbt = new Compound(false, array(
  504. new Long("firstPlayed", floor(microtime(true) * 1000)),
  505. new Long("lastPlayed", floor(microtime(true) * 1000)),
  506. new Enum("Pos", array(
  507. new Double(0, $spawn->x),
  508. new Double(1, $spawn->y),
  509. new Double(2, $spawn->z)
  510. )),
  511. new String("Level", $this->getDefaultLevel()->getName()),
  512. new String("SpawnLevel", $this->getDefaultLevel()->getName()),
  513. new Int("SpawnX", (int) $spawn->x),
  514. new Int("SpawnY", (int) $spawn->y),
  515. new Int("SpawnZ", (int) $spawn->z),
  516. new Byte("SpawnForced", 1), //TODO
  517. new Enum("Inventory", []),
  518. new Compound("Achievements", []),
  519. new Int("playerGameType", $this->getGamemode()),
  520. new Enum("Motion", array(
  521. new Double(0, 0.0),
  522. new Double(1, 0.0),
  523. new Double(2, 0.0)
  524. )),
  525. new Enum("Rotation", array(
  526. new Float(0, 0.0),
  527. new Float(1, 0.0)
  528. )),
  529. new Float("FallDistance", 0.0),
  530. new Short("Fire", 0),
  531. new Short("Air", 0),
  532. new Byte("OnGround", 1),
  533. new Byte("Invulnerable", 0),
  534. new String("NameTag", $name),
  535. ));
  536. $nbt->Pos->setTagType(NBT::TAG_Double);
  537. $nbt->Inventory->setTagType(NBT::TAG_Compound);
  538. $nbt->Motion->setTagType(NBT::TAG_Double);
  539. $nbt->Rotation->setTagType(NBT::TAG_Float);
  540. if(file_exists($path . "$name.yml")){ //Importing old PocketMine-MP files
  541. $data = new Config($path . "$name.yml", Config::YAML, []);
  542. $nbt["playerGameType"] = (int) $data->get("gamemode");
  543. $nbt["Level"] = $data->get("position")["level"];
  544. $nbt["Pos"][0] = $data->get("position")["x"];
  545. $nbt["Pos"][1] = $data->get("position")["y"];
  546. $nbt["Pos"][2] = $data->get("position")["z"];
  547. $nbt["SpawnLevel"] = $data->get("spawn")["level"];
  548. $nbt["SpawnX"] = (int) $data->get("spawn")["x"];
  549. $nbt["SpawnY"] = (int) $data->get("spawn")["y"];
  550. $nbt["SpawnZ"] = (int) $data->get("spawn")["z"];
  551. $this->logger->notice("Old Player data found for \"" . $name . "\", upgrading profile");
  552. foreach($data->get("inventory") as $slot => $item){
  553. if(count($item) === 3){
  554. $nbt->Inventory[$slot + 9] = new Compound(false, array(
  555. new Short("id", $item[0]),
  556. new Short("Damage", $item[1]),
  557. new Byte("Count", $item[2]),
  558. new Byte("Slot", $slot + 9),
  559. new Byte("TrueSlot", $slot + 9)
  560. ));
  561. }
  562. }
  563. foreach($data->get("hotbar") as $slot => $itemSlot){
  564. if(isset($nbt->Inventory[$itemSlot + 9])){
  565. $item = $nbt->Inventory[$itemSlot + 9];
  566. $nbt->Inventory[$slot] = new Compound(false, array(
  567. new Short("id", $item->id),
  568. new Short("Damage", $item->Damage),
  569. new Byte("Count", $item->Count),
  570. new Byte("Slot", $slot),
  571. new Byte("TrueSlot", $item->TrueSlot)
  572. ));
  573. }
  574. }
  575. foreach($data->get("armor") as $slot => $item){
  576. if(count($item) === 2){
  577. $nbt->Inventory[$slot + 100] = new Compound(false, array(
  578. new Short("id", $item[0]),
  579. new Short("Damage", $item[1]),
  580. new Byte("Count", 1),
  581. new Byte("Slot", $slot + 100)
  582. ));
  583. }
  584. }
  585. foreach($data->get("achievements") as $achievement => $status){
  586. $nbt->Achievements[$achievement] = new Byte($achievement, $status == true ? 1 : 0);
  587. }
  588. unlink($path . "$name.yml");
  589. }else{
  590. $this->logger->notice("Player data not found for \"" . $name . "\", creating new profile");
  591. }
  592. $this->saveOfflinePlayerData($name, $nbt);
  593. return $nbt;
  594. }else{
  595. $nbt = new NBT(NBT::BIG_ENDIAN);
  596. $nbt->readCompressed(file_get_contents($path . "$name.dat"));
  597. return $nbt->getData();
  598. }
  599. }
  600. /**
  601. * @param string $name
  602. * @param Compound $nbtTag
  603. */
  604. public function saveOfflinePlayerData($name, Compound $nbtTag){
  605. $nbt = new NBT(NBT::BIG_ENDIAN);
  606. $nbt->setData($nbtTag);
  607. file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed());
  608. }
  609. /**
  610. * @param string $name
  611. *
  612. * @return Player
  613. */
  614. public function getPlayer($name){
  615. $found = null;
  616. $name = strtolower($name);
  617. $delta = PHP_INT_MAX;
  618. foreach($this->getOnlinePlayers() as $player){
  619. if(stripos($player->getName(), $name) === 0){
  620. $curDelta = strlen($player->getName()) - strlen($name);
  621. if($curDelta < $delta){
  622. $found = $player;
  623. $delta = $curDelta;
  624. }
  625. if($curDelta === 0){
  626. break;
  627. }
  628. }
  629. }
  630. return $found;
  631. }
  632. /**
  633. * @param string $name
  634. *
  635. * @return Player
  636. */
  637. public function getPlayerExact($name){
  638. $name = strtolower($name);
  639. foreach($this->getOnlinePlayers() as $player){
  640. if(strtolower($player->getName()) === $name){
  641. return $player;
  642. }
  643. }
  644. return null;
  645. }
  646. /**
  647. * @param string $partialName
  648. *
  649. * @return Player[]
  650. */
  651. public function matchPlayer($partialName){
  652. $partialName = strtolower($partialName);
  653. $matchedPlayers = [];
  654. foreach($this->getOnlinePlayers() as $player){
  655. if(strtolower($player->getName()) === $partialName){
  656. $matchedPlayers = array($player);
  657. break;
  658. }elseif(stripos($player->getName(), $partialName) !== false){
  659. $matchedPlayers[] = $player;
  660. }
  661. }
  662. return $matchedPlayers;
  663. }
  664. /**
  665. * @param Player $player
  666. */
  667. public function removePlayer(Player $player){
  668. if($player->isOnline() === false){
  669. unset($this->players[$player->getAddress() . ":" . $player->getPort()]);
  670. }
  671. }
  672. /**
  673. * @return Level[]
  674. */
  675. public function getLevels(){
  676. return $this->levels;
  677. }
  678. /**
  679. * @return Level
  680. */
  681. public function getDefaultLevel(){
  682. return $this->levelDefault;
  683. }
  684. /**
  685. * Sets the default level to a different level
  686. * This won't change the level-name property,
  687. * it only affects the server on runtime
  688. *
  689. * @param Level $level
  690. */
  691. public function setDefaultLevel($level){
  692. if($level === null or ($this->isLevelLoaded($level->getFolderName()) and $level !== $this->levelDefault)){
  693. $this->levelDefault = $level;
  694. }
  695. }
  696. /**
  697. * @param string $name
  698. *
  699. * @return bool
  700. */
  701. public function isLevelLoaded($name){
  702. return $this->getLevelByName($name) instanceof Level;
  703. }
  704. /**
  705. * @param int $levelId
  706. *
  707. * @return Level
  708. */
  709. public function getLevel($levelId){
  710. if(isset($this->levels[$levelId])){
  711. return $this->levels[$levelId];
  712. }
  713. return null;
  714. }
  715. /**
  716. * @param $name
  717. *
  718. * @return Level
  719. */
  720. public function getLevelByName($name){
  721. foreach($this->getLevels() as $level){
  722. if($level->getFolderName() === $name){
  723. return $level;
  724. }
  725. }
  726. return null;
  727. }
  728. /**
  729. * @param Level $level
  730. * @param bool $forceUnload
  731. */
  732. public function unloadLevel(Level $level, $forceUnload = false){
  733. if($level->unload($forceUnload) === true and $this->isLevelLoaded($level->getFolderName())){
  734. unset($this->levels[$level->getID()]);
  735. }
  736. }
  737. /**
  738. * Loads a level from the data directory
  739. *
  740. * @param string $name
  741. *
  742. * @return bool
  743. *
  744. * @throws \Exception
  745. */
  746. public function loadLevel($name){
  747. if(trim($name) === ""){
  748. throw new \Exception("Invalid empty level name");
  749. }
  750. if($this->isLevelLoaded($name)){
  751. return true;
  752. }elseif(!$this->isLevelGenerated($name)){
  753. $this->logger->notice("Level \"" . $name . "\" not found");
  754. return false;
  755. }
  756. $path = $this->getDataPath() . "worlds/" . $name . "/";
  757. $provider = LevelProviderManager::getProvider($path);
  758. if($provider === null){
  759. $this->logger->error("Could not load level \"" . $name . "\"");
  760. return false;
  761. }
  762. //$entities = new Config($path."entities.yml", Config::YAML);
  763. //if(file_exists($path . "tileEntities.yml")){
  764. // @rename($path . "tileEntities.yml", $path . "tiles.yml");
  765. //}
  766. $level = new Level($this, $name, $path, $provider);
  767. $this->levels[$level->getID()] = $level;
  768. /*foreach($entities->getAll() as $entity){
  769. if(!isset($entity["id"])){
  770. break;
  771. }
  772. if($entity["id"] === 64){ //Item Drop
  773. $e = $this->server->api->entity->add($this->levels[$name], ENTITY_ITEM, $entity["Item"]["id"], array(
  774. "meta" => $entity["Item"]["Damage"],
  775. "stack" => $entity["Item"]["Count"],
  776. "x" => $entity["Pos"][0],
  777. "y" => $entity["Pos"][1],
  778. "z" => $entity["Pos"][2],
  779. "yaw" => $entity["Rotation"][0],
  780. "pitch" => $entity["Rotation"][1],
  781. ));
  782. }elseif($entity["id"] === FALLING_SAND){
  783. $e = $this->server->api->entity->add($this->levels[$name], ENTITY_FALLING, $entity["id"], $entity);
  784. $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]);
  785. $e->setHealth($entity["Health"]);
  786. }elseif($entity["id"] === OBJECT_PAINTING or $entity["id"] === OBJECT_ARROW){ //Painting
  787. $e = $this->server->api->entity->add($this->levels[$name], ENTITY_OBJECT, $entity["id"], $entity);
  788. $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]);
  789. $e->setHealth(1);
  790. }else{
  791. $e = $this->server->api->entity->add($this->levels[$name], ENTITY_MOB, $entity["id"], $entity);
  792. $e->setPosition(new Vector3($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2]), $entity["Rotation"][0], $entity["Rotation"][1]);
  793. $e->setHealth($entity["Health"]);
  794. }
  795. }*/
  796. /*if(file_exists($path . "tiles.yml")){
  797. $tiles = new Config($path . "tiles.yml", Config::YAML);
  798. foreach($tiles->getAll() as $tile){
  799. if(!isset($tile["id"])){
  800. continue;
  801. }
  802. $level->loadChunk($tile["x"] >> 4, $tile["z"] >> 4);
  803. $nbt = new Compound(false, []);
  804. foreach($tile as $index => $data){
  805. switch($index){
  806. case "Items":
  807. $tag = new Enum("Items", []);
  808. $tag->setTagType(NBT::TAG_Compound);
  809. foreach($data as $slot => $fields){
  810. $tag[(int) $slot] = new Compound(false, array(
  811. "Count" => new Byte("Count", $fields["Count"]),
  812. "Slot" => new Short("Slot", $fields["Slot"]),
  813. "Damage" => new Short("Damage", $fields["Damage"]),
  814. "id" => new String("id", $fields["id"])
  815. ));
  816. }
  817. $nbt["Items"] = $tag;
  818. break;
  819. case "id":
  820. case "Text1":
  821. case "Text2":
  822. case "Text3":
  823. case "Text4":
  824. $nbt[$index] = new String($index, $data);
  825. break;
  826. case "x":
  827. case "y":
  828. case "z":
  829. case "pairx":
  830. case "pairz":
  831. $nbt[$index] = new Int($index, $data);
  832. break;
  833. case "BurnTime":
  834. case "CookTime":
  835. case "MaxTime":
  836. $nbt[$index] = new Short($index, $data);
  837. break;
  838. }
  839. }
  840. switch($tile["id"]){
  841. case Tile::FURNACE:
  842. new Furnace($level, $nbt);
  843. break;
  844. case Tile::CHEST:
  845. new Chest($level, $nbt);
  846. break;
  847. case Tile::SIGN:
  848. new Sign($level, $nbt);
  849. break;
  850. }
  851. }
  852. unlink($path . "tiles.yml");
  853. $level->save(true, true);
  854. }*/
  855. return true;
  856. }
  857. /**
  858. * Generates a new level if it does not exists
  859. *
  860. * @param string $name
  861. * @param int $seed
  862. * @param string $generator Class name that extends pocketmine\level\generator\Generator
  863. * @param array $options
  864. *
  865. * @return bool
  866. */
  867. public function generateLevel($name, $seed = null, $generator = null, array $options = []){
  868. if(trim($name) === "" or $this->isLevelGenerated($name)){
  869. return false;
  870. }
  871. $seed = $seed === null ? Binary::readInt(Utils::getRandomBytes(4, false)) : (int) $seed;
  872. if($generator !== null and class_exists($generator) and is_subclass_of($generator, "pocketmine\\level\\generator\\Generator")){
  873. $generator = new $generator($options);
  874. }else{
  875. if(strtoupper($this->getLevelType()) == "FLAT"){
  876. $generator = Generator::getGenerator("flat");
  877. $options["preset"] = $this->getConfigString("generator-settings", "");
  878. }else{
  879. $generator = Generator::getGenerator("normal");
  880. }
  881. }
  882. $provider = "pocketmine\\level\\format\\anvil\\Anvil";
  883. $path = $this->getDataPath() . "worlds/" . $name . "/";
  884. /** @var \pocketmine\level\format\LevelProvider $provider */
  885. $provider::generate($path, $name, $seed, $generator, $options);
  886. $level = new Level($this, $name, $path, $provider);
  887. $this->levels[$level->getID()] = $level;
  888. for($Z = 5; $Z <= 11; ++$Z){
  889. for($X = 5; $X <= 11; ++$X){
  890. $level->generateChunk($X, $Z);
  891. }
  892. }
  893. return true;
  894. }
  895. /**
  896. * @param string $name
  897. *
  898. * @return bool
  899. */
  900. public function isLevelGenerated($name){
  901. if(trim($name) === ""){
  902. return false;
  903. }
  904. $path = $this->getDataPath() . "worlds/" . $name . "/";
  905. if(!($this->getLevelByName($name) instanceof Level)){
  906. if(LevelProviderManager::getProvider($path) === null){
  907. return false;
  908. }
  909. /*if(file_exists($path)){
  910. $level = new LevelImport($path);
  911. if($level->import() === false){ //Try importing a world
  912. return false;
  913. }
  914. }else{
  915. return false;
  916. }*/
  917. }
  918. return true;
  919. }
  920. /**
  921. * @param string $variable
  922. * @param string $defaultValue
  923. *
  924. * @return string
  925. */
  926. public function getConfigString($variable, $defaultValue = ""){
  927. $v = getopt("", array("$variable::"));
  928. if(isset($v[$variable])){
  929. return (string) $v[$variable];
  930. }
  931. return $this->properties->exists($variable) ? $this->properties->get($variable) : $defaultValue;
  932. }
  933. /**
  934. * @param string $variable
  935. * @param mixed $defaultValue
  936. *
  937. * @return mixed
  938. */
  939. public function getProperty($variable, $defaultValue = null){
  940. $vars = explode(".", $variable);
  941. $base = array_shift($vars);
  942. if($this->config->exists($base)){
  943. $base = $this->config->get($base);
  944. }else{
  945. return $defaultValue;
  946. }
  947. while(count($vars) > 0){
  948. $baseKey = array_shift($vars);
  949. if(is_array($base) and isset($base[$baseKey])){
  950. $base = $base[$baseKey];
  951. }else{
  952. return $defaultValue;
  953. }
  954. }
  955. return $base;
  956. }
  957. /**
  958. * @param string $variable
  959. * @param string $value
  960. */
  961. public function setConfigString($variable, $value){
  962. $this->properties->set($variable, $value);
  963. }
  964. /**
  965. * @param string $variable
  966. * @param int $defaultValue
  967. *
  968. * @return int
  969. */
  970. public function getConfigInt($variable, $defaultValue = 0){
  971. $v = getopt("", array("$variable::"));
  972. if(isset($v[$variable])){
  973. return (int) $v[$variable];
  974. }
  975. return $this->properties->exists($variable) ? (int) $this->properties->get($variable) : (int) $defaultValue;
  976. }
  977. /**
  978. * @param string $variable
  979. * @param int $value
  980. */
  981. public function setConfigInt($variable, $value){
  982. $this->properties->set($variable, (int) $value);
  983. }
  984. /**
  985. * @param string $variable
  986. * @param boolean $defaultValue
  987. *
  988. * @return boolean
  989. */
  990. public function getConfigBoolean($variable, $defaultValue = false){
  991. $v = getopt("", array("$variable::"));
  992. if(isset($v[$variable])){
  993. $value = $v[$variable];
  994. }else{
  995. $value = $this->properties->exists($variable) ? $this->properties->get($variable) : $defaultValue;
  996. }
  997. if(is_bool($value)){
  998. return $value;
  999. }
  1000. switch(strtolower($value)){
  1001. case "on":
  1002. case "true":
  1003. case "1":
  1004. case "yes":
  1005. return true;
  1006. }
  1007. return false;
  1008. }
  1009. /**
  1010. * @param string $variable
  1011. * @param bool $value
  1012. */
  1013. public function setConfigBool($variable, $value){
  1014. $this->properties->set($variable, $value == true ? "1" : "0");
  1015. }
  1016. /**
  1017. * @param string $name
  1018. *
  1019. * @return PluginIdentifiableCommand
  1020. */
  1021. public function getPluginCommand($name){
  1022. if(($command = $this->commandMap->getCommand($name)) instanceof PluginIdentifiableCommand){
  1023. return $command;
  1024. }else{
  1025. return null;
  1026. }
  1027. }
  1028. /**
  1029. * @return BanList
  1030. */
  1031. public function getNameBans(){
  1032. return $this->banByName;
  1033. }
  1034. /**
  1035. * @return BanList
  1036. */
  1037. public function getIPBans(){
  1038. return $this->banByIP;
  1039. }
  1040. /**
  1041. * @param string $name
  1042. */
  1043. public function addOp($name){
  1044. $this->operators->set(strtolower($name), true);
  1045. if(($player = $this->getPlayerExact($name)) instanceof Player){
  1046. $player->recalculatePermissions();
  1047. }
  1048. $this->operators->save();
  1049. }
  1050. /**
  1051. * @param string $name
  1052. */
  1053. public function removeOp($name){
  1054. $this->operators->remove(strtolower($name));
  1055. if(($player = $this->getPlayerExact($name)) instanceof Player){
  1056. $player->recalculatePermissions();
  1057. }
  1058. $this->operators->save();
  1059. }
  1060. /**
  1061. * @param string $name
  1062. */
  1063. public function addWhitelist($name){
  1064. $this->whitelist->set(strtolower($name), true);
  1065. $this->whitelist->save();
  1066. }
  1067. /**
  1068. * @param string $name
  1069. */
  1070. public function removeWhitelist($name){
  1071. $this->whitelist->remove(strtolower($name));
  1072. $this->whitelist->save();
  1073. }
  1074. /**
  1075. * @param string $name
  1076. *
  1077. * @return bool
  1078. */
  1079. public function isWhitelisted($name){
  1080. return !$this->hasWhitelist() or $this->operators->exists($name, true) or $this->whitelist->exists($name, true);
  1081. }
  1082. /**
  1083. * @param string $name
  1084. *
  1085. * @return bool
  1086. */
  1087. public function isOp($name){
  1088. return $this->operators->exists($name, true);
  1089. }
  1090. /**
  1091. * @return Config
  1092. */
  1093. public function getWhitelisted(){
  1094. return $this->whitelist;
  1095. }
  1096. /**
  1097. * @return Config
  1098. */
  1099. public function getOPs(){
  1100. return $this->operators;
  1101. }
  1102. public function reloadWhitelist(){
  1103. $this->whitelist->reload();
  1104. }
  1105. /**
  1106. * @return string[]
  1107. */
  1108. public function getCommandAliases(){
  1109. $section = $this->getProperty("aliases");
  1110. $result = [];
  1111. if(is_array($section)){
  1112. foreach($section as $key => $value){
  1113. $commands = [];
  1114. if(is_array($value)){
  1115. $commands = $value;
  1116. }else{
  1117. $commands[] = $value;
  1118. }
  1119. $result[$key] = $commands;
  1120. }
  1121. }
  1122. return $result;
  1123. }
  1124. /**
  1125. * @return Server
  1126. */
  1127. public static function getInstance(){
  1128. return self::$instance;
  1129. }
  1130. /**
  1131. * @param \SplClassLoader $autoloader
  1132. * @param \ThreadedLogger $logger
  1133. * @param string $filePath
  1134. * @param string $dataPath
  1135. * @param string $pluginPath
  1136. */
  1137. public function __construct(\SplClassLoader $autoloader, \ThreadedLogger $logger, $filePath, $dataPath, $pluginPath){
  1138. self::$instance = $this;
  1139. $this->autoloader = $autoloader;
  1140. $this->logger = $logger;
  1141. $this->filePath = $filePath;
  1142. $this->dataPath = $dataPath;
  1143. $this->pluginPath = $pluginPath;
  1144. @mkdir($this->dataPath . "worlds/", 0777);
  1145. @mkdir($this->dataPath . "players/", 0777);
  1146. @mkdir($this->pluginPath, 0777);
  1147. $this->entityMetadata = new EntityMetadataStore();
  1148. $this->playerMetadata = new PlayerMetadataStore();
  1149. $this->levelMetadata = new LevelMetadataStore();
  1150. $this->operators = new Config($this->dataPath . "ops.txt", Config::ENUM);
  1151. $this->whitelist = new Config($this->dataPath . "white-list.txt", Config::ENUM);
  1152. if(file_exists($this->dataPath . "banned.txt") and !file_exists($this->dataPath . "banned-players.txt")){
  1153. @rename($this->dataPath . "banned.txt", $this->dataPath . "banned-players.txt");
  1154. }
  1155. @touch($this->dataPath . "banned-players.txt");
  1156. $this->banByName = new BanList($this->dataPath . "banned-players.txt");
  1157. $this->banByName->load();
  1158. @touch($this->dataPath . "banned-ips.txt");
  1159. $this->banByIP = new BanList($this->dataPath . "banned-ips.txt");
  1160. $this->banByIP->load();
  1161. $this->console = new CommandReader();
  1162. $version = new VersionString($this->getPocketMineVersion());
  1163. $this->logger->info("Starting Minecraft: PE server version " . TextFormat::AQUA . $this->getVersion());
  1164. $this->logger->info("Loading properties...");
  1165. if(!file_exists($this->dataPath . "pocketmine.yml")){
  1166. @file_put_contents($this->dataPath . "pocketmine.yml", file_get_contents($this->filePath . "src/pocketmine/resources/pocketmine.yml"));
  1167. }
  1168. $this->config = new Config($this->dataPath . "pocketmine.yml", Config::YAML, []);
  1169. $this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, array(
  1170. "motd" => "Minecraft: PE Server",
  1171. "server-port" => 19132,
  1172. "memory-limit" => "256M",
  1173. "white-list" => false,
  1174. "announce-player-achievements" => true,
  1175. "spawn-protection" => 16,
  1176. "view-distance" => 8,
  1177. "max-players" => 20,
  1178. "allow-flight" => false,
  1179. "spawn-animals" => true,
  1180. "spawn-mobs" => true,
  1181. "gamemode" => 0,
  1182. "force-gamemode" => false,
  1183. "hardcore" => false,
  1184. "pvp" => true,
  1185. "difficulty" => 1,
  1186. "generator-settings" => "",
  1187. "level-name" => "world",
  1188. "level-seed" => "",
  1189. "level-type" => "DEFAULT",
  1190. "enable-query" => true,
  1191. "enable-rcon" => false,
  1192. "rcon.password" => substr(base64_encode(Utils::getRandomBytes(20, false)), 3, 10),
  1193. "auto-save" => true,
  1194. ));
  1195. ServerScheduler::$WORKERS = $this->getProperty("settings.async-workers", ServerScheduler::$WORKERS);
  1196. $this->scheduler = new ServerScheduler();
  1197. if($this->getConfigBoolean("enable-rcon", false) === true){
  1198. $this->rcon = new RCON($this->getConfigString("rcon.password", ""), $this->getConfigInt("rcon.port", $this->getPort()), ($ip = $this->getIp()) != "" ? $ip : "0.0.0.0", $this->getConfigInt("rcon.threads", 1), $this->getConfigInt("rcon.clients-per-thread", 50));
  1199. }
  1200. $this->maxPlayers = $this->getConfigInt("max-players", 20);
  1201. if(($memory = str_replace("B", "", strtoupper($this->getConfigString("memory-limit", "128M")))) !== false){
  1202. $value = array("M" => 1, "G" => 1024);
  1203. $real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
  1204. if($real < 128){
  1205. $this->logger->warning("PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0);
  1206. }
  1207. @ini_set("memory_limit", $memory);
  1208. }else{
  1209. $this->setConfigString("memory-limit", "128M");
  1210. }
  1211. if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
  1212. $this->setConfigInt("difficulty", 3);
  1213. }
  1214. define("pocketmine\\DEBUG", $this->getProperty("debug.level", 1));
  1215. if($this->logger instanceof MainLogger){
  1216. $this->logger->setLogDebug(\pocketmine\DEBUG > 1);
  1217. }
  1218. define("ADVANCED_CACHE", $this->getProperty("settings.advanced-cache", false));
  1219. if(ADVANCED_CACHE == true){
  1220. $this->logger->info("Advanced cache enabled");
  1221. }
  1222. Level::$COMPRESSION_LEVEL = $this->getProperty("chunk-sending.compression-level", 7);
  1223. if(defined("pocketmine\\DEBUG") and \pocketmine\DEBUG >= 0 and function_exists("cli_set_process_title")){
  1224. @cli_set_process_title("PocketMine-MP " . $this->getPocketMineVersion());
  1225. }
  1226. $this->logger->info("Starting Minecraft PE server on " . ($this->getIp() === "" ? "*" : $this->getIp()) . ":" . $this->getPort());
  1227. define("BOOTUP_RANDOM", Utils::getRandomBytes(16));
  1228. $this->serverID = Binary::readLong(substr(Utils::getUniqueID(true, $this->getIp() . $this->getPort()), 0, 8));
  1229. $this->interfaces[] = new RakLibInterface($this);
  1230. $this->logger->info("This server is running PocketMine-MP version " . ($version->isDev() ? TextFormat::YELLOW : "") . $version->get(false) . TextFormat::RESET . " \"" . $this->getCodename() . "\" (API " . $this->getApiVersion() . ")", true, true, 0);
  1231. $this->logger->info("PocketMine-MP is distributed under the LGPL License", true, true, 0);
  1232. $this->consoleSender = new ConsoleCommandSender();
  1233. $this->commandMap = new SimpleCommandMap($this);
  1234. InventoryType::init();
  1235. Block::init();
  1236. Item::init();
  1237. $this->craftingManager = new CraftingManager();
  1238. $this->pluginManager = new PluginManager($this, $this->commandMap);
  1239. $this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender);
  1240. $this->pluginManager->registerInterface("pocketmine\\plugin\\PharPluginLoader");
  1241. $this->pluginManager->loadPlugins($this->pluginPath);
  1242. $this->updater = new AutoUpdater($this, $this->getProperty("auto-updater.host", "www.pocketmine.net"));
  1243. $this->enablePlugins(PluginLoadOrder::STARTUP);
  1244. $this->generationManager = new GenerationRequestManager($this);
  1245. LevelProviderManager::addProvider($this, "pocketmine\\level\\format\\anvil\\Anvil");
  1246. Generator::addGenerator("pocketmine\\level\\generator\\Flat", "flat");
  1247. Generator::addGenerator("pocketmine\\level\\generator\\Normal", "normal");
  1248. Generator::addGenerator("pocketmine\\level\\generator\\Normal", "default");
  1249. if($this->getDefaultLevel() === null){
  1250. $default = $this->getConfigString("level-name", "world");
  1251. if(trim($default) == ""){
  1252. trigger_error("level-name cannot be null, using default", E_USER_WARNING);
  1253. $default = "world";
  1254. $this->setConfigString("level-name", "world");
  1255. }
  1256. if($this->loadLevel($default) === false){
  1257. $seed = $this->getConfigInt("level-seed", time());
  1258. $this->generateLevel($default, $seed === 0 ? time() : $seed);
  1259. }
  1260. $this->setDefaultLevel($this->getLevelByName($default));
  1261. }
  1262. $this->properties->save();
  1263. $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask("pocketmine\\utils\\Cache::cleanup"), $this->getProperty("ticks-per.cache-cleanup", 900), $this->getProperty("ticks-per.cache-cleanup", 900));
  1264. if($this->getConfigBoolean("auto-save", true) === true and $this->getProperty("ticks-per.autosave", 6000) > 0){
  1265. $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "doAutoSave")), $this->getProperty("ticks-per.autosave", 6000), $this->getProperty("ticks-per.autosave", 6000));
  1266. }
  1267. if($this->getProperty("chunk-gc.period-in-ticks", 600) > 0){
  1268. $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "doLevelGC")), $this->getProperty("chunk-gc.period-in-ticks", 600), $this->getProperty("chunk-gc.period-in-ticks", 600));
  1269. }
  1270. $this->enablePlugins(PluginLoadOrder::POSTWORLD);
  1271. }
  1272. /**
  1273. * @param $message
  1274. *
  1275. * @return int
  1276. */
  1277. public function broadcastMessage($message){
  1278. return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS);
  1279. }
  1280. /**
  1281. * @param string $message
  1282. * @param string $permissions
  1283. *
  1284. * @return int
  1285. */
  1286. public function broadcast($message, $permissions){
  1287. /** @var CommandSender[] $recipients */
  1288. $recipients = [];
  1289. foreach(explode(";", $permissions) as $permission){
  1290. foreach($this->pluginManager->getPermissionSubscriptions($permission) as $permissible){
  1291. if($permissible instanceof CommandSender and $permissible->hasPermission($permission)){
  1292. $recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated
  1293. }
  1294. }
  1295. }
  1296. foreach($recipients as $recipient){
  1297. $recipient->sendMessage($message);
  1298. }
  1299. return count($recipients);
  1300. }
  1301. /**
  1302. * Broadcasts a Minecraft packet to a list of players
  1303. *
  1304. * @param Player[] $players
  1305. * @param DataPacket $packet
  1306. */
  1307. public function broadcastPacket(array $players, DataPacket $packet){
  1308. foreach($players as $player){
  1309. $player->dataPacket(clone $packet);
  1310. }
  1311. }
  1312. /**
  1313. * @param int $type
  1314. */
  1315. public function enablePlugins($type){
  1316. foreach($this->pluginManager->getPlugins() as $plugin){
  1317. if(!$plugin->isEnabled() and $plugin->getDescription()->getOrder() === $type){
  1318. $this->loadPlugin($plugin);
  1319. }
  1320. }
  1321. if($type === PluginLoadOrder::POSTWORLD){
  1322. $this->commandMap->registerServerAliases();
  1323. DefaultPermissions::registerCorePermissions();
  1324. }
  1325. }
  1326. /**
  1327. * @param Plugin $plugin
  1328. */
  1329. public function loadPlugin(Plugin $plugin){
  1330. $this->pluginManager->enablePlugin($plugin);
  1331. }
  1332. public function disablePlugins(){
  1333. $this->pluginManager->disablePlugins();
  1334. }
  1335. public function checkConsole(){
  1336. if(($line = $this->console->getLine()) !== null){
  1337. $this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line));
  1338. $this->dispatchCommand($this->consoleSender, $ev->getCommand());
  1339. }
  1340. }
  1341. /**
  1342. * Executes a command from a CommandSender
  1343. *
  1344. * @param CommandSender $sender
  1345. * @param string $commandLine
  1346. *
  1347. * @return bool
  1348. */
  1349. public function dispatchCommand(CommandSender $sender, $commandLine){
  1350. if($this->commandMap->dispatch($sender, $commandLine)){
  1351. return true;
  1352. }
  1353. if($sender instanceof Player){
  1354. $sender->sendMessage("Unknown command. Type \"/help\" for help.");
  1355. }else{
  1356. $sender->sendMessage("Unknown command. Type \"help\" for help.");
  1357. }
  1358. return false;
  1359. }
  1360. public function reload(){
  1361. $this->logger->info("Saving levels...");
  1362. foreach($this->levels as $level){
  1363. $level->save();
  1364. }
  1365. $this->pluginManager->disablePlugins();
  1366. $this->pluginManager->clearPlugins();
  1367. $this->commandMap->clearCommands();
  1368. $this->logger->info("Reloading properties...");
  1369. $this->properties->reload();
  1370. $this->maxPlayers = $this->getConfigInt("max-players", 20);
  1371. if(($memory = str_replace("B", "", strtoupper($this->getConfigString("memory-limit", "256M")))) !== false){
  1372. $value = array("M" => 1, "G" => 1024);
  1373. $real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
  1374. if($real < 256){
  1375. $this->logger->warning("PocketMine-MP may not work right with less than 256MB of RAM", true, true, 0);
  1376. }
  1377. @ini_set("memory_limit", $memory);
  1378. }else{
  1379. $this->setConfigString("memory-limit", "128M");
  1380. }
  1381. if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
  1382. $this->setConfigInt("difficulty", 3);
  1383. }
  1384. $this->banByIP->load();
  1385. $this->banByName->load();
  1386. $this->reloadWhitelist();
  1387. $this->operators->reload();
  1388. $this->pluginManager->registerInterface("pocketmine\\plugin\\PharPluginLoader");
  1389. $this->pluginManager->loadPlugins($this->pluginPath);
  1390. $this->enablePlugins(PluginLoadOrder::STARTUP);
  1391. $this->enablePlugins(PluginLoadOrder::POSTWORLD);
  1392. }
  1393. /**
  1394. * Shutdowns the server correctly
  1395. */
  1396. public function shutdown(){
  1397. $this->isRunning = false;
  1398. }
  1399. public function forceShutdown(){
  1400. $this->shutdown();
  1401. if($this->rcon instanceof RCON){
  1402. $this->rcon->stop();
  1403. }
  1404. if($this->getProperty("settings.upnp-forwarding", false) === true){
  1405. $this->logger->info("[UPnP] Removing port forward...");
  1406. UPnP::RemovePortForward($this->getPort());
  1407. }
  1408. $this->pluginManager->disablePlugins();
  1409. foreach($this->players as $player){
  1410. $player->close($player->getName() . " has left the game", $this->getProperty("settings.shutdown-message", "Server closed"));
  1411. }
  1412. foreach($this->getLevels() as $level){
  1413. $this->unloadLevel($level, true);
  1414. }
  1415. $this->generationManager->shutdown();
  1416. HandlerList::unregisterAll();
  1417. $this->scheduler->cancelAllTasks();
  1418. $this->scheduler->mainThreadHeartbeat(PHP_INT_MAX);
  1419. $this->properties->save();
  1420. $this->console->kill();
  1421. foreach($this->interfaces as $interface){
  1422. $interface->shutdown();
  1423. }
  1424. }
  1425. /**
  1426. * Starts the PocketMine-MP server and starts processing ticks and packets
  1427. */
  1428. public function start(){
  1429. if($this->getConfigBoolean("enable-query", true) === true){
  1430. //$this->queryHandler = new QueryHandler();
  1431. //TODO: query
  1432. }
  1433. if($this->getProperty("settings.send-usage", true) !== false){
  1434. $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "sendUsage")), 6000, 6000);
  1435. $this->sendUsage();
  1436. }
  1437. if($this->getProperty("settings.upnp-forwarding", false) == true){
  1438. $this->logger->info("[UPnP] Trying to port forward...");
  1439. UPnP::PortForward($this->getPort());
  1440. }
  1441. $this->tickCounter = 0;
  1442. //register_shutdown_function(array($this, "dumpError"));
  1443. register_shutdown_function(array($this, "forceShutdown"));
  1444. if(function_exists("pcntl_signal")){
  1445. pcntl_signal(SIGTERM, array($this, "shutdown"));
  1446. pcntl_signal(SIGINT, array($this, "shutdown"));
  1447. pcntl_signal(SIGHUP, array($this, "shutdown"));
  1448. }
  1449. $this->logger->info("Default game type: " . self::getGamemodeString($this->getGamemode())); //TODO: string name
  1450. $this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"');
  1451. if(Utils::getOS() === "win"){ //Workaround less usleep() waste
  1452. $this->tickProcessorWindows();
  1453. }else{
  1454. $this->tickProcessor();
  1455. }
  1456. $this->forceShutdown();
  1457. }
  1458. private function tickProcessorWindows(){
  1459. $lastLoop = 0;
  1460. while($this->isRunning){
  1461. foreach($this->interfaces as $interface){
  1462. if($interface->process()){
  1463. $lastLoop = 0;
  1464. }
  1465. }
  1466. $this->generationManager->handlePackets();
  1467. if(($ticks = $this->tick()) !== true){
  1468. ++$lastLoop;
  1469. if($lastLoop > 128){
  1470. usleep(1000);
  1471. }
  1472. }else{
  1473. $lastLoop = 0;
  1474. }
  1475. }
  1476. }
  1477. public function checkTicks(){
  1478. if($this->getTicksPerSecond() < 12){
  1479. $this->logger->warning("Can't keep up! Is the server overloaded?");
  1480. }
  1481. }
  1482. public function checkMemory(){
  1483. //TODO
  1484. $info = $this->debugInfo();
  1485. $data = $info["memory_usage"] . "," . $info["players"] . "," . $info["entities"];
  1486. $i = count($this->memoryStats) - 1;
  1487. if($i < 0 or $this->memoryStats[$i] !== $data){
  1488. $this->memoryStats[] = $data;
  1489. }
  1490. }
  1491. public function dumpError(){
  1492. //TODO
  1493. if($this->stop === true){
  1494. return;
  1495. }
  1496. ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems
  1497. $this->logger->emergency("An unrecoverable has occurred and the server has crashed. Creating an error dump");
  1498. $dump = "```\r\n# PocketMine-MP Error Dump " . date("D M j H:i:s T Y") . "\r\n";
  1499. $er = error_get_last();
  1500. $errorConversion = array(
  1501. E_ERROR => "E_ERROR",
  1502. E_WARNING => "E_WARNING",
  1503. E_PARSE => "E_PARSE",
  1504. E_NOTICE => "E_NOTICE",
  1505. E_CORE_ERROR => "E_CORE_ERROR",
  1506. E_CORE_WARNING => "E_CORE_WARNING",
  1507. E_COMPILE_ERROR => "E_COMPILE_ERROR",
  1508. E_COMPILE_WARNING => "E_COMPILE_WARNING",
  1509. E_USER_ERROR => "E_USER_ERROR",
  1510. E_USER_WARNING => "E_USER_WARNING",
  1511. E_USER_NOTICE => "E_USER_NOTICE",
  1512. E_STRICT => "E_STRICT",
  1513. E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR",
  1514. E_DEPRECATED => "E_DEPRECATED",
  1515. E_USER_DEPRECATED => "E_USER_DEPRECATED",
  1516. );
  1517. $er["type"] = isset($errorConversion[$er["type"]]) ? $errorConversion[$er["type"]] : $er["type"];
  1518. $dump .= "Error: " . var_export($er, true) . "\r\n\r\n";
  1519. if(stripos($er["file"], "plugin") !== false){
  1520. $dump .= "THIS ERROR WAS CAUSED BY A PLUGIN. REPORT IT TO THE PLUGIN DEVELOPER.\r\n";
  1521. }
  1522. $dump .= "Code: \r\n";
  1523. $file = @file($er["file"], FILE_IGNORE_NEW_LINES);
  1524. for($l = max(0, $er["line"] - 10); $l < $er["line"] + 10; ++$l){
  1525. $dump .= "[" . ($l + 1) . "] " . @$file[$l] . "\r\n";
  1526. }
  1527. $dump .= "\r\n\r\n";
  1528. $dump .= "Backtrace: \r\n";
  1529. foreach(getTrace() as $line){
  1530. $dump .= "$line\r\n";
  1531. }
  1532. $dump .= "\r\n\r\n";
  1533. $version = new VersionString();
  1534. $dump .= "PocketMine-MP version: " . $version->get(false). " #" . $version->getNumber() . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]\r\n";
  1535. $dump .= "Git commit: " . GIT_COMMIT . "\r\n";
  1536. $dump .= "uname -a: " . php_uname("a") . "\r\n";
  1537. $dump .= "PHP Version: " . phpversion() . "\r\n";
  1538. $dump .= "Zend version: " . zend_version() . "\r\n";
  1539. $dump .= "OS : " . PHP_OS . ", " . Utils::getOS() . "\r\n";
  1540. $dump .= "Debug Info: " . var_export($this->debugInfo(false), true) . "\r\n\r\n\r\n";
  1541. global $arguments;
  1542. $dump .= "Parameters: " . var_export($arguments, true) . "\r\n\r\n\r\n";
  1543. $p = $this->api->getProperties();
  1544. if($p["rcon.password"] != ""){
  1545. $p["rcon.password"] = "******";
  1546. }
  1547. $dump .= "server.properties: " . var_export($p, true) . "\r\n\r\n\r\n";
  1548. if(class_exists("pocketmine\\plugin\\PluginManager", false)){
  1549. $dump .= "Loaded plugins:\r\n";
  1550. foreach($this->getPluginManager()->getPlugins() as $p){
  1551. $d = $p->getDescription();
  1552. $dump .= $d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . "\r\n";
  1553. }
  1554. $dump .= "\r\n\r\n";
  1555. }
  1556. $extensions = [];
  1557. foreach(get_loaded_extensions() as $ext){
  1558. $extensions[$ext] = phpversion($ext);
  1559. }
  1560. $dump .= "Loaded Modules: " . var_export($extensions, true) . "\r\n";
  1561. $this->checkMemory();
  1562. $dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n";
  1563. ob_start();
  1564. phpinfo();
  1565. $dump .= "\r\nphpinfo(): \r\n" . chunk_split(base64_encode(gzdeflate(ob_get_contents(), 9))) . "\r\n";
  1566. ob_end_clean();
  1567. $dump .= "\r\n```";
  1568. $name = "Error_Dump_" . date("D_M_j-H.i.s-T_Y");
  1569. //log($dump, $name, true, 0, true);
  1570. $this->logger->emergency("Please submit the \"{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0);
  1571. }
  1572. private function tickProcessor(){
  1573. $lastLoop = 0;
  1574. while($this->isRunning){
  1575. foreach($this->interfaces as $interface){
  1576. if($interface->process()){
  1577. $lastLoop = 0;
  1578. }
  1579. }
  1580. $this->generationManager->handlePackets();
  1581. if(($ticks = $this->tick()) !== true){
  1582. ++$lastLoop;
  1583. if($lastLoop > 16 and $lastLoop < 128){
  1584. usleep(200);
  1585. }elseif($lastLoop < 512){
  1586. usleep(400);
  1587. }else{
  1588. usleep(1000);
  1589. }
  1590. }else{
  1591. $lastLoop = 0;
  1592. }
  1593. }
  1594. }
  1595. public function addPlayer($identifier, Player $player){
  1596. $this->players[$identifier] = $player;
  1597. }
  1598. private function checkTickUpdates($currentTick){
  1599. //Update entities that need update
  1600. if(count(Entity::$needUpdate) > 0){
  1601. foreach(Entity::$needUpdate as $id => $entity){
  1602. if($entity->onUpdate() === false){
  1603. unset(Entity::$needUpdate[$id]);
  1604. }
  1605. }
  1606. }
  1607. //Update tiles that need update
  1608. if(count(Tile::$needUpdate) > 0){
  1609. foreach(Tile::$needUpdate as $id => $tile){
  1610. if($tile->onUpdate() === false){
  1611. unset(Tile::$needUpdate[$id]);
  1612. }
  1613. }
  1614. }
  1615. //TODO: Add level blocks
  1616. //Do level ticks
  1617. foreach($this->getLevels() as $level){
  1618. $level->doTick($currentTick);
  1619. }
  1620. }
  1621. public function doAutoSave(){
  1622. /*foreach($this->getOnlinePlayers() as $player){
  1623. $player->save();
  1624. }*/
  1625. foreach($this->getLevels() as $level){
  1626. $level->save();
  1627. }
  1628. }
  1629. public function doLevelGC(){
  1630. foreach($this->getLevels() as $level){
  1631. $level->doChunkGarbageCollection();
  1632. }
  1633. }
  1634. public function sendUsage(){
  1635. if($this->lastSendUsage instanceof SendUsageTask){
  1636. if(!$this->lastSendUsage->isFinished()){ //do not call multiple times
  1637. return;
  1638. }
  1639. }
  1640. $plist = "";
  1641. foreach($this->getPluginManager()->getPlugins() as $p){
  1642. $d = $p->getDescription();
  1643. $plist .= str_replace(array(";", ":"), "", $d->getName()) . ":" . str_replace(array(";", ":"), "", $d->getVersion()) . ";";
  1644. }
  1645. $version = new VersionString();
  1646. $this->lastSendUsage = new SendUsageTask("http://stats.pocketmine.net/usage.php", array(
  1647. "serverid" => Binary::readLong(substr(Utils::getUniqueID(true, $this->getIp() . ":" . $this->getPort()), 0, 8)),
  1648. "port" => $this->getPort(),
  1649. "os" => Utils::getOS(),
  1650. "memory_total" => $this->getConfigString("memory-limit"),
  1651. "memory_usage" => memory_get_usage(),
  1652. "php_version" => \pocketmine\PHP_VERSION,
  1653. "version" => $version->get(false),
  1654. "build" => $version->getBuild(),
  1655. "mc_version" => \pocketmine\MINECRAFT_VERSION,
  1656. "protocol" => network\protocol\Info::CURRENT_PROTOCOL,
  1657. "online" => count($this->players),
  1658. "max" => $this->getMaxPlayers(),
  1659. "plugins" => $plist,
  1660. ));
  1661. $this->scheduler->scheduleAsyncTask($this->lastSendUsage);
  1662. }
  1663. public function titleTick(){
  1664. if(defined("pocketmine\\DEBUG") and \pocketmine\DEBUG >= 0 and \pocketmine\ANSI === true){
  1665. echo "\x1b]0;PocketMine-MP " . $this->getPocketMineVersion() . " | Online " . count($this->players) . "/" . $this->getMaxPlayer

Large files files are truncated, but you can click here to view the full file