PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
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 
22 namespace {
23  function safe_var_dump(){
24  static $cnt = 0;
25  foreach(func_get_args() as $var){
26  switch(true){
27  case is_array($var):
28  echo str_repeat(" ", $cnt) . "array(" . count($var) . ") {" . PHP_EOL;
29  foreach($var as $key => $value){
30  echo str_repeat(" ", $cnt + 1) . "[" . (is_integer($key) ? $key : '"' . $key . '"') . "]=>" . PHP_EOL;
31  ++$cnt;
32  safe_var_dump($value);
33  --$cnt;
34  }
35  echo str_repeat(" ", $cnt) . "}" . PHP_EOL;
36  break;
37  case is_integer($var):
38  echo str_repeat(" ", $cnt) . "int(" . $var . ")" . PHP_EOL;
39  break;
40  case is_float($var):
41  echo str_repeat(" ", $cnt) . "float(" . $var . ")" . PHP_EOL;
42  break;
43  case is_bool($var):
44  echo str_repeat(" ", $cnt) . "bool(" . ($var === true ? "true" : "false") . ")" . PHP_EOL;
45  break;
46  case is_string($var):
47  echo str_repeat(" ", $cnt) . "string(" . strlen($var) . ") \"$var\"" . PHP_EOL;
48  break;
49  case is_resource($var):
50  echo str_repeat(" ", $cnt) . "resource() of type (" . get_resource_type($var) . ")" . PHP_EOL;
51  break;
52  case is_object($var):
53  echo str_repeat(" ", $cnt) . "object(" . get_class($var) . ")" . PHP_EOL;
54  break;
55  case is_null($var):
56  echo str_repeat(" ", $cnt) . "NULL" . PHP_EOL;
57  break;
58  }
59  }
60  }
61 
62  function dummy(){
63 
64  }
65 }
66 
67 namespace pocketmine {
72 
73  const VERSION = "1.4";
74  const API_VERSION = "1.10.0";
75  const CODENAME = "絶好(Zekkou)ケーキ(Cake)";
76  const MINECRAFT_VERSION = "v0.10.4 alpha";
77 
78  /*
79  * Startup code. Do not look at it, it may harm you.
80  * Most of them are hacks to fix date-related bugs, or basic functions used after this
81  * This is the only non-class based file on this project.
82  * Enjoy it as much as I did writing it. I don't want to do it again.
83  */
84 
85  if(\Phar::running(true) !== ""){
86  @define("pocketmine\\PATH", \Phar::running(true) . "/");
87  }else{
88  @define("pocketmine\\PATH", \getcwd() . DIRECTORY_SEPARATOR);
89  }
90 
91  if(!extension_loaded("pthreads")){
92  echo "[CRITICAL] Unable to find the pthreads extension." . PHP_EOL;
93  echo "[CRITICAL] Please use the installer provided on the homepage." . PHP_EOL;
94  exit(1);
95  }
96 
97  if(!class_exists("ClassLoader", false)){
98  require_once(\pocketmine\PATH . "src/spl/ClassLoader.php");
99  require_once(\pocketmine\PATH . "src/spl/BaseClassLoader.php");
100  require_once(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php");
101  }
102 
103  $autoloader = new CompatibleClassLoader();
104  $autoloader->addPath(\pocketmine\PATH . "src");
105  $autoloader->addPath(\pocketmine\PATH . "src" . DIRECTORY_SEPARATOR . "spl");
106  $autoloader->addPath(\pocketmine\PATH . "src" . DIRECTORY_SEPARATOR . "raklib");
107  $autoloader->register(true);
108 
109 
110  set_time_limit(0); //Who set it to 30 seconds?!?!
111 
112  gc_enable();
113  error_reporting(-1);
114  ini_set("allow_url_fopen", 1);
115  ini_set("display_errors", 1);
116  ini_set("display_startup_errors", 1);
117  ini_set("default_charset", "utf-8");
118 
119  ini_set("memory_limit", "256M"); //Default
120  define("pocketmine\\START_TIME", microtime(true));
121 
122  $opts = getopt("", ["enable-ansi", "disable-ansi", "data:", "plugins:", "no-wizard", "enable-profiler"]);
123 
124  define("pocketmine\\DATA", isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR);
125  define("pocketmine\\PLUGIN_PATH", isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
126 
127  define("pocketmine\\ANSI", (Utils::getOS() !== "win" or isset($opts["enable-ansi"])) and !isset($opts["disable-ansi"]));
128 
129 
130  @mkdir(\pocketmine\DATA, 0777, true);
131 
132  //Logger has a dependency on timezone, so we'll set it to UTC until we can get the actual timezone.
133  date_default_timezone_set("UTC");
134  $logger = new MainLogger(\pocketmine\DATA . "server.log", \pocketmine\ANSI);
135 
136  if(!ini_get("date.timezone")){
137  if(($timezone = detect_system_timezone()) and date_default_timezone_set($timezone)){
138  //Success! Timezone has already been set and validated in the if statement.
139  //This here is just for redundancy just in case some program wants to read timezone data from the ini.
140  ini_set("date.timezone", $timezone);
141  }else{
142  //If system timezone detection fails or timezone is an invalid value.
143  if($response = Utils::getURL("http://ip-api.com/json")
144  and $ip_geolocation_data = json_decode($response, true)
145  and $ip_geolocation_data['status'] != 'fail'
146  and date_default_timezone_set($ip_geolocation_data['timezone'])
147  ){
148  //Again, for redundancy.
149  ini_set("date.timezone", $ip_geolocation_data['timezone']);
150  }else{
151  ini_set("date.timezone", "UTC");
152  date_default_timezone_set("UTC");
153  $logger->warning("Timezone could not be automatically determined. An incorrect timezone will result in incorrect timestamps on console logs. It has been set to \"UTC\" by default. You can change it on the php.ini file.");
154  }
155  }
156  }else{
157  /*
158  * This is here so that people don't come to us complaining and fill up the issue tracker when they put
159  * an incorrect timezone abbreviation in php.ini apparently.
160  */
161  $default_timezone = date_default_timezone_get();
162  if(strpos($default_timezone, "/") === false){
163  $default_timezone = timezone_name_from_abbr($default_timezone);
164  ini_set("date.timezone", $default_timezone);
165  date_default_timezone_set($default_timezone);
166  }
167  }
168 
169  function detect_system_timezone(){
170  switch(Utils::getOS()){
171  case 'win':
172  $regex = '/(UTC)(\+*\-*\d*\d*\:*\d*\d*)/';
173 
174  /*
175  * wmic timezone get Caption
176  * Get the timezone offset
177  *
178  * Sample Output var_dump
179  * array(3) {
180  * [0] =>
181  * string(7) "Caption"
182  * [1] =>
183  * string(20) "(UTC+09:30) Adelaide"
184  * [2] =>
185  * string(0) ""
186  * }
187  */
188  exec("wmic timezone get Caption", $output);
189 
190  $string = trim(implode("\n", $output));
191 
192  //Detect the Time Zone string
193  preg_match($regex, $string, $matches);
194 
195  if(!isset($matches[2])){
196  return false;
197  }
198 
199  $offset = $matches[2];
200 
201  if($offset == ""){
202  return "UTC";
203  }
204 
205  return parse_offset($offset);
206  break;
207  case 'linux':
208  // Ubuntu / Debian.
209  if(file_exists('/etc/timezone')){
210  $data = file_get_contents('/etc/timezone');
211  if($data){
212  return trim($data);
213  }
214  }
215 
216  // RHEL / CentOS
217  if(file_exists('/etc/sysconfig/clock')){
218  $data = parse_ini_file('/etc/sysconfig/clock');
219  if(!empty($data['ZONE'])){
220  return trim($data['ZONE']);
221  }
222  }
223 
224  //Portable method for incompatible linux distributions.
225 
226  $offset = trim(exec('date +%:z'));
227 
228  if($offset == "+00:00"){
229  return "UTC";
230  }
231 
232  return parse_offset($offset);
233  break;
234  case 'mac':
235  if(is_link('/etc/localtime')){
236  $filename = readlink('/etc/localtime');
237  if(strpos($filename, '/usr/share/zoneinfo/') === 0){
238  $timezone = substr($filename, 20);
239  return trim($timezone);
240  }
241  }
242 
243  return false;
244  break;
245  default:
246  return false;
247  break;
248  }
249  }
250 
256  function parse_offset($offset){
257  //Make signed offsets unsigned for date_parse
258  if(strpos($offset, '-') !== false){
259  $negative_offset = true;
260  $offset = str_replace('-', '', $offset);
261  }else{
262  if(strpos($offset, '+') !== false){
263  $negative_offset = false;
264  $offset = str_replace('+', '', $offset);
265  }else{
266  return false;
267  }
268  }
269 
270  $parsed = date_parse($offset);
271  $offset = $parsed['hour'] * 3600 + $parsed['minute'] * 60 + $parsed['second'];
272 
273  //After date_parse is done, put the sign back
274  if($negative_offset == true){
275  $offset = -abs($offset);
276  }
277 
278  //And then, look the offset up.
279  //timezone_name_from_abbr is not used because it returns false on some(most) offsets because it's mapping function is weird.
280  //That's been a bug in PHP since 2008!
281  foreach(timezone_abbreviations_list() as $zones){
282  foreach($zones as $timezone){
283  if($timezone['offset'] == $offset){
284  return $timezone['timezone_id'];
285  }
286  }
287  }
288 
289  return false;
290  }
291 
292  if(isset($opts["enable-profiler"])){
293  if(function_exists("profiler_enable")){
294  \profiler_enable();
295  $logger->notice("Execution is being profiled");
296  }else{
297  $logger->notice("No profiler found. Please install https://github.com/krakjoe/profiler");
298  }
299  }
300 
301  function kill($pid){
302  switch(Utils::getOS()){
303  case "win":
304  exec("taskkill.exe /F /PID " . ((int) $pid) . " > NUL");
305  break;
306  case "mac":
307  case "linux":
308  default:
309  exec("kill -9 " . ((int) $pid) . " > /dev/null 2>&1");
310  }
311  }
312 
313  function getTrace($start = 1, $trace = null){
314  if($trace === null){
315  if(function_exists("xdebug_get_function_stack")){
316  $trace = array_reverse(xdebug_get_function_stack());
317  }else{
318  $e = new \Exception();
319  $trace = $e->getTrace();
320  }
321  }
322 
323  $messages = [];
324  $j = 0;
325  for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){
326  $params = "";
327  if(isset($trace[$i]["args"]) or isset($trace[$i]["params"])){
328  if(isset($trace[$i]["args"])){
329  $args = $trace[$i]["args"];
330  }else{
331  $args = $trace[$i]["params"];
332  }
333  foreach($args as $name => $value){
334  $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . @strval($value)) . ", ";
335  }
336  }
337  $messages[] = "#$j " . (isset($trace[$i]["file"]) ? cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")";
338  }
339 
340  return $messages;
341  }
342 
343  function cleanPath($path){
344  return rtrim(str_replace(["\\", ".php", "phar://", rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PATH), "/"), rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PLUGIN_PATH), "/")], ["/", "", "", "", ""], $path), "/");
345  }
346 
347  set_error_handler([\ExceptionHandler::class, "handler"], -1);
348 
349  $errors = 0;
350 
351  if(version_compare("5.6.0", PHP_VERSION) > 0){
352  $logger->critical("You must use PHP >= 5.6");
353  ++$errors;
354  }
355 
356  if(php_sapi_name() !== "cli"){
357  $logger->critical("You must run PocketMine-MP using the CLI.");
358  ++$errors;
359  }
360 
361  if(!extension_loaded("sockets")){
362  $logger->critical("Unable to find the Socket extension.");
363  ++$errors;
364  }
365 
366  $pthreads_version = phpversion("pthreads");
367  if(substr_count($pthreads_version, ".") < 2){
368  $pthreads_version = "0.$pthreads_version";
369  }
370  if(version_compare($pthreads_version, "2.0.9") < 0){
371  $logger->critical("pthreads >= 2.0.9 is required, while you have $pthreads_version.");
372  ++$errors;
373  }
374 
375  if(!extension_loaded("uopz")){
376  //$logger->notice("Couldn't find the uopz extension. Some functions may be limited");
377  }
378 
379  if(extension_loaded("pocketmine")){
380  if(version_compare(phpversion("pocketmine"), "0.0.1") < 0){
381  $logger->critical("You have the native PocketMine extension, but your version is lower than 0.0.1.");
382  ++$errors;
383  }elseif(version_compare(phpversion("pocketmine"), "0.0.4") > 0){
384  $logger->critical("You have the native PocketMine extension, but your version is higher than 0.0.4.");
385  ++$errors;
386  }
387  }
388 
389  if(!extension_loaded("Weakref") and !extension_loaded("weakref")){
390  $logger->critical("Unable to find the Weakref extension.");
391  ++$errors;
392  }
393 
394  if(!extension_loaded("curl")){
395  $logger->critical("Unable to find the cURL extension.");
396  ++$errors;
397  }
398 
399  if(!extension_loaded("sqlite3")){
400  $logger->critical("Unable to find the SQLite3 extension.");
401  ++$errors;
402  }
403 
404  if(!extension_loaded("yaml")){
405  $logger->critical("Unable to find the YAML extension.");
406  ++$errors;
407  }
408 
409  if(!extension_loaded("zlib")){
410  $logger->critical("Unable to find the Zlib extension.");
411  ++$errors;
412  }
413 
414  if($errors > 0){
415  $logger->critical("Please use the installer provided on the homepage, or recompile PHP again.");
416  $logger->shutdown();
417  $logger->join();
418  exit(1); //Exit with error
419  }
420 
421  if(file_exists(\pocketmine\PATH . ".git/refs/heads/master")){ //Found Git information!
422  define("pocketmine\\GIT_COMMIT", strtolower(trim(file_get_contents(\pocketmine\PATH . ".git/refs/heads/master"))));
423  }else{ //Unknown :(
424  define("pocketmine\\GIT_COMMIT", str_repeat("00", 20));
425  }
426 
427  @define("ENDIANNESS", (pack("d", 1) === "\77\360\0\0\0\0\0\0" ? Binary::BIG_ENDIAN : Binary::LITTLE_ENDIAN));
428  @define("INT32_MASK", is_int(0xffffffff) ? 0xffffffff : -1);
429  @ini_set("opcache.mmap_base", bin2hex(Utils::getRandomBytes(8, false))); //Fix OPCache address errors
430 
431  if(!file_exists(\pocketmine\DATA . "server.properties") and !isset($opts["no-wizard"])){
432  new Installer();
433  }
434 
435  if(substr(__FILE__, 0, 7) !== "phar://"){
436  $logger->warning("Non-packaged PocketMine-MP installation detected, do not use on production.");
437  }
438 
439  ThreadManager::init();
440  $server = new Server($autoloader, $logger, \pocketmine\PATH, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
441 
442  $logger->info("Stopping other threads");
443 
444  foreach(ThreadManager::getInstance()->getAll() as $id => $thread){
445  if($thread->isRunning()){
446  $logger->debug("Stopping " . (new \ReflectionClass($thread))->getShortName() . " thread");
447  if($thread instanceof Thread){
448  $thread->kill();
449  sleep(1);
450  if($thread->isRunning()){
451  $thread->detach();
452  }
453  }elseif($thread instanceof Worker){
454  $thread->kill();
455  sleep(1);
456  if($thread->isRunning()){
457  $thread->detach();
458  }
459  }
460  }elseif(!$thread->isJoined()){
461  $logger->debug("Joining " . (new \ReflectionClass($thread))->getShortName() . " thread");
462  $thread->join();
463  }
464  }
465 
466  $logger->shutdown();
467  $logger->join();
468 
469  exit(0);
470 
471 }
parse_offset($offset)
Definition: PocketMine.php:256
static getRandomBytes($length=16, $secure=true, $raw=true, $startEntropy="", &$rounds=0, &$drop=0)
Definition: Utils.php:223
static getURL($page, $timeout=10)
Definition: Utils.php:338