Merge remote-tracking branch 'origin/threading'

This commit is contained in:
Shoghi Cervantes Pueyo 2012-12-29 02:31:48 +01:00
commit 5df48f3b7c
48 changed files with 86 additions and 244 deletions

8
.gitignore vendored
View File

@ -1,11 +1,11 @@
data/players/*
data/maps/*
data/plugins/*
players/*
worlds/*
plugins/*
logs/*
*.log
server.properties
white-list.txt
banned-ips.txt
console.in
#################
## Eclipse

View File

@ -12,9 +12,9 @@ the Free Software Foundation, either version 3 of the License, or
*/
require_once("common/dependencies.php");
require_once("src/common/dependencies.php");
require_once("classes/PocketMinecraftServer.class.php");
require_once("classes/API/ServerAPI.php");
require_once("API/ServerAPI.php");
while(true){
$server = new ServerAPI();

View File

@ -32,12 +32,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# PocketMine-MP
Github repo: https://github.com/shoghicp/PocketMine-MP
PocketMine-MP is a Server for Minecraft Pocket Edition done in PHP. It has a Plugin API that enables a developer to extend it and add new features, or change default ones.
Server (and client) Minecraft Pocket Edition library written in PHP.
Currently a work in progress, and used to document http://www.wiki.vg/Pocket_Minecraft_Protocol
The entire server is done in PHP, and has been tested, profiled and optimized to run smoothly.
Check the wiki! https://github.com/shoghicp/PocketMine-MP/wiki
[Go to Wiki for more information](https://github.com/shoghicp/PocketMine-MP/wiki)
[FAQ: Frequently Asked Questions](https://github.com/shoghicp/PocketMine-MP/wiki/Frequently-Asked-Questions)
**Project Status: `ALPHA`**
@ -61,7 +62,10 @@ Check the wiki! https://github.com/shoghicp/PocketMine-MP/wiki
## Third-party Libraries Used
* __[PHP NBT](https://github.com/TheFrozenFire/PHP-NBT-Decoder-Encoder/blob/master/nbt.class.php)__ by _[TheFrozenFire](https://github.com/TheFrozenFire)_: Class for reading in NBT-format files
* __[PHP cURL](http://php.net/manual/en/book.curl.php)__
* __[PHP Sockets](http://php.net/manual/en/book.sockets.php)__
* __[PHP SQLite3](http://php.net/manual/en/book.sqlite3.php)__
* __[PHP pthreads](https://github.com/krakjoe/pthreads)__ by _[krakjoe](https://github.com/krakjoe)_: Threading for PHP - Share Nothing, Do Everything
* __[PHP NBT](https://github.com/TheFrozenFire/PHP-NBT-Decoder-Encoder/blob/master/nbt.class.php)__ by _[TheFrozenFire](https://github.com/TheFrozenFire)_: Class for reading in NBT-format files (modified to handle Little-Endian files)
* __[Math_BigInteger](http://phpseclib.sourceforge.net/math/intro.html)__ by _[phpseclib](http://phpseclib.sourceforge.net/)_: Pure-PHP arbitrary precission integer arithmetic library
* __[Spyc](https://github.com/mustangostang/spyc/blob/master/Spyc.php)__ by _[Vlad Andersen](https://github.com/mustangostang)_: A simple YAML loader/dumper class for PHP

View File

@ -1,2 +0,0 @@
To import a Pocket Edition Map, drop to the folder "maps"
the chunk.dat, level.dat and entities.dat from the savegame file.

View File

@ -1,34 +0,0 @@
<?php
/*
__PocketMine Plugin__
name=ExamplePlugin
version=0.0.1
author=shoghicp
class=ExamplePlugin
*/
class ExamplePlugin implements Plugin{
private $api;
public function __construct(ServerAPI $api, $server = false){
$this->api = $api;
}
public function init(){
$this->api->console->register("example", "Example command", array($this, "handleCommand"));
}
public function __destruct(){
}
public function handleCommand($cmd, $arg){
switch($cmd){
case "example":
console("EXAMPLE!!!");
break;
}
}
}

View File

@ -1,46 +0,0 @@
<?php
/*
__PocketMine Plugin__
name=ReactorAsWater
description=Replaces the Nether Reactor with Water
version=0.0.2
author=shoghicp
class=ReactorAsWater
*/
class ReactorAsWater implements Plugin{
private $api;
public function __construct(ServerAPI $api, $server = false){
$this->api = $api;
}
public function init(){
$this->api->addHandler("player.block.action", array($this, "handle"), 15); //Priority higher that API
$this->api->addHandler("player.equipment.change", array($this, "handle"), 15);
}
public function __destruct(){
}
public function handle(&$data, $event){
switch($event){
case "player.equipment.change":
if($data["block"] === 247){
$this->api->player->getByEID($data["eid"])->eventHandler("[ReactorAsWater] Placing water", "server.chat");
$data["block"] = 9;
$data["meta"] = 0;
}
break;
case "player.block.action":
if($data["block"] === 247){ //nether reactor
$data["block"] = 9; //water source
$data["meta"] = 0;
}
break;
}
}
}

View File

@ -1,105 +0,0 @@
<?php
/*
__PocketMine Plugin__
name=SpawnChanger
description=Change the spawn coordinates, or make it default for all players!
version=0.0.1
author=shoghicp
class=SpawnChanger
*/
class SpawnChanger implements Plugin{
private $api, $config, $path;
public function __construct(ServerAPI $api, $server = false){
$this->api = $api;
}
public function init(){
$spawn = $this->api->level->getSpawn();
$this->path = $this->api->plugin->createConfig($this, array(
"spawnX" => $spawn["x"],
"spawnY" => $spawn["y"],
"spawnZ" => $spawn["z"],
"custom-spawn" => false,
"force-spawn" => false,
));
$this->config = $this->api->plugin->readYAML($this->path."config.yml");
if($this->config["custom-spawn"] === false){
$this->config["spawnX"] = $spawn["x"];
$this->config["spawnY"] = $spawn["y"];
$this->config["spawnZ"] = $spawn["z"];
$this->api->plugin->writeYAML($this->path."config.yml", $this->config);
}
$this->api->addHandler("api.player.offline.get", array($this, "handle"), 15);
$this->api->console->register("spawnchanger", "SpawnChanger init point managing", array($this, "command"));
$this->api->console->register("spawn", "Teleports to spawn", array($this, "command"));
}
public function __destruct(){
}
public function command($cmd, $args){
switch($cmd){
case "spawnchanger":
switch(strtolower(array_shift($args))){
case "force":
$l = array_shift($args);
if($l != "0" and $l != "1"){
console("[SpawnChanger] Usage: /spawnchanger force <1 | 0>");
}else{
$this->config["force-spawn"] = $l == "0" ? false:true;
if($this->config["force-spawn"] === true){
console("[SpawnChanger] Forced spawn point");
}else{
console("[SpawnChanger] Freed pawn point");
}
$this->api->plugin->writeYAML($this->path."config.yml", $this->config);
}
break;
case "set":
$z = array_pop($args);
$y = array_pop($args);
$x = array_pop($args);
if($x === null or $y === null or $z === null){
console("[SpawnChanger] Usage: /spawnchanger set <x> <y> <z>");
}else{
$this->config["custom-spawn"] = true;
$this->config["spawnX"] = (float) $x;
$this->config["spawnY"] = (float) $y;
$this->config["spawnZ"] = (float) $z;
console("[SpawnChanger] Spawn point set at X ".$this->config["spawnX"]." Y ".$this->config["spawnY"]." Z ".$this->config["spawnZ"]);
$this->api->plugin->writeYAML($this->path."config.yml", $this->config);
}
break;
default:
console("[SpawnChanger] Always spawn player in spawn point: /spawnchanger force <1 | 0>");
console("[SpawnChanger] Set the spawn point: /spawnchanger set <x> <y> <z>");
break;
}
break;
case "spawn":
if($this->api->player->tppos(implode(" ", $args), $this->config["spawnX"], $this->config["spawnY"], $this->config["spawnZ"]) !== false){
console("[SpawnChanger] Teleported to spawn!");
}else{
console("[SpawnChanger] Usage: /spawn <player>");
}
break;
}
}
public function handle(&$data, $event){
switch($event){
case "api.player.offline.get":
if($this->config["force-spawn"] === true){
$data["spawn"]["x"] = $this->config["spawnX"];
$data["spawn"]["y"] = $this->config["spawnY"];
$data["spawn"]["z"] = $this->config["spawnZ"];
}
break;
}
}
}

View File

@ -44,13 +44,24 @@ class BlockAPI{
$this->server->addHandler("player.block.action", array($this, "blockAction"), 1);
}
private function cancelAction($block){
$this->server->trigger("world.block.change", array(
"x" => $block[2][0],
"y" => $block[2][1],
"z" => $block[2][2],
"block" => $block[0],
"meta" => $block[1],
));
return false;
}
public function blockBreak($data, $event){
if($event !== "player.block.break"){
return;
}
$target = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]);
if(isset(Material::$unbreakable[$target[0]])){
return false;
return $this->cancelAction($target);
}
$drop = array(
$target[0], //Block
@ -247,7 +258,10 @@ class BlockAPI{
}
if($cancelPlace === true){
return false;
$this->cancelAction($target);
BlockFace::setPosition($data, $data["face"]);
$target = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]);
return $this->cancelAction($target);
}
$replace = false;
@ -275,13 +289,13 @@ class BlockAPI{
$block = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]);
if($replace === false and !isset(Material::$replaceable[$block[0]])){
return false;
return $this->cancelAction($block);
}
if(isset(Material::$placeable[$data["block"]])){
$data["block"] = Material::$placeable[$data["block"]] === true ? $data["block"]:Material::$placeable[$data["block"]];
}else{
return false;
return $this->cancelAction($block);
}
$direction = $this->server->api->entity->get($data["eid"])->getDirection();

View File

@ -30,7 +30,7 @@ class ConsoleAPI{
function __construct(PocketMinecraftServer $server){
$this->help = array();
$this->server = $server;
$this->input = fopen(FILE_PATH."console.in", "w+b");
$this->input = fopen(FILE_PATH."logs/console.in", "w+b");
$this->last = microtime(true);
}

View File

@ -189,7 +189,7 @@ class PlayerAPI{
}
public function getOffline($name){
if(!file_exists(FILE_PATH."data/players/".$name.".dat")){
if(!file_exists(FILE_PATH."players/".$name.".dat")){
console("[NOTICE] Player data not found for \"".$name."\", creating new profile");
$data = array(
"spawn" => array(
@ -203,7 +203,7 @@ class PlayerAPI{
);
$this->saveOffline($name, $data);
}else{
$data = unserialize(file_get_contents(FILE_PATH."data/players/".$name.".dat"));
$data = unserialize(file_get_contents(FILE_PATH."players/".$name.".dat"));
}
$this->server->handle("api.player.offline.get", $data);
return $data;
@ -211,6 +211,6 @@ class PlayerAPI{
public function saveOffline($name, $data){
$this->server->handle("api.player.offline.save", $data);
file_put_contents(FILE_PATH."data/players/".str_replace("/", "", $name).".dat", serialize($data));
file_put_contents(FILE_PATH."players/".str_replace("/", "", $name).".dat", serialize($data));
}
}

View File

@ -115,7 +115,7 @@ class PluginAPI extends stdClass{
if($p === false){
return false;
}
$path = FILE_PATH."data/plugins/".$p[1]["name"]."/";
$path = FILE_PATH."plugins/".$p[1]["name"]."/";
$this->plugins[$p[1]["class"]][1]["path"] = $path;
if(!file_exists($path."config.yml")){
@mkdir($path, 0777);
@ -155,11 +155,11 @@ class PluginAPI extends stdClass{
public function loadAll(){
console("[INFO] Loading Plugins...");
$dir = dir(FILE_PATH."data/plugins/");
$dir = dir(FILE_PATH."plugins/");
while(false !== ($file = $dir->read())){
if($file !== "." and $file !== ".."){
if(strtolower(substr($file, -3)) === "php"){
$this->load(FILE_PATH."data/plugins/" . $file);
$this->load(FILE_PATH."plugins/" . $file);
}
}
}

View File

@ -30,9 +30,14 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
private $server, $config, $apiList = array();
function __construct(){
console("[INFO] Starting ServerAPI server handler...");
file_put_contents(FILE_PATH."packets.log", "");
file_put_contents(FILE_PATH."console.in", "");
if(!file_exists(FILE_PATH."test.bin.log") or md5_file(FILE_PATH."test.bin.log") !== TEST_MD5){
console("[DEBUG] Checking data folders...", true, true, 2);
@mkdir(FILE_PATH."logs/", 0777, true);
@mkdir(FILE_PATH."players/", 0777);
@mkdir(FILE_PATH."worlds/", 0777);
@mkdir(FILE_PATH."plugins/", 0777);
file_put_contents(FILE_PATH."logs/packets.log", "");
file_put_contents(FILE_PATH."logs/console.in", "");
if(!file_exists(FILE_PATH."logs/test.bin.log") or md5_file(FILE_PATH."logs/test.bin.log") !== TEST_MD5){
console("[NOTICE] Executing integrity tests...");
console("[INFO] OS: ".PHP_OS.", ".Utils::getOS());
console("[INFO] uname -a: ".php_uname("a"));
@ -60,7 +65,7 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
$test .= Utils::writeLong($str->hashCode());
$test .= Utils::writeDataArray(array("a", "b", "c", "\xff\xff\xff\xff"));
$test .= Utils::hexToStr("012334567890");
file_put_contents(FILE_PATH."test.bin.log", $test);
file_put_contents(FILE_PATH."logs/test.bin.log", $test);
if(md5($test) !== TEST_MD5){
console("[ERROR] Test error, please send your console.log + test.bin.log to the Github repo");
die();
@ -79,14 +84,9 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
if(!file_exists(FILE_PATH."server.properties")){
console("[NOTICE] No server.properties found, using default settings");
copy(FILE_PATH."common/default.properties", FILE_PATH."server.properties");
copy(FILE_PATH."src/common/default.properties", FILE_PATH."server.properties");
}
console("[DEBUG] Checking data folders...", true, true, 2);
@mkdir(FILE_PATH."data/players/", 0777, true);
@mkdir(FILE_PATH."data/maps/", 0777);
@mkdir(FILE_PATH."data/plugins/", 0777);
console("[DEBUG] Loading server.properties...", true, true, 2);
$this->parseProperties();
define("DEBUG", $this->config["debug"]);
@ -138,17 +138,17 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
}
}
if(file_exists(FILE_PATH."data/maps/level.dat")){
if(file_exists(FILE_PATH."worlds/level.dat")){
console("[NOTICE] Detected unimported map data. Importing...");
$this->importMap(FILE_PATH."data/maps/", true);
$this->importMap(FILE_PATH."worlds/", true);
}
$this->server->mapName = $this->getProperty("level-name");
$this->server->mapDir = FILE_PATH."data/maps/".$this->server->mapName."/";
$this->server->mapDir = FILE_PATH."worlds/".$this->server->mapName."/";
if($this->server->mapName === false or trim($this->server->mapName) === "" or !file_exists($this->server->mapDir."chunks.dat")){
if($this->server->mapName === false or trim($this->server->mapName) === ""){
$this->server->mapName = "world";
}
$this->server->mapDir = FILE_PATH."data/maps/".$this->server->mapName."/";
$this->server->mapDir = FILE_PATH."worlds/".$this->server->mapName."/";
$generator = "SuperflatGenerator";
if($this->getProperty("generator") !== false and class_exists($this->getProperty("generator"))){
$generator = $this->getProperty("generator");
@ -168,7 +168,7 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
//Autoload all default APIs
console("[INFO] Loading default APIs");
$dir = dir(FILE_PATH."classes/API/");
$dir = dir(FILE_PATH."src/API/");
while(false !== ($file = $dir->read())){
if($file !== "." and $file !== ".."){
$API = basename($file, ".php");
@ -317,24 +317,24 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
console("[DEBUG] Importing map \"".$level["LevelName"]."\" gamemode ".$level["GameType"]." with seed ".$level["RandomSeed"], true, true, 2);
unset($level["Player"]);
$lvName = $level["LevelName"]."/";
@mkdir(FILE_PATH."data/maps/".$lvName, 0777);
file_put_contents(FILE_PATH."data/maps/".$lvName."level.dat", serialize($level));
@mkdir(FILE_PATH."worlds/".$lvName, 0777);
file_put_contents(FILE_PATH."worlds/".$lvName."level.dat", serialize($level));
$entities = parseNBTData($nbt->loadFile($dir."entities.dat"));
file_put_contents(FILE_PATH."data/maps/".$lvName."entities.dat", serialize($entities["Entities"]));
file_put_contents(FILE_PATH."worlds/".$lvName."entities.dat", serialize($entities["Entities"]));
if(!isset($entities["TileEntities"])){
$entities["TileEntities"] = array();
}
file_put_contents(FILE_PATH."data/maps/".$lvName."tileEntities.dat", serialize($entities["TileEntities"]));
file_put_contents(FILE_PATH."worlds/".$lvName."tileEntities.dat", serialize($entities["TileEntities"]));
console("[DEBUG] Imported ".count($entities["Entities"])." Entities and ".count($entities["TileEntities"])." TileEntities", true, true, 2);
if($remove === true){
rename($dir."chunks.dat", FILE_PATH."data/maps/".$lvName."chunks.dat");
rename($dir."chunks.dat", FILE_PATH."worlds/".$lvName."chunks.dat");
unlink($dir."level.dat");
@unlink($dir."level.dat_old");
@unlink($dir."player.dat");
unlink($dir."entities.dat");
}else{
copy($dir."chunks.dat", FILE_PATH."data/maps/".$lvName."chunks.dat");
copy($dir."chunks.dat", FILE_PATH."worlds/".$lvName."chunks.dat");
}
if($this->getProperty("level-name") === false){
console("[INFO] Setting default level to \"".$level["LevelName"]."\"");
@ -370,7 +370,7 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
public function loadAPI($name, $class, $dir = false){
if($dir === false){
$dir = FILE_PATH."classes/API/";
$dir = FILE_PATH."src/API/";
}
$file = $dir.$class.".php";
if(!file_exists($file)){

View File

@ -33,8 +33,8 @@ define("ENTITY_ITEM", 3);
define("ENTITY_PAINTING", 4);
class Entity extends stdClass{
var $eid, $type, $name, $x, $y, $z, $yaw, $pitch, $dead, $data, $class, $attach, $metadata, $closed, $player;
var $eid, $type, $name, $x, $y, $z, $yaw, $pitch, $dead, $data, $class, $attach, $metadata, $closed, $player, $onTick;
private $ev, $server;
function __construct($server, $eid, $class, $type = 0, $data = array()){
$this->server = $server;
$this->eid = (int) $eid;
@ -49,6 +49,7 @@ class Entity extends stdClass{
$this->closed = false;
$this->name = "";
$this->server->query("INSERT OR REPLACE INTO entities (EID, type, class, health) VALUES (".$this->eid.", ".$this->type.", ".$this->class.", ".$this->health.");");
$this->ev = $this->server->event("server.tick", array($this, "update"));
$this->metadata = array();
$this->x = isset($this->data["x"]) ? $this->data["x"]:0;
$this->y = isset($this->data["y"]) ? $this->data["y"]:0;
@ -74,6 +75,10 @@ class Entity extends stdClass{
}
}
public function update(){
}
public function getDirection(){
$rotation = ($this->yaw - 90) % 360;
if ($rotation < 0) {
@ -147,6 +152,7 @@ class Entity extends stdClass{
if($this->closed === false){
$this->server->query("DELETE FROM entities WHERE EID = ".$this->eid.";");
$this->server->trigger("entity.remove", $this->eid);
$this->server->deleteEvent($this->ev);
$this->closed = true;
}
}

View File

@ -30,7 +30,7 @@ class PocketMinecraftServer extends stdClass{
private $database, $interface, $evCnt, $handCnt, $events, $handlers, $version, $serverType, $lastTick;
function __construct($name, $gamemode = 1, $seed = false, $protocol = CURRENT_PROTOCOL, $port = 19132, $serverID = false, $version = CURRENT_VERSION){
$this->port = (int) $port; //19132 - 19135
console("[INFO] PocketMine-MP ".MAJOR_VERSION." by @shoghicp, LGPL License. http://bit.ly/TbrimG", true, true, 0);
console("[INFO] PocketMine-MP ".MAJOR_VERSION." by @shoghicp, LGPL License", true, true, 0);
console("[INFO] Starting Minecraft PE Server at *:".$this->port);
if($this->port < 19132 or $this->port > 19135){
console("[WARNING] You've selected a not-standard port. Normal port range is from 19132 to 19135 included");

View File

@ -32,8 +32,8 @@ error_reporting(E_ALL ^ E_NOTICE);
ini_set("allow_url_fopen", 1);
ini_set("display_errors", 1);
ini_set('default_charset', 'utf-8');
define("FILE_PATH", dirname(__FILE__)."/../");
set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATOR . FILE_PATH . "/classes/");
define("FILE_PATH", dirname(__FILE__)."/../../");
set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATOR . FILE_PATH . "/src/" . PATH_SEPARATOR . FILE_PATH . "/src/classes/");
ini_set("memory_limit", "256M");
define("CURRENT_PROTOCOL", 5);
define("CURRENT_VERSION", 1);

View File

@ -54,6 +54,11 @@ if(!extension_loaded("sockets")){
++$errors;
}
/*if(!extension_loaded("pthreads")){
console("[ERROR] Unable to find pthreads extension. [https://github.com/krakjoe/pthreads]", true, true, 0);
++$errors;
}*/
if(!extension_loaded("curl")){
console("[ERROR] Unable to find cURL extension", true, true, 0);
++$errors;
@ -85,6 +90,6 @@ require_once("classes/SerializedPacketHandler.class.php");
require_once("classes/CustomPacketHandler.class.php");
require_once("classes/MinecraftInterface.class.php");
require_once("classes/BigInteger.class.php");
require_all("misc/");
require_all(FILE_PATH . "src/misc/");
?>

View File

@ -170,7 +170,7 @@ function logg($message, $name, $EOL = true, $level = 2, $close = false){
$fpointers = array();
}
if(!isset($fpointers[$name])){
$fpointers[$name] = fopen(FILE_PATH."/".$name.".log", "ab");
$fpointers[$name] = fopen(FILE_PATH."logs/".$name.".log", "ab");
}
fwrite($fpointers[$name], $message);
if($close === true){

View File

@ -25,7 +25,7 @@ the Free Software Foundation, either version 3 of the License, or
*/
$fp = fopen(dirname(__FILE__)."/console.in","wb");
$fp = fopen(dirname(__FILE__)."/../../logs/console.in","wb");
while(true){
$l = fgets(STDIN);
fwrite($fp, $l);

View File

@ -26,7 +26,7 @@ if not "%PHPOUTPUT%"=="1" (
echo [ERROR] Couldn't find PHP binary in PATH.
ping 127.0.0.1 -n 3 -w 1000>nul
) else (
START /B CMD /C CALL php server.php
START /B /WAIT php input.php 1
START /B CMD /C CALL php PocketMine-MP.php
START /B /WAIT php src/common/input.php 1
ping 127.0.0.1 -n 5 -w 1000>nul
)

View File

@ -1,3 +1,3 @@
#!/bin/bash
php server.php &
cat>console.in
php PocketMine-MP.php &
cat > src/console.in