From 3a2010696f36cb320e9f4c52bfe25cb35fcc6d2d Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Pueyo Date: Sun, 16 Dec 2012 23:33:18 +0100 Subject: [PATCH] Map saving, breaking blocks. Huge rewrite --- TODO | 3 + classes/API/ConsoleAPI.php | 5 +- classes/API/EntityAPI.php | 94 +++++++++ classes/API/LevelAPI.php | 39 ++++ classes/API/PlayerAPI.php | 20 +- classes/API/ServerAPI.php | 2 + classes/ChunkParser.class.php | 67 ++++++- classes/CustomPacketHandler.class.php | 2 +- classes/Entity.class.php | 140 ++++++++------ classes/MinecraftInterface.class.php | 7 +- classes/PocketMinecraftClient.class.php | 242 ------------------------ classes/PocketMinecraftServer.class.php | 122 ++++++------ classes/Session.class.php | 159 ++++++---------- common/dependencies.php | 1 + common/functions.php | 4 + 15 files changed, 423 insertions(+), 484 deletions(-) create mode 100644 TODO create mode 100644 classes/API/EntityAPI.php delete mode 100644 classes/PocketMinecraftClient.class.php diff --git a/TODO b/TODO new file mode 100644 index 000000000..9e4371b92 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- Save broken/placed blocks and relay them to other players. +- Fix spawn position resetting to 0,128,0 +- Mob spawning, item drop and pick up \ No newline at end of file diff --git a/classes/API/ConsoleAPI.php b/classes/API/ConsoleAPI.php index 9b90144d6..0de052892 100644 --- a/classes/API/ConsoleAPI.php +++ b/classes/API/ConsoleAPI.php @@ -31,11 +31,14 @@ class ConsoleAPI{ $this->help = array(); $this->server = $server; $this->input = fopen(FILE_PATH."console.in", "w+b"); + } + + public function init(){ $this->event = $this->server->event("onTick", array($this, "handle")); } function __destroy(){ - $this->server->deleteEvent("onTick", $this->event); + $this->server->deleteEvent($this->event); fclose($this->input); } diff --git a/classes/API/EntityAPI.php b/classes/API/EntityAPI.php new file mode 100644 index 000000000..fd6983d3e --- /dev/null +++ b/classes/API/EntityAPI.php @@ -0,0 +1,94 @@ +server = $server; + } + + public function init(){ + $this->server->api->console->register("give", "Give items to a player", array($this, "commandHandler")); + } + + public function commandHandler($cmd, $params){ + switch($cmd){ + case "give": + break; + } + } + + public function get($eid){ + if(isset($this->server->entities[$eid])){ + return $this->server->entities[$eid]; + } + return false; + } + + public function getAll(){ + return $this->server->entities; + } + + public function add($class, $type = 0, $data = array()){ + $eid = $this->server->eidCnt++; + $this->server->entities[$eid] = new Entity($this->server, $eid, $class, $type, $data); + return $this->server->entities[$eid]; + } + + public function spawnTo($eid, $player){ + $e = $this->get($eid); + if($e === false){ + return false; + } + $e->spawn($player); + } + + public function spawnToAll($eid){ + $e = $this->get($eid); + if($e === false){ + return false; + } + foreach($this->server->api->player->getAll() as $player){ + if($player->eid !== false){ + $e->spawn($player); + } + } + } + + public function spawnAll($player){ + foreach($this->getAll() as $e){ + $e->spawn($player); + } + } + + public function remove($eid){ + if(isset($this->server->entities[$eid])){ + $this->server->entities[$eid]->close(); + unset($this->server->entities[$eid]); + } + } +} \ No newline at end of file diff --git a/classes/API/LevelAPI.php b/classes/API/LevelAPI.php index 38214484c..e6bab64e1 100644 --- a/classes/API/LevelAPI.php +++ b/classes/API/LevelAPI.php @@ -35,6 +35,32 @@ class LevelAPI{ } } + public function init(){ + //$this->server->event("onBlockBreak", array($this, "handle")); + } + + public function handle($data, $event){ + switch($event){ + case "onBlockBreak": + $block = $this->getBlock($data["x"], $data["y"], $data["z"]); + console("[DEBUG] EID ".$data["eid"]." broke block ".$block[0].":".$block[1]." at X ".$data["x"]." Y ".$data["y"]." Z ".$data["z"], true, true, 2); + + if($block[0] === 0){ + break; + } + $this->setBlock($data["x"], $data["y"], $data["z"], 0, 0); + $data["block"] = $block[0]; + $data["meta"] = $block[1]; + $data["stack"] = 1; + $data["x"] += mt_rand(2, 8) / 10; + $data["y"] += mt_rand(2, 8) / 10; + $data["z"] += mt_rand(2, 8) / 10; + $e = $this->server->api->entity->add(ENTITY_ITEM, $block[0], $data); + $this->server->api->entity->spawnToAll($e->eid); + break; + } + } + private function check(){ if($this->active === false and $this->server->map === false){ return false; @@ -57,6 +83,19 @@ class LevelAPI{ return array(0,0); } + public function setBlock($x, $y, $z, $block, $meta = 0){ + if($this->check()){ + $this->map->setBlock($x, $y, $z, $block, $meta); + } + $this->server->trigger("onBlockUpdate", array( + "x" => $x, + "y" => $y, + "z" => $z, + "block" => $block, + "meta" => $meta, + )); + } + public function getOrderedChunk($X, $Z, $columnsPerPacket = 2){ $columnsPerPacket = max(1, (int) $columnsPerPacket); $c = $this->getChunk($X, $Z); diff --git a/classes/API/PlayerAPI.php b/classes/API/PlayerAPI.php index 90f711ed9..3ec26076c 100644 --- a/classes/API/PlayerAPI.php +++ b/classes/API/PlayerAPI.php @@ -73,7 +73,7 @@ class PlayerAPI{ case "list": console("[INFO] Player list:"); foreach($this->server->clients as $c){ - console("[INFO] ".$c->username." (".$c->ip.":".$c->port."), ClientID ".$c->clientID.", (".round($c->username->entity->position["x"], 2).", ".round($c->username->entity->position["y"], 2).", ".round($c->username->entity->position["z"], 2).")"); + console("[INFO] ".$c->username." (".$c->ip.":".$c->port."), ClientID ".$c->clientID.", (".round($c->username->entity->x, 2).", ".round($c->username->entity->y, 2).", ".round($c->username->entity->z, 2).")"); } break; } @@ -82,7 +82,7 @@ class PlayerAPI{ public function teleport($name, $target){ $target = $this->get($target); if($target !== false){ - $this->tppos($name, $target->entity->position["x"], $target->entity->position["y"], $target->entity->position["z"]); + $this->tppos($name, $target->entity->x, $target->entity->y, $target->entity->z); } } @@ -102,6 +102,10 @@ class PlayerAPI{ return false; } + public function getAll(){ + return $this->server->clients; + } + public function getByEID($eid){ $eid = (int) $eid; $CID = $this->server->query("SELECT ip,port FROM players WHERE EID = '".$eid."';", true); @@ -112,6 +116,16 @@ class PlayerAPI{ return false; } + public function getByClientID($clientID){ + $clientID = (int) $clientID; + $CID = $this->server->query("SELECT ip,port FROM players WHERE clientID = '".$clientID."';", true); + $CID = $this->server->clientID($CID["ip"], $CID["port"]); + if(isset($this->server->clients[$CID])){ + return $this->server->clients[$CID]; + } + return false; + } + public function online(){ $o = array(); foreach($this->server->clients as $p){ @@ -127,7 +141,7 @@ class PlayerAPI{ $player = $this->server->clients[$CID]; console("[INFO] Player \"".$player->username."\" connected from ".$player->ip.":".$player->port); $player->data = $this->getOffline($player->username); - $this->server->query("INSERT OR REPLACE INTO players (clientID, EID, ip, port, name) VALUES (".$player->clientID.", ".$player->eid.", '".$player->ip."', ".$player->port.", '".$player->username."');"); + $this->server->query("INSERT OR REPLACE INTO players (clientID, ip, port, name) VALUES (".$player->clientID.", '".$player->ip."', ".$player->port.", '".$player->username."');"); } } diff --git a/classes/API/ServerAPI.php b/classes/API/ServerAPI.php index 937715b08..9d8e7049c 100644 --- a/classes/API/ServerAPI.php +++ b/classes/API/ServerAPI.php @@ -99,6 +99,8 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run $this->$a->init(); } } + + $this->server->loadEntities(); } public function getList(){ diff --git a/classes/ChunkParser.class.php b/classes/ChunkParser.class.php index 7090ff128..6a2432dad 100644 --- a/classes/ChunkParser.class.php +++ b/classes/ChunkParser.class.php @@ -26,7 +26,7 @@ the Free Software Foundation, either version 3 of the License, or */ class ChunkParser{ - private $location, $raw = b""; + private $location, $raw = b"", $file; var $sectorLenght = 4096; //16 * 16 * 16 var $chunkLenght = 86016; //21 * $sectorLenght var $map; @@ -62,6 +62,7 @@ class ChunkParser{ if(!file_exists($file)){ return false; } + $this->file = $file; $this->raw = file_get_contents($file); $this->chunkLenght = $this->sectorLenght * ord($this->raw{0}); return true; @@ -78,15 +79,25 @@ class ChunkParser{ return 0x1000 + (($Z * $sectors) << 12) + (($X * $sectors) << 16); } - public function getChunk($X, $Z, $header = true){ + public function getChunk($X, $Z){ $X = (int) $X; $Z = (int) $Z; - if($header === false){ - $add = 4; - }else{ - $add = 0; + return substr($this->raw, $this->getOffset($X, $Z), $this->chunkLenght); + } + + public function writeChunk($X, $Z){ + $X = (int) $X; + $Z = (int) $Z; + if(!isset($this->map[$X][$Z])){ + return false; } - return substr($this->raw, $this->getOffset($X, $Z) + $add, $this->chunkLenght - $add); + $chunk = ""; + foreach($this->map[$X][$Z] as $section => $data){ + foreach($data as $r){ + $chunk .= $r; + } + } + return Utils::writeLInt(strlen($chunk)).$chunk; } public function parseChunk($X, $Z){ @@ -154,6 +165,20 @@ class ChunkParser{ console("[DEBUG] Chunks loaded!", true, true, 2); } + public function saveMap(){ + console("[DEBUG] Saving chunks...", true, true, 2); + $fp = fopen($this->file, "r+b"); + flock($fp, LOCK_EX); + foreach($this->map as $x => $d){ + foreach($d as $z => $chunk){ + fseek($fp, $this->getOffset($x, $z)); + fwrite($fp, $this->writeChunk($x, $z), $this->chunkLenght); + } + } + flock($fp, LOCK_UN); + fclose($fp); + } + public function getBlock($x, $y, $z){ $x = (int) $x; $y = (int) $y; @@ -164,8 +189,32 @@ class ChunkParser{ $aZ = $z - ($Z << 4); $index = $aX + ($aZ << 4); $block = ord($this->map[$X][$Z][0][$index]{$y}); - //$meta = $this->getOffset($X, $Z) + 4 + (($x << 6) + $y + ($z << 10)); - return array($block, 0); + $meta = ord($this->map[$X][$Z][1][$index]{$y >> 1}); + if(($y & 1) === 0){ + $meta = $meta & 0x0F; + }else{ + $meta = $meta >> 4; + } + return array($block, $meta); + } + + public function setBlock($x, $y, $z, $block, $meta = 0){ + $x = (int) $x; + $y = (int) $y; + $z = (int) $z; + $X = $x >> 4; + $Z = $z >> 4; + $aX = $x - ($X << 4); + $aZ = $z - ($Z << 4); + $index = $aX + ($aZ << 4); + $this->map[$X][$Z][0][$index]{$y} = chr($block); + $old_meta = ord($this->map[$X][$Z][1][$index]{$y >> 1}); + if(($y & 1) === 0){ + $meta = ($old_meta & 0xF0) | ($meta & 0x0F); + }else{ + $meta = (($meta << 4) & 0xF0) | ($old_meta & 0x0F); + } + $this->map[$X][$Z][1][$index]{$y >> 1} = chr($meta); } } \ No newline at end of file diff --git a/classes/CustomPacketHandler.class.php b/classes/CustomPacketHandler.class.php index 10ca279db..3a9e04bf3 100644 --- a/classes/CustomPacketHandler.class.php +++ b/classes/CustomPacketHandler.class.php @@ -186,7 +186,7 @@ class CustomPacketHandler{ break; case MC_ADD_MOB: if($this->c === false){ - $this->data["int"] = Utils::readInt($this->get(4)); + $this->data["eid"] = Utils::readInt($this->get(4)); $this->data["type"] = Utils::readInt($this->get(4)); $this->data["x"] = Utils::readFloat($this->get(4)); $this->data["y"] = Utils::readFloat($this->get(4)); diff --git a/classes/Entity.class.php b/classes/Entity.class.php index 20c8ff8a5..f04237c0f 100644 --- a/classes/Entity.class.php +++ b/classes/Entity.class.php @@ -32,17 +32,17 @@ define("ENTITY_OBJECT", 2); define("ENTITY_ITEM", 3); define("ENTITY_PAINTING", 4); -class Entity{ - var $eid, $type, $name, $position, $dead, $metadata, $class, $attach, $data, $closed; - protected $health, $client; +class Entity extends stdClass{ + var $eid, $type, $name, $x, $y, $z, $yaw, $pitch, $dead, $data, $class, $attach, $metadata, $closed, $player; - function __construct($eid, $class, $type = 0, $server){ //$type = 0 ---> player + function __construct($server, $eid, $class, $type = 0, $data = array()){ $this->server = $server; $this->eid = (int) $eid; $this->type = (int) $type; $this->class = (int) $class; + $this->player = false; $this->attach = false; - $this->data = array(); + $this->data = $data; $this->status = 0; $this->health = 20; $this->dead = false; @@ -50,19 +50,72 @@ class Entity{ $this->name = ""; $this->server->query("INSERT OR REPLACE INTO entities (EID, type, class, health) VALUES (".$this->eid.", ".$this->type.", ".$this->class.", ".$this->health.");"); $this->metadata = array(); - /*include("misc/entities.php"); + $this->x = isset($this->data["x"]) ? $this->data["x"]:0; + $this->y = isset($this->data["y"]) ? $this->data["y"]:0; + $this->z = isset($this->data["z"]) ? $this->data["z"]:0; + $this->yaw = isset($this->data["yaw"]) ? $this->data["yaw"]:0; + $this->pitch = isset($this->data["pitch"]) ? $this->data["pitch"]:0; + $this->position = array("x" => &$this->x, "y" => &$this->y, "z" => &$this->z, "yaw" => &$this->yaw, "pitch" => &$this->pitch); switch($this->class){ case ENTITY_PLAYER: - case ENTITY_ITEM: + $this->player = $this->data["player"]; + $this->health = &$this->player->data["health"]; + break; + case ENTITY_ITEM: + $this->meta = (int) $this->data["meta"]; + $this->stack = (int) $this->data["stack"]; break; - case ENTITY_MOB: - $this->setName((isset($mobs[$this->type]) ? $mobs[$this->type]:$this->type)); + //$this->setName((isset($mobs[$this->type]) ? $mobs[$this->type]:$this->type)); break; case ENTITY_OBJECT: - $this->setName((isset($objects[$this->type]) ? $objects[$this->type]:$this->type)); + //$this->setName((isset($objects[$this->type]) ? $objects[$this->type]:$this->type)); break; - }*/ + } + } + + public function spawn($player){ + if(!is_object($player)){ + $player = $this->server->api->player->get($player); + } + if($player->eid === $this->eid){ + return false; + } + switch($this->class){ + case ENTITY_PLAYER: + $player->dataPacket(MC_ADD_PLAYER, array( + "clientID" => $this->player->clientID, + "username" => $this->player->username, + "eid" => $this->eid, + "x" => $this->x, + "y" => $this->y, + "z" => $this->z, + )); + break; + case ENTITY_ITEM: + $player->dataPacket(MC_ADD_ITEM_ENTITY, array( + "eid" => $this->eid, + "x" => $this->x, + "y" => $this->y, + "z" => $this->z, + "block" => $this->type, + "meta" => $this->meta, + "stack" => $this->stack, + )); + break; + case ENTITY_MOB: + $player->dataPacket(MC_ADD_MOB, array( + "type" => $this->type, + "eid" => $this->eid, + "x" => $this->x, + "y" => $this->y, + "z" => $this->z, + )); + break; + case ENTITY_OBJECT: + //$this->setName((isset($objects[$this->type]) ? $objects[$this->type]:$this->type)); + break; + } } public function close(){ @@ -93,59 +146,36 @@ class Entity{ public function look($pos2){ $pos = $this->getPosition(); $angle = Utils::angle3D($pos2, $pos); - $this->position["yaw"] = $angle["yaw"]; - $this->position["pitch"] = $angle["pitch"]; - $this->server->query("UPDATE entities SET pitch = ".$this->position["pitch"].", yaw = ".$this->position["yaw"]." WHERE EID = ".$this->eid.";"); + $this->yaw = $angle["yaw"]; + $this->pitch = $angle["pitch"]; + $this->server->query("UPDATE entities SET pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";"); } public function setCoords($x, $y, $z){ - if(!isset($this->position)){ - $this->position = array( - "x" => 0, - "y" => 0, - "z" => 0, - "yaw" => 0, - "pitch" => 0, - "ground" => 0, - ); - } - $this->position["x"] = $x; - $this->position["y"] = $y; - $this->position["z"] = $z; - $this->server->query("UPDATE entities SET x = ".$this->position["x"].", y = ".$this->position["y"].", z = ".$this->position["z"]." WHERE EID = ".$this->eid.";"); + $this->x = $x; + $this->y = $y; + $this->z = $z; + $this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z." WHERE EID = ".$this->eid.";"); } public function move($x, $y, $z, $yaw = 0, $pitch = 0){ - if(!isset($this->position)){ - $this->position = array( - "x" => 0, - "y" => 0, - "z" => 0, - "yaw" => 0, - "pitch" => 0, - "ground" => 0, - ); - } - $this->position["x"] += $x; - $this->position["y"] += $y; - $this->position["z"] += $z; - $this->position["yaw"] += $yaw; - $this->position["yaw"] %= 360; - $this->position["pitch"] += $pitch; - $this->position["pitch"] %= 90; - $this->server->query("UPDATE entities SET x = ".$this->position["x"].", y = ".$this->position["y"].", z = ".$this->position["z"].", pitch = ".$this->position["pitch"].", yaw = ".$this->position["yaw"]." WHERE EID = ".$this->eid.";"); + $this->x += $x; + $this->y += $y; + $this->z += $z; + $this->yaw += $yaw; + $this->yaw %= 360; + $this->pitch += $pitch; + $this->pitch %= 90; + $this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z.", pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";"); } public function setPosition($x, $y, $z, $yaw, $pitch){ - $this->position = array( - "x" => $x, - "y" => $y, - "z" => $z, - "yaw" => $yaw, - "pitch" => $pitch, - "ground" => $ground, - ); - $this->server->query("UPDATE entities SET x = ".$this->position["x"].", y = ".$this->position["y"].", z = ".$this->position["z"].", pitch = ".$this->position["pitch"].", yaw = ".$this->position["yaw"]." WHERE EID = ".$this->eid.";"); + $this->x = $x; + $this->y = $y; + $this->z = $z; + $this->yaw = $yaw; + $this->pitch = $pitch; + $this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z.", pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";"); return true; } diff --git a/classes/MinecraftInterface.class.php b/classes/MinecraftInterface.class.php index fc0b461c7..fa7ec37d1 100644 --- a/classes/MinecraftInterface.class.php +++ b/classes/MinecraftInterface.class.php @@ -26,8 +26,8 @@ the Free Software Foundation, either version 3 of the License, or */ class MinecraftInterface{ - var $pstruct, $name, $protocol, $client, $buffer, $dataName; - private $socket; + var $pstruct, $name, $protocol, $client, $dataName; + private $socket, $data; function __construct($server, $protocol = CURRENT_PROTOCOL, $port = 25565, $listen = false, $client = true){ $this->socket = new UDPSocket($server, $port, (bool) $listen); $this->protocol = (int) $protocol; @@ -88,8 +88,7 @@ class MinecraftInterface{ $p = "[".(microtime(true) - $this->start)."] [".((($origin === "client" and $this->client === true) or ($origin === "server" and $this->client === false)) ? "CLIENT->SERVER":"SERVER->CLIENT")." ".$ip.":".$port."]: Error, bad packet id 0x".Utils::strTohex(chr($pid))." [length ".strlen($raw)."]".PHP_EOL; $p .= Utils::hexdump($data[0]); $p .= PHP_EOL; - logg($p, "packets", true, 2); - $this->buffer = ""; + logg($p, "packets", true, 2); return false; } diff --git a/classes/PocketMinecraftClient.class.php b/classes/PocketMinecraftClient.class.php deleted file mode 100644 index 6c6590ff3..000000000 --- a/classes/PocketMinecraftClient.class.php +++ /dev/null @@ -1,242 +0,0 @@ -player = new Player($username); - $this->start = microtime(true); - $this->version = (int) $version; - $this->username = $username; - $this->connected = false; - $this->cnt = 1; - $this->clientID = $clientID === false ? Utils::readLong(Utils::getRandomBytes(8)):$clientID; - $this->events = array("disabled" => array()); - $this->actions = array(); - $this->interface = new MinecraftInterface("255.255.255.255", $protocol, 19132); - console("[INFO] Starting Minecraft Client"); - console("[INFO] Username: ".$this->username); - console("[INFO] Client GUID: ".$this->clientID); - $this->event("onReceivedPacket", "packetHandler", true); - $this->stop = false; - $this->counter = array(0,0); - declare(ticks=15); - register_tick_function(array($this, "tickerFunction")); - } - - public function action($microseconds, $code){ - $this->actions[] = array($microseconds / 1000000, microtime(true), $code); - console("[INTERNAL] Attached to action ".$microseconds, true, true, 3); - } - - public function tickerFunction(){ - //actions that repeat every x time will go here - $time = microtime(true); - foreach($this->actions as $id => $action){ - if($action[1] <= ($time - $action[0])){ - $this->actions[$id][1] = $time; - eval($action[2]); - } - } - } - - public function start($ip){ - $this->stop = false; - $this->action(50000, '$this->trigger("onTick", $time);'); - $this->interface = new MinecraftInterface($ip, $this->protocol, 19132); - console("[INFO] Connecting to Server ".$ip); - $this->send(0x05, array( - MAGIC, - $this->version, - str_repeat("\x00", 1447), - )); - $this->process(); - } - - public function getServerList(){ - $this->action(1000000, '$this->send(0x02, array(((microtime(true) - $this->start) * 1000)));'); - $this->action(5000000, '$this->actions = array();$this->stop = true;'); - $this->process(); - $list = array(); - foreach($this->serverList as $ip => $info){ - $info["ip"] = $ip; - $list[] = $info; - } - return $list; - } - - public function packetHandler($packet, $event){ - $data =& $packet["data"]; - switch($packet["pid"]){ - case 0x1c: - $pingID = $data[0]; - $this->serverID = $data[1]; - $info = explode(";", $data[3]); - $this->serverList[$packet["ip"]] = array("serverID" => $serverID, "username" => array_pop($info)); - break; - case 0x06: - $serverID = $data[1]; - $length = $data[3]; - $this->send(0x07, array( - MAGIC, - "\x04\x3f\x57\xfe\xfd", - 19132, - $length, - $this->clientID, - )); - break; - case 0x08: - $serverID = $data[1]; - $this->send(0x84, array( - $this->counter[0], - 0x00, - array( - "id" => 0x09, - "clientID" => $this->clientID, - "session" => Utils::readLong("\x00\x00\x00\x00\x03\x1c\xaf\x05"), - ), - )); - ++$this->counter[0]; - break; - case 0x84: - if(isset($data[0])){ - $this->counter[1] = $data[0]; - $this->send(0xc0, array(1, true, $data[0])); - } - switch($data["id"]){ - case 0x10: - $this->send(0x84, array( - $this->counter[0], - 0x00, - array( - "id" => 0x13, - "port" => 19132, - "dataArray" => $data["dataArray"], - "session" => $data["session"], - "session2" => $data["session2"], - ), - )); - ++$this->counter[0]; - $this->send(0x84, array( - $this->counter[0], - 0x00, - array( - "id" => 0x00, - "payload" => $data["session"], - ), - )); - ++$this->counter[0]; - $this->send(0x84, array( - $this->counter[0], - 0x00, - array( - "id" => 0x82, - "username" => $this->username, - ), - )); - ++$this->counter[0]; - /*$this->send(0x84, array( - $this->counter[0], - 0x00, - array( - "id" => 0xb1, - "message" => $this->username, - ), - )); - ++$this->counter[0];*/ - break; - case 0x86: - console("[DEBUG] Time: ".$data["time"], true, true, 2); - break; - } - break; - } - } - - public function send($pid, $data = array(), $raw = false, $dest = false, $port = false){ - $this->trigger($pid, $data); - $this->trigger("onSentPacket", $data); - $this->interface->writePacket($pid, $data, $raw, $dest, $port); - } - - public function process(){ - while($this->stop === false){ - $packet = $this->interface->readPacket(); - if($packet !== false){ - $this->trigger("onReceivedPacket", $packet); - $this->trigger($packet["pid"], $packet); - }else{ - usleep(10000); - } - } - } - - public function trigger($event, $data = ""){ - console("[INTERNAL] Event ". $event, true, true, 3); - if(isset($this->events[$event]) and !isset($this->events["disabled"][$event])){ - foreach($this->events[$event] as $eid => $ev){ - if(isset($ev[1]) and ($ev[1] === true or is_object($ev[1]))){ - $this->responses[$eid] = call_user_func(array(($ev[1] === true ? $this:$ev[1]), $ev[0]), $data, $event, $this); - }else{ - $this->responses[$eid] = call_user_func($ev[0], $data, $event, $this); - } - } - } - } - public function toggleEvent($event){ - if(isset($this->events["disabled"][$event])){ - unset($this->events["disabled"][$event]); - console("[INTERNAL] Enabled event ".$event, true, true, 3); - }else{ - $this->events["disabled"][$event] = false; - console("[INTERNAL] Disabled event ".$event, true, true, 3); - } - } - - public function event($event, $func, $in = false){ - ++$this->cnt; - if(!isset($this->events[$event])){ - $this->events[$event] = array(); - } - $this->events[$event][$this->cnt] = array($func, $in); - console("[INTERNAL] Attached to event ".$event, true, true, 3); - return $this->cnt; - } - - public function deleteEvent($event, $id = -1){ - if($id === -1){ - unset($this->events[$event]); - }else{ - unset($this->events[$event][$id]); - if(isset($this->events[$event]) and count($this->events[$event]) === 0){ - unset($this->events[$event]); - } - } - } - -} \ No newline at end of file diff --git a/classes/PocketMinecraftServer.class.php b/classes/PocketMinecraftServer.class.php index 22259348c..e8cf83ee7 100644 --- a/classes/PocketMinecraftServer.class.php +++ b/classes/PocketMinecraftServer.class.php @@ -31,9 +31,12 @@ class PocketMinecraftServer extends stdClass{ var $seed, $protocol, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd, $timePerSecond, $responses, $spawn, $entities, $mapDir, $mapName, $map, $level, $tileEntities; private $database, $interface, $cnt, $events, $version, $serverType, $lastTick; function __construct($name, $gamemode = 1, $seed = false, $protocol = CURRENT_PROTOCOL, $port = 19132, $serverID = false, $version = CURRENT_VERSION){ - $this->port = (int) $port; + $this->port = (int) $port; //19132 - 19135 console("[INFO] Pocket-Minecraft-PHP by @shoghicp, LGPL License. http://bit.ly/RE7uaW", 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"); + } console("[INFO] Loading database..."); $this->startDatabase(); $this->gamemode = (int) $gamemode; @@ -47,7 +50,7 @@ class PocketMinecraftServer extends stdClass{ $this->tileEntities = array(); $this->entities = array(); $this->custom = array(); - $this->cnt = 1; + $this->cnt = 0; $this->eidCnt = 1; $this->maxClients = 20; $this->description = ""; @@ -61,8 +64,6 @@ class PocketMinecraftServer extends stdClass{ $this->spawn = array("x" => 128.5,"y" => 100,"z" => 128.5); $this->time = 0; $this->timePerSecond = 10; - console("[INFO] Loading events..."); - $this->loadEvents(); $this->setType("normal"); $this->interface = new MinecraftInterface("255.255.255.255", $this->protocol, $this->port, true, false); $this->reloadConfig(); @@ -73,9 +74,7 @@ class PocketMinecraftServer extends stdClass{ $this->stop = false; } - public function loadEvents(){ - $this->events = array("disabled" => array()); - + public function loadEvents(){ $this->event("onChat", "eventHandler", true); $this->event("onPlayerDeath", "eventHandler", true); $this->event("onPlayerAdd", "eventHandler", true); @@ -85,7 +84,9 @@ class PocketMinecraftServer extends stdClass{ $this->action(5000000, '$this->trigger("onHealthRegeneration", 1);'); $this->action(1000000 * 60, '$this->reloadConfig();'); $this->action(1000000 * 60 * 10, '$this->custom = array();'); - $this->action(1000000 * 80, '$this->chat(false, count($this->clients)."/".$this->maxClients." online: ".implode(", ",$this->api->player->online()));'); + if($this->api !== false){ + $this->action(1000000 * 80, '$this->chat(false, count($this->clients)."/".$this->maxClients." online: ".implode(", ",$this->api->player->online()));'); + } $this->action(1000000 * 75, '$this->debugInfo(true);'); } @@ -95,7 +96,7 @@ class PocketMinecraftServer extends stdClass{ $this->query("CREATE TABLE entities (EID INTEGER PRIMARY KEY, type NUMERIC, class NUMERIC, name TEXT, x NUMERIC, y NUMERIC, z NUMERIC, yaw NUMERIC, pitch NUMERIC, health NUMERIC);"); $this->query("CREATE TABLE metadata (EID INTEGER PRIMARY KEY, name TEXT, value TEXT);"); $this->query("CREATE TABLE actions (ID INTEGER PRIMARY KEY, interval NUMERIC, last NUMERIC, code TEXT, repeat NUMERIC);"); - $this->query("CREATE TABLE events (ID INTEGER PRIMARY KEY, eventName TEXT, priority NUMERIC, disabled INTEGER);"); + $this->query("CREATE TABLE events (ID INTEGER PRIMARY KEY, eventName TEXT, priority NUMERIC);"); } public function query($sql, $fetch = false){ @@ -216,18 +217,6 @@ class PocketMinecraftServer extends stdClass{ }else{ $this->map->loadMap(); } - if($this->map !== false){ - console("[INFO] Loading entities..."); - $entities = unserialize(file_get_contents($this->mapDir."entities.dat")); - foreach($entities as $entity){ - $this->entities[$this->eidCnt] = new Entity($this->eidCnt, ENTITY_MOB, $entity["id"], $this); - $this->entities[$this->eidCnt]->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]); - $this->entities[$this->eidCnt]->setHealth($entity["Health"]); - ++$this->eidCnt; - } - console("[DEBUG] Loaded ".count($this->entities)." Entities", true, true, 2); - $this->action(1000000 * 60 * 15, '$this->chat(false, "Forcing save...");$this->save();$this->chat(false, "Done");'); - } }else{ console("[INFO] Time: ".$this->time); console("[INFO] Seed: ".$this->seed); @@ -235,16 +224,47 @@ class PocketMinecraftServer extends stdClass{ } } + public function loadEntities(){ + if($this->map !== false){ + console("[INFO] Loading entities..."); + $entities = unserialize(file_get_contents($this->mapDir."entities.dat")); + foreach($entities as $entity){ + if(!isset($entity["id"])){ + break; + } + if(!isset($this->api) or $this->api === false){ + $this->entities[$this->eidCnt] = new Entity($his, $this->eidCnt, ENTITY_MOB, $entity["id"]); + $this->entities[$this->eidCnt]->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]); + $this->entities[$this->eidCnt]->setHealth($entity["Health"]); + ++$this->eidCnt; + }else{ + $e = $this->api->entity->add(ENTITY_MOB, $entity["id"]); + $e->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]); + $e->setHealth($entity["Health"]); + } + } + console("[DEBUG] Loaded ".count($this->entities)." Entities", true, true, 2); + $this->action(1000000 * 60 * 15, '$this->chat(false, "Forcing save...");$this->save();$this->chat(false, "Done");'); + } + } + public function save(){ if($this->mapName !== false){ file_put_contents($this->mapDir."level.dat", serialize($this->level)); + $this->map->saveMap(); } } public function start(){ + if($this->mapName !== false and $this->map === false){ + $this->loadMap(); + $this->loadEntities(); + } + console("[INFO] Loading events..."); + $this->loadEvents(); declare(ticks=15); register_tick_function(array($this, "tick")); - $this->event("onTick", array($this, "tickerFunction")); + $this->event("onTick", "tickerFunction", true); register_shutdown_function(array($this, "close")); console("[INFO] Server started!"); $this->process(); @@ -327,8 +347,7 @@ class PocketMinecraftServer extends stdClass{ $port = $data[2]; $MTU = $data[3]; $clientID = $data[4]; - $eid = $this->eidCnt++; - $this->clients[$CID] = new Session($this, $clientID, $eid, $packet["ip"], $packet["port"], $MTU); + $this->clients[$CID] = new Session($this, $clientID, $packet["ip"], $packet["port"], $MTU); $this->clients[$CID]->handle(0x07, $data); break; } @@ -351,18 +370,14 @@ class PocketMinecraftServer extends stdClass{ } public function trigger($event, $data = ""){ - $events = $this->query("SELECT ID FROM events WHERE eventName = '".$event."' AND disabled = 0;"); + $events = $this->query("SELECT ID FROM events WHERE eventName = '".$event."';"); if($events === false or $events === true){ return; } - //console("[INTERNAL] Event ". $event, true, true, 3); while($evn = $events->fetchArray(SQLITE3_ASSOC)){ - $ev = $this->events[$event][$evn["ID"]]; - if(isset($ev[1]) and ($ev[1] === true or is_object($ev[1]))){ - $this->responses[$evn["ID"]] = call_user_func(array(($ev[1] === true ? $this:$ev[1]), $ev[0]), $data, $event, $this); - }else{ - $this->responses[$evn["ID"]] = call_user_func($ev[0], $data, $event, $this); - } + $evid = (int) $evn["ID"]; + $this->responses[$evid] = call_user_func($this->events[$evid], $data, $event, $this); + } return true; } @@ -397,42 +412,23 @@ class PocketMinecraftServer extends stdClass{ $this->query("UPDATE actions SET last = ".$time." WHERE last <= (".$time." - interval);"); } - public function toggleEvent($event){ - if(isset($this->events["disabled"][$event])){ - unset($this->events["disabled"][$event]); - $this->query("UPDATE events SET disabled = 0 WHERE eventName = '".$event."';"); - console("[INTERNAL] Enabled event ".$event, true, true, 3); - }else{ - $this->events["disabled"][$event] = false; - $this->query("UPDATE events SET disabled = 1 WHERE eventName = '".$event."';"); - console("[INTERNAL] Disabled event ".$event, true, true, 3); - } - } - public function event($event, $func, $in = false){ - ++$this->cnt; - if(!isset($this->events[$event])){ - $this->events[$event] = array(); + if($in === true){ + $func = array($this, $func); } - $this->query("INSERT INTO events (ID, eventName, disabled) VALUES (".$this->cnt.", '".str_replace("'", "\\'", $event)."', 0);"); - $this->events[$event][$this->cnt] = array($func, $in); - console("[INTERNAL] Attached to event ".$event, true, true, 3); - return $this->cnt; + if(!is_callable($func)){ + return false; + } + $this->events[$this->cnt] = $func; + $this->query("INSERT INTO events (ID, eventName) VALUES (".$this->cnt.", '".str_replace("'", "\\'", $event)."');"); + console("[INTERNAL] Attached ".(is_array($func) ? get_class($func[0])."::".$func[1]:$func)." to event ".$event." (ID ".$this->cnt.")", true, true, 3); + return $this->cnt++; } - public function deleteEvent($event, $id = -1){ + public function deleteEvent($id){ $id = (int) $id; - if($id === -1){ - unset($this->events[$event]); - $this->query("DELETE FROM events WHERE eventName = '".str_replace("'", "\\'", $event)."';"); - }else{ - unset($this->events[$event][$id]); - $this->query("DELETE FROM events WHERE ID = ".$id.";"); - if(isset($this->events[$event]) and count($this->events[$event]) === 0){ - unset($this->events[$event]); - $this->query("DELETE FROM events WHERE eventName = '".str_replace("'", "\\'", $event)."';"); - } - } + unset($this->events[$id]); + $this->query("DELETE FROM events WHERE ID = ".$id.";"); } } \ No newline at end of file diff --git a/classes/Session.class.php b/classes/Session.class.php index 5c3337b53..e28702055 100644 --- a/classes/Session.class.php +++ b/classes/Session.class.php @@ -29,22 +29,22 @@ the Free Software Foundation, either version 3 of the License, or class Session{ private $server, $timeout, $connected, $evid, $queue, $buffer; var $clientID, $ip, $port, $counter, $username, $eid, $data, $entity, $auth, $CID, $MTU; - function __construct($server, $clientID, $eid, $ip, $port, $MTU){ + function __construct($server, $clientID, $ip, $port, $MTU){ $this->queue = array(); $this->buffer = array(); $this->MTU = $MTU; $this->server = $server; $this->clientID = $clientID; $this->CID = $this->server->clientID($ip, $port); - $this->eid = $eid; + $this->eid = false; $this->data = array(); $this->ip = $ip; $this->entity = false; $this->port = $port; $this->timeout = microtime(true) + 25; $this->evid = array(); - $this->evid[] = array("onTick", $this->server->event("onTick", array($this, "onTick"))); - $this->evid[] = array("onClose", $this->server->event("onClose", array($this, "close"))); + $this->evid[] = $this->server->event("onTick", array($this, "onTick")); + $this->evid[] = $this->server->event("onClose", array($this, "close")); console("[DEBUG] New Session started with ".$ip.":".$port.". MTU ".$this->MTU.", Client ID ".$this->clientID, true, true, 2); $this->connected = true; $this->auth = false; @@ -83,9 +83,9 @@ class Session{ public function save(){ if(is_object($this->entity)){ $this->data["spawn"] = array( - "x" => $this->entity->position["x"], - "y" => $this->entity->position["y"], - "z" => $this->entity->position["z"], + "x" => $this->entity->x, + "y" => $this->entity->y, + "z" => $this->entity->z, ); } } @@ -94,7 +94,7 @@ class Session{ $reason = $reason == "" ? "server stop":$reason; $this->save(); foreach($this->evid as $ev){ - $this->server->deleteEvent($ev[0], $ev[1]); + $this->server->deleteEvent($ev); } $this->eventHandler("You have been kicked. Reason: ".$reason, "onChat"); $this->dataPacket(MC_DISCONNECT); @@ -113,6 +113,9 @@ class Session{ $this->server->trigger("onPlayerDeath", array("name" => $this->username, "cause" => $data["cause"])); } break; + case "onBlockUpdate": + $this->dataPacket(MC_UPDATE_BLOCK, $data); + break; case "onTeleport": if($data["eid"] !== $this->eid){ break; @@ -133,11 +136,11 @@ class Session{ $entity = $this->server->entities[$data]; $this->dataPacket(MC_MOVE_ENTITY_POSROT, array( "eid" => $data, - "x" => $entity->position["x"], - "y" => $entity->position["y"], - "z" => $entity->position["z"], - "yaw" => $entity->position["yaw"], - "pitch" => $entity->position["pitch"], + "x" => $entity->x, + "y" => $entity->y, + "z" => $entity->z, + "yaw" => $entity->yaw, + "pitch" => $entity->pitch, )); break; case "onHealthRegeneration": @@ -151,24 +154,11 @@ class Session{ "health" => $data["health"], )); $this->data["health"] = $data["health"]; - if(is_object($this->entity)){ + /*if(is_object($this->entity)){ $this->entity->setHealth($data["health"]); - } + }*/ } break; - case "onPlayerAdd": - if($data["eid"] === $this->eid){ - break; - } - $this->dataPacket(MC_ADD_PLAYER, array( - "clientID" => $data["clientID"], - "username" => $data["username"], - "eid" => $data["eid"], - "x" => $data["x"], - "y" => $data["y"], - "z" => $data["z"], - )); - break; case "onEntityRemove": if($data === $this->eid){ break; @@ -260,10 +250,13 @@ class Session{ break; case MC_LOGIN: $this->username = str_replace("/", "", $data["username"]); - foreach($this->server->clients as $c){ - if($c->eid !== $this->eid and $c->username === $this->username){ - $c->close("logged in from another location"); - } + $u = $this->server->api->player->get($this->username); + $c = $this->server->api->player->getByClientID($this->clientID); + if($u !== false){ + $c->close("logged in from another location"); + } + if($c !== false){ + $c->close("logged in from another location"); } if($this->server->whitelist !== false and !in_array($this->username, $this->server->whitelist)){ $this->close("\"".$this->username."\" not being on white-list", false); @@ -273,16 +266,6 @@ class Session{ $this->auth = true; $this->data["lastIP"] = $this->ip; $this->data["lastID"] = $this->clientID; - $this->evid[] = array("onTimeChange", $this->server->event("onTimeChange", array($this, "eventHandler"))); - $this->evid[] = array("onChat", $this->server->event("onChat", array($this, "eventHandler"))); - $this->evid[] = array("onDeath", $this->server->event("onDeath", array($this, "eventHandler"))); - $this->evid[] = array("onPlayerAdd", $this->server->event("onPlayerAdd", array($this, "eventHandler"))); - $this->evid[] = array("onEntityRemove", $this->server->event("onEntityRemove", array($this, "eventHandler"))); - $this->evid[] = array("onEntityMove", $this->server->event("onEntityMove", array($this, "eventHandler"))); - $this->evid[] = array("onHealthChange", $this->server->event("onHealthChange", array($this, "eventHandler"))); - $this->evid[] = array("onHealthRegeneration", $this->server->event("onHealthRegeneration", array($this, "eventHandler"))); - $this->evid[] = array("onAnimate", $this->server->event("onAnimate", array($this, "eventHandler"))); - $this->evid[] = array("onTeleport", $this->server->event("onTeleport", array($this, "eventHandler"))); $this->dataPacket(MC_LOGIN_STATUS, array( "status" => 0, )); @@ -300,43 +283,25 @@ class Session{ if(is_object($this->entity)){ break; } - $this->server->trigger("onHealthChange", array("eid" => $this->eid, "health" => $this->data["health"], "cause" => "respawn")); - console("[DEBUG] Player with EID ".$this->eid." \"".$this->username."\" spawned!", true, true, 2); - $this->entity = new Entity($this->eid, ENTITY_PLAYER, 0, $this->server); + $this->entity = $this->server->api->entity->add(ENTITY_PLAYER, 0, array("player" => $this)); + $this->eid = $this->entity->eid; + $this->server->query("UPDATE players SET EID = ".$this->eid." WHERE clientID = ".$this->clientID.";"); $this->entity->setName($this->username); - $this->entity->setHealth($this->data["health"]); $this->entity->data["clientID"] = $this->clientID; - $this->server->entities[$this->eid] = &$this->entity; - $this->server->trigger("onPlayerAdd", array( - "clientID" => $this->clientID, - "username" => $this->username, - "eid" => $this->eid, - "x" => $this->data["spawn"]["x"], - "y" => $this->data["spawn"]["y"], - "z" => $this->data["spawn"]["z"], - )); - foreach($this->server->entities as $entity){ - if($entity->eid !== $this->eid){ - if($entity->class === ENTITY_PLAYER){ - $this->eventHandler(array( - "clientID" => $entity->data["clientID"], - "username" => $entity->name, - "eid" => $entity->eid, - "x" => $entity->position["x"], - "y" => $entity->position["y"], - "z" => $entity->position["z"], - ), "onPlayerAdd"); - }else{ - $this->dataPacket(MC_ADD_MOB, array( - "eid" => $entity->eid, - "type" => $entity->type, - "x" => $entity->position["x"], - "y" => $entity->position["y"], - "z" => $entity->position["z"], - )); - } - } - } + $this->server->api->entity->spawnAll($this); + $this->server->api->entity->spawnToAll($this->eid); + $this->evid[] = $this->server->event("onTimeChange", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onChat", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onDeath", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onEntityRemove", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onEntityMove", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onHealthChange", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onHealthRegeneration", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onAnimate", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onTeleport", array($this, "eventHandler")); + $this->evid[] = $this->server->event("onBlockUpdate", array($this, "eventHandler")); + console("[DEBUG] Player with EID ".$this->eid." \"".$this->username."\" spawned!", true, true, 2); + $this->eventHandler($this->server->motd, "onChat"); if($this->MTU <= 548){ $this->eventHandler("Your connection is bad, you may experience lag and slow map loading.", "onChat"); @@ -363,40 +328,22 @@ class Session{ ), true); } '); - console("[DEBUG] Chunk X ".$data["x"]." Z ".$data["z"]." requested", true, true, 2); + console("[INTERNAL] Chunk X ".$data["x"]." Z ".$data["z"]." requested", true, true, 3); + break; + case MC_PLACE_BLOCK: + var_dump($data); break; case MC_REMOVE_BLOCK: - console("[DEBUG] EID ".$this->eid." broke block at X ".$data["x"]." Y ".$data["y"]." Z ".$data["z"], true, true, 2); - $block = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]); - if($block[0] === 0){ - break; - } - $this->dataPacket(MC_ADD_ITEM_ENTITY, array( - "eid" => $this->server->eidCnt++, - "x" => $data["x"] + mt_rand(0, 100)/100, - "y" => $data["y"], - "z" => $data["z"] + mt_rand(0, 100)/100, - "block" => $block[0], - "meta" => $block[1], - "stack" => 1, - )); - /*$this->send(0x84, array( - $this->counter[0], - 0x00, - array( - "id" => MC_UPDATE_BLOCK, - "x" => $data["x"], - "y" => $data["y"], - "z" => $data["z"], - "block" => 56, - "meta" => 0, - ), - )); - ++$this->counter[0];*/ + $data["eid"] = $this->eid; + $this->server->api->level->handle($data, "onBlockBreak"); + //$this->server->trigger("onBlockBreak", $data); break; case MC_INTERACT: - if($this->server->gamemode !== 1 and $this->server->difficulty > 0 and isset($this->server->entities[$data["target"]]) and Utils::distance($this->entity->position, $this->server->entities[$data["target"]]->position) <= 8){ + if(isset($this->server->entities[$data["target"]]) and Utils::distance($this->entity->position, $this->server->entities[$data["target"]]->position) <= 8){ console("[DEBUG] EID ".$this->eid." attacked EID ".$data["target"], true, true, 2); + if($this->server->gamemode !== 1 and $this->server->difficulty > 0){ + + } $this->server->trigger("onHealthChange", array("eid" => $data["target"], "health" => $this->server->entities[$data["target"]]->getHealth() - $this->server->difficulty, "cause" => $this->eid)); } break; diff --git a/common/dependencies.php b/common/dependencies.php index 4db8464a7..ab18b0826 100644 --- a/common/dependencies.php +++ b/common/dependencies.php @@ -28,6 +28,7 @@ the Free Software Foundation, either version 3 of the License, or require_once(dirname(__FILE__)."/config.php"); require_once("common/functions.php"); +//set_error_handler("error_handler"); $errors = 0; diff --git a/common/functions.php b/common/functions.php index 029ba2ddd..cbc3e9f5f 100644 --- a/common/functions.php +++ b/common/functions.php @@ -120,6 +120,10 @@ function console($message, $EOL = true, $log = true, $level = 1){ } } +function error_handler($errno, $errstr, $errfile, $errline){ + console("[ERROR] A level ".$errno." error happened: \"$errstr\" in \"$errfile\" at line $errline", true, true, 0); +} + function logg($message, $name, $EOL = true, $level = 2, $close = false){ global $fpointers; if((!defined("DEBUG") or DEBUG >= $level) and (!defined("LOG") or LOG === true)){