From db8f50f408f9b03340c08e2bba3455cf57e76cb1 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Pueyo Date: Sun, 28 Apr 2013 23:44:43 +0200 Subject: [PATCH] PMF & Multiworld [part 1] --- src/API/BlockAPI.php | 100 +++---- src/API/EntityAPI.php | 43 ++- src/API/LevelAPI.php | 266 ++++++++++++------ src/API/PlayerAPI.php | 14 +- src/API/ServerAPI.php | 34 +-- src/API/TileEntityAPI.php | 53 ++-- src/Player.php | 10 +- src/PocketMinecraftServer.php | 167 +---------- src/dependencies.php | 4 + src/material/Block.php | 34 ++- src/material/block/GenericBlock.php | 20 +- src/pmf/Level.php | 98 +++++-- src/world/Entity.php | 58 ++-- src/world/Level.php | 151 ++++++++++ src/world/LevelImport.php | 13 +- src/{BlockIterator.php => world/Position.php} | 15 +- src/world/TileEntity.php | 28 +- src/world/generator/NormalGenerator.class.php | 66 ----- 18 files changed, 626 insertions(+), 548 deletions(-) create mode 100644 src/world/Level.php rename src/{BlockIterator.php => world/Position.php} (64%) delete mode 100644 src/world/generator/NormalGenerator.class.php diff --git a/src/API/BlockAPI.php b/src/API/BlockAPI.php index bd98fed3e..24b211908 100644 --- a/src/API/BlockAPI.php +++ b/src/API/BlockAPI.php @@ -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", " [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]); diff --git a/src/API/EntityAPI.php b/src/API/EntityAPI.php index 3d8270d10..89127d545 100644 --- a/src/API/EntityAPI.php +++ b/src/API/EntityAPI.php @@ -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); diff --git a/src/API/LevelAPI.php b/src/API/LevelAPI.php index fdfd8f834..2be425878 100644 --- a/src/API/LevelAPI.php +++ b/src/API/LevelAPI.php @@ -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; - } } \ No newline at end of file diff --git a/src/API/PlayerAPI.php b/src/API/PlayerAPI.php index 602fac1af..e5cf3da70 100644 --- a/src/API/PlayerAPI.php +++ b/src/API/PlayerAPI.php @@ -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)), diff --git a/src/API/ServerAPI.php b/src/API/ServerAPI.php index c61b9ec60..b128ed91f 100644 --- a/src/API/ServerAPI.php +++ b/src/API/ServerAPI.php @@ -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)); } } \ No newline at end of file diff --git a/src/API/TileEntityAPI.php b/src/API/TileEntityAPI.php index cf74dd1db..33320f54f 100644 --- a/src/API/TileEntityAPI.php +++ b/src/API/TileEntityAPI.php @@ -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.";"); diff --git a/src/Player.php b/src/Player.php index 49bc734fc..7a2ff661c 100644 --- a/src/Player.php +++ b/src/Player.php @@ -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, diff --git a/src/PocketMinecraftServer.php b/src/PocketMinecraftServer.php index 3acf08111..3aae2d393 100644 --- a/src/PocketMinecraftServer.php +++ b/src/PocketMinecraftServer.php @@ -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")); diff --git a/src/dependencies.php b/src/dependencies.php index bfb588d5a..b2cfbd096 100644 --- a/src/dependencies.php +++ b/src/dependencies.php @@ -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***/ \ No newline at end of file diff --git a/src/material/Block.php b/src/material/Block.php index 1d7aece94..c8b3b4adc 100644 --- a/src/material/Block.php +++ b/src/material/Block.php @@ -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***/ diff --git a/src/material/block/GenericBlock.php b/src/material/block/GenericBlock.php index d7ed2b778..8531be22d 100644 --- a/src/material/block/GenericBlock.php +++ b/src/material/block/GenericBlock.php @@ -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); } } \ No newline at end of file diff --git a/src/pmf/Level.php b/src/pmf/Level.php index de444d5d3..b74d797ac 100644 --- a/src/pmf/Level.php +++ b/src/pmf/Level.php @@ -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); + } + } } \ No newline at end of file diff --git a/src/world/Entity.php b/src/world/Entity.php index 7efaefbc4..b89434bd6 100644 --- a/src/world/Entity.php +++ b/src/world/Entity.php @@ -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; diff --git a/src/world/Level.php b/src/world/Level.php new file mode 100644 index 000000000..693ad9ade --- /dev/null +++ b/src/world/Level.php @@ -0,0 +1,151 @@ +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"); + } +} \ No newline at end of file diff --git a/src/world/LevelImport.php b/src/world/LevelImport.php index 18875df3e..0d7436c12 100644 --- a/src/world/LevelImport.php +++ b/src/world/LevelImport.php @@ -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; } diff --git a/src/BlockIterator.php b/src/world/Position.php similarity index 64% rename from src/BlockIterator.php rename to src/world/Position.php index a699b3b74..d54240544 100644 --- a/src/BlockIterator.php +++ b/src/world/Position.php @@ -24,8 +24,17 @@ the Free Software Foundation, either version 3 of the License, or */ -/* -class BlockIterator implements Iterator{ +class Position{ + public $level; -}*/ \ No newline at end of file + 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.")"; + } + +} \ No newline at end of file diff --git a/src/world/TileEntity.php b/src/world/TileEntity.php index 4d5a314da..a4491dca9 100644 --- a/src/world/TileEntity.php +++ b/src/world/TileEntity.php @@ -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.";"); } } diff --git a/src/world/generator/NormalGenerator.class.php b/src/world/generator/NormalGenerator.class.php deleted file mode 100644 index 69125e404..000000000 --- a/src/world/generator/NormalGenerator.class.php +++ /dev/null @@ -1,66 +0,0 @@ -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; - } - -} \ No newline at end of file