PocketMine-MP 5.11.1 git-db894e3a4a5bb9a80b3ac07ac91f58bdaf15176a
PocketMine.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20*/
21
22declare(strict_types=1);
23
24namespace pocketmine {
25
26 use Composer\InstalledVersions;
38 use Symfony\Component\Filesystem\Path;
39 use function defined;
40 use function extension_loaded;
41 use function function_exists;
42 use function getcwd;
43 use function is_dir;
44 use function mkdir;
45 use function phpversion;
46 use function preg_match;
47 use function preg_quote;
48 use function realpath;
49 use function version_compare;
50 use const DIRECTORY_SEPARATOR;
51
52 require_once __DIR__ . '/VersionInfo.php';
53
54 const MIN_PHP_VERSION = "8.1.0";
55
60 function critical_error($message){
61 echo "[ERROR] $message" . PHP_EOL;
62 }
63
64 /*
65 * Startup code. Do not look at it, it may harm you.
66 * This is the only non-class based file on this project.
67 * Enjoy it as much as I did writing it. I don't want to do it again.
68 */
69
74 if(version_compare(MIN_PHP_VERSION, PHP_VERSION) > 0){
75 //If PHP version isn't high enough, anything below might break, so don't bother checking it.
76 return [
77 "PHP >= " . MIN_PHP_VERSION . " is required, but you have PHP " . PHP_VERSION . "."
78 ];
79 }
80
81 $messages = [];
82
83 if(PHP_INT_SIZE < 8){
84 $messages[] = "32-bit systems/PHP are no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system.";
85 }
86
87 if(php_sapi_name() !== "cli"){
88 $messages[] = "Only PHP CLI is supported.";
89 }
90
91 $extensions = [
92 "chunkutils2" => "PocketMine ChunkUtils v2",
93 "curl" => "cURL",
94 "crypto" => "php-crypto",
95 "ctype" => "ctype",
96 "date" => "Date",
97 "gmp" => "GMP",
98 "hash" => "Hash",
99 "igbinary" => "igbinary",
100 "json" => "JSON",
101 "leveldb" => "LevelDB",
102 "mbstring" => "Multibyte String",
103 "morton" => "morton",
104 "openssl" => "OpenSSL",
105 "pcre" => "PCRE",
106 "phar" => "Phar",
107 "pmmpthread" => "pmmpthread",
108 "reflection" => "Reflection",
109 "sockets" => "Sockets",
110 "spl" => "SPL",
111 "yaml" => "YAML",
112 "zip" => "Zip",
113 "zlib" => "Zlib"
114 ];
115
116 foreach($extensions as $ext => $name){
117 if(!extension_loaded($ext)){
118 $messages[] = "Unable to find the $name ($ext) extension.";
119 }
120 }
121
122 if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
123 if(version_compare($pmmpthread_version, "6.0.7") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
124 $messages[] = "pmmpthread ^6.0.7 is required, while you have $pmmpthread_version.";
125 }
126 }
127
128 if(($leveldb_version = phpversion("leveldb")) !== false){
129 if(version_compare($leveldb_version, "0.2.1") < 0){
130 $messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
131 }
132 if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
133 $messages[] = "Given version of php-leveldb doesn't support ZLIB_RAW compression (use https://github.com/pmmp/php-leveldb)";
134 }
135 }
136
137 $chunkutils2_version = phpversion("chunkutils2");
138 $wantedVersionLock = "0.3";
139 $wantedVersionMin = "$wantedVersionLock.0";
140 if($chunkutils2_version !== false && (
141 version_compare($chunkutils2_version, $wantedVersionMin) < 0 ||
142 preg_match("/^" . preg_quote($wantedVersionLock, "/") . "\.\d+(?:-dev)?$/", $chunkutils2_version) === 0 //lock in at ^0.2, optionally at a patch release
143 )){
144 $messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
145 }
146
147 if(($libdeflate_version = phpversion("libdeflate")) !== false){
148 //make sure level 0 compression is available
149 if(version_compare($libdeflate_version, "0.2.0") < 0 || version_compare($libdeflate_version, "0.3.0") >= 0){
150 $messages[] = "php-libdeflate ^0.2.0 is required, while you have $libdeflate_version.";
151 }
152 }
153
154 if(extension_loaded("pocketmine")){
155 $messages[] = "The native PocketMine extension is no longer supported.";
156 }
157
158 if(!defined('AF_INET6')){
159 $messages[] = "IPv6 support is required, but your PHP binary was built without IPv6 support.";
160 }
161
162 return $messages;
163 }
164
169 if(PHP_DEBUG !== 0){
170 $logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
171 }
172 if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){
173 $logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
174 }
175 if(((int) ini_get('zend.assertions')) !== -1){
176 $logger->warning("Debugging assertions are enabled. This may degrade performance. To disable them, set `zend.assertions = -1` in php.ini.");
177 }
178 if(\Phar::running(true) === ""){
179 $logger->warning("Non-packaged installation detected. This will degrade autoloading speed and make startup times longer.");
180 }
181 if(function_exists('opcache_get_status') && ($opcacheStatus = opcache_get_status(false)) !== false){
182 $jitEnabled = $opcacheStatus["jit"]["on"] ?? false;
183 if($jitEnabled !== false){
184 $logger->warning(<<<'JIT_WARNING'
185
186
187 --------------------------------------- ! WARNING ! ---------------------------------------
188 You're using PHP with JIT enabled. This provides significant performance improvements.
189 HOWEVER, it is EXPERIMENTAL, and has already been seen to cause weird and unexpected bugs.
190 Proceed with caution.
191 If you want to report any bugs, make sure to mention that you have enabled PHP JIT.
192 To turn off JIT, change `opcache.jit` to `0` in your php.ini file.
193 -------------------------------------------------------------------------------------------
194
195JIT_WARNING
196);
197 }
198 }
199 }
200
204 function set_ini_entries(){
205 ini_set("allow_url_fopen", '1');
206 ini_set("display_errors", '1');
207 ini_set("display_startup_errors", '1');
208 ini_set("default_charset", "utf-8");
209 ini_set('assert.exception', '1');
210 }
211
212 function getopt_string(string $opt) : ?string{
213 $opts = getopt("", ["$opt:"]);
214 if(isset($opts[$opt])){
215 if(is_string($opts[$opt])){
216 return $opts[$opt];
217 }
218 if(is_array($opts[$opt])){
219 critical_error("Cannot specify --$opt multiple times");
220 }else{
221 critical_error("Missing value for --$opt");
222 }
223 exit(1);
224 }
225 return null;
226 }
227
231 function server(){
232 if(count($messages = check_platform_dependencies()) > 0){
233 echo PHP_EOL;
234 $binary = version_compare(PHP_VERSION, "5.4") >= 0 ? PHP_BINARY : "unknown";
235 critical_error("Selected PHP binary does not satisfy some requirements.");
236 foreach($messages as $m){
237 echo " - $m" . PHP_EOL;
238 }
239 critical_error("PHP binary used: " . $binary);
240 critical_error("Loaded php.ini: " . (($file = php_ini_loaded_file()) !== false ? $file : "none"));
241 $phprc = getenv("PHPRC");
242 critical_error("Value of PHPRC environment variable: " . ($phprc === false ? "" : $phprc));
243 critical_error("Please recompile PHP with the needed configuration, or refer to the installation instructions at http://pmmp.rtfd.io/en/rtfd/installation.html.");
244 echo PHP_EOL;
245 exit(1);
246 }
247 unset($messages);
248
249 error_reporting(-1);
250 set_ini_entries();
251
252 $bootstrap = dirname(__FILE__, 2) . '/vendor/autoload.php';
253 if(!is_file($bootstrap)){
254 critical_error("Composer autoloader not found at " . $bootstrap);
255 critical_error("Please install/update Composer dependencies or use provided builds.");
256 exit(1);
257 }
258 require_once($bootstrap);
259
260 $composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
261 if($composerGitHash !== null){
262 //we can't verify dependency versions if we were installed without using git
263 $currentGitHash = explode("-", VersionInfo::GIT_HASH())[0];
264 if($currentGitHash !== $composerGitHash){
265 critical_error("Composer dependencies and/or autoloader are out of sync.");
266 critical_error("- Current revision is $currentGitHash");
267 critical_error("- Composer dependencies were last synchronized for revision $composerGitHash");
268 critical_error("Out-of-sync Composer dependencies may result in crashes and classes not being found.");
269 critical_error("Please synchronize Composer dependencies before running the server.");
270 exit(1);
271 }
272 }
273
275
276 $cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
277 $dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd;
278 $pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
279 Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
280
281 if(!@mkdir($dataPath, 0777, true) && !is_dir($dataPath)){
282 critical_error("Unable to create/access data directory at $dataPath. Check that the target location is accessible by the current user.");
283 exit(1);
284 }
285 //this has to be done after we're sure the data path exists
286 $dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
287
288 $lockFilePath = Path::join($dataPath, 'server.lock');
289 try{
290 $pid = Filesystem::createLockFile($lockFilePath);
291 }catch(\InvalidArgumentException $e){
292 critical_error($e->getMessage());
293 critical_error("Please ensure that there is enough space on the disk and that the current user has read/write permissions to the selected data directory $dataPath.");
294 exit(1);
295 }
296 if($pid !== null){
297 critical_error("Another " . VersionInfo::NAME . " instance (PID $pid) is already using this folder (" . realpath($dataPath) . ").");
298 critical_error("Please stop the other server first before running a new one.");
299 exit(1);
300 }
301
302 if(!@mkdir($pluginPath, 0777, true) && !is_dir($pluginPath)){
303 critical_error("Unable to create plugin directory at $pluginPath. Check that the target location is accessible by the current user.");
304 exit(1);
305 }
306 $pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
307
308 //Logger has a dependency on timezone
309 Timezone::init();
310
312 if(isset($opts[BootstrapOptions::ENABLE_ANSI])){
313 Terminal::init(true);
314 }elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){
315 Terminal::init(false);
316 }else{
317 Terminal::init();
318 }
319
320 $logger = new MainLogger(Path::join($dataPath, "server.log"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()));
321 \GlobalLogger::set($logger);
322
324
325 $exitCode = 0;
326 do{
327 if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts[BootstrapOptions::NO_WIZARD])){
328 $installer = new SetupWizard($dataPath);
329 if(!$installer->run()){
330 $exitCode = -1;
331 break;
332 }
333 }
334
335 /*
336 * We now use the Composer autoloader, but this autoloader is still for loading plugins.
337 */
338 $autoloader = new ThreadSafeClassLoader();
339 $autoloader->register(false);
340
341 new Server($autoloader, $logger, $dataPath, $pluginPath);
342
343 $logger->info("Stopping other threads");
344
345 $killer = new ServerKiller(8);
346 $killer->start();
347 usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
348
349 if(ThreadManager::getInstance()->stopAll() > 0){
350 $logger->debug("Some threads could not be stopped, performing a force-kill");
351 Process::kill(Process::pid());
352 }
353 }while(false);
354
355 $logger->shutdownLogWriterThread();
356
357 echo Terminal::$FORMAT_RESET . PHP_EOL;
358
359 Filesystem::releaseLockFile($lockFilePath);
360
361 exit($exitCode);
362 }
363
364 \pocketmine\server();
365}
static createLockFile(string $lockFilePath)
Definition: Filesystem.php:185
static releaseLockFile(string $lockFilePath)
Definition: Filesystem.php:214
static kill(int $pid, bool $subprocesses=false)
Definition: Process.php:131
static assumeNotFalse(mixed $value, \Closure|string $context="This should never be false")
Definition: Utils.php:623
debug($message)
info($message)
warning($message)
critical_error($message)
Definition: PocketMine.php:60
check_platform_dependencies()
Definition: PocketMine.php:73
emit_performance_warnings(\Logger $logger)
Definition: PocketMine.php:168