PMF & Multiworld [part 1]

This commit is contained in:
Shoghi Cervantes Pueyo 2013-04-28 23:44:43 +02:00
parent bbbc54f606
commit db8f50f408
18 changed files with 626 additions and 548 deletions

View File

@ -180,7 +180,7 @@ class BlockAPI{
}else{
$b = new GenericBlock($id, $meta);
}
if($v instanceof Vector3){
if($v instanceof Position){
$b->position($v);
}
return $b;
@ -197,44 +197,12 @@ class BlockAPI{
return $i;
}
public function setBlock(Vector3 $block, $id, $meta, $update = true, $tiles = false){
if(($block instanceof Vector3) or (($block instanceof Block) and $block->inWorld === true)){
$this->server->api->level->setBlock($block->x, $block->y, $block->z, (int) $id, (int) $meta, $update, $tiles);
if($update === true){
$this->blockUpdate($block, BLOCK_UPDATE_NORMAL); //????? water?
$this->blockUpdateAround($block, BLOCK_UPDATE_NORMAL);
}
if($tiles === true){
if(($t = $this->server->api->tileentity->get($block->x, $block->y, $block->z)) !== false){
$t[0]->close();
}
}
return true;
}
return false;
}
public function getBlock($x, $y = 0, $z = 0){
if($x instanceof Vector3){
$y = $x->y;
$z = $x->z;
$x = $x->x;
}
$b = $this->server->api->level->getBlock($x, $y, $z);
return BlockAPI::get($b[0], $b[1], new Vector3($b[2][0], $b[2][1], $b[2][2]));
}
public function getBlockFace(Block $block, $face){
return $this->getBlock($block->getSide($face));
}
function __construct(){
$this->server = ServerAPI::request();
}
public function init(){
$this->server->event("server.tick", array($this, "blockUpdateTick"));
$this->server->addHandler("block.update", array($this, "blockUpdateRemote"), 1);
$this->server->api->console->register("give", "<player> <item[:damage]> [amount]", array($this, "commandHandler"));
}
@ -291,7 +259,7 @@ class BlockAPI{
public function playerBlockBreak(Player $player, Vector3 $vector){
$target = $this->getBlock($vector);
$target = $player->level->getBlock($vector);
$item = $player->equipment;
if($this->server->api->dhandle("player.block.touch", array("type" => "break", "player" => $player, "target" => $target, "item" => $item)) === false){
@ -306,7 +274,7 @@ class BlockAPI{
if($this->server->api->dhandle("player.block.break", array("player" => $player, "target" => $target, "item" => $item)) !== false){
$player->lastBreak = microtime(true);
$drops = $target->getDrops($item, $player);
$target->onBreak($this, $item, $player);
$target->onBreak($item, $player);
}else{
return $this->cancelAction($target, $player);
}
@ -320,7 +288,7 @@ class BlockAPI{
return false;
}
public function drop(Vector3 $pos, Item $item){
public function drop(Position $pos, Item $item){
if($item->getID() === AIR or $item->count <= 0){
return;
}
@ -335,7 +303,7 @@ class BlockAPI{
$item->count = min($item->getMaxStackSize(), $count);
$count -= $item->count;
$server = ServerAPI::request();
$e = $server->api->entity->add(ENTITY_ITEM, $item->getID(), $data);
$e = $server->api->entity->add($pos->level, ENTITY_ITEM, $item->getID(), $data);
//$e->speedX = mt_rand(-10, 10) / 100;
//$e->speedY = mt_rand(0, 5) / 100;
//$e->speedZ = mt_rand(-10, 10) / 100;
@ -349,8 +317,8 @@ class BlockAPI{
return false;
}
$target = $this->getBlock($vector);
$block = $this->getBlockFace($target, $face);
$target = $player->level->getBlock($vector);
$block = $target->getSide($face);
$item = $player->equipment;
if($target->getID() === AIR and $this->server->api->dhandle("player.block.place.invalid", array("player" => $player, "block" => $block, "target" => $target, "item" => $item)) !== true){ //If no block exists or not allowed in CREATIVE
@ -364,7 +332,7 @@ class BlockAPI{
$this->blockUpdate($target, BLOCK_UPDATE_TOUCH);
if($target->isActivable === true){
if($this->server->api->dhandle("player.block.activate", array("player" => $player, "block" => $block, "target" => $target, "item" => $item)) !== false and $target->onActivate($this, $item, $player) === true){
if($this->server->api->dhandle("player.block.activate", array("player" => $player, "block" => $block, "target" => $target, "item" => $item)) !== false and $target->onActivate($item, $player) === true){
return false;
}
}
@ -397,11 +365,11 @@ class BlockAPI{
if($this->server->api->dhandle("player.block.place", array("player" => $player, "block" => $block, "target" => $target, "item" => $item)) === false){
return $this->cancelAction($block, $player);
}elseif($hand->place($this, $item, $player, $block, $target, $face, $fx, $fy, $fz) === false){
}elseif($hand->place($item, $player, $block, $target, $face, $fx, $fy, $fz) === false){
return $this->cancelAction($block, $player);
}
if($hand->getID() === SIGN_POST or $hand->getID() === WALL_POST){
$t = $this->server->api->tileentity->addSign($block->x, $block->y, $block->z);
$t = $this->server->api->tileentity->addSign($player->level, $block->x, $block->y, $block->z);
$t->data["creator"] = $player->username;
}
@ -769,36 +737,42 @@ class BlockAPI{
}
}*/
public function blockUpdateAround(Vector3 $pos, $type = BLOCK_UPDATE_NORMAL){
$this->blockUpdate($pos->getSide(0), $type);
$this->blockUpdate($pos->getSide(1), $type);
$this->blockUpdate($pos->getSide(2), $type);
$this->blockUpdate($pos->getSide(3), $type);
$this->blockUpdate($pos->getSide(4), $type);
$this->blockUpdate($pos->getSide(5), $type);
}
public function blockUpdate(Vector3 $pos, $type = BLOCK_UPDATE_NORMAL){
public function blockUpdateAround(Position $pos, $type = BLOCK_UPDATE_NORMAL){
if(!($pos instanceof Block)){
$pos = $pos->floor();
$block = $this->getBlock($pos);
$block = $pos->level->getBlock($pos);
}else{
$block = $pos;
}
$level = $block->onUpdate($this, $type);
$this->blockUpdate($block->getSide(0), $type);
$this->blockUpdate($block->getSide(1), $type);
$this->blockUpdate($block->getSide(2), $type);
$this->blockUpdate($block->getSide(3), $type);
$this->blockUpdate($block->getSide(4), $type);
$this->blockUpdate($block->getSide(5), $type);
}
public function blockUpdate(Position $pos, $type = BLOCK_UPDATE_NORMAL){
if(!($pos instanceof Block)){
$pos = $pos->floor();
$block = $pos->level->getBlock($pos);
}else{
$block = $pos;
}
$level = $block->onUpdate($type);
if($level === BLOCK_UPDATE_NORMAL){
$this->blockUpdateAround($block, $level);
}
return $level;
}
public function scheduleBlockUpdate(Vector3 $pos, $delay, $type = BLOCK_UPDATE_SCHEDULED){
public function scheduleBlockUpdate(Position $pos, $delay, $type = BLOCK_UPDATE_SCHEDULED){
$type = (int) $type;
if($delay < 0){
return false;
}
$pos = $pos->floor();
$index = $pos->x.".".$pos->y.".".$pos->z;
$index = $pos->x.".".$pos->y.".".$pos->z.".".$pos->level->getName();
$delay = microtime(true) + $delay * 0.05;
if(!isset($this->scheduledUpdates[$index])){
$this->scheduledUpdates[$index] = array(
@ -806,29 +780,21 @@ class BlockAPI{
$type,
$delay,
);
$this->server->query("INSERT INTO blockUpdates (x, y, z, delay) VALUES (".$pos->x.", ".$pos->y.", ".$pos->z.", ".$delay.");");
$this->server->query("INSERT INTO blockUpdates (x, y, z, level, delay) VALUES (".$pos->x.", ".$pos->y.", ".$pos->z.", '".$pos->level->getName()."', ".$delay.");");
return true;
}
return false;
}
public function blockUpdateRemote($data, $event){
if($event !== "block.update"){
return;
}
$this->blockUpdate(new Vector3($data["x"], $data["y"], $data["z"]), isset($data["type"]) ? ((int) $data["type"]):BLOCK_UPDATE_RANDOM);
return true;
}
public function blockUpdateTick($time, $event){
if($event !== "server.tick"){ //WTF??
return;
}
if(count($this->scheduledUpdates) > 0){
$update = $this->server->query("SELECT x,y,z FROM blockUpdates WHERE delay <= ".$time.";");
$update = $this->server->query("SELECT x,y,z,level FROM blockUpdates WHERE delay <= ".$time.";");
if($update !== false and $update !== true){
while(($up = $update->fetchArray(SQLITE3_ASSOC)) !== false){
$index = $up["x"].".".$up["y"].".".$up["z"];
$index = $up["x"].".".$up["y"].".".$up["z"].".".$up["level"];
if(isset($this->scheduledUpdates[$index])){
$up = $this->scheduledUpdates[$index];
unset($this->scheduledUpdates[$index]);

View File

@ -27,13 +27,17 @@ the Free Software Foundation, either version 3 of the License, or
class EntityAPI{
private $server;
private $entities;
private $eCnt = 1;
function __construct(){
$this->entities = array();
$this->server = ServerAPI::request();
}
public function get($eid){
if(isset($this->server->entities[$eid])){
return $this->server->entities[$eid];
if(isset($this->entities[$eid])){
return $this->entities[$eid];
}
return false;
}
@ -42,8 +46,21 @@ class EntityAPI{
}
public function getAll(){
return $this->server->entities;
public function getAll($level = null){
if($level instanceof Level){
$entities = array();
$l = $this->server->query("SELECT EID FROM entities WHERE level = '".$this->level->getName()."';");
if($l !== false and $l !== true){
while(($e = $l->fetchArray(SQLITE3_ASSOC)) !== false){
$e = $this->get($e["EID"]);
if($e instanceof Entity){
$entities[$e->eid] = $e;
}
}
}
return $entities;
}
return $this->entities;
}
public function heal($eid, $heal = 1, $cause){
@ -58,11 +75,11 @@ class EntityAPI{
$e->setHealth($e->getHealth() - $attack, $cause, $force);
}
public function add($class, $type = 0, $data = array()){
$eid = $this->server->eidCnt++;
$this->server->entities[$eid] = new Entity($eid, $class, $type, $data);
$this->server->handle("entity.add", $this->server->entities[$eid]);
return $this->server->entities[$eid];
public function add(Level $level, $class, $type = 0, $data = array()){
$eid = $this->eCnt++;
$this->entities[$eid] = new Entity($level, $eid, $class, $type, $data);
$this->server->handle("entity.add", $this->entities[$eid]);
return $this->entities[$eid];
}
public function spawnTo($eid, $player){
@ -92,10 +109,10 @@ class EntityAPI{
}
public function remove($eid){
if(isset($this->server->entities[$eid])){
$entity = $this->server->entities[$eid];
$this->server->entities[$eid] = null;
unset($this->server->entities[$eid]);
if(isset($this->entities[$eid])){
$entity = $this->entities[$eid];
$this->entities[$eid] = null;
unset($this->entities[$eid]);
$entity->closed = true;
$this->server->query("DELETE FROM entities WHERE EID = ".$eid.";");
$this->server->api->dhandle("entity.remove", $entity);

View File

@ -26,15 +26,80 @@ the Free Software Foundation, either version 3 of the License, or
*/
class LevelAPI{
private $server, $map;
function __construct(){
private $server, $levels, $default;
public function __construct(){
$this->server = ServerAPI::request();
$this->map = $this->server->map;
$this->heightMap = array_fill(0, 256, array());
$this->levels = array();
$this->map = $this->server->level;
}
public function getDefault(){
return $this->levels[$this->default];
}
public function init(){
$this->default = $this->server->api->getProperty("level-name");
if($this->loadLevel($this->default) === false){
$path = DATA_PATH."worlds/".$this->default."/";
$generator = "SuperflatGenerator";
if($this->server->api->getProperty("generator") !== false and class_exists($this->server->api->getProperty("generator"))){
$generator = $this->server->api->getProperty("generator");
}
$gen = new WorldGenerator($generator, $this->server->seed);
if($this->server->api->getProperty("generator-settings") !== false and trim($this->server->api->getProperty("generator-settings")) != ""){
$gen->set("preset", $this->server->api->getProperty("generator-settings"));
}
$gen->init();
$gen->generate();
$gen->save($path, $this->default);
$this->loadLevel($this->default)
}
}
public function loadLevel($name){
$path = DATA_PATH."worlds/".$name."/";
if(!file_exists($path."level.pmf")){
$level = new LevelImport($path);
if($level->import() === false){
return false;
}
}
console("[INFO] Preparing level \"".$name."\"");
$level = new PMFLevel($path."level.pmf");
$entities = new Config($path."entities.yml", CONFIG_YAML);
$tileEntities = new Config($path."tileEntities.yml", CONFIG_YAML);
$this->levels[$name] = new Level($level, $entities, $tileEntities);
foreach($entities->getAll() as $entity){
if(!isset($entity["id"])){
break;
}
if($entity["id"] === 64){ //Item Drop
$e = $this->server->api->entity->add($this->levels[$name], ENTITY_ITEM, $entity["Item"]["id"], array(
"meta" => $entity["Item"]["Damage"],
"stack" => $entity["Item"]["Count"],
"x" => $entity["Pos"][0],
"y" => $entity["Pos"][1],
"z" => $entity["Pos"][2],
"yaw" => $entity["Rotation"][0],
"pitch" => $entity["Rotation"][1],
));
}elseif($entity["id"] === OBJECT_PAINTING){ //Painting
$e = $this->server->api->entity->add($this->levels[$name], ENTITY_OBJECT, $entity["id"], $entity);
$e->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]);
$e->setHealth($entity["Health"]);
}else{
$e = $this->server->api->entity->add($this->levels[$name], ENTITY_MOB, $entity["id"], $entity);
$e->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]);
$e->setHealth($entity["Health"]);
}
}
foreach($tileEntities->getAll() as $tile){
if(!isset($tile["id"])){
break;
}
$t = $this->server->api->tileentity->add($this->levels[$name], $tile["id"], $tile["x"], $tile["y"], $tile["z"], $tile);
}
}
public function handle($data, $event){
@ -45,92 +110,125 @@ class LevelAPI{
public function getSpawn(){
return $this->server->spawn;
}
public function getChunk($X, $Z){
return $this->map->map[$X][$Z];
}
public function getBlockFace($block, $face){
$data = array("x" => $block[2][0], "y" => $block[2][1], "z" => $block[2][2]);
BlockFace::setPosition($data, $face);
return $this->getBlock($data["x"], $data["y"], $data["z"]);
}
public function getBlock($x, $y, $z){
$b = $this->map->getBlock($x, $y, $z);
$b[2] = array($x, $y, $z);
return $b;
}
public function getFloor($x, $z){
if(!isset($this->heightMap[$z][$x])){
$this->heightMap[$z][$x] = $this->map->getFloor($x, $z);
/*
if(file_exists(DATA_PATH."worlds/level.dat")){
console("[NOTICE] Detected unimported map data. Importing...");
$this->importMap(DATA_PATH."worlds/", true);
}
$this->server->mapName = $this->getProperty("level-name");
$this->server->mapDir = DATA_PATH."worlds/".$this->server->mapName."/";
if($this->server->mapName === false or trim($this->server->mapName) === "" or (!file_exists($this->server->mapDir."chunks.dat") and !file_exists($this->server->mapDir."chunks.dat.gz") and !file_exists($this->server->mapDir."level.pmf"))){
if($this->server->mapName === false or trim($this->server->mapName) === ""){
$this->server->mapName = "world";
}
$this->server->mapDir = DATA_PATH."worlds/".$this->server->mapName."/";
$generator = "SuperflatGenerator";
if($this->getProperty("generator") !== false and class_exists($this->getProperty("generator"))){
$generator = $this->getProperty("generator");
}
$this->gen = new WorldGenerator($generator, $this->server->seed);
if($this->getProperty("generator-settings") !== false and trim($this->getProperty("generator-settings")) != ""){
$this->gen->set("preset", $this->getProperty("generator-settings"));
}
$this->gen->init();
$this->gen->generate();
$this->gen->save($this->server->mapDir, $this->server->mapName);
$this->setProperty("level-name", $this->server->mapName);
$this->setProperty("gamemode", SURVIVAL);
}
$this->server->loadMap();
*/
public function loadMap(){
if($this->mapName !== false and trim($this->mapName) !== ""){
if(!file_exists($this->mapDir."level.pmf")){
$level = new LevelImport($this->mapDir);
$level->import();
}
$this->level = new PMFLevel($this->mapDir."level.pmf");
console("[INFO] Preparing level \"".$this->level->getData("name")."\"");
$this->time = (int) $this->level->getData("time");
$this->seed = (int) $this->level->getData("seed");
$this->spawn = $this->level->getSpawn();
}
return $this->heightMap[$z][$x];
}
public function setBlock($x, $y, $z, $block, $meta = 0){
if($x < 0 or $y < 0 or $z < 0){
return false;
}
if($this->server->api->dhandle("block.change", array(
"x" => $x,
"y" => $y,
"z" => $z,
"block" => $block,
"meta" => $meta,
)) !== false){
$this->map->setBlock($x, $y, $z, $block, $meta);
$this->heightMap[$z][$x] = $this->map->getFloor($x, $z);
}
return true;
}
public function getOrderedChunks($X, $Z, $columnsPerPacket = 2){
$columnsPerPacket = max(1, (int) $columnsPerPacket);
$ordered = array();
$i = 0;
$cnt = 0;
$ordered[$i] = "";
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < 16; ++$x){
if($cnt >= $columnsPerPacket){
++$i;
$ordered[$i] = str_repeat("\x00", $i * $columnsPerPacket);
$cnt = 0;
public function save($final = false){
if($this->mapName !== false){
$this->levelData["Time"] = $this->time;
file_put_contents($this->mapDir."level.dat", serialize($this->levelData));
$this->map->saveMap($final);
$this->trigger("server.save", $final);
if(count($this->entities) > 0){
$entities = array();
foreach($this->entities as $entity){
if($entity->class === ENTITY_MOB){
$entities[] = array(
"id" => $entity->type,
"Color" => @$entity->data["Color"],
"Sheared" => @$entity->data["Sheared"],
"Health" => $entity->health,
"Pos" => array(
0 => $entity->x,
1 => $entity->y,
2 => $entity->z,
),
"Rotation" => array(
0 => $entity->yaw,
1 => $entity->pitch,
),
);
}elseif($entity->class === ENTITY_OBJECT){
$entities[] = array(
"id" => $entity->type,
"TileX" => $entity->x,
"TileX" => $entity->y,
"TileX" => $entity->z,
"Health" => $entity->health,
"Motive" => $entity->data["Motive"],
"Pos" => array(
0 => $entity->x,
1 => $entity->y,
2 => $entity->z,
),
"Rotation" => array(
0 => $entity->yaw,
1 => $entity->pitch,
),
);
}elseif($entity->class === ENTITY_ITEM){
$entities[] = array(
"id" => 64,
"Item" => array(
"id" => $entity->type,
"Damage" => $entity->meta,
"Count" => $entity->stack,
),
"Health" => $entity->health,
"Pos" => array(
0 => $entity->x,
1 => $entity->y,
2 => $entity->z,
),
"Rotation" => array(
0 => 0,
1 => 0,
),
);
}
}
$ordered[$i] .= "\xff";
$block = $this->map->getChunkColumn($X, $Z, $x, $z, 0);
$meta = $this->map->getChunkColumn($X, $Z, $x, $z, 1);
for($k = 0; $k < 8; ++$k){
$ordered[$i] .= substr($block, $k << 4, 16);
$ordered[$i] .= substr($meta, $k << 3, 8);
file_put_contents($this->mapDir."entities.dat", serialize($entities));
}
if(count($this->tileEntities) > 0){
$tiles = array();
foreach($this->tileEntities as $tile){
$tiles[] = $tile->data;
}
++$cnt;
file_put_contents($this->mapDir."tileEntities.dat", serialize($tiles));
}
}
return $ordered;
}
public function getMiniChunk($X, $Z, $Y, $MTU){
$ordered = array();
$i = 0;
$ordered[$i] = "";
$cnt = 0;
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < 16; ++$x){
if((strlen($ordered[$i]) + 16 + 8 + 1) > $MTU){
++$i;
$ordered[$i] = str_repeat("\x00", $cnt);
}
$ordered[$i] .= chr(1 << $Y);
$block = $this->map->getChunkColumn($X, $Z, $x, $z, 0);
$meta = $this->map->getChunkColumn($X, $Z, $x, $z, 1);
$ordered[$i] .= substr($block, $Y << 4, 16);
$ordered[$i] .= substr($meta, $Y << 3, 8);
++$cnt;
}
}
return $ordered;
}
}

View File

@ -318,14 +318,16 @@ class PlayerAPI{
$default = array(
"caseusername" => $name,
"position" => array(
"x" => $this->server->spawn["x"],
"y" => $this->server->spawn["y"],
"z" => $this->server->spawn["z"],
"level" => $this->server->spawn->level->getName(),
"x" => $this->server->spawn->x,
"y" => $this->server->spawn->y,
"z" => $this->server->spawn->z,
),
"spawn" => array(
"x" => $this->server->spawn["x"],
"y" => $this->server->spawn["y"],
"z" => $this->server->spawn["z"],
"level" => $this->server->spawn->level->getName(),
"x" => $this->server->spawn->x,
"y" => $this->server->spawn->y,
"z" => $this->server->spawn->z,
),
"inventory" => array_fill(0, 36, array(AIR, 0, 0)),
"armor" => array_fill(0, 4, array(AIR, 0, 0)),

View File

@ -54,7 +54,7 @@ class ServerAPI{
@mkdir(DATA_PATH."players/", 0755);
@mkdir(DATA_PATH."worlds/", 0755);
@mkdir(DATA_PATH."plugins/", 0755);
console("[INFO] \x1b[33;1mPocketMine-MP ".MAJOR_VERSION.", LGPL License", true, true, 0);
console("[INFO] \x1b[33;1mPocketMine-MP ".MAJOR_VERSION." API #".CURRENT_API_VERSION.", LGPL License", true, true, 0);
console("[INFO] Loading properties...");
$this->config = new Config(DATA_PATH . "server.properties", CONFIG_PROPERTIES, array(
@ -149,33 +149,9 @@ class ServerAPI{
}
}
if(file_exists(DATA_PATH."worlds/level.dat")){
console("[NOTICE] Detected unimported map data. Importing...");
$this->importMap(DATA_PATH."worlds/", true);
}
$this->server->mapName = $this->getProperty("level-name");
$this->server->mapDir = DATA_PATH."worlds/".$this->server->mapName."/";
if($this->server->mapName === false or trim($this->server->mapName) === "" or (!file_exists($this->server->mapDir."chunks.dat") and !file_exists($this->server->mapDir."chunks.dat.gz"))){
if($this->server->mapName === false or trim($this->server->mapName) === ""){
$this->server->mapName = "world";
}
$this->server->mapDir = DATA_PATH."worlds/".$this->server->mapName."/";
$generator = "SuperflatGenerator";
if($this->getProperty("generator") !== false and class_exists($this->getProperty("generator"))){
$generator = $this->getProperty("generator");
}
$this->gen = new WorldGenerator($generator, $this->server->seed);
if($this->getProperty("generator-settings") !== false and trim($this->getProperty("generator-settings")) != ""){
$this->gen->set("preset", $this->getProperty("generator-settings"));
}
$this->gen->init();
$this->gen->generate();
$this->gen->save($this->server->mapDir, $this->server->mapName);
$this->setProperty("level-name", $this->server->mapName);
$this->setProperty("gamemode", SURVIVAL);
}
$this->loadProperties();
$this->server->loadMap();
$this->loadAPI("console", "ConsoleAPI");
$this->loadAPI("level", "LevelAPI");
@ -228,7 +204,7 @@ class ServerAPI{
"online" => count($this->server->clients),
"max" => $this->server->maxClients,
"plugins" => $plist,
), 2);
), 10);
}
public function __destruct(){
@ -480,7 +456,7 @@ class ServerAPI{
}
$this->$name = new $class();
$this->apiList[] = $this->$name;
console("[".($internal === true ? "INTERNAL":"DEBUG")."] API \x1b[36m".$name."\x1b[0m [\x1b[30;1m".$class."\x1b[0m] loaded", true, true, ($internal === true ? 2:1));
console("[".($internal === true ? "INTERNAL":"DEBUG")."] API \x1b[36m".$name."\x1b[0m [\x1b[30;1m".$class."\x1b[0m] loaded", true, true, ($internal === true ? 3:2));
}
}

View File

@ -27,21 +27,15 @@ the Free Software Foundation, either version 3 of the License, or
class TileEntityAPI{
private $server;
private $tileEntities;
private $tCnt = 1;
function __construct(){
$this->tileEntities = array();
$this->server = ServerAPI::request();
}
public function get($x, $y = false, $z = false){
if(($x instanceof Vector3) or ($x instanceof Block)){
$z = (int) $x->z;
$y = (int) $x->y;
$x = (int) $x->x;
}else{
$x = (int) $x;
$y = (int) $y;
$z = (int) $z;
}
$tiles = $this->server->query("SELECT * FROM tileentities WHERE x = $x AND y = $y AND z = $z;");
public function get(Position $pos){
$tiles = $this->server->query("SELECT * FROM tileentities WHERE level = '".$pos->level->getName()."' AND x = {$pos->x} AND y = {$pos->y} AND z = {$pos->z};");
$ret = array();
if($tiles !== false and $tiles !== true){
while(($t = $tiles->fetchArray(SQLITE3_ASSOC)) !== false){
@ -61,8 +55,8 @@ class TileEntityAPI{
public function getByID($id){
if($id instanceof TileEntity){
return $id;
}elseif(isset($this->server->tileEntities[$id])){
return $this->server->tileEntities[$id];
}elseif(isset($this->tileEntities[$id])){
return $this->tileEntities[$id];
}
return false;
}
@ -71,18 +65,31 @@ class TileEntityAPI{
}
public function getAll(){
return $this->server->tileEntities;
public function getAll($level = null){
if($level instanceof Level){
$tileEntities = array();
$l = $this->server->query("SELECT ID FROM tileentities WHERE level = '".$this->level->getName()."';");
if($l !== false and $l !== true){
while(($t = $l->fetchArray(SQLITE3_ASSOC)) !== false){
$t = $this->get($e["ID"]);
if($t instanceof TileEntity){
$tileEntities[$t->id] = $t;
}
}
}
return $tileEntities;
}
return $this->tileEntities;
}
public function add($class, $x, $y, $z, $data = array()){
public function add(Level $level, $class, $x, $y, $z, $data = array()){
$id = $this->tCnt++;
$this->server->tileEntities[$id] = new TileEntity($id, $class, $x, $y, $z, $data);
$this->tileEntities[$id] = new TileEntity($id, $class, $x, $y, $z, $data);
$this->spawnToAll($id);
return $this->server->tileEntities[$id];
return $this->tileEntities[$id];
}
public function addSign($x, $y, $z, $lines = array("", "", "", "")){
public function addSign(Level $level, $x, $y, $z, $lines = array("", "", "", "")){
return $this->add(TILE_SIGN, $x, $y, $z, $data = array(
"id" => "Sign",
"x" => $x,
@ -122,10 +129,10 @@ class TileEntityAPI{
}
public function remove($id){
if(isset($this->server->tileEntities[$id])){
$t = $this->server->tileEntities[$id];
$this->server->tileEntities[$id] = null;
unset($this->server->tileEntities[$id]);
if(isset($this->tileEntities[$id])){
$t = $this->tileEntities[$id];
$this->tileEntities[$id] = null;
unset($this->tileEntities[$id]);
$t->closed = true;
$t->close();
$this->server->query("DELETE FROM tileentities WHERE ID = ".$id.";");

View File

@ -81,19 +81,19 @@ class Player{
$this->ip = $ip;
$this->port = $port;
$this->itemEnforcement = $this->server->api->getProperty("item-enforcement");
$this->spawnPosition = new Vector3($this->server->spawn["x"], $this->server->spawn["y"], $this->server->spawn["z"]);
$this->spawnPosition = $this->server->spawn;
$this->timeout = microtime(true) + 20;
$this->inventory = array_fill(0, 36, array(AIR, 0, 0));
$this->armor = array_fill(0, 4, array(AIR, 0, 0));
$this->gamemode = $this->server->gamemode;
$this->level = $this->server->api->level;
$this->level = $this->server->api->level->getDefault();
$this->equipment = BlockAPI::getItem(AIR);
$this->evid[] = $this->server->event("server.tick", array($this, "onTick"));
$this->evid[] = $this->server->event("server.close", array($this, "close"));
console("[DEBUG] New Session started with ".$ip.":".$port.". MTU ".$this->MTU.", Client ID ".$this->clientID, true, true, 2);
}
public function setSpawn(Vector3 $pos){
public function setSpawn(Position $pos){
$this->spawnPosition = $pos;
$this->dataPacket(MC_SET_SPAWN_POSITION, array(
"x" => (int) $this->spawnPosition->x,
@ -141,7 +141,7 @@ class Player{
$z = $Z << 4;
$y = $Y << 4;
$MTU = $this->MTU - 16;
$chunk = $this->level->getMiniChunk($X, $Z, $Y, $MTU);
$chunk = $this->level->getOrderedMiniChunk($X, $Z, $Y, $MTU);
foreach($chunk as $d){
$this->dataPacket(MC_CHUNK_DATA, array(
"x" => $X,
@ -197,11 +197,13 @@ class Player{
public function save(){
if($this->entity instanceof Entity){
$this->data->set("position", array(
"level" => $this->entity->level->getName(),
"x" => $this->entity->x,
"y" => $this->entity->y,
"z" => $this->entity->z,
));
$this->data->set("spawn", array(
"level" => $this->entity->level->getName(),
"x" => $this->spawnPosition->x,
"y" => $this->spawnPosition->y,
"z" => $this->spawnPosition->z,

View File

@ -27,7 +27,7 @@ the Free Software Foundation, either version 3 of the License, or
class PocketMinecraftServer{
public $tCnt;
public $version, $invisible, $api, $tickMeasure, $preparedSQL, $seed, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd, $timePerSecond, $spawn, $entities, $mapDir, $mapName, $map, $levelData, $tileEntities;
public $version, $invisible, $api, $tickMeasure, $preparedSQL, $seed, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd, $timePerSecond;
private $port, $serverip, $database, $interface, $evCnt, $handCnt, $events, $eventsID, $handlers, $serverType, $lastTick;
private function load(){
@ -45,12 +45,9 @@ class PocketMinecraftServer{
$this->startDatabase();
$this->api = false;
$this->tCnt = 1;
$this->mapDir = false;
$this->mapName = false;
$this->events = array();
$this->eventsID = array();
$this->handlers = array();
$this->map = false;
$this->invisible = false;
$this->levelData = false;
$this->difficulty = 1;
@ -113,15 +110,15 @@ class PocketMinecraftServer{
public function startDatabase(){
$this->preparedSQL = new stdClass();
$this->database = new SQLite3(":memory:");
//$this->query("PRAGMA journal_mode = OFF;");
//$this->query("PRAGMA encoding = \"UTF-8\";");
//$this->query("PRAGMA secure_delete = OFF;");
$this->query("PRAGMA journal_mode = OFF;");
$this->query("PRAGMA encoding = \"UTF-8\";");
$this->query("PRAGMA secure_delete = OFF;");
$this->query("CREATE TABLE players (clientID INTEGER PRIMARY KEY, EID NUMERIC, ip TEXT, port NUMERIC, name TEXT UNIQUE COLLATE NOCASE);");
$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 tileentities (ID INTEGER PRIMARY KEY, class TEXT, x NUMERIC, y NUMERIC, z NUMERIC, spawnable NUMERIC);");
$this->query("CREATE TABLE entities (EID INTEGER PRIMARY KEY, level TEXT, type NUMERIC, class NUMERIC, name TEXT, x NUMERIC, y NUMERIC, z NUMERIC, yaw NUMERIC, pitch NUMERIC, health NUMERIC);");
$this->query("CREATE TABLE tileentities (ID INTEGER PRIMARY KEY, level TEXT, class TEXT, x NUMERIC, y NUMERIC, z NUMERIC, spawnable NUMERIC);");
$this->query("CREATE TABLE actions (ID INTEGER PRIMARY KEY, interval NUMERIC, last NUMERIC, code TEXT, repeat NUMERIC);");
$this->query("CREATE TABLE handlers (ID INTEGER PRIMARY KEY, name TEXT, priority NUMERIC);");
$this->query("CREATE TABLE blockUpdates (x INTEGER, y INTEGER, z INTEGER, delay NUMERIC);");
$this->query("CREATE TABLE blockUpdates (level TEXT, x INTEGER, y INTEGER, z INTEGER, delay NUMERIC);");
//$this->query("PRAGMA synchronous = OFF;");
$this->preparedSQL->selectHandlers = $this->database->prepare("SELECT DISTINCT ID FROM handlers WHERE name = :name ORDER BY priority DESC;");
$this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);");
@ -255,34 +252,6 @@ class PocketMinecraftServer{
}
}
public function loadMap(){
if($this->mapName !== false and trim($this->mapName) !== ""){
$this->levelData = unserialize(file_get_contents($this->mapDir."level.dat"));
if($this->levelData === false){
console("[ERROR] Invalid world data for \"".$this->mapDir."\. Please import the world correctly");
$this->close("invalid world data");
}
$this->time = (int) $this->levelData["Time"];
$this->seed = (int) $this->levelData["RandomSeed"];
if(isset($this->levelData["SpawnX"])){
$this->spawn = array("x" => $this->levelData["SpawnX"], "y" => $this->levelData["SpawnY"], "z" => $this->levelData["SpawnZ"]);
}else{
$this->levelData["SpawnX"] = $this->spawn["x"];
$this->levelData["SpawnY"] = $this->spawn["y"];
$this->levelData["SpawnZ"] = $this->spawn["z"];
}
$this->levelData["Time"] = $this->time;
console("[INFO] Preparing level \"".$this->levelData["LevelName"]."\"");
$this->map = new ChunkParser();
if(!$this->map->loadFile($this->mapDir."chunks.dat")){
console("[ERROR] Couldn't load the map \"\x1b[32m".$this->levelData["LevelName"]."\x1b[0m\"!", true, true, 0);
$this->map = false;
}else{
$this->map->loadMap();
}
}
}
public function getGamemode(){
switch($this->gamemode){
case SURVIVAL:
@ -296,131 +265,9 @@ class PocketMinecraftServer{
}
}
public function loadEntities(){
if($this->map !== false){
$entities = unserialize(file_get_contents($this->mapDir."entities.dat"));
if($entities === false or !is_array($entities)){
console("[ERROR] Invalid world data for \"".$this->mapDir."\. Please import the world correctly");
$this->close("invalid world data");
}
foreach($entities as $entity){
if(!isset($entity["id"])){
break;
}
if(isset($this->api) and $this->api !== false){
if($entity["id"] === 64){ //Item Drop
$e = $this->api->entity->add(ENTITY_ITEM, $entity["Item"]["id"], array(
"meta" => $entity["Item"]["Damage"],
"stack" => $entity["Item"]["Count"],
"x" => $entity["Pos"][0],
"y" => $entity["Pos"][1],
"z" => $entity["Pos"][2],
"yaw" => $entity["Rotation"][0],
"pitch" => $entity["Rotation"][1],
));
}elseif($entity["id"] === OBJECT_PAINTING){ //Painting
$e = $this->api->entity->add(ENTITY_OBJECT, $entity["id"], $entity);
$e->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]);
$e->setHealth($entity["Health"]);
}else{
$e = $this->api->entity->add(ENTITY_MOB, $entity["id"], $entity);
$e->setPosition($entity["Pos"][0], $entity["Pos"][1], $entity["Pos"][2], $entity["Rotation"][0], $entity["Rotation"][1]);
$e->setHealth($entity["Health"]);
}
}
}
$tiles = unserialize(file_get_contents($this->mapDir."tileEntities.dat"));
foreach($tiles as $tile){
if(!isset($tile["id"])){
break;
}
$t = $this->api->tileentity->add($tile["id"], $tile["x"], $tile["y"], $tile["z"], $tile);
}
$this->action(1000000 * 60 * 25, '$this->api->chat->broadcast("Forcing save...");$this->save();');
}
}
public function save($final = false){
if($this->mapName !== false){
$this->levelData["Time"] = $this->time;
file_put_contents($this->mapDir."level.dat", serialize($this->levelData));
$this->map->saveMap($final);
$this->trigger("server.save", $final);
if(count($this->entities) > 0){
$entities = array();
foreach($this->entities as $entity){
if($entity->class === ENTITY_MOB){
$entities[] = array(
"id" => $entity->type,
"Color" => @$entity->data["Color"],
"Sheared" => @$entity->data["Sheared"],
"Health" => $entity->health,
"Pos" => array(
0 => $entity->x,
1 => $entity->y,
2 => $entity->z,
),
"Rotation" => array(
0 => $entity->yaw,
1 => $entity->pitch,
),
);
}elseif($entity->class === ENTITY_OBJECT){
$entities[] = array(
"id" => $entity->type,
"TileX" => $entity->x,
"TileX" => $entity->y,
"TileX" => $entity->z,
"Health" => $entity->health,
"Motive" => $entity->data["Motive"],
"Pos" => array(
0 => $entity->x,
1 => $entity->y,
2 => $entity->z,
),
"Rotation" => array(
0 => $entity->yaw,
1 => $entity->pitch,
),
);
}elseif($entity->class === ENTITY_ITEM){
$entities[] = array(
"id" => 64,
"Item" => array(
"id" => $entity->type,
"Damage" => $entity->meta,
"Count" => $entity->stack,
),
"Health" => $entity->health,
"Pos" => array(
0 => $entity->x,
1 => $entity->y,
2 => $entity->z,
),
"Rotation" => array(
0 => 0,
1 => 0,
),
);
}
}
file_put_contents($this->mapDir."entities.dat", serialize($entities));
}
if(count($this->tileEntities) > 0){
$tiles = array();
foreach($this->tileEntities as $tile){
$tiles[] = $tile->data;
}
file_put_contents($this->mapDir."tileEntities.dat", serialize($tiles));
}
}
}
public function init(){
if($this->mapName !== false and $this->map === false){
$this->loadMap();
$this->loadEntities();
}
$this->loadEvents();
declare(ticks=40);
register_tick_function(array($this, "tick"));

View File

@ -92,5 +92,9 @@ if($errors > 0){
}
/***REM_START***/
require_once(FILE_PATH."/src/math/Vector3.php");
require_once(FILE_PATH."/src/world/Position.php");
require_once(FILE_PATH."/src/pmf/PMF.php");
require_all(FILE_PATH . "src/");
/***REM_END***/

View File

@ -25,11 +25,7 @@ the Free Software Foundation, either version 3 of the License, or
*/
/***REM_START***/
require_once(FILE_PATH."/src/math/Vector3.php");
/***REM_END***/
abstract class Block extends Vector3{
abstract class Block extends Position{
public static $class = array(
AIR => "AirBlock",
STONE => "StoneBlock",
@ -147,12 +143,12 @@ abstract class Block extends Vector3{
public $isTransparent = false;
public $isReplaceable = false;
public $isPlaceable = true;
public $inWorld = false;
public $level = false;
public $hasPhysics = false;
public $isLiquid = false;
public $x;
public $y;
public $z;
public $x = 0;
public $y = 0;
public $z = 0;
public function __construct($id, $meta = 0, $name = "Unknown"){
$this->id = (int) $id;
@ -173,8 +169,8 @@ abstract class Block extends Vector3{
return $this->meta & 0x0F;
}
final public function position(Vector3 $v){
$this->inWorld = true;
final public function position(Position $v){
$this->level = $v->level;
$this->x = (int) $v->x;
$this->y = (int) $v->y;
$this->z = (int) $v->z;
@ -197,19 +193,27 @@ abstract class Block extends Vector3{
return $this->breakTime;
}
public function getSide($side){
$v = parent::getSide($side);
if($this->level instanceof Level){
return $this->level->getBlock($v);
}
return $v;
}
final public function __toString(){
return "Block ". $this->name ." (".$this->id.":".$this->meta.")";
}
abstract function isBreakable(Item $item, Player $player);
abstract function onBreak(BlockAPI $level, Item $item, Player $player);
abstract function onBreak(Item $item, Player $player);
abstract function place(BlockAPI $level, Item $item, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz);
abstract function place(Item $item, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz);
abstract function onActivate(BlockAPI $level, Item $item, Player $player);
abstract function onActivate(Item $item, Player $player);
abstract function onUpdate(BlockAPI $level, $type);
abstract function onUpdate($type);
}
/***REM_START***/

View File

@ -30,31 +30,23 @@ class GenericBlock extends Block{
public function __construct($id, $meta = 0, $name = "Unknown"){
parent::__construct($id, $meta, $name);
}
public function place(BlockAPI $level, Item $item, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){
if($block->inWorld === true){
$level->setBlock($block, $this->id, $this->getMetadata());
return true;
}
return false;
public function place(Item $item, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){
return $this->level->setBlock($block, $this);
}
public function isBreakable(Item $item, Player $player){
return ($this->breakable);
}
public function onBreak(BlockAPI $level, Item $item, Player $player){
if($this->inWorld === true){
$level->setBlock($this, AIR, 0);
return true;
}
return false;
public function onBreak(Item $item, Player $player){
return $this->level->setBlock($this, BlockAPI::getBlock(AIR, 0));
}
public function onUpdate(BlockAPI $level, $type){
public function onUpdate($type){
return false;
}
public function onActivate(BlockAPI $level, Item $item, Player $player){
public function onActivate(Item $item, Player $player){
return ($this->isActivable);
}
}

View File

@ -25,10 +25,6 @@ the Free Software Foundation, either version 3 of the License, or
*/
/***REM_START***/
require_once(FILE_PATH."/src/pmf/PMF.php");
/***REM_END***/
define("PMF_CURRENT_LEVEL_VERSION", 0x00);
class PMFLevel extends PMF{
@ -40,6 +36,21 @@ class PMFLevel extends PMF{
private $chunks = array();
private $chunkChange = array();
public function getData($index){
if(!isset($this->levelData[$index])){
return false;
}
return ($this->levelData[$index]);
}
public function setData($index, $data){
if(!isset($this->levelData[$index])){
return false;
}
$this->levelData[$index] = $data;
return true;
}
public function __construct($file, $blank = false){
if(is_array($blank)){
$this->create($file, 0);
@ -62,8 +73,9 @@ class PMFLevel extends PMF{
}
}
private function createBlank(){
public function saveData($locationTable = true){
$this->levelData["version"] = PMF_CURRENT_LEVEL_VERSION;
@ftruncate($this->fp, 5);
$this->seek(5);
$this->write(chr($this->levelData["version"]));
$this->write(Utils::writeShort(strlen($this->levelData["name"])).$this->levelData["name"]);
@ -74,11 +86,20 @@ class PMFLevel extends PMF{
$this->write(Utils::writeFloat($this->levelData["spawnZ"]));
$this->write(chr($this->levelData["width"]));
$this->write(chr($this->levelData["height"]));
$this->write(gzdeflate("", 9));
$extra = gzdeflate($this->levelData["extra"], 9);
$this->write(Utils::writeShort(strlen($extra)).$extra);
$this->payloadOffset = ftell($this->fp);
if($locationTable !== false){
$this->writeLocationTable();
}
}
private function createBlank(){
$this->saveData(false);
$this->locationTable = array();
$cnt = pow($this->levelData["width"], 2);
@mkdir(dirname($this->file)."/chunks/", 0755);
$this->payloadOffset = ftell($this->fp);
for($index = 0; $index < $cnt; ++$index){
$this->chunks[$index] = false;
$this->chunkChange[$index] = false;
@ -154,6 +175,15 @@ class PMFLevel extends PMF{
return true;
}
private function writeLocationTable(){
$cnt = pow($this->levelData["width"], 2);
@ftruncate($this->fp, $this->payloadOffset);
$this->seek($this->payloadOffset);
for($index = 0; $index < $cnt; ++$index){
$this->write(Utils::writeShort($this->locationTable[$index]));
}
}
private function getChunkPath($X, $Z){
return dirname($this->file)."/chunks/".$Z.".".$X.".pmc";
}
@ -181,11 +211,23 @@ class PMFLevel extends PMF{
}else{
$this->chunks[$index][$Y] = false;
}
}
}
@gzclose($chunk);
return true;
}
public function unloadChunk($X, $Z, $save = true){
$X = (int) $X;
$Z = (int) $Z;
if($this->isChunkLoaded($X, $Z)){
return false;
}elseif($save !== false){
$this->saveChunk($X, $Z);
}
unset($this->chunks[$index], $this->chunkChange[$index]);
return true;
}
public function isChunkLoaded($X, $Z){
$index = $this->getIndex($X, $Z);
if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){
@ -205,9 +247,9 @@ class PMFLevel extends PMF{
}
public function getBlock($x, $y, $z){
$X = $x << 4;
$Z = $z << 4;
$Y = $y << 4;
$X = $x >> 4;
$Z = $z >> 4;
$Y = $y >> 4;
if($X >= 32 or $Z >= 32){
return array(AIR, 0);
}
@ -222,8 +264,8 @@ class PMFLevel extends PMF{
$aX = $x - ($X << 4);
$aZ = $z - ($Z << 4);
$aY = $y - ($Y << 4);
$bindex = $aY + $aZ << 5 + $aX << 9;
$mindex = $aY >> 1 + 16 + $aZ << 5 + $aX << 9;
$bindex = $aY + ($aX << 5) + ($aZ << 9);
$mindex = ($aY >> 1) + 16 + ($aX << 5) + ($aZ << 9);
$b = ord($this->chunks[$index][$Y]{$bindex});
$m = ord($this->chunks[$index][$Y]{$mindex});
if(($y & 1) === 0){
@ -245,11 +287,11 @@ class PMFLevel extends PMF{
}
public function getMiniChunk($X, $Z, $Y){
if($this->isChunkLoaded($X, $Z) === false){
if($this->loadChunk($X, $Z) === false){
return str_repeat("\x00", 8192);
}
$index = $this->getIndex($X, $Z);
if(!isset($this->chunks[$index][$Y])){
if($this->chunks[$index][$Y] === false){
return str_repeat("\x00", 8192);
}
return $this->chunks[$index][$Y];
@ -259,16 +301,20 @@ class PMFLevel extends PMF{
if($this->isChunkLoaded($X, $Z) === false){
$this->loadChunk($X, $Z);
}
if(strlen($data) !== 8192){
return false;
}
$index = $this->getIndex($X, $Z);
$this->chunks[$index][$Y] = substr($data, 0, 8192);
$this->chunks[$index][$Y] = $data;
$this->chunkChange[$index][$Y] = 8192;
$this->locationTable[$index][0] |= 1 << $Y;
return true;
}
public function setBlock($x, $y, $z, $block, $meta = 0){
$X = $x << 4;
$Z = $z << 4;
$Y = $y << 4;
$X = $x >> 4;
$Z = $z >> 4;
$Y = $y >> 4;
$block &= 0xFF;
$meta &= 0x0F;
if($X >= 32 or $Z >= 32){
@ -285,8 +331,8 @@ class PMFLevel extends PMF{
$aX = $x - ($X << 4);
$aZ = $z - ($Z << 4);
$aY = $y - ($Y << 4);
$bindex = $aY + $aZ << 5 + $aX << 9;
$mindex = $aY >> 1 + 16 + $aZ << 5 + $aX << 9;
$bindex = $aY + ($aX << 5) + ($aZ << 9);
$mindex = ($aY >> 1) + 16 + ($aX << 5) + ($aZ << 9);
$old_b = $this->chunks[$index][$Y]{$bindex};
$old_m = ord($this->map[$X][$Z][1][$index]{$y >> 1});
if(($y & 1) === 0){
@ -319,16 +365,24 @@ class PMFLevel extends PMF{
$chunk = @gzopen($this->getChunkPath($X, $Z), "wb9");
$bitmap = 0;
for($Y = 0; $Y < $this->levelData["height"]; ++$Y){
if($this->chunks[$index][$Y] !== false and !$this->isMiniChunkEmpty($X, $Z, $Y)){
if($this->chunks[$index][$Y] !== false and ((isset($this->chunkChange[$index][$Y]) and $this->chunkChange[$index][$Y] === 0) or !$this->isMiniChunkEmpty($X, $Z, $Y))){
gzwrite($chunk, $this->chunks[$index][$Y]);
$bitmap |= 1 << $Y;
}else{
$this->chunks[$index][$Y] = false;
}
$this->chunkChange[$index][$Y] = 0;
}
$this->locationTable[$index][0] = $bitmap;
$this->seek($this->payloadOffset + ($index << 1));
$this->write(Utils::writeShort($this->locationTable[$index][0]));
}
public function doSaveRound(){
foreach($this->chunks as $index => $chunk){
$this->getXZ($index, $X, $Z);
$this->saveChunk($X, $Z);
}
}
}

View File

@ -45,7 +45,7 @@ define("ENTITY_OBJECT", 2);
define("ENTITY_ITEM", 3);
class Entity extends stdClass{
class Entity extends Position{
public $age;
public $air;
public $spawntime;
@ -74,7 +74,9 @@ class Entity extends stdClass{
private $tickCounter;
private $speedMeasure = array(0, 0, 0, 0, 0);
private $server;
function __construct($eid, $class, $type = 0, $data = array()){
public $level;
function __construct(Level $level, $eid, $class, $type = 0, $data = array()){
$this->level = $level;
$this->fallY = false;
$this->fallStart = false;
$this->server = ServerAPI::request();
@ -97,7 +99,7 @@ class Entity extends stdClass{
$this->closed = false;
$this->name = "";
$this->tickCounter = 0;
$this->server->query("INSERT OR REPLACE INTO entities (EID, type, class, health) VALUES (".$this->eid.", ".$this->type.", ".$this->class.", ".$this->health.");");
$this->server->query("INSERT OR REPLACE INTO entities (EID, level, type, class, health) VALUES (".$this->eid.", '".$this->level->getName()."', ".$this->type.", ".$this->class.", ".$this->health.");");
$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;
@ -107,7 +109,7 @@ class Entity extends stdClass{
$this->speed = 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);
$this->position = array("level" => $this->level, "x" => &$this->x, "y" => &$this->y, "z" => &$this->z, "yaw" => &$this->yaw, "pitch" => &$this->pitch);
switch($this->class){
case ENTITY_PLAYER:
$this->player = $this->data["player"];
@ -175,7 +177,7 @@ class Entity extends stdClass{
private function spawnDrops(){
foreach($this->getDrops() as $drop){
$this->server->api->block->drop(new Vector3($this->x, $this->y, $this->z), BlockAPI::getItem($drop[0] & 0xFFFF, $drop[1] & 0xFFFF, $drop[2] & 0xFF), true);
$this->server->api->block->drop($this, BlockAPI::getItem($drop[0] & 0xFFFF, $drop[1] & 0xFFFF, $drop[2] & 0xFF), true);
}
}
@ -186,8 +188,8 @@ class Entity extends stdClass{
$this->close(); //Despawn timer
return false;
}
if(($time - $this->spawntime) >= 2){
$player = $this->server->query("SELECT EID FROM entities WHERE class = ".ENTITY_PLAYER." AND abs(x - {$this->x}) <= 1.5 AND abs(y - {$this->y}) <= 1.5 AND abs(z - {$this->z}) <= 1.5 LIMIT 1;", true);
if(($time - $this->spawntime) >= 0.6){ //TODO: set this on Player class, updates things when it moves
$player = $this->server->query("SELECT EID FROM entities WHERE level = '".$this->level->getName()."' AND class = ".ENTITY_PLAYER." AND abs(x - {$this->x}) <= 1.5 AND abs(y - {$this->y}) <= 1.5 AND abs(z - {$this->z}) <= 1.5 LIMIT 1;", true);
$player = $this->server->api->entity->get($player["EID"]);
if($player instanceof Entity){
$player = $player->player;
@ -439,7 +441,7 @@ class Entity extends stdClass{
if(!($player instanceof Player)){
$player = $this->server->api->player->get($player);
}
if($player->eid === $this->eid or $this->closed !== false){
if($player->eid === $this->eid or $this->closed !== false or $player->level !== $this->level){
return false;
}
switch($this->class){
@ -530,17 +532,23 @@ class Entity extends stdClass{
$this->server->query("UPDATE entities SET pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";");
}
public function setCoords($x, $y, $z){
$this->x = $x;
$this->y = $y;
$this->z = $z;
public function setCoords(Vector3 $pos){
if($pos instanceof Position){
$this->level = $pos->level;
$this->server->query("UPDATE entities SET level = '".$this->level->getName()."' WHERE EID = ".$this->eid.";");
}
$this->x = $pos->x;
$this->y = $pos->y;
$this->z = $pos->z;
$this->yaw = $yaw;
$this->pitch = $pitch;
$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){
$this->x += $x;
$this->y += $y;
$this->z += $z;
public function move(Vector3 $pos, $yaw = 0, $pitch = 0){
$this->x += $pos->x;
$this->y += $pos->y;
$this->z += $pos->z;
$this->yaw += $yaw;
$this->yaw %= 360;
$this->pitch += $pitch;
@ -548,17 +556,20 @@ class Entity extends stdClass{
$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->x = $x;
$this->y = $y;
$this->z = $z;
public function setPosition(Vector3 $pos, $yaw, $pitch){
if($pos instanceof Position){
$this->level = $pos->level;
$this->server->query("UPDATE entities SET level = '".$this->level->getName()."' WHERE EID = ".$this->eid.";");
}
$this->x = $pos->x;
$this->y = $pos->y;
$this->z = $pos->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.";");
}
public function inBlock($x, $y, $z, $radius = 0.8){
$block = new Vector3($x, $y, $z);
public function inBlock(Vector3 $block, $radius = 0.8){
$me = new Vector3($this->x - 0.5, $this->y, $this->z - 0.5);
if(($y == ((int) $this->y) or $y == (((int) $this->y) + 1)) and $block->maxPlainDistance($me) < $radius){
return true;
@ -566,8 +577,7 @@ class Entity extends stdClass{
return false;
}
public function touchingBlock($x, $y, $z, $radius = 0.9){
$block = new Vector3($x, $y, $z);
public function touchingBlock(Vector3 $block, $radius = 0.9){
$me = new Vector3($this->x - 0.5, $this->y, $this->z - 0.5);
if(($y == (((int) $this->y) - 1) or $y == ((int) $this->y) or $y == (((int) $this->y) + 1)) and $block->maxPlainDistance($me) < $radius){
return true;

151
src/world/Level.php Normal file
View File

@ -0,0 +1,151 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @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.
*/
class Level{
public $entities, $tileEntities;
private $level, $time, $startCheck, $startTime, $server;
public function __construct(PMFLevel $level, Config $entities, Config $tileEntities){
$this->server = ServerAPI::request();
$this->level = $level;
$this->entities = $entities;
$this->tileEntities = $tileEntities;
$this->startTime = $this->time = (int) $this->level->getData("time");
$this->startCheck = microtime(true);
$this->server->schedule(15, array($this, "checkThings"));
}
public function __destruct(){
$this->save();
unset($this->level);
}
public function save(){
$this->level->setData("time", $this->time);
$this->level->doSaveRound();
$this->level->saveData();
}
public function getBlock(Vector3 $pos){
if(($pos instanceof Position) and $pos->level !== $this){
return false;
}
$b = $this->level->getBlock($pos->x, $pos->y, $pos->z);
return BlockAPI::get($b[0], $b[1], new Position($pos->x, $pos->y, $pos->z, $this));
}
public function setBlock(Position $pos, Block $block, $update = true, $tiles = false){
if((($pos instanceof Position) and $pos->level !== $this) or $pos->x < 0 or $pos->y < 0 or $pos->z < 0){
return false;
}elseif($this->server->api->dhandle("block.change", array(
"position" => $pos,
"block" => $block,
)) !== false){
$ret = $this->level->setBlock($pos->x, $pos->y, $pos->z, $block->getID(), $block->getMetadata());
if($update === true){
$this->server->api->block->blockUpdate($pos, BLOCK_UPDATE_NORMAL); //????? water?
$this->server->api->block->blockUpdateAround($pos, BLOCK_UPDATE_NORMAL);
}
if($tiles === true){
if(($t = $this->server->api->tileentity->get($pos)) !== false){
$t[0]->close();
}
}
return $ret;
}
return false;
}
public function getMiniChunk($X, $Z){
return $this->level->getMiniChunk($X, $Z);
}
public function setMiniChunk($X, $Z, $data){
return $this->level->setMiniChunk($X, $Z, $data);
}
public function loadChunk($X, $Z){
return $this->level->loadChunk($X, $Z);
}
public function unloadChunk($X, $Z){
return $this->level->unloadChunk($X, $Z);
}
public function getOrderedMiniChunk($X, $Z, $Y, $MTU){
$raw = $this->map->getMiniChunk($X, $Z, $Y);
$ordered = array();
$i = 0;
$ordered[$i] = "";
$cnt = 0;
$flag = chr(1 << $Y);
for($j = 0; $j < 256; ++$j){
if((strlen($ordered[$i]) + 16 + 8 + 1) > $MTU){
++$i;
$ordered[$i] = str_repeat("\x00", $cnt);
}
$index = $j << 5;
$ordered[$i] .= $flag;
$ordered[$i] .= substr($raw, $index, 16);
$ordered[$i] .= substr($raw, $index + 16, 8);
++$cnt;
}
return $ordered;
}
public function getSpawn(){
return new Position($this->level->getData("spawnX"), $this->level->getData("spawnY"), $this->level->getData("spawnZ"), $this);
}
public function setSpawn(Vector3 $pos){
$this->level->setData("spawnX", $pos->x);
$this->level->setData("spawnY", $pos->y);
$this->level->setData("spawnZ", $pos->z);
}
public function getTime(){
return ($this->time);
}
public function getName(){
return $this->level->getData("name");
}
public function setTime($time){
$this->startTime = $this->time = (int) $time;
$this->startCheck = microtime(true);
}
public function checkThings(){
$now = microtime(true);
$this->time = $this->startTime + ($now - $this->startCheck) * 20;
}
public function getSeed(){
return (int) $this->level->getData("seed");
}
}

View File

@ -33,6 +33,8 @@ class LevelImport{
public function import(){
if(file_exists($this->path."tileEntities.dat")){ //OldPM
$level = unserialize(file_get_contents($this->path."level.dat"));
console("[INFO] Importing OldPM level \"".$level["LevelName"]."\" to PMF format");
$entities = new Config($this->path."entities.yml", CONFIG_YAML, unserialize(file_get_contents($this->path."entities.dat")));
$entities->save();
$tileEntities = new Config($this->path."tileEntities.yml", CONFIG_YAML, unserialize(file_get_contents($this->path."tileEntities.dat")));
@ -43,9 +45,8 @@ class LevelImport{
if($level["LevelName"] == ""){
$level["LevelName"] = "world".time();
}
console("[DEBUG] Importing map \"".$level["LevelName"]."\" gamemode ".$level["GameType"]." with seed ".$level["RandomSeed"], true, true, 2);
console("[INFO] Importing Pocket level \"".$level["LevelName"]."\" to PMF format");
unset($level["Player"]);
$lvName = $level["LevelName"]."/";
$entities = parseNBTData($nbt->loadFile($this->path."entities.dat"));
if(!isset($entities["TileEntities"])){
$entities["TileEntities"] = array();
@ -56,6 +57,8 @@ class LevelImport{
$entities->save();
$tileEntities = new Config($this->path."tileEntities.yml", CONFIG_YAML, $tileEntities);
$tileEntities->save();
}else{
return false;
}
$pmf = new PMFLevel($this->path."level.pmf", array(
@ -65,6 +68,7 @@ class LevelImport{
"spawnX" => $level["SpawnX"],
"spawnY" => $level["SpawnY"],
"spawnZ" => $level["SpawnZ"],
"extra" => "",
"width" => 16,
"height" => 8
));
@ -99,16 +103,17 @@ class LevelImport{
}
$pmf->saveChunk($X, $Z);
}
console("[NOTICE] Importing level ".ceil(($Z + 1)/0.16)."%");
}
$chunks->map = null;
$chunks = null;
/*@unlink($this->path."level.dat");
@unlink($this->path."level.dat");
@unlink($this->path."level.dat_old");
@unlink($this->path."player.dat");
@unlink($this->path."entities.dat");
@unlink($this->path."chunks.dat");
@unlink($this->path."chunks.dat.gz");
@unlink($this->path."tileEntities.dat");*/
@unlink($this->path."tileEntities.dat");
unset($chunks, $level, $entities, $tileEntities, $nbt);
return true;
}

View File

@ -24,8 +24,17 @@ the Free Software Foundation, either version 3 of the License, or
*/
/*
class BlockIterator implements Iterator{
class Position{
public $level;
}*/
public function __construct($x = 0, $y = 0, $z = 0, Level $level){
parent::__construct($x, $y, $z);
$this->level = $level;
}
public function __toString(){
return "Position(level=".$this->level->getName().",x=".$this->x.",y=".$this->y.",z=".$this->z.")";
}
}

View File

@ -32,7 +32,7 @@ define("TILE_CHEST", "Chest");
define("TILE_FURNACE", "Furnace");
define("FURNACE_SLOTS", 3);
class TileEntity extends stdClass{
class TileEntity extends Position{
public $name;
public $normal;
public $id;
@ -45,8 +45,9 @@ class TileEntity extends stdClass{
public $metadata;
public $closed;
private $server;
function __construct($id, $class, $x, $y, $z, $data = array()){
function __construct(Level $level, $id, $class, $x, $y, $z, $data = array()){
$this->server = ServerAPI::request();
$this->level = $level;
$this->normal = true;
$this->class = $class;
$this->data = $data;
@ -59,7 +60,7 @@ class TileEntity extends stdClass{
$this->x = (int) $x;
$this->y = (int) $y;
$this->z = (int) $z;
$this->server->query("INSERT OR REPLACE INTO tileentities (ID, class, x, y, z) VALUES (".$this->id.", '".$this->class."', ".$this->x.", ".$this->y.", ".$this->z.");");
$this->server->query("INSERT OR REPLACE INTO tileentities (ID, level, class, x, y, z) VALUES (".$this->id.", '".$this->level->getName()."', '".$this->class."', ".$this->x.", ".$this->y.", ".$this->z.");");
switch($this->class){
case TILE_SIGN:
$this->server->query("UPDATE tileentities SET spawnable = 1 WHERE ID = ".$this->id.";");
@ -148,7 +149,7 @@ class TileEntity extends stdClass{
public function close(){
if($this->closed === false){
$this->closed = true;
$this->server->api->entity->remove($this->eid);
$this->server->api->tileentity->remove($this->id);
}
}
@ -160,17 +161,16 @@ class TileEntity extends stdClass{
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 setPosition($x, $y, $z){
$this->x = (int) $x;
$this->y = (int) $y;
$this->z = (int) $z;
$this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z." WHERE EID = ".$this->eid.";");
public function setPosition(Vector3 $pos){
if($pos instanceof Position){
$this->level = $pos->level;
$this->server->query("UPDATE tileentities SET level = '".$this->level->getName()."' WHERE ID = ".$this->id.";");
}
$this->x = (int) $pos->x;
$this->y = (int) $pos->y;
$this->z = (int) $pos->z;
$this->server->query("UPDATE tileentities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z." WHERE ID = ".$this->id.";");
}
}

View File

@ -1,66 +0,0 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @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.
*/
class NormalGenerator{
private $config, $spawn, $structure;
public function __construct($seed){
$this->config = array(
"seed" => (int) $seed,
);
}
public function set($name, $value){
$this->config[$name] = $value;
}
public function init(){
$this->spawn = array(128, 128, 128);
}
public function getSpawn(){
return $this->spawn;
}
public function getColumn($x, $z){
$x = (int) $x;
$z = (int) $z;
$column = $this->structure;
if(floor(sqrt(pow($x - $this->spawn[0], 2) + pow($z - $this->spawn[2], 2))) <= $this->config["spawn-radius"]){
$column[0]{strlen($column[0])-1} = chr($this->config["spawn-surface"]);
}
if(($x % 8) === 0 and ($z % 8) === 0 and $this->config["torches"] == "1"){
$column[0] .= chr(50);
}
$column[0] .= str_repeat(chr(0), 128 - strlen($column[0]));
$column[1] .= str_repeat(chr(0), 64 - strlen($column[1]));
$column[2] .= str_repeat(chr(0), 64 - strlen($column[2]));
$column[3] .= str_repeat(chr(0), 64 - strlen($column[3]));
return $column;
}
}