SQLite database, Entities, position, health

This commit is contained in:
Shoghi Cervantes Pueyo 2012-12-07 02:23:57 +01:00
parent 842e01ffac
commit ea1725ed57
15 changed files with 472 additions and 77 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
data/*
*.log *.log
*.bat *.bat
server.properties server.properties

View File

@ -184,6 +184,31 @@ class CustomPacketHandler{
$this->raw .= Utils::writeFloat($this->data["z"]); $this->raw .= Utils::writeFloat($this->data["z"]);
} }
break; break;
case MC_ADD_PLAYER:
if($this->c === false){
$this->data["clientID"] = Utils::readLong($this->get(8));
$this->data["username"] = $this->get(Utils::readShort($this->get(2), false));
$this->data["eid"] = Utils::readInt($this->get(4));
$this->data["x"] = Utils::readFloat($this->get(4));
$this->data["y"] = Utils::readFloat($this->get(4));
$this->data["z"] = Utils::readFloat($this->get(4));
$this->data["yaw"] = Utils::readFloat($this->get(4));
$this->data["pitch"] = Utils::readFloat($this->get(4));
$this->data["block"] = Utils::readShort($this->get(2), false);
$this->data["meta"] = Utils::readShort($this->get(2), false);
}else{
$this->raw .= Utils::writeLong($this->data["clientID"]);
$this->raw .= Utils::writeShort(strlen($this->data["username"])).$this->data["username"];
$this->raw .= Utils::writeInt($this->data["eid"]);
$this->raw .= Utils::writeFloat($this->data["x"]);
$this->raw .= Utils::writeFloat($this->data["y"]);
$this->raw .= Utils::writeFloat($this->data["z"]);
$this->raw .= Utils::writeFloat($this->data["yaw"]);
$this->raw .= Utils::writeFloat($this->data["pitch"]);
$this->raw .= Utils::writeShort($this->data["block"]);
$this->raw .= Utils::writeShort($this->data["meta"]);
}
break;
case MC_REMOVE_ENTITY: case MC_REMOVE_ENTITY:
if($this->c === false){ if($this->c === false){
$this->data["eid"] = Utils::readInt($this->get(4)); $this->data["eid"] = Utils::readInt($this->get(4));
@ -233,8 +258,8 @@ class CustomPacketHandler{
case MC_PLAYER_EQUIPMENT: case MC_PLAYER_EQUIPMENT:
if($this->c === false){ if($this->c === false){
$this->data["eid"] = Utils::readInt($this->get(4)); $this->data["eid"] = Utils::readInt($this->get(4));
$this->data["block"] = Utils::readShort($this->get(2), true); $this->data["block"] = Utils::readShort($this->get(2), false);
$this->data["meta"] = Utils::readShort($this->get(2), true); $this->data["meta"] = Utils::readShort($this->get(2), false);
}else{ }else{
$this->raw .= Utils::writeInt($this->data["eid"]); $this->raw .= Utils::writeInt($this->data["eid"]);
$this->raw .= Utils::writeShort($this->data["block"]); $this->raw .= Utils::writeShort($this->data["block"]);
@ -248,6 +273,19 @@ class CustomPacketHandler{
$this->raw .= chr($this->data["health"]); $this->raw .= chr($this->data["health"]);
} }
break; break;
case MC_RESPAWN:
if($this->c === false){
$this->data["eid"] = Utils::readInt($this->get(4));
$this->data["x"] = Utils::readFloat($this->get(4));
$this->data["y"] = Utils::readFloat($this->get(4));
$this->data["z"] = Utils::readFloat($this->get(4));
}else{
$this->raw .= Utils::writeInt($this->data["eid"]);
$this->raw .= Utils::writeFloat($this->data["x"]);
$this->raw .= Utils::writeFloat($this->data["y"]);
$this->raw .= Utils::writeFloat($this->data["z"]);
}
break;
case MC_CLIENT_MESSAGE: case MC_CLIENT_MESSAGE:
if($this->c === false){ if($this->c === false){
$this->data["message"] = $this->get(Utils::readShort($this->get(2), false)); $this->data["message"] = $this->get(Utils::readShort($this->get(2), false));

157
classes/Entity.class.php Normal file
View File

@ -0,0 +1,157 @@
<?php
/*
-
/ \
/ \
/ POCKET \
/ MINECRAFT PHP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
define("ENTITY_PLAYER", 0);
define("ENTITY_MOB", 1);
define("ENTITY_OBJECT", 2);
define("ENTITY_ITEM", 3);
define("ENTITY_PAINTING", 4);
class Entity{
var $eid, $type, $name, $position, $dead, $metadata, $class, $attach;
protected $health, $client;
function __construct($eid, $class, $type = 0, $server){ //$type = 0 ---> player
$this->server = $server;
$this->eid = (int) $eid;
$this->type = (int) $type;
$this->class = (int) $class;
$this->attach = false;
$this->status = 0;
$this->health = 20;
$this->dead = false;
$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");
switch($this->class){
case ENTITY_PLAYER:
case ENTITY_ITEM:
break;
case ENTITY_MOB:
$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));
break;
}*/
}
public function __destruct(){
$this->server->query("DELETE FROM entities WHERE EID = ".$this->eid.";");
$this->server->trigger("onEntityRemove", $this->eid);
}
public function getEID(){
return $this->eid;
}
public function getName(){
return $this->name;
}
public function setName($name){
$this->name = $name;
$this->server->query("UPDATE entities SET name = '".str_replace("'", "", $this->name)."' WHERE EID = ".$this->eid.";");
}
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.";");
}
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.";");
}
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.";");
}
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.";");
return true;
}
public function getPosition($round = false){
return !isset($this->position) ? false:($round === true ? array_map("floor", $this->position):$this->position);
}
public function setHealth($health){
$this->health = (int) $health;
$this->server->query("UPDATE entities SET health = ".$this->health." WHERE EID = ".$this->eid.";");
}
public function getHealth(){
return $this->health;
}
}
?>

View File

@ -28,11 +28,14 @@ the Free Software Foundation, either version 3 of the License, or
require_once("classes/Session.class.php"); require_once("classes/Session.class.php");
class PocketMinecraftServer{ class PocketMinecraftServer{
var $seed, $protocol, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd; var $seed, $protocol, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd, $timePerSecond, $responses, $spawn, $entities;
protected $interface, $entities, $player, $cnt, $events, $version, $serverType; 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){ function __construct($name, $gamemode = 1, $seed = false, $protocol = CURRENT_PROTOCOL, $port = 19132, $serverID = false, $version = CURRENT_VERSION){
$this->gamemode = (int) $gamemode;
$this->port = (int) $port; $this->port = (int) $port;
console("[INFO] Starting Minecraft PE Server at *:".$this->port);
console("[INFO] Loading database...");
$this->startDatabase();
$this->gamemode = (int) $gamemode;
$this->version = (int) $version; $this->version = (int) $version;
$this->name = $name; $this->name = $name;
$this->custom = array(); $this->custom = array();
@ -44,23 +47,16 @@ class PocketMinecraftServer{
$this->bannedIPs = array(); $this->bannedIPs = array();
$this->motd = "Welcome to ".$name; $this->motd = "Welcome to ".$name;
$this->serverID = $serverID === false ? Utils::readLong(Utils::getRandomBytes(8)):$serverID; $this->serverID = $serverID === false ? Utils::readLong(Utils::getRandomBytes(8)):$serverID;
$this->seed = $seed === false ? Utils::readInt(Utils::getRandomBytes(4)):((int) $seed); $this->seed = $seed === false ? Utils::readInt(Utils::getRandomBytes(4)):$seed;
$this->events = array("disabled" => array());
$this->actions = array();
$this->clients = array(); $this->clients = array();
$this->protocol = (int) $protocol; $this->protocol = (int) $protocol;
$this->spawn = array("x" => 128.5,"y" => 100,"z" => 128.5);
$this->time = 0; $this->time = 0;
//$this->event("onTick", "onTick", true); $this->timePerSecond = 10;
$this->event("onChat", "eventHandler", true); console("[INFO] Loading events...");
$this->action(1000000, '$this->time += 10;$this->trigger("onTimeChange", $this->time);'); $this->loadEvents();
$this->action(1000000 * 60, '$this->reloadConfig();');
$this->action(1000000 * 60 * 10, '$this->custom = array();');
$this->setType("normal"); $this->setType("normal");
$this->interface = new MinecraftInterface("255.255.255.255", $this->protocol, $this->port, true, false); $this->interface = new MinecraftInterface("255.255.255.255", $this->protocol, $this->port, true, false);
console("[INFO] Starting Minecraft PE Server at *:".$this->port);
$this->action(1000000 * 3 * 60, '$this->chat(false, "This server uses Pocket-Minecraft-PHP");');
sleep(2);
$this->action(1000000 * 3 * 60, '$this->chat(false, "Check it at http://bit.ly/RE7uaW");');
$this->reloadConfig(); $this->reloadConfig();
console("[INFO] Server Name: ".$this->name); console("[INFO] Server Name: ".$this->name);
console("[INFO] Server GUID: ".$this->serverID); console("[INFO] Server GUID: ".$this->serverID);
@ -69,6 +65,37 @@ class PocketMinecraftServer{
console("[INFO] Gamemode: ".($this->gamemode === 0 ? "survival":"creative")); console("[INFO] Gamemode: ".($this->gamemode === 0 ? "survival":"creative"));
console("[INFO] Max Clients: ".$this->maxClients); console("[INFO] Max Clients: ".$this->maxClients);
$this->stop = false; $this->stop = false;
console("[INFO] Server started!");
}
public function loadEvents(){
$this->events = array("disabled" => array());
$this->event("onChat", "eventHandler", true);
$this->action(1000000, '$this->time += $this->timePerSecond;$this->trigger("onTimeChange", $this->time);');
$this->action(1000000 * 60, '$this->reloadConfig();');
$this->action(1000000 * 60 * 10, '$this->custom = array();');
$this->action(1000000 * 80, '$list = ""; foreach($this->clients as $c){$list .= ", ".$c->username;}$this->chat(false, count($this->clients)."/".$this->maxClients." online: ".substr($list, 2));');
$this->action(1000000 * 3 * 60, '$this->chat(false, "This server uses Pocket-Minecraft-PHP");');
$this->action(1000000 * 3 * 60, '$this->chat(false, "Check it at http://bit.ly/RE7uaW");');
$this->action(1000000 * 75, '$this->debugInfo(true);');
}
public function startDatabase(){
$this->database = new SQLite3(":memory:");
$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, disabled INTEGER);");
}
public function query($sql, $fetch = false){
$result = $this->database->query($sql) or console("[ERROR] [SQL Error] ".$this->database->lastErrorMsg().". Query: ".$sql, true, true, 0);
if($fetch === true and ($result !== false and $result !== true)){
$result = $result->fetchArray(SQLITE3_ASSOC);
}
return $result;
} }
public function reloadConfig(){ public function reloadConfig(){
@ -78,6 +105,23 @@ class PocketMinecraftServer{
$this->bannedIPs = explode("\n", str_replace(array(" ","\t","\r"), "", file_get_contents(FILE_PATH."banned-ips.txt"))); $this->bannedIPs = explode("\n", str_replace(array(" ","\t","\r"), "", file_get_contents(FILE_PATH."banned-ips.txt")));
} }
public function debugInfo($console = false){
$info = array();
$info["memory_usage"] = round((memory_get_usage(true) / 1024) / 1024, 2)."MB";
$info["memory_peak_usage"] = round((memory_get_peak_usage(true) / 1024) / 1024, 2)."MB";
$info["entities"] = $this->query("SELECT count(EID) as count FROM entities;", true);
$info["entities"] = $info["entities"]["count"];
$info["events"] = $this->query("SELECT count(ID) as count FROM events;", true);
$info["events"] = $info["events"]["count"];
$info["actions"] = $this->query("SELECT count(ID) as count FROM actions;", true);
$info["actions"] = $info["actions"]["count"];
$info["garbage"] = gc_collect_cycles();
if($console === true){
console("[DEBUG] Memory usage: ".$info["memory_usage"]." (Peak ".$info["memory_peak_usage"]."), Entities: ".$info["entities"].", Events: ".$info["events"].", Actions: ".$info["actions"].", Garbage: ".$info["garbage"], true, true, 2);
}
return $info;
}
public function close($reason = "stop"){ public function close($reason = "stop"){
$this->chat(false, "Stopping server..."); $this->chat(false, "Stopping server...");
$this->stop = true; $this->stop = true;
@ -113,28 +157,20 @@ class PocketMinecraftServer{
} }
} }
public function action($microseconds, $code){
$this->actions[] = array($microseconds / 1000000, microtime(true), $code);
console("[INTERNAL] Attached to action ".$microseconds, true, true, 3);
}
public function start(){ public function start(){
declare(ticks=15); declare(ticks=15);
register_tick_function(array($this, "tickerFunction")); register_tick_function(array($this, "tick"));
$this->action(50000, '$this->trigger("onTick", $time);'); $this->event("onTick", "tickerFunction", true);
$this->event("onReceivedPacket", "packetHandler", true); $this->event("onReceivedPacket", "packetHandler", true);
register_shutdown_function(array($this, "close")); register_shutdown_function(array($this, "close"));
$this->process(); $this->process();
} }
public function tickerFunction(){ public function tick(){
//actions that repeat every x time will go here
$time = microtime(true); $time = microtime(true);
foreach($this->actions as $id => $action){ if($this->lastTick <= ($time - 0.05)){
if($action[1] <= ($time - $action[0])){ $this->lastTick = $time;
$this->actions[$id][1] = $time; $this->trigger("onTick", $time);
eval($action[2]);
}
} }
} }
@ -201,9 +237,8 @@ class PocketMinecraftServer{
$port = $data[2]; $port = $data[2];
$MTU = $data[3]; $MTU = $data[3];
$clientID = $data[4]; $clientID = $data[4];
$EID = $this->eidCnt++; $eid = $this->eidCnt++;
$this->clients[$CID] = new Session($this, $clientID, $EID, $packet["ip"], $packet["port"]); $this->clients[$CID] = new Session($this, $clientID, $eid, $packet["ip"], $packet["port"]);
$entities[$EID] = &$this->clients[$CID];
$this->clients[$CID]->handle(0x07, $data); $this->clients[$CID]->handle(0x07, $data);
break; break;
} }
@ -229,23 +264,59 @@ class PocketMinecraftServer{
} }
public function trigger($event, $data = ""){ public function trigger($event, $data = ""){
$events = $this->query("SELECT ID FROM events WHERE eventName = '".$event."' AND disabled = 0;");
if($events === false or $events === true){
return;
}
console("[INTERNAL] Event ". $event, true, true, 3); console("[INTERNAL] Event ". $event, true, true, 3);
if(isset($this->events[$event]) and !isset($this->events["disabled"][$event])){ while($evn = $events->fetchArray(SQLITE3_ASSOC)){
foreach($this->events[$event] as $evid => $ev){ $ev = $this->events[$event][$evn["ID"]];
if(isset($ev[1]) and ($ev[1] === true or is_object($ev[1]))){ if(isset($ev[1]) and ($ev[1] === true or is_object($ev[1]))){
$this->responses[$evid] = call_user_func(array(($ev[1] === true ? $this:$ev[1]), $ev[0]), $data, $event, $this); $this->responses[$evn["ID"]] = call_user_func(array(($ev[1] === true ? $this:$ev[1]), $ev[0]), $data, $event, $this);
}else{ }else{
$this->responses[$evid] = call_user_func($ev[0], $data, $event, $this); $this->responses[$evn["ID"]] = call_user_func($ev[0], $data, $event, $this);
} }
} }
} }
public function response($eid){
if(isset($this->responses[$eid])){
$res = $this->responses[$eid];
unset($this->responses[$eid]);
return $res;
} }
return false;
}
public function action($microseconds, $code, $repeat = true){
$this->query("INSERT INTO actions (interval, last, code, repeat) VALUES(".($microseconds / 1000000).", ".microtime(true).", '".str_replace("'", "\\'", $code)."', ".($repeat === true ? 1:0).");");
console("[INTERNAL] Attached to action ".$microseconds, true, true, 3);
}
public function tickerFunction(){
//actions that repeat every x time will go here
$time = microtime(true);
$actions = $this->query("SELECT ID,code,repeat FROM actions WHERE last <= (".$time." - interval);");
if($actions === false or $actions === true){
return;
}
while($action = $actions->fetchArray(SQLITE3_ASSOC)){
eval($action["code"]);
if($action["repeat"] === 0){
$this->query("DELETE FROM actions WHERE ID = ".$action["ID"].";");
}
}
$this->query("UPDATE actions SET last = ".$time." WHERE last <= (".$time." - interval);");
}
public function toggleEvent($event){ public function toggleEvent($event){
if(isset($this->events["disabled"][$event])){ if(isset($this->events["disabled"][$event])){
unset($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); console("[INTERNAL] Enabled event ".$event, true, true, 3);
}else{ }else{
$this->events["disabled"][$event] = false; $this->events["disabled"][$event] = false;
$this->query("UPDATE events SET disabled = 1 WHERE eventName = '".$event."';");
console("[INTERNAL] Disabled event ".$event, true, true, 3); console("[INTERNAL] Disabled event ".$event, true, true, 3);
} }
} }
@ -255,18 +326,23 @@ class PocketMinecraftServer{
if(!isset($this->events[$event])){ if(!isset($this->events[$event])){
$this->events[$event] = array(); $this->events[$event] = array();
} }
$this->query("INSERT INTO events (ID, eventName, disabled) VALUES (".$this->cnt.", '".str_replace("'", "\\'", $event)."', 0);");
$this->events[$event][$this->cnt] = array($func, $in); $this->events[$event][$this->cnt] = array($func, $in);
console("[INTERNAL] Attached to event ".$event, true, true, 3); console("[INTERNAL] Attached to event ".$event, true, true, 3);
return $this->cnt; return $this->cnt;
} }
public function deleteEvent($event, $id = -1){ public function deleteEvent($event, $id = -1){
$id = (int) $id;
if($id === -1){ if($id === -1){
unset($this->events[$event]); unset($this->events[$event]);
$this->query("DELETE FROM events WHERE eventName = '".str_replace("'", "\\'", $event)."';");
}else{ }else{
unset($this->events[$event][$id]); unset($this->events[$event][$id]);
$this->query("DELETE FROM events WHERE ID = ".$id.";");
if(isset($this->events[$event]) and count($this->events[$event]) === 0){ if(isset($this->events[$event]) and count($this->events[$event]) === 0){
unset($this->events[$event]); unset($this->events[$event]);
$this->query("DELETE FROM events WHERE eventName = '".str_replace("'", "\\'", $event)."';");
} }
} }
} }

View File

@ -28,13 +28,15 @@ the Free Software Foundation, either version 3 of the License, or
class Session{ class Session{
protected $server, $serverID, $timeout, $connected, $evid; protected $server, $serverID, $timeout, $connected, $evid;
var $clientID, $ip, $port, $counter, $username, $EID; var $clientID, $ip, $port, $counter, $username, $eid, $data;
function __construct($server, $clientID, $EID, $ip, $port){ function __construct($server, $clientID, $eid, $ip, $port){
$this->server = $server; $this->server = $server;
$this->clientID = $clientID; $this->clientID = $clientID;
$this->CID = $this->server->clientID($ip, $port); $this->CID = $this->server->clientID($ip, $port);
$this->EID = $EID; $this->eid = $eid;
$this->data = array();
$this->ip = $ip; $this->ip = $ip;
$this->entity = false;
$this->port = $port; $this->port = $port;
$this->serverID =& $this->server->serverID; $this->serverID =& $this->server->serverID;
$this->timeout = microtime(true) + 25; $this->timeout = microtime(true) + 25;
@ -52,6 +54,21 @@ class Session{
} }
} }
function __destruct(){
//$this->close("destruct");
}
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"],
);
}
file_put_contents(FILE_PATH."data/players/".str_replace("/", "", $this->username).".dat", serialize($this->data));
}
public function close($reason = "server stop", $msg = true){ public function close($reason = "server stop", $msg = true){
foreach($this->evid as $ev){ foreach($this->evid as $ev){
$this->server->deleteEvent($ev[0], $ev[1]); $this->server->deleteEvent($ev[0], $ev[1]);
@ -60,12 +77,69 @@ class Session{
if($msg === true){ if($msg === true){
$this->server->trigger("onChat", $this->username." left the game"); $this->server->trigger("onChat", $this->username." left the game");
} }
$this->save();
if(is_object($this->entity)){
$this->entity->__destruct();
}
console("[INFO] Session with ".$this->ip.":".$this->port." closed due to ".$reason); console("[INFO] Session with ".$this->ip.":".$this->port." closed due to ".$reason);
unset($this->server->entities[$this->eid]);
unset($this->server->clients[$this->CID]); unset($this->server->clients[$this->CID]);
} }
public function eventHandler($data, $event){ public function eventHandler($data, $event){
switch($event){ switch($event){
case "onHealthChange":
if($data["eid"] === $this->eid){
$this->send(0x84, array(
$this->counter[0],
0x00,
array(
"id" => MC_SET_HEALTH,
"health" => $data["health"],
),
));
$this->data["health"] = $data["health"];
if(is_object($this->entity)){
$this->entity->setHealth($data["health"]);
}
}
break;
case "onPlayerAdd":
if($data["eid"] === $this->eid){
break;
}
$this->send(0x84, array(
$this->counter[0],
0x00,
array(
"id" => MC_ADD_PLAYER,
"clientID" => $data["clientID"],
"username" => $data["username"],
"eid" => $data["eid"],
"x" => $data["x"],
"y" => $data["y"],
"z" => $data["z"],
"yaw" => $data["yaw"],
"pitch" => $data["pitch"],
"block" => $data["block"],
"meta" => $data["meta"],
),
));
break;
case "onEntityRemove":
if($data === $this->eid){
$this->close("despawn");
}else{
$this->send(0x84, array(
$this->counter[0],
0x00,
array(
"id" => MC_ENTITY_REMOVE,
"eid" => $data,
),
));
}
break;
case "onTimeChange": case "onTimeChange":
$this->send(0x84, array( $this->send(0x84, array(
$this->counter[0], $this->counter[0],
@ -133,14 +207,39 @@ class Session{
break; break;
case MC_LOGIN: case MC_LOGIN:
$this->username = $data["username"]; $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");
}
}
if($this->server->whitelist !== false and !in_array($this->username, $this->server->whitelist)){ if($this->server->whitelist !== false and !in_array($this->username, $this->server->whitelist)){
$this->close("\"".$this->username."\" not being on white-list", false); $this->close("\"".$this->username."\" not being on white-list", false);
break; break;
} }
console("[INFO] Player \"".$this->username."\" connected from ".$this->ip.":".$this->port); console("[INFO] Player \"".$this->username."\" connected from ".$this->ip.":".$this->port);
if(!file_exists(FILE_PATH."data/players/".$this->username.".dat")){
console("[NOTICE] Player data not found for \"".$this->username."\", creating new");
$this->data = array(
"spawn" => array(
"x" => $this->server->spawn["x"],
"y" => $this->server->spawn["y"],
"z" => $this->server->spawn["z"],
),
"health" => 20,
"lastIP" => $this->ip,
"lastID" => $this->clientID,
);
}else{
$this->data = unserialize(file_get_contents(FILE_PATH."data/players/".str_replace("/", "", $this->username).".dat"));
$this->data["lastIP"] = $this->ip;
$this->data["lastID"] = $this->clientID;
}
$this->evid[] = array("onTimeChange", $this->server->event("onTimeChange", array($this, "eventHandler"))); $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("onChat", $this->server->event("onChat", array($this, "eventHandler")));
$this->evid[] = array("onPlayerAdd", $this->server->event("onPlayerAdd", array($this, "eventHandler")));
$this->evid[] = array("onEntityDespawn", $this->server->event("onEntityDespawn", array($this, "eventHandler")));
$this->evid[] = array("onHealthChange", $this->server->event("onHealthChange", array($this, "eventHandler")));
$this->send(0x84, array( $this->send(0x84, array(
$this->counter[0], $this->counter[0],
0x00, 0x00,
@ -156,44 +255,47 @@ class Session{
array( array(
"id" => MC_START_GAME, "id" => MC_START_GAME,
"seed" => $this->server->seed, "seed" => $this->server->seed,
"x" => 128.5, "x" => $this->data["spawn"]["x"],
"y" => 100, "y" => $this->data["spawn"]["y"],
"z" => 128.5, "z" => $this->data["spawn"]["z"],
"unknown1" => 0, "unknown1" => 0,
"gamemode" => $this->server->gamemode, "gamemode" => $this->server->gamemode,
"eid" => $this->EID, "eid" => $this->eid,
), ),
)); ));
++$this->counter[0]; ++$this->counter[0];
break; break;
case MC_READY: case MC_READY:
$this->send(0x84, array( $this->server->trigger("onHealthChange", array("eid" => $this->eid, "health" => $this->data["health"]));
$this->counter[0], console("[DEBUG] Player with EID ".$this->eid." \"".$this->username."\" spawned!", true, true, 2);
0x00, $this->entity = new Entity($this->eid, ENTITY_PLAYER, 0, $this->server);
array( $this->entity->setName($this->username);
"id" => MC_SET_TIME, $this->server->entities[$this->eid] = &$this->entity;
"time" => $this->server->time, /*$this->server->trigger("onPlayerAdd", array(
), "clientID" => $this->clientID,
)); "username" => $this->username,
console("[DEBUG] Player with EID ".$this->EID." \"".$this->username."\" spawned!", true, true, 2); "eid" => $this->eid,
$this->server->trigger("onChat", $this->username." joined the game"); "x" => $this->data["spawn"]["x"],
"y" => $this->data["spawn"]["y"],
"z" => $this->data["spawn"]["z"],
"yaw" => $this->data["spawn"]["yaw"],
"pitch" => $this->data["spawn"]["pitch"],
"block" => 0,
"meta" => 0,
));*/
$this->eventHandler($this->server->motd, "onChat"); $this->eventHandler($this->server->motd, "onChat");
$this->server->trigger("onChat", $this->username." joined the game");
break; break;
case MC_MOVE_PLAYER: case MC_MOVE_PLAYER:
console("[DEBUG] EID ".$this->EID." moved: X ".$data["x"].", Y ".$data["y"].", Z ".$data["z"].", Pitch ".$data["pitch"].", Yaw ".$data["yaw"], true, true, 2); $this->entity->setPosition($data["x"], $data["y"], $data["z"], $data["x"], $data["yaw"], $data["pitch"]);
$this->server->trigger("onPlayerMove", $this->eid);
break; break;
case MC_PLAYER_EQUIPMENT: case MC_PLAYER_EQUIPMENT:
$this->send(0x84, array( console("[DEBUG] EID ".$this->eid." has now ".$data["block"]." with metadata ".$data["meta"]." in their hands!", true, true, 2);
$this->counter[0], break;
0x00, case MC_RESPAWN:
array( $this->server->trigger("onHealthChange", array("eid" => $this->eid, "health" => 20));
"id" => MC_PLAYER_EQUIPMENT, $this->entity->setPosition($data["x"], $data["y"], $data["z"], $data["x"], 0, 0);
"eid" => 0,
"block" => 323,
"meta" => 0,
),
));
console("[DEBUG] EID ".$this->EID." has now ".$data["block"]." with metadata ".$data["meta"]." in their hands!", true, true, 2);
break; break;
} }

View File

@ -38,6 +38,15 @@ console("[DEBUG] Endianness: ".(ENDIANNESS === LITTLE_ENDIAN ? "Little Endian":"
class Utils{ class Utils{
/*public static function hashCode($str){
$h = 0;
$len = strlen($str);
for($i = 0; $i < $len; ++$i) {
$h = 31 * $h + $str[$i];
}
return $h;
}*/
public static function getOS(){ public static function getOS(){
$uname = strtoupper(php_uname("s")); $uname = strtoupper(php_uname("s"));
if(strpos($uname, "WIN") !== false){ if(strpos($uname, "WIN") !== false){

View File

@ -31,7 +31,7 @@ ini_set("display_errors", 1);
ini_set('default_charset', 'utf-8'); ini_set('default_charset', 'utf-8');
define("FILE_PATH", dirname(__FILE__)."/../"); define("FILE_PATH", dirname(__FILE__)."/../");
set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATOR . FILE_PATH . "/classes/"); set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATOR . FILE_PATH . "/classes/");
ini_set("memory_limit", "512M"); ini_set("memory_limit", "1024M");
define("CURRENT_PROTOCOL", 5); define("CURRENT_PROTOCOL", 5);
define("CURRENT_VERSION", 1); define("CURRENT_VERSION", 1);
define("LOG", true); define("LOG", true);

View File

@ -9,6 +9,7 @@ seed=false
server-id=false server-id=false
server-type=normal server-type=normal
max-players=20 max-players=20
time-per-second=10
white-list=false white-list=false
debug=1 debug=1
spawn=128.5;100;128.5 spawn=128.5;100;128.5

View File

@ -70,6 +70,7 @@ if($errors > 0){
require_once("classes/Utils.class.php"); require_once("classes/Utils.class.php");
require_once("classes/UDPSocket.class.php"); require_once("classes/UDPSocket.class.php");
require_once("classes/Packet.class.php"); require_once("classes/Packet.class.php");
require_once("classes/Entity.class.php");
require_once("classes/SerializedPacketHandler.class.php"); require_once("classes/SerializedPacketHandler.class.php");
require_once("classes/CustomPacketHandler.class.php"); require_once("classes/CustomPacketHandler.class.php");
require_once("classes/MinecraftInterface.class.php"); require_once("classes/MinecraftInterface.class.php");

View File

@ -44,6 +44,8 @@ define("MC_CHAT", 0x85);
define("MC_SET_TIME", 0x86); define("MC_SET_TIME", 0x86);
define("MC_START_GAME", 0x87); define("MC_START_GAME", 0x87);
define("MC_ADD_PLAYER", 0x89);
define("MC_REMOVE_ENTITY", 0x8d); define("MC_REMOVE_ENTITY", 0x8d);
define("MC_MOVE_ENTITY_POSROT", 0x93); define("MC_MOVE_ENTITY_POSROT", 0x93);

View File

@ -44,6 +44,8 @@ $dataName = array(
MC_SET_TIME => "SetTime", MC_SET_TIME => "SetTime",
MC_START_GAME => "StartGame", MC_START_GAME => "StartGame",
MC_ADD_PLAYER => "AddPlayer",
MC_REMOVE_ENTITY => "RemoveEntity", MC_REMOVE_ENTITY => "RemoveEntity",
MC_MOVE_ENTITY_POSROT => "MoveEntity_PosRot", MC_MOVE_ENTITY_POSROT => "MoveEntity_PosRot",

View File

@ -28,7 +28,6 @@ the Free Software Foundation, either version 3 of the License, or
require_once("common/dependencies.php"); require_once("common/dependencies.php");
require_once("classes/PocketMinecraftServer.class.php"); require_once("classes/PocketMinecraftServer.class.php");
file_put_contents("packets.log", ""); file_put_contents("packets.log", "");
file_put_contents("console.log", "");
if(!file_exists(FILE_PATH."white-list.txt")){ if(!file_exists(FILE_PATH."white-list.txt")){
console("[WARNING] No white-list.txt found, creating blank file"); console("[WARNING] No white-list.txt found, creating blank file");
@ -43,8 +42,11 @@ if(!file_exists(FILE_PATH."banned-ips.txt")){
if(!file_exists(FILE_PATH."server.properties")){ if(!file_exists(FILE_PATH."server.properties")){
console("[WARNING] No server.properties found, using default settings"); console("[WARNING] No server.properties found, using default settings");
copy(FILE_PATH."common/default.properties", FILE_PATH."server.properties"); copy(FILE_PATH."common/default.properties", FILE_PATH."server.properties");
} }
@mkdir(FILE_PATH."data/entities/", 0777, true);
@mkdir(FILE_PATH."data/players/", 0777);
$prop = file_get_contents(FILE_PATH."server.properties"); $prop = file_get_contents(FILE_PATH."server.properties");
$prop = explode("\n", str_replace("\r", "", $prop)); $prop = explode("\n", str_replace("\r", "", $prop));
$config = array(); $config = array();
@ -65,11 +67,13 @@ foreach($prop as $line){
case "max-players": case "max-players":
case "port": case "port":
case "debug": case "debug":
case "time-per-second":
$v = (int) $v; $v = (int) $v;
break; break;
case "seed": case "seed":
case "server-id": case "server-id":
$v = trim($v) == "false" ? false:preg_replace("/^[0-9\-]/", "", $v); $v = trim($v);
$v = $v == "false" ? false:(preg_match("/[^0-9\-]/", $v) > 0 ? Utils::readInt(substr(md5($v, true), 0, 4)):$v);
break; break;
case "spawn": case "spawn":
$v = explode(";", $v); $v = explode(";", $v);
@ -86,9 +90,11 @@ define("DEBUG", $config["debug"]);
$server = new PocketMinecraftServer($config["server-name"], $config["gamemode"], $config["seed"], $config["protocol"], $config["port"], $config["server-id"]); $server = new PocketMinecraftServer($config["server-name"], $config["gamemode"], $config["seed"], $config["protocol"], $config["port"], $config["server-id"]);
$server->setType($config["type"]); $server->setType($config["type"]);
$server->timePerSecond = $config["time-per-second"];
$server->maxClients = $config["max-players"]; $server->maxClients = $config["max-players"];
$server->description = $config["description"]; $server->description = $config["description"];
$server->motd = $config["motd"]; $server->motd = $config["motd"];
$server->spawn = $config["spawn"];
$server->whitelist = $config["white-list"]; $server->whitelist = $config["white-list"];
$server->reloadConfig(); $server->reloadConfig();