diff --git a/src/API/LevelAPI.php b/src/API/LevelAPI.php index 07726c77f..56d96ec17 100644 --- a/src/API/LevelAPI.php +++ b/src/API/LevelAPI.php @@ -94,7 +94,7 @@ class LevelAPI{ if(strtoupper($this->server->api->getProperty("level-type")) == "FLAT"){ $generator = new SuperflatGenerator($options); }else{ - $generator = new TemporalGenerator($options); + $generator = new NormalGenerator($options); } } $gen = new WorldGenerator($generator, $name, $seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)):(int) $seed); diff --git a/src/API/PluginAPI.php b/src/API/PluginAPI.php index 1f1ff514f..7581e263a 100644 --- a/src/API/PluginAPI.php +++ b/src/API/PluginAPI.php @@ -26,6 +26,30 @@ class PluginAPI extends stdClass{ public function __construct(){ $this->server = ServerAPI::request(); $this->randomNonce = Utils::getRandomBytes(16, false); + $this->server->api->console->register("plugins", "", array($this, "commandHandler")); + $this->server->api->console->register("version", "", array($this, "commandHandler")); + $this->server->api->ban->cmdWhitelist("version"); + } + + public function commandHandler($cmd, $params, $issuer, $alias){ + $output = ""; + switch($cmd){ + case "plugins": + $output = "Plugins: "; + foreach($this->getList() as $plugin){ + $output .= $plugin["name"] . ": ".$plugin["version"] .", "; + } + $output = $output === "Plugins: " ? "No plugins installed.\n" : substr($output, 0, -2)."\n"; + break; + case "version": + $output = "PocketMine-MP ".MAJOR_VERSION." 「".CODENAME."」 API #".CURRENT_API_VERSION." for Minecraft: PE ".CURRENT_MINECRAFT_VERSION." protocol #".ProtocolInfo::CURRENT_PROTOCOL; + if(GIT_COMMIT !== str_repeat("00", 20)){ + $output .= " (git ".GIT_COMMIT.")"; + } + $output .= "\n"; + break; + } + return $output; } public function __destruct(){ diff --git a/src/EntityOLD.php b/src/EntityOLD.php index 25d43aef1..f986bed75 100644 --- a/src/EntityOLD.php +++ b/src/EntityOLD.php @@ -296,7 +296,7 @@ class EntityOLD extends Position{ } } - if($this->class !== ENTITY_PLAYER and ($this->x <= 0 or $this->z <= 0 or $this->x >= 256 or $this->z >= 256 or $this->y >= 128 or $this->y <= 0)){ + if($this->class !== ENTITY_PLAYER and ($this->y >= 128 or $this->y <= 0)){ $this->close(); return false; } diff --git a/src/Player.php b/src/Player.php index 966f9cf5b..6bd62aa62 100644 --- a/src/Player.php +++ b/src/Player.php @@ -294,7 +294,6 @@ class Player{ $this->directDataPacket(new DisconnectPacket); $this->connected = false; $this->level->freeAllChunks($this); - $this->spawned = false; $this->loggedIn = false; $this->buffer = null; unset($this->buffer); @@ -306,6 +305,7 @@ class Player{ if($msg === true and $this->username != "" and $this->spawned !== false){ $this->server->api->chat->broadcast($this->username." left the game"); } + $this->spawned = false; console("[INFO] ".FORMAT_AQUA.$this->username.FORMAT_RESET."[/".$this->ip.":".$this->port."] logged out due to ".$reason); $this->windows = array(); $this->armor = array(); @@ -1311,7 +1311,7 @@ class Player{ $this->close("Incorrect protocol #".$packet->protocol1, false); return; } - if(preg_match('#[^a-zA-Z0-9_]#', $packet->username) > 0 or $packet->username === ""){ + if(preg_match('#[^a-zA-Z0-9_]#', $this->username) > 0 or $this->username === "" or $this->iusername === "rcon" or $this->iusername === "console"){ $this->close("Bad username", false); return; } diff --git a/src/config.php b/src/config.php index 760395a1d..d908f7a2f 100644 --- a/src/config.php +++ b/src/config.php @@ -70,7 +70,7 @@ define("START_TIME", microtime(true)); define("MAJOR_VERSION", "Alpha_1.4dev"); define("CODENAME", "絶好(Zekkou)ケーキ(Cake)"); define("CURRENT_MINECRAFT_VERSION", "v0.8.1 alpha"); -define("CURRENT_API_VERSION", 12); +define("CURRENT_API_VERSION", 13); define("CURRENT_PHP_VERSION", "5.5"); $gitsha1 = false; if(file_exists(FILE_PATH.".git/refs/heads/master")){ //Found Git information! diff --git a/src/network/MinecraftInterface.php b/src/network/MinecraftInterface.php index f7e149c04..587131a2d 100644 --- a/src/network/MinecraftInterface.php +++ b/src/network/MinecraftInterface.php @@ -71,7 +71,7 @@ class MinecraftInterface{ $packet = new QueryPacket; $packet->ip = $source; $packet->port = $port; - $packet->buffer = $buffer; + $packet->buffer =& $buffer; if(EventHandler::callEvent(new PacketReceiveEvent($packet)) === BaseEvent::DENY){ return false; } @@ -80,7 +80,7 @@ class MinecraftInterface{ $packet = new Packet(); $packet->ip = $source; $packet->port = $port; - $packet->buffer = $buffer; + $packet->buffer =& $buffer; EventHandler::callEvent(new PacketReceiveEvent($packet)); return false; } diff --git a/src/network/protocol/ProtocolInfo.php b/src/network/protocol/ProtocolInfo.php index b321c4c2c..45c100489 100644 --- a/src/network/protocol/ProtocolInfo.php +++ b/src/network/protocol/ProtocolInfo.php @@ -20,7 +20,7 @@ */ -class ProtocolInfo{ +abstract class ProtocolInfo{ const CURRENT_PROTOCOL = 14; @@ -95,63 +95,6 @@ class ProtocolInfo{ const ADVENTURE_SETTINGS_PACKET = 0xb7; const ENTITY_DATA_PACKET = 0xb8; //const PLAYER_INPUT_PACKET = 0xb9; - - public static $packets = array( - -1 => "UnknownPacket", - ProtocolInfo::PING_PACKET => "PingPacket", - ProtocolInfo::PONG_PACKET => "PongPacket", - ProtocolInfo::CLIENT_CONNECT_PACKET => "ClientConnectPacket", - ProtocolInfo::SERVER_HANDSHAKE_PACKET => "ServerHandshakePacket", - ProtocolInfo::DISCONNECT_PACKET => "DisconnectPacket", - ProtocolInfo::LOGIN_PACKET => "LoginPacket", - ProtocolInfo::LOGIN_STATUS_PACKET => "LoginStatusPacket", - ProtocolInfo::READY_PACKET => "ReadyPacket", - ProtocolInfo::MESSAGE_PACKET => "MessagePacket", - ProtocolInfo::SET_TIME_PACKET => "SetTimePacket", - ProtocolInfo::START_GAME_PACKET => "StartGamePacket", - ProtocolInfo::ADD_MOB_PACKET => "AddMobPacket", - ProtocolInfo::ADD_PLAYER_PACKET => "AddPlayerPacket", - ProtocolInfo::REMOVE_PLAYER_PACKET => "RemovePlayerPacket", - ProtocolInfo::ADD_ENTITY_PACKET => "AddEntityPacket", - ProtocolInfo::REMOVE_ENTITY_PACKET => "RemoveEntityPacket", - ProtocolInfo::ADD_ITEM_ENTITY_PACKET => "AddItemEntityPacket", - ProtocolInfo::TAKE_ITEM_ENTITY_PACKET => "TakeItemEntityPacket", - ProtocolInfo::MOVE_ENTITY_PACKET => "MoveEntityPacket", - ProtocolInfo::MOVE_ENTITY_PACKET_POSROT => "MoveEntityPacket_PosRot", - ProtocolInfo::ROTATE_HEAD_PACKET => "RotateHeadPacket", - ProtocolInfo::MOVE_PLAYER_PACKET => "MovePlayerPacket", - ProtocolInfo::REMOVE_BLOCK_PACKET => "RemoveBlockPacket", - ProtocolInfo::UPDATE_BLOCK_PACKET => "UpdateBlockPacket", - ProtocolInfo::ADD_PAINTING_PACKET => "AddPaintingPacket", - ProtocolInfo::EXPLODE_PACKET => "ExplodePacket", - ProtocolInfo::LEVEL_EVENT_PACKET => "LevelEventPacket", - ProtocolInfo::TILE_EVENT_PACKET => "TileEventPacket", - ProtocolInfo::ENTITY_EVENT_PACKET => "EntityEventPacket", - ProtocolInfo::REQUEST_CHUNK_PACKET => "RequestChunkPacket", - ProtocolInfo::CHUNK_DATA_PACKET => "ChunkDataPacket", - ProtocolInfo::PLAYER_EQUIPMENT_PACKET => "PlayerEquipmentPacket", - ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET => "PlayerArmorEquipmentPacket", - ProtocolInfo::INTERACT_PACKET => "InteractPacket", - ProtocolInfo::USE_ITEM_PACKET => "UseItemPacket", - ProtocolInfo::PLAYER_ACTION_PACKET => "PlayerActionPacket", - ProtocolInfo::HURT_ARMOR_PACKET => "HurtArmorPacket", - ProtocolInfo::SET_ENTITY_DATA_PACKET => "SetEntityDataPacket", - ProtocolInfo::SET_ENTITY_MOTION_PACKET => "SetEntityMotionPacket", - ProtocolInfo::SET_HEALTH_PACKET => "SetHealthPacket", - ProtocolInfo::SET_SPAWN_POSITION_PACKET => "SetSpawnPositionPacket", - ProtocolInfo::ANIMATE_PACKET => "AnimatePacket", - ProtocolInfo::RESPAWN_PACKET => "RespawnPacket", - ProtocolInfo::SEND_INVENTORY_PACKET => "SendInventoryPacket", - ProtocolInfo::DROP_ITEM_PACKET => "DropItemPacket", - ProtocolInfo::CONTAINER_OPEN_PACKET => "ContainerOpenPacket", - ProtocolInfo::CONTAINER_CLOSE_PACKET => "ContainerClosePacket", - ProtocolInfo::CONTAINER_SET_SLOT_PACKET => "ContainerSetSlotPacket", - ProtocolInfo::CONTAINER_SET_DATA_PACKET => "ContainerSetDataPacket", - ProtocolInfo::CONTAINER_SET_CONTENT_PACKET => "ContainerSetContentPacket", - ProtocolInfo::CHAT_PACKET => "ChatPacket", - ProtocolInfo::ADVENTURE_SETTINGS_PACKET => "AdventureSettingsPacket", - ProtocolInfo::ENTITY_DATA_PACKET => "EntityDataPacket", - ); } diff --git a/src/network/protocol/packet/SendInventoryPacket.php b/src/network/protocol/packet/SendInventoryPacket.php index 250d3abce..b868c250b 100644 --- a/src/network/protocol/packet/SendInventoryPacket.php +++ b/src/network/protocol/packet/SendInventoryPacket.php @@ -36,7 +36,7 @@ class SendInventoryPacket extends RakNetDataPacket{ for($s = 0; $s < $count and !$this->feof(); ++$s){ $this->slots[$s] = $this->getSlot(); } - if($this->windowid === 1){ //Armir is sent + if($this->windowid === 1){ //Armor is sent for($s = 0; $s < 4; ++$s){ $this->armor[$s] = $this->getSlot(); } diff --git a/src/network/protocol/packet/TakeItemEntity.php b/src/network/protocol/packet/TakeItemEntityPacket.php similarity index 100% rename from src/network/protocol/packet/TakeItemEntity.php rename to src/network/protocol/packet/TakeItemEntityPacket.php diff --git a/src/network/raknet/RakNetInfo.php b/src/network/raknet/RakNetInfo.php index 485f53973..2823ee433 100644 --- a/src/network/raknet/RakNetInfo.php +++ b/src/network/raknet/RakNetInfo.php @@ -19,7 +19,7 @@ * */ -class RakNetInfo{ +abstract class RakNetInfo{ const STRUCTURE = 5; const MAGIC = "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"; const UNCONNECTED_PING = 0x01; diff --git a/src/network/raknet/RakNetParser.php b/src/network/raknet/RakNetParser.php index fa2f6dca2..98699be9c 100644 --- a/src/network/raknet/RakNetParser.php +++ b/src/network/raknet/RakNetParser.php @@ -186,11 +186,170 @@ class RakNetParser{ if(strlen($buffer) < ($length - 1)){ return false; } - if(isset(ProtocolInfo::$packets[$pid])){ - $data = new ProtocolInfo::$packets[$pid]; - }else{ - $data = new UnknownPacket(); - $data->packetID = $pid; + switch($pid){ + case ProtocolInfo::PING_PACKET: + $data = new PingPacket; + break; + case ProtocolInfo::PONG_PACKET: + $data = new PongPacket; + break; + case ProtocolInfo::CLIENT_CONNECT_PACKET: + $data = new ClientConnectPacket; + break; + case ProtocolInfo::SERVER_HANDSHAKE_PACKET: + $data = new ServerHandshakePacket; + break; + case ProtocolInfo::DISCONNECT_PACKET: + $data = new DisconnectPacket; + break; + case ProtocolInfo::LOGIN_PACKET: + $data = new LoginPacket; + break; + case ProtocolInfo::LOGIN_STATUS_PACKET: + $data = new LoginStatusPacket; + break; + case ProtocolInfo::READY_PACKET: + $data = new ReadyPacket; + break; + case ProtocolInfo::MESSAGE_PACKET: + $data = new MessagePacket; + break; + case ProtocolInfo::SET_TIME_PACKET: + $data = new SetTimePacket; + break; + case ProtocolInfo::START_GAME_PACKET: + $data = new StartGamePacket; + break; + case ProtocolInfo::ADD_MOB_PACKET: + $data = new AddMobPacket; + break; + case ProtocolInfo::ADD_PLAYER_PACKET: + $data = new AddPlayerPacket; + break; + case ProtocolInfo::REMOVE_PLAYER_PACKET: + $data = new RemovePlayerPacket; + break; + case ProtocolInfo::ADD_ENTITY_PACKET: + $data = new AddEntityPacket; + break; + case ProtocolInfo::REMOVE_ENTITY_PACKET: + $data = new RemoveEntityPacket; + break; + case ProtocolInfo::ADD_ITEM_ENTITY_PACKET: + $data = new AddItemEntityPacket; + break; + case ProtocolInfo::TAKE_ITEM_ENTITY_PACKET: + $data = new TakeItemEntityPacket; + break; + case ProtocolInfo::MOVE_ENTITY_PACKET: + $data = new MoveEntityPacket; + break; + case ProtocolInfo::MOVE_ENTITY_PACKET_POSROT: + $data = new MoveEntityPacket_PosRot; + break; + case ProtocolInfo::ROTATE_HEAD_PACKET: + $data = new RotateHeadPacket; + break; + case ProtocolInfo::MOVE_PLAYER_PACKET: + $data = new MovePlayerPacket; + break; + case ProtocolInfo::REMOVE_BLOCK_PACKET: + $data = new RemoveBlockPacket; + break; + case ProtocolInfo::UPDATE_BLOCK_PACKET: + $data = new UpdateBlockPacket; + break; + case ProtocolInfo::ADD_PAINTING_PACKET: + $data = new AddPaintingPacket; + break; + case ProtocolInfo::EXPLODE_PACKET: + $data = new ExplodePacket; + break; + case ProtocolInfo::LEVEL_EVENT_PACKET: + $data = new LevelEventPacket; + break; + case ProtocolInfo::TILE_EVENT_PACKET: + $data = new TileEventPacket; + break; + case ProtocolInfo::ENTITY_EVENT_PACKET: + $data = new EntityEventPacket; + break; + case ProtocolInfo::REQUEST_CHUNK_PACKET: + $data = new RequestChunkPacket; + break; + case ProtocolInfo::CHUNK_DATA_PACKET: + $data = new ChunkDataPacket; + break; + case ProtocolInfo::PLAYER_EQUIPMENT_PACKET: + $data = new PlayerEquipmentPacket; + break; + case ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET: + $data = new PlayerArmorEquipmentPacket; + break; + case ProtocolInfo::INTERACT_PACKET: + $data = new InteractPacket; + break; + case ProtocolInfo::USE_ITEM_PACKET: + $data = new UseItemPacket; + break; + case ProtocolInfo::PLAYER_ACTION_PACKET: + $data = new PlayerActionPacket; + break; + case ProtocolInfo::HURT_ARMOR_PACKET: + $data = new HurtArmorPacket; + break; + case ProtocolInfo::SET_ENTITY_DATA_PACKET: + $data = new SetEntityDataPacket; + break; + case ProtocolInfo::SET_ENTITY_MOTION_PACKET: + $data = new SetEntityMotionPacket; + break; + case ProtocolInfo::SET_HEALTH_PACKET: + $data = new SetHealthPacket; + break; + case ProtocolInfo::SET_SPAWN_POSITION_PACKET: + $data = new SetSpawnPositionPacket; + break; + case ProtocolInfo::ANIMATE_PACKET: + $data = new AnimatePacket; + break; + case ProtocolInfo::RESPAWN_PACKET: + $data = new RespawnPacket; + break; + case ProtocolInfo::SEND_INVENTORY_PACKET: + $data = new SendInventoryPacket; + break; + case ProtocolInfo::DROP_ITEM_PACKET: + $data = new DropItemPacket; + break; + case ProtocolInfo::CONTAINER_OPEN_PACKET: + $data = new ContainerOpenPacket; + break; + case ProtocolInfo::CONTAINER_CLOSE_PACKET: + $data = new ContainerClosePacket; + break; + case ProtocolInfo::CONTAINER_SET_SLOT_PACKET: + $data = new ContainerSetSlotPacket; + break; + case ProtocolInfo::CONTAINER_SET_DATA_PACKET: + $data = new ContainerSetDataPacket; + break; + case ProtocolInfo::CONTAINER_SET_CONTENT_PACKET: + $data = new ContainerSetContentPacket; + break; + case ProtocolInfo::CHAT_PACKET: + $data = new ChatPacket; + break; + case ProtocolInfo::ADVENTURE_SETTINGS_PACKET: + $data = new AdventureSettingsPacket; + break; + case ProtocolInfo::ENTITY_DATA_PACKET: + $data = new EntityDataPacket; + break; + default: + $data = new UnknownPacket(); + $data->packetID = $pid; + break; } $data->reliability = $reliability; $data->hasSplit = $hasSplit; diff --git a/src/pmf/Level.php b/src/pmf/PMFLevel.php similarity index 62% rename from src/pmf/Level.php rename to src/pmf/PMFLevel.php index e4c8824fd..3b3707029 100644 --- a/src/pmf/Level.php +++ b/src/pmf/PMFLevel.php @@ -19,16 +19,17 @@ * */ -define("PMF_CURRENT_LEVEL_VERSION", 0x00); - class PMFLevel extends PMF{ - private $levelData = array(); - private $locationTable = array(); + const VERSION = 0x01; + const DEFLATE_LEVEL = 9; + + public $level; + public $levelData = array(); public $isLoaded = true; - private $log = 4; - private $payloadOffset = 0; private $chunks = array(); private $chunkChange = array(); + private $chunkInfo = array(); + public $isGenerating = 0; public function getData($index){ if(!isset($this->levelData[$index])){ @@ -46,8 +47,8 @@ class PMFLevel extends PMF{ } public function close(){ - $chunks = null; - unset($chunks, $chunkChange, $locationTable); + $this->chunks = null; + unset($this->chunks, $this->chunkChange, $this->chunkInfo, $this->level); parent::close(); } @@ -57,7 +58,6 @@ class PMFLevel extends PMF{ $this->levelData = $blank; $this->createBlank(); $this->isLoaded = true; - $this->log = (int) ((string) log($this->levelData["width"], 2)); }else{ if($this->load($file) !== false){ $this->parseInfo(); @@ -65,7 +65,6 @@ class PMFLevel extends PMF{ $this->isLoaded = false; }else{ $this->isLoaded = true; - $this->log = (int) ((string) log($this->levelData["width"], 2)); } }else{ $this->isLoaded = false; @@ -73,8 +72,8 @@ class PMFLevel extends PMF{ } } - public function saveData($locationTable = true){ - $this->levelData["version"] = PMF_CURRENT_LEVEL_VERSION; + public function saveData(){ + $this->levelData["version"] = PMFLevel::VERSION; @ftruncate($this->fp, 5); $this->seek(5); $this->write(chr($this->levelData["version"])); @@ -84,33 +83,17 @@ class PMFLevel extends PMF{ $this->write(Utils::writeFloat($this->levelData["spawnX"])); $this->write(Utils::writeFloat($this->levelData["spawnY"])); $this->write(Utils::writeFloat($this->levelData["spawnZ"])); - $this->write(chr($this->levelData["width"])); $this->write(chr($this->levelData["height"])); - $extra = gzdeflate($this->levelData["extra"], PMF_LEVEL_DEFLATE_LEVEL); + $this->write(Utils::writeShort(strlen($this->levelData["generator"])).$this->levelData["generator"]); + $settings = serialize($this->levelData["generatorSettings"]); + $this->write(Utils::writeShort(strlen($settings)).$settings); + $extra = gzdeflate($this->levelData["extra"], PMFLevel::DEFLATE_LEVEL); $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); - for($index = 0; $index < $cnt; ++$index){ - $this->chunks[$index] = false; - $this->chunkChange[$index] = false; - $this->locationTable[$index] = array( - 0 => 0, - ); - $this->write(Utils::writeShort(0)); - $X = $Z = null; - $this->getXZ($index, $X, $Z); - @file_put_contents($this->getChunkPath($X, $Z), gzdeflate("", PMF_LEVEL_DEFLATE_LEVEL)); - } if(!file_exists(dirname($this->file)."/entities.yml")){ $entities = new Config(dirname($this->file)."/entities.yml", CONFIG_YAML); $entities->save(); @@ -127,7 +110,8 @@ class PMFLevel extends PMF{ } $this->seek(5); $this->levelData["version"] = ord($this->read(1)); - if($this->levelData["version"] > PMF_CURRENT_LEVEL_VERSION){ + if($this->levelData["version"] > PMFLevel::VERSION){ + console("[ERROR] New unsupported PMF Level format version #".$this->levelData["version"].", current version is #".PMFLevel::VERSION); return false; } $this->levelData["name"] = $this->read(Utils::readShort($this->read(2), false)); @@ -136,90 +120,117 @@ class PMFLevel extends PMF{ $this->levelData["spawnX"] = Utils::readFloat($this->read(4)); $this->levelData["spawnY"] = Utils::readFloat($this->read(4)); $this->levelData["spawnZ"] = Utils::readFloat($this->read(4)); - $this->levelData["width"] = ord($this->read(1)); - $this->levelData["height"] = ord($this->read(1)); - if(($this->levelData["width"] !== 16 and $this->levelData["width"] !== 32) or $this->levelData["height"] !== 8){ - return false; - } - $lastseek = ftell($this->fp); - if(($len = $this->read(2)) === false or ($this->levelData["extra"] = @gzinflate($this->read(Utils::readShort($len, false)))) === false){ //Corruption protection - console("[NOTICE] Empty/corrupt location table detected, forcing recovery"); - fseek($this->fp, $lastseek); - $c = gzdeflate(""); - $this->write(Utils::writeShort(strlen($c)).$c); - $this->payloadOffset = ftell($this->fp); - $this->levelData["extra"] = ""; - $cnt = pow($this->levelData["width"], 2); - for($index = 0; $index < $cnt; ++$index){ - $this->write("\x00\xFF"); //Force index recreation - } - fseek($this->fp, $this->payloadOffset); + if($this->levelData["version"] === 0){ + $this->read(1); + $this->levelData["height"] = ord($this->read(1)); }else{ - $this->payloadOffset = ftell($this->fp); + $this->levelData["height"] = ord($this->read(1)); + if($this->levelData["height"] !== 8){ + return false; + } + $this->levelData["generator"] = $this->read(Utils::readShort($this->read(2), false)); + $this->levelData["generatorSettings"] = unserialize($this->read(Utils::readShort($this->read(2), false))); + + } + $this->levelData["extra"] = @gzinflate($this->read(Utils::readShort($this->read(2), false))); + + if($this->levelData["version"] === 0){ + $this->upgrade_From0_To1(); } - return $this->readLocationTable(); } - public function getIndex($X, $Z){ - $X = (int) $X; - $Z = (int) $Z; - return ($Z << $this->log) + $X; + private function upgrade_From0_To1(){ + console("[NOTICE] Old PMF Level format version #0 detected, upgrading to version #1"); + for($index = 0; $index < 256; ++$index){ + $X = $index & 0x0F; + $Z = $index >> 4; + + $this->chunks[$index] = false; + $this->chunkChange[$index] = false; + $bitflags = Utils::readShort($this->read(2)); + $oldPath = dirname($this->file)."/chunks/".$Z.".".$X.".pmc"; + $chunkOld = gzopen($oldPath, "rb"); + $newPath = dirname($this->file)."/chunks/".(($X ^ $Z) & 0xff)."/".$Z.".".$X.".pmc"; + @mkdir(dirname($newPath)); + $chunkNew = gzopen($newPath, "wb".PMFLevel::DEFLATE_LEVEL); + gzwrite($chunkNew, chr($bitflags) . "\x00\x00\x00\x01"); + while(gzeof($chunkOld) === false){ + gzwrite($chunkNew, gzread($chunkOld, 65535)); + } + gzclose($chunkNew); + gzclose($chunkOld); + @unlink($oldPath); + } + $this->levelData["version"] = 0x01; + $this->levelData["generator"] = "NormalGenerator"; + $this->levelData["generatorSettings"] = ""; + $this->saveData(); } - public function getXZ($index, &$X = null, &$Z = null){ - $X = $index >> $this->log; - $Z = $index & (pow($this->log, 2) - 1); + public static function getIndex($X, $Z){ + return ($Z << 16) + $X; + } + + public static function getXZ($index, &$X = null, &$Z = null){ + $Z = $index >> 16; + $X = $index & 0xFFFF; return array($X, $Z); } - private function readLocationTable(){ - $this->locationTable = array(); - $cnt = pow($this->levelData["width"], 2); - $this->seek($this->payloadOffset); - for($index = 0; $index < $cnt; ++$index){ - $this->chunks[$index] = false; - $this->chunkChange[$index] = false; - $this->locationTable[$index] = array( - 0 => Utils::readShort($this->read(2)), //16 bit flags - ); - } - 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][0])); - } - } - private function getChunkPath($X, $Z){ - return dirname($this->file)."/chunks/".$Z.".".$X.".pmc"; + return dirname($this->file)."/chunks/".(($X ^ $Z) & 0xff)."/".$Z.".".$X.".pmc"; + } + + public function generateChunk($X, $Z){ + $path = $this->getChunkPath($X, $Z); + if(!file_exists(dirname($path))){ + @mkdir(dirname($path), 0755); + } + ++$this->isGenerating; + $this->initCleanChunk($X, $Z); + $ret = $this->level->generateChunk($X, $Z); + $ret = $ret and $this->level->populateChunk($X, $Z); + $this->saveChunk($X, $Z); + --$this->isGenerating; + return $ret; } public function loadChunk($X, $Z){ $X = (int) $X; $Z = (int) $Z; - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if($this->isChunkLoaded($X, $Z)){ return true; - }elseif(!isset($this->locationTable[$index])){ - return false; } - $info = $this->locationTable[$index]; - $this->seek($info[0]); - - $chunk = @gzopen($this->getChunkPath($X, $Z), "rb"); + $path = $this->getChunkPath($X, $Z); + if(!file_exists($path)){ + if($this->isGenerating > 0){ + $this->level->generateChunk($X, $Z); + $this->saveChunk($X, $Z); + }elseif($this->generateChunk($X, $Z) === false){ + return false; + } + } + + if($this->isGenerating === 0 and !$this->isPopulated($X, $Z)){ + ++$this->isGenerating; + $this->level->populateChunk($X, $Z); + --$this->isGenerating; + } + + $chunk = @gzopen($path, "rb"); if($chunk === false){ return false; } + $this->chunkInfo[$index] = array( + 0 => ord(gzread($chunk, 1)), + 1 => Utils::readInt(gzread($chunk, 4)), + ); $this->chunks[$index] = array(); $this->chunkChange[$index] = array(-1 => false); - for($Y = 0; $Y < $this->levelData["height"]; ++$Y){ + for($Y = 0; $Y < $this->chunkInfo[$index][0]; ++$Y){ $t = 1 << $Y; - if(($info[0] & $t) === $t){ + if(($this->chunkInfo[$index][0] & $t) === $t){ // 4096 + 2048 + 2048, Block Data, Meta, Light if(strlen($this->chunks[$index][$Y] = gzread($chunk, 8192)) < 8192){ console("[NOTICE] Empty corrupt chunk detected [$X,$Z,:$Y], recovering contents"); @@ -241,15 +252,16 @@ class PMFLevel extends PMF{ }elseif($save !== false){ $this->saveChunk($X, $Z); } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); $this->chunks[$index] = null; $this->chunkChange[$index] = null; - unset($this->chunks[$index], $this->chunkChange[$index]); + $this->chunkInfo[$index] = null; + unset($this->chunks[$index], $this->chunkChange[$index], $this->chunkInfo[$index]); return true; } public function isChunkLoaded($X, $Z){ - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ return false; } @@ -257,7 +269,7 @@ class PMFLevel extends PMF{ } protected function isMiniChunkEmpty($X, $Z, $Y){ - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if($this->chunks[$index][$Y] !== false){ if(substr_count($this->chunks[$index][$Y], "\x00") < 8192){ return false; @@ -270,11 +282,11 @@ class PMFLevel extends PMF{ if($this->isChunkLoaded($X, $Z) === false){ return false; } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); $this->chunks[$index][$Y] = str_repeat("\x00", 8192); $this->chunkChange[$index][-1] = true; $this->chunkChange[$index][$Y] = 8192; - $this->locationTable[$index][0] |= 1 << $Y; + $this->chunkInfo[$index][0] |= 1 << $Y; return true; } @@ -282,36 +294,62 @@ class PMFLevel extends PMF{ if($this->loadChunk($X, $Z) === false){ return str_repeat("\x00", 8192); } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if(!isset($this->chunks[$index][$Y]) or $this->chunks[$index][$Y] === false){ return str_repeat("\x00", 8192); } return $this->chunks[$index][$Y]; } + public function initCleanChunk($X, $Z){ + $index = self::getIndex($X, $Z); + if(!isset($this->chunks[$index])){ + $this->chunks[$index] = array( + 0 => false, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + 5 => false, + 6 => false, + 7 => false, + ); + $this->chunkChange[$index] = array(-1 => false); + $this->chunkInfo[$index] = array( + 0 => 0, + 1 => 0, + ); + } + } + public function setMiniChunk($X, $Z, $Y, $data){ - if($this->isChunkLoaded($X, $Z) === false){ + if($this->isGenerating > 0){ + $this->initCleanChunk($X, $Z); + }elseif($this->isChunkLoaded($X, $Z) === false){ $this->loadChunk($X, $Z); } if(strlen($data) !== 8192){ return false; } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); $this->chunks[$index][$Y] = (string) $data; $this->chunkChange[$index][-1] = true; $this->chunkChange[$index][$Y] = 8192; - $this->locationTable[$index][0] |= 1 << $Y; + $this->chunkInfo[$index][0] |= 1 << $Y; return true; } public function getBlockID($x, $y, $z){ - if($y > 127 or $y < 0 or $x < 0 or $z < 0 or $x > 255 or $z > 255){ + if($y > 127 or $y < 0){ return 0; } $X = $x >> 4; $Z = $z >> 4; $Y = $y >> 4; - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); + if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ + return 0; + } $aX = $x - ($X << 4); $aZ = $z - ($Z << 4); $aY = $y - ($Y << 4); @@ -320,17 +358,17 @@ class PMFLevel extends PMF{ } public function setBlockID($x, $y, $z, $block){ - if($y > 127 or $y < 0 or $x < 0 or $z < 0 or $x > 255 or $z > 255){ + if($y > 127 or $y < 0){ return false; } $X = $x >> 4; $Z = $z >> 4; $Y = $y >> 4; $block &= 0xFF; - if($X >= 32 or $Z >= 32 or $Y >= $this->levelData["height"] or $y < 0){ + $index = self::getIndex($X, $Z); + if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ return false; } - $index = $this->getIndex($X, $Z); $aX = $x - ($X << 4); $aZ = $z - ($Z << 4); $aY = $y - ($Y << 4); @@ -345,13 +383,16 @@ class PMFLevel extends PMF{ } public function getBlockDamage($x, $y, $z){ - if($y > 127 or $y < 0 or $x < 0 or $z < 0 or $x > 255 or $z > 255){ + if($y > 127 or $y < 0){ return 0; } $X = $x >> 4; $Z = $z >> 4; $Y = $y >> 4; - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); + if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ + return 0; + } $aX = $x - ($X << 4); $aZ = $z - ($Z << 4); $aY = $y - ($Y << 4); @@ -365,17 +406,17 @@ class PMFLevel extends PMF{ } public function setBlockDamage($x, $y, $z, $damage){ - if($y > 127 or $y < 0 or $x < 0 or $z < 0 or $x > 255 or $z > 255){ + if($y > 127 or $y < 0){ return false; } $X = $x >> 4; $Z = $z >> 4; $Y = $y >> 4; $damage &= 0x0F; - if($X >= 32 or $Z >= 32 or $Y >= $this->levelData["height"] or $y < 0){ + $index = self::getIndex($X, $Z); + if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ return false; } - $index = $this->getIndex($X, $Z); $aX = $x - ($X << 4); $aZ = $z - ($Z << 4); $aY = $y - ($Y << 4); @@ -404,10 +445,10 @@ class PMFLevel extends PMF{ $X = $x >> 4; $Z = $z >> 4; $Y = $y >> 4; - if($x < 0 or $z < 0 or $X >= $this->levelData["width"] or $Z >= $this->levelData["width"] or $Y >= $this->levelData["height"] or $y < 0){ + if($y < 0 or $y > 127){ return array(AIR, 0); } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ if($this->loadChunk($X, $Z) === false){ return array(AIR, 0); @@ -429,15 +470,15 @@ class PMFLevel extends PMF{ } public function setBlock($x, $y, $z, $block, $meta = 0){ + if($y > 127 or $y < 0){ + return false; + } $X = $x >> 4; $Z = $z >> 4; $Y = $y >> 4; $block &= 0xFF; $meta &= 0x0F; - if($X >= 32 or $Z >= 32 or $Y >= $this->levelData["height"] or $y < 0){ - return false; - } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if(!isset($this->chunks[$index]) or $this->chunks[$index] === false){ if($this->loadChunk($X, $Z) === false){ return false; @@ -486,35 +527,71 @@ class PMFLevel extends PMF{ public function saveChunk($X, $Z){ $X = (int) $X; $Z = (int) $Z; - if(!$this->isChunkLoaded($X, $Z)){ + if($this->isGenerating > 0){ + $this->initCleanChunk($X, $Z); + }elseif(!$this->isChunkLoaded($X, $Z)){ return false; } - $index = $this->getIndex($X, $Z); + $index = self::getIndex($X, $Z); if(!isset($this->chunkChange[$index]) or $this->chunkChange[$index][-1] === false){//No changes in chunk return true; } - $chunk = @gzopen($this->getChunkPath($X, $Z), "wb".PMF_LEVEL_DEFLATE_LEVEL); + $path = $this->getChunkPath($X, $Z); + if(!file_exists(dirname($path))){ + @mkdir(dirname($path), 0755); + } $bitmap = 0; - for($Y = 0; $Y < $this->levelData["height"]; ++$Y){ + for($Y = 0; $Y < 8; ++$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; } + $chunk = @gzopen($path, "wb".PMFLevel::DEFLATE_LEVEL); + gzwrite($chunk, chr($bitmap)); + gzwrite($chunk, Utils::writeInt($this->chunkInfo[$index][0])); + for($Y = 0; $Y < 8; ++$Y){ + $t = 1 << $Y; + if(($bitmap & $t) === $t){ + gzwrite($chunk, $this->chunks[$index][$Y]); + } + } + gzclose($chunk); $this->chunkChange[$index][-1] = false; - $this->locationTable[$index][0] = $bitmap; - $this->seek($this->payloadOffset + ($index << 1)); - $this->write(Utils::writeShort($this->locationTable[$index][0])); + $this->chunkInfo[$index][0] = $bitmap; return true; } + public function setPopulated($X, $Z){ + if(!$this->isChunkLoaded($X, $Z)){ + return false; + } + $index = self::getIndex($X, $Z); + $this->chunkInfo[$index][1] |= 0b00000000000000000000000000000001; + } + + public function unsetPopulated($X, $Z){ + if(!$this->isChunkLoaded($X, $Z)){ + return false; + } + $index = self::getIndex($X, $Z); + $this->chunkInfo[$index][1] &= ~0b00000000000000000000000000000001; + } + + public function isPopulated($X, $Z){ + if(!$this->isChunkLoaded($X, $Z)){ + return false; + } + $index = self::getIndex($X, $Z); + return ($this->chunkInfo[$index][1] & 0b00000000000000000000000000000001) > 0; + } + public function doSaveRound(){ foreach($this->chunks as $index => $chunk){ - $this->getXZ($index, $X, $Z); + self::getXZ($index, $X, $Z); $this->saveChunk($X, $Z); } } diff --git a/src/pmf/Plugin.php b/src/pmf/PMFPlugin.php similarity index 100% rename from src/pmf/Plugin.php rename to src/pmf/PMFPlugin.php diff --git a/src/utils/Random.php b/src/utils/Random.php index fd0273ecb..da7e9bf92 100644 --- a/src/utils/Random.php +++ b/src/utils/Random.php @@ -22,50 +22,45 @@ //Unsecure, not used for "Real Randomness" class Random{ - private $x, $y, $z, $w; + private $z, $w; public function __construct($seed = false){ $this->setSeed($seed); } public function setSeed($seed = false){ - $seed = $seed !== false ? Utils::writeInt((int) $seed):Utils::getRandomBytes(4, false); - $state = array(); - for($i = 0; $i < 256; ++$i){ - $state[] = $i; - } - for($i = $j = 0; $i < 256; ++$i){ - $j = ($j + ord($seed{$i & 0x03}) + $state[$i]) & 0xFF; - $state[$i] ^= $state[$j]; - $state[$j] ^= $state[$i]; - $state[$i] ^= $state[$j]; - } - $this->state = $state; - $this->i = $this->j = 0; + $seed = $seed !== false ? (int) $seed:Utils::readInt(Utils::getRandomBytes(4, false)); + $this->z = $seed ^ 0xdeadbeef; + $this->w = $seed ^ 0xc0de1337; } public function nextInt(){ return Utils::readInt($this->nextBytes(4)) & 0x7FFFFFFF; } + public function nextSignedInt(){ + return Utils::readInt($this->nextBytes(4)); + } + public function nextFloat(){ return $this->nextInt() / 0x7FFFFFFF; } + public function nextSignedFloat(){ + return $this->nextSignedInt() / 0x7FFFFFFF; + } + public function nextBytes($byteCount){ $bytes = ""; - for($i = 0; $i < $byteCount; ++$i){ - $this->i = ($this->i + 1) & 0xFF; - $this->j = ($this->j + $this->state[$this->i]) & 0xFF; - $this->state[$this->i] ^= $this->state[$this->j]; - $this->state[$this->j] ^= $this->state[$this->i]; - $this->state[$this->i] ^= $this->state[$this->j]; - $bytes .= chr($this->state[($this->state[$this->i] + $this->state[$this->j]) & 0xFF]); + while(strlen($bytes) < $byteCount){ + $this->z = 36969 * ($this->z & 65535) + ($this->z >> 16); + $this->w = 18000 * ($this->w & 65535) + ($this->w >> 16); + $bytes .= pack("N", ($this->z << 16) + $this->w); } - return $bytes; + return substr($bytes, 0, $byteCount); } public function nextBoolean(){ - return ($this->nextBytes(1) & 0x01) == 0; + return ($this->nextSignedInt() & 0x01) === 0; } public function nextRange($start = 0, $end = PHP_INT_MAX){ diff --git a/src/world/Level.php b/src/world/Level.php index 84ff983be..05e9ce4c0 100644 --- a/src/world/Level.php +++ b/src/world/Level.php @@ -21,11 +21,12 @@ class Level{ public $entities, $tiles, $blockUpdates, $nextSave, $players = array(), $level; - private $time, $startCheck, $startTime, $server, $name, $usedChunks, $changedBlocks, $changedCount, $stopTime; + private $time, $startCheck, $startTime, $server, $name, $usedChunks, $changedBlocks, $changedCount, $stopTime, $generator; public function __construct(PMFLevel $level, Config $entities, Config $tiles, Config $blockUpdates, $name){ $this->server = ServerAPI::request(); $this->level = $level; + $level->level = $this; $this->level->level = $this; $this->entities = $entities; $this->tiles = $tiles; @@ -40,6 +41,17 @@ class Level{ $this->usedChunks = array(); $this->changedBlocks = array(); $this->changedCount = array(); + if(class_exists($this->level->levelData["generator"])){ + $gen = $this->level->levelData["generator"]; + $this->generator = new $gen((array) $this->level->levelData["generatorSettings"]); + }else{ + if(strtoupper($this->server->api->getProperty("level-type")) == "FLAT"){ + $this->generator = new SuperflatGenerator(); + }else{ + $this->generator = new NormalGenerator(); + } + } + $this->generator->init($this, new Random($this->level->levelData["seed"])); } public function close(){ @@ -93,9 +105,8 @@ class Level{ $now = microtime(true); $this->players = $this->server->api->player->getAll($this); - if(count($this->changedCount) > 0){ + if($this->level->isGenerating === 0 and count($this->changedCount) > 0){ arsort($this->changedCount); - $resendChunks = array(); foreach($this->changedCount as $index => $count){ if($count < 582){//Optimal value, calculated using the relation between minichunks and single packets break; @@ -136,6 +147,14 @@ class Level{ } } + public function generateChunk($X, $Z){ + $this->generator->generateChunk($X, $Z); + } + + public function populateChunk($X, $Z){ + $this->generator->populateChunk($X, $Z); + } + public function __destruct(){ if(isset($this->level)){ $this->save(false, false); diff --git a/src/world/LevelImport.php b/src/world/LevelImport.php index 39676d2ff..b2af2f7ab 100644 --- a/src/world/LevelImport.php +++ b/src/world/LevelImport.php @@ -64,9 +64,10 @@ class LevelImport{ "spawnX" => $level["SpawnX"], "spawnY" => $level["SpawnY"], "spawnZ" => $level["SpawnZ"], - "extra" => "", - "width" => 16, - "height" => 8 + "height" => 8, + "generator" => "NormalGenerator", + "generatorSettings" => "", + "extra" => "" )); $chunks = new PocketChunkParser(); $chunks->loadFile($this->path."chunks.dat"); diff --git a/src/world/generator/LevelGenerator.php b/src/world/generator/LevelGenerator.php index 5da986456..939d356f3 100644 --- a/src/world/generator/LevelGenerator.php +++ b/src/world/generator/LevelGenerator.php @@ -20,7 +20,8 @@ */ interface LevelGenerator{ - public function __construct(array $options = array()); + + public function __construct(array $settings = array()); public function init(Level $level, Random $random); @@ -28,7 +29,9 @@ interface LevelGenerator{ public function populateChunk($chunkX, $chunkZ); - public function populateLevel(); + public function getSettings(); + + //public function populateLevel(); public function getSpawn(); } \ No newline at end of file diff --git a/src/world/generator/NormalGenerator.php b/src/world/generator/NormalGenerator.php new file mode 100644 index 000000000..0f0cc025a --- /dev/null +++ b/src/world/generator/NormalGenerator.php @@ -0,0 +1,131 @@ +level = $level; + $this->random = $random; + $this->random->setSeed($this->level->getSeed()); + $this->noiseBase = new NoiseGeneratorSimplex($this->random, 4); + $this->noiseGen1 = new NoiseGeneratorPerlin($this->random, 4); + $this->noiseGen2 = new NoiseGeneratorPerlin($this->random, 4); + + + $ores = new OrePopulator(); + $ores->setOreTypes(array( + new OreType(new CoalOreBlock(), 20, 16, 0, 128), + new OreType(New IronOreBlock(), 20, 8, 0, 64), + new OreType(new RedstoneOreBlock(), 8, 7, 0, 16), + new OreType(new LapisOreBlock(), 1, 6, 0, 32), + new OreType(new GoldOreBlock(), 2, 8, 0, 32), + new OreType(new DiamondOreBlock(), 1, 7, 0, 16), + new OreType(new DirtBlock(), 20, 32, 0, 128), + new OreType(new GravelBlock(), 10, 16, 0, 128), + )); + $this->populators[] = $ores; + } + + public function generateChunk($chunkX, $chunkZ){ + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); + + for($chunkY = 0; $chunkY < 8; ++$chunkY){ + $chunk = ""; + $startY = $chunkY << 4; + $endY = $startY + 16; + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $noise1 = $this->noiseGen1->noise2D($x + ($chunkX << 4), $z + ($chunkZ << 4), 0.6, 32, true) * 2; + $noise2 = $this->noiseGen2->noise2D($x + ($chunkX << 4), $z + ($chunkZ << 4), 0.35, 64, true) * 15; + $noiseBase = $this->noiseBase->noise2D($x + ($chunkX << 4), $z + ($chunkZ << 4), 1/5, 16, true) * 3; + $height = (int) ($this->worldHeight + $noise1 + $noise2 + $noiseBase); + + for($y = $startY; $y < $endY; ++$y){ + $diff = $height - $y; + if($y <= 4 and ($y === 0 or $this->random->nextFloat() < 0.75)){ + $chunk .= "\x07"; //bedrock + }elseif($diff > 3){ + $chunk .= "\x01"; //stone + }elseif($diff > 0){ + $chunk .= "\x03"; //dirt + }elseif($y <= $this->waterHeight){ + if($y === $this->waterHeight and $diff === 0){ + $chunk .= "\x0c"; //sand + }else{ + $chunk .= "\x09"; //still_water + } + }elseif($diff === 0){ + $chunk .= "\x02"; //grass + }else{ + $chunk .= "\x00"; + } + } + $chunk .= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + } + } + $this->level->setMiniChunk($chunkX, $chunkZ, $chunkY, $chunk); + } + + } + + public function populateChunk($chunkX, $chunkZ){ + foreach($this->populators as $populator){ + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); + $populator->populate($this->level, $chunkX, $chunkZ, $this->random); + } + + $this->level->level->setPopulated($chunkX, $chunkZ); + } + + public function populateLevel(){ + + } + + public function getSpawn(){ + return $this->level->getSafeSpawn(new Vector3(127.5, 128, 127.5)); + } + +} \ No newline at end of file diff --git a/src/world/generator/SuperflatGenerator.php b/src/world/generator/SuperflatGenerator.php index b7235694f..95162d42b 100644 --- a/src/world/generator/SuperflatGenerator.php +++ b/src/world/generator/SuperflatGenerator.php @@ -26,6 +26,10 @@ require_once("LevelGenerator.php"); class SuperflatGenerator implements LevelGenerator{ private $level, $random, $structure, $chunks, $options, $floorLevel, $populators = array(); + public function getSettings(){ + return $this->options; + } + public function __construct(array $options = array()){ $this->preset = "2;7,59x1,3x3,2;1;spawn(radius=10 block=89),decoration(treecount=80 grasscount=45)"; $this->options = $options; @@ -127,9 +131,10 @@ class SuperflatGenerator implements LevelGenerator{ public function populateChunk($chunkX, $chunkZ){ foreach($this->populators as $populator){ - $this->random->setSeed((int) ($chunkX * 0xdead + $chunkZ * 0xbeef) ^ $this->level->getSeed()); + $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } + $this->level->level->setPopulated($chunkX, $chunkZ); } public function populateLevel(){ diff --git a/src/world/generator/TemporalGenerator.php b/src/world/generator/TemporalGenerator.php deleted file mode 100644 index d03fde3db..000000000 --- a/src/world/generator/TemporalGenerator.php +++ /dev/null @@ -1,95 +0,0 @@ -level = $level; - $this->random = $random; - $this->index = mt_rand(0, count(TemporalGenerator::$levels) - 1); - $this->world = new PocketChunkParser(); - $this->world->loadRaw(gzinflate(gzinflate(base64_decode(TemporalGenerator::$levels[$this->index][4]))), ""); - $this->world->loadMap(); - $this->level->setSeed(TemporalGenerator::$levels[$this->index][0]); - } - - public function generateChunk($chunkX, $chunkZ){ - - } - - public function populateChunk($chunkX, $chunkZ){ - - } - - public function populateLevel(){ - for($Z = 0; $Z < 16; ++$Z){ - for($X = 0; $X < 16; ++$X){ - $chunk = array( - 0 => "", - 1 => "", - 2 => "", - 3 => "", - 4 => "", - 5 => "", - 6 => "", - 7 => "" - ); - for($z = 0; $z < 16; ++$z){ - for($x = 0; $x < 16; ++$x){ - $block = $this->world->getChunkColumn($X, $Z, $x, $z, 0); - $meta = $this->world->getChunkColumn($X, $Z, $x, $z, 1); - for($Y = 0; $Y < 8; ++$Y){ - $chunk[$Y] .= substr($block, $Y << 4, 16); - $chunk[$Y] .= substr($meta, $Y << 3, 8); - $chunk[$Y] .= "\x00\x00\x00\x00\x00\x00\x00\x00"; - } - } - } - foreach($chunk as $Y => $data){ - $this->level->setMiniChunk($X, $Z, $Y, $data); - } - } - console("[NOTICE] Importing level ".ceil(($Z + 1)/0.16)."%"); - } - $this->level->save(true, true); - $this->world->map = null; - $this->world = null; - } - - public function getSpawn(){ - return new Vector3(TemporalGenerator::$levels[$this->index][1], TemporalGenerator::$levels[$this->index][2], TemporalGenerator::$levels[$this->index][3]); - } -} \ No newline at end of file diff --git a/src/world/generator/WorldGenerator.php b/src/world/generator/WorldGenerator.php index 4b79bf8fb..7dc4519e6 100644 --- a/src/world/generator/WorldGenerator.php +++ b/src/world/generator/WorldGenerator.php @@ -21,10 +21,9 @@ class WorldGenerator{ private $seed, $level, $path, $random, $generator, $width; - public function __construct(LevelGenerator $generator, $name, $seed = false, $width = 16, $height = 8){ + public function __construct(LevelGenerator $generator, $name, $seed = false, $height = 8){ $this->seed = $seed !== false ? (int) $seed:Utils::readInt(Utils::getRandomBytes(4, false)); $this->random = new Random($this->seed); - $this->width = (int) $width; $this->height = (int) $height; $this->path = DATA_PATH."worlds/".$name."/"; $this->generator = $generator; @@ -35,9 +34,10 @@ class WorldGenerator{ "spawnX" => 128, "spawnY" => 128, "spawnZ" => 128, - "extra" => "", - "width" => $this->width, - "height" => $this->height + "height" => $this->height, + "generator" => get_class($this->generator), + "generatorSettings" => $this->generator->getSettings(), + "extra" => "" )); $entities = new Config($this->path."entities.yml", CONFIG_YAML); $tiles = new Config($this->path."tiles.yml", CONFIG_YAML); @@ -45,25 +45,25 @@ class WorldGenerator{ $this->level = new Level($level, $entities, $tiles, $blockUpdates, $name); } - public function generate(){ + public function generate(){ + ++$this->level->level->isGenerating; $this->generator->init($this->level, $this->random); - for($Z = 0; $Z < $this->width; ++$Z){ - for($X = 0; $X < $this->width; ++$X){ + + for($Z = 7; $Z <= 9; ++$Z){ + for($X = 7; $X <= 9; ++$X){ $this->generator->generateChunk($X, $Z); } - console("[NOTICE] Generating level ".ceil((($Z + 1)/$this->width) * 100)."%"); } - console("[NOTICE] Populating level"); - $this->generator->populateLevel(); - for($Z = 0; $Z < $this->width; ++$Z){ - for($X = 0; $X < $this->width; ++$X){ + + for($Z = 7; $Z <= 9; ++$Z){ + for($X = 7; $X <= 9; ++$X){ $this->generator->populateChunk($X, $Z); } - console("[NOTICE] Populating level ".ceil((($Z + 1)/$this->width) * 100)."%"); } $this->level->setSpawn($this->generator->getSpawn()); $this->level->save(true, true); + --$this->level->level->isGenerating; } public function close(){ diff --git a/src/world/generator/noise/NoiseGenerator.php b/src/world/generator/noise/NoiseGenerator.php index b6d24472c..ca0b655a8 100644 --- a/src/world/generator/noise/NoiseGenerator.php +++ b/src/world/generator/noise/NoiseGenerator.php @@ -19,6 +19,78 @@ * */ + abstract class NoiseGenerator{ + protected $perm = array(); + protected $offsetX = 0; + protected $offsetY = 0; + protected $offsetZ = 0; + protected $octaves = 8; + public static function floor($x){ + return $x >= 0 ? (int) $x : (int) ($x - 1); + } + + public static function fade($x){ + return $x * $x * $x * ($x * ($x * 6 - 15) + 10); + } + + public static function lerp($x, $y, $z){ + return $y + $x * ($z - $y); + } + + public static function grad($hash, $x, $y, $z){ + $hash &= 15; + $u = $hash < 8 ? $x : $y; + $v = $hash < 4 ? $y : (($hash === 12 or $hash === 14) ? $x : $z); + return (($hash & 1) === 0 ? $u : -$u) + (($hash & 2) === 0 ? $v : -$v); + } + + abstract public function getNoise2D($x, $z); + + abstract public function getNoise3D($x, $y, $z); + + public function noise2D($x, $z, $frequency, $amplitude, $normalized = false){ + $result = 0; + $amp = 1; + $freq = 1; + $max = 0; + + for($i = 0; $i < $this->octaves; ++$i){ + $result += $this->getNoise2D($x * $freq, $z * $freq) * $amp; + $max += $amp; + $freq *= $frequency; + $amp *= $amplitude; + } + if($normalized === true){ + $result /= $max; + } + + return $result; + } + + public function noise3D($x, $y, $z, $frequency, $amplitude, $normalized = false){ + $result = 0; + $amp = 1; + $freq = 1; + $max = 0; + + for($i = 0; $i < $this->octaves; ++$i){ + $result += $this->getNoise3D($x * $freq, $y * $freq, $z * $freq) * $amp; + $max += $amp; + $freq *= $frequency; + $amp *= $amplitude; + } + if($normalized === true){ + $result /= $max; + } + + return $result; + } + + public function setOffset($x, $y, $z){ + $this->offsetX = $x; + $this->offsetY = $y; + $this->offsetZ = $z; + } } \ No newline at end of file diff --git a/src/world/generator/noise/NoiseGeneratorOctaves.php b/src/world/generator/noise/NoiseGeneratorOctaves.php deleted file mode 100644 index ac5661432..000000000 --- a/src/world/generator/noise/NoiseGeneratorOctaves.php +++ /dev/null @@ -1,68 +0,0 @@ -generatorCollection = array(); - $this->octaves = (int) $octaves; - for($o = 0; $o < $this->octaves; ++$o){ - $this->generatorCollection[$o] = new NoiseGeneratorPerlin($random); - } - } - - public function generateNoiseOctaves($int1, $int2, $int3, $int4, $int5, $int6, $par1 = false, $par2 = false, $par3 = false){ - if($par1 === false or $par2 === false or $par3 === false){ - return $this->generateNoiseOctaves($int1, 10, $int2, $int3, 1, $int4, $int5, 1, $int6); - } - - $floats = array(); - $cnt = $int4 * $int5 * $int6; - for($i = 0; $i < $cnt; ++$i){ - $floats[$i] = 0; - } - - $d1 = 1; - - for($j = 0; $j < $this->octaves; ++$j){ - $d2 = $int1 * $d1 * $par1; - $d3 = $int2 * $d1 * $par2; - $d4 = $int3 * $d1 * $par3; - $l1 = floor($d2); - $l2 = floor($d4); - $d2 -= $l1; - $d4 -= $l2; - $l1 %= 16777216; - $l2 %= 16777216; - - $d2 += $l1; - $d4 += $l2; - $this->generatorCollection[$j]->populateNoiseArray($floats, $d2, $d3, $d4, $int4, $int5, $int6, $par1 * $d1, $par2 * $d1, $par3 * $d1, $d1); - $d1 /= 2; - } - return $floats; - } -} \ No newline at end of file diff --git a/src/world/generator/noise/NoiseGeneratorPerlin.php b/src/world/generator/noise/NoiseGeneratorPerlin.php index 079bda1f1..8fcf6a34c 100644 --- a/src/world/generator/noise/NoiseGeneratorPerlin.php +++ b/src/world/generator/noise/NoiseGeneratorPerlin.php @@ -24,146 +24,79 @@ require_once("NoiseGenerator.php"); /***REM_END***/ class NoiseGeneratorPerlin extends NoiseGenerator{ - private $permutations = array(); - public $xCoord, $yCoord, $zCoord; + public static $grad3 = [ + [1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0], + [1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1], + [0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1] + ]; - public function __construct($random = false){ - if(!($random instanceof Random)){ - $random = new Random(); - } - $this->xCoord = $random->nextFloat() * 256; - $this->yCoord = $random->nextFloat() * 256; - $this->zCoord = $random->nextFloat() * 256; + + public function __construct(Random $random, $octaves){ + $this->octaves = $octaves; + $this->offsetX = $random->nextFloat() * 256; + $this->offsetY = $random->nextFloat() * 256; + $this->offsetZ = $random->nextFloat() * 256; for($i = 0; $i < 512; ++$i){ - $this->permutations[$i] = 0; - } - for($i = 0; $i < 256; ++$i){ - $this->permutations[$i] = $i; + $this->perm[$i] = 0; } for($i = 0; $i < 256; ++$i){ - $j = $random->nextRange(0, 256 - $i) + $i; - $k = $this->permutations[$i]; - $this->permutations[$i] = $this->permutations[$j]; - $this->permutations[$j] = $k; - $this->permutations[$i + 256] = $this->permutations[$i]; - } - - } - - public final function curve($par1, $par2, $par3){ - return $par2 + $par1 * ($par3 - $par2); - } - - public function grad2D($int, $par1, $par2){ - $i = $int & 0x0F; - $d1 = (1 - (($i & 0x08) >> 3)) * $par1; - $d2 = ($i === 12 or $i === 14) ? $par1:($i < 4 ? 0:$par2); - - return (($i & 0x01) === 0 ? $d1:-$d1) + (($i & 0x02) === 0 ? $d2:-$d2); - } - - public function grad3D($int, $par1, $par2, $par3){ - $i = $int & 0x0F; - $d1 = $i < 8 ? $par1 : $par2; - $d2 = ($i === 12 or $i === 14) ? $par1:($i < 4 ? $par2:$par3); - - return (($i & 0x01) === 0 ? $d1:-$d1) + (($i & 0x02) === 0 ? $d2:-$d2); - } - - public function populateNoiseArray(&$floats, $par1, $par2, $par3, $int1, $int2, $int3, $par4, $par5, $par6, $par7){ - if($int2 === 1){ - $n = 0; - $d3 = 1 / $par7; - for($i1 = 0; $i1 < $int1; ++$i1){ - $d4 = $par1 + $i1 * $par4 + $this->xCoord; - $i2 = (int) $d4; - if($d4 < $i2){ - --$i2; - } - $i3 = $i2 & 0xFF; - $d4 -= $i2; - $d5 = $d4 * $d4 * $d4 * ($d4 * ($d4 * 6 - 15) + 10); - - for($i4 = 0; $i4 < $int3; ++$i4){ - $d6 = $par3 + $i4 * $par6 + $this->zCoord; - $i5 = (int) $d6; - if($d6 < $i5){ - --$i5; - } - $i6 = $i5 & 0xFF; - $d6 -= $i5; - $d7 = $d6 * $d6 * $d6 * ($d6 * ($d6 * 6 - 15) + 10); - - $i = $this->permutations[$i3]; - $j = $this->permutations[$i] + $i6; - $k = $this->permutations[$i3 + 1]; - $m = $this->permutations[$k] + $i6; - $d1 = $this->curve($d5, $this->grad2D($this->permutations[$j], $d4, $d6), $this->grad3D($this->permutations[$m], $d4 - 1, 0, $d6)); - $d2 = $this->curve($d5, $this->grad3D($this->permutations[$j + 1], $d4, 0, $d6 - 1), $this->grad3D($this->permutations[$m + 1], $d4 - 1, 0, $d6 - 1)); - - $d8 = $this->curve($d7, $d1, $d2); - $floats[$n++] += $d8 * $d3; - } - } - return; + $this->perm[$i] = $random->nextRange(0, 255); } - $d9 = 1 / $par7; - $m = -1; - $n = 0; - $i = 0; - - for($i4 = 0; $i4 < $int1; ++$i4){ - $d6 = $par1 + $i4 * $par4 + $this->xCoord; - $i5 = (int) $d6; - if($d6 < $i5){ - --$i5; - } - $i6 = $i5 & 0xFF; - $d6 -= $i5; - $d7 = $d6 * $d6 * $d6 * ($d6 * ($d6 * 6 - 15) + 10); + for($i = 0; $i < 256; ++$i){ + $pos = $random->nextRange(0, 255 - $i) + $i; + $old = $this->perm[$i]; - for($i12 = 0; $i12 < $int3; ++$i12){ - $d12 = $par3 + $i12 * $par6 + $this->zCoord; - $i13 = (int) $d12; - if($d12 < $i13){ - --$i13; - } - $i14 = $i13 & 0xFF; - $d12 -= $i13; - $d13 = $d12 * $d12 * $d12 * ($d12 * ($d12 * 6 - 15) + 10); - - for($i15 = 0; $i15 < $int2; ++$i15){ - $d14 = $par2 + $i15 * $par5 + $this->yCoord; - $i16 = (int) $d14; - if($d14 < $i16){ - --$i16; - } - $d14 -= $i16; - $d15 = $d14 * $d14 * $d14 * ($d14 * ($d14 * 6 - 15) + 10); - - if($i15 === 0 or $i17 !== $m){ - $m = $i17; - $i7 = $this->permutations[$i6] + $i17; - $i8 = $this->permutations[$i7] + $i14; - $i9 = $this->permutations[$i7 + 1] + $i14; - $i10 = $this->permutations[$i6 + 1] + $i17; - $n = $this->permutations[$i10] + $i14; - $i11 = $this->permutations[$i10 + 1] + $i14; - $d10 = $this->curve($d7, $this->grad3D($this->permutations[$i8], $d6, $d14, $d12), $this->grad3D($this->permutations[$n], $d6 - 1, $d14, $d12)); - $d4 = $this->curve($d7, $this->grad3D($this->permutations[$i9], $d6, $d14 - 1, $d12), $this->grad3D($this->permutations[$i11], $d6 - 1, $d14 - 1, $d12)); - $d11 = $this->curve($d7, $this->grad3D($this->permutations[$i8 + 1], $d6, $d14, $d12 - 1), $this->grad3D($this->permutations[$n + 1], $d6 - 1, $d14, $d12 - 1)); - $d5 = $this->curve($d7, $this->grad3D($this->permutations[$i9 + 1], $d6, $d14 - 1, $d12 - 1), $this->grad3D($this->permutations[$i11 + 1], $d6 - 1, $d14 - 1, $d12 - 1)); - } - - $d16 = $this->curve($d15, $d10, $d4); - $d17 = $this->curve($d15, $d11, $d5); - $d18 = $this->curve($d13, $d16, $d17); - $floats[$i++] += $d18 * $d9; - } - } + $this->perm[$i] = $this->perm[$pos]; + $this->perm[$pos] = $old; + $this->perm[$i + 256] = $this->perm[$i]; } + + } + + public function getNoise3D($x, $y, $z){ + $x += $this->offsetX; + $y += $this->offsetY; + $z += $this->offsetZ; + + $floorX = self::floor($x); + $floorY = self::floor($y); + $floorZ = self::floor($z); + + $X = $floorX & 0xFF; + $Y = $floorY & 0xFF; + $Z = $floorZ & 0xFF; + + $x -= $floorX; + $y -= $floorY; + $z -= $floorZ; + + //Fade curves + $fX = self::fade($x); + $fY = self::fade($y); + $fZ = self::fade($z); + + //Cube corners + $A = $this->perm[$X] + $Y; + $AA = $this->perm[$A] + $Z; + $AB = $this->perm[$A + 1] + $Z; + $B = $this->perm[$X + 1] + $Y; + $BA = $this->perm[$B] + $Z; + $BB = $this->perm[$B + 1] + $Z; + + return self::lerp($fZ, self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA], $x, $y, $z), + self::grad($this->perm[$BA], $x - 1, $y, $z)), + self::lerp($fX, self::grad($this->perm[$AB], $x, $y - 1, $z), + self::grad($this->perm[$BB], $x - 1, $y - 1, $z))), + self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA + 1], $x, $y, $z - 1), + self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1)), + self::lerp($fX, self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1), + self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1)))); + } + + public function getNoise2D($x, $y){ + return $this->getNoise3D($x, $y, 0); } } \ No newline at end of file diff --git a/src/world/generator/noise/NoiseGeneratorSimplex.php b/src/world/generator/noise/NoiseGeneratorSimplex.php new file mode 100644 index 000000000..2989ba718 --- /dev/null +++ b/src/world/generator/noise/NoiseGeneratorSimplex.php @@ -0,0 +1,438 @@ + + * This is a modified version of the freely published version in the paper by + * Stefan Gustavson at + * + * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + */ +class NoiseGeneratorSimplex extends NoiseGeneratorPerlin{ + protected static $SQRT_3; + protected static $SQRT_5; + protected static $F2; + protected static $G2; + protected static $G22; + protected static $F3; + protected static $G3; + protected static $F4; + protected static $G4; + protected static $G42; + protected static $G43; + protected static $G44; + protected static $grad4 = [[0, 1, 1, 1],[0, 1, 1, -1],[0, 1, -1, 1],[0, 1, -1, -1], + [0, -1, 1, 1],[0, -1, 1, -1],[0, -1, -1, 1],[0, -1, -1, -1], + [1, 0, 1, 1],[1, 0, 1, -1],[1, 0, -1, 1],[1, 0, -1, -1], + [-1, 0, 1, 1],[-1, 0, 1, -1],[-1, 0, -1, 1],[-1, 0, -1, -1], + [1, 1, 0, 1],[1, 1, 0, -1],[1, -1, 0, 1],[1, -1, 0, -1], + [-1, 1, 0, 1],[-1, 1, 0, -1],[-1, -1, 0, 1],[-1, -1, 0, -1], + [1, 1, 1, 0],[1, 1, -1, 0],[1, -1, 1, 0],[1, -1, -1, 0], + [-1, 1, 1, 0],[-1, 1, -1, 0],[-1, -1, 1, 0],[-1, -1, -1, 0]]; + protected static $simplex = [ + [0, 1, 2, 3],[0, 1, 3, 2],[0, 0, 0, 0],[0, 2, 3, 1],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[1, 2, 3, 0], + [0, 2, 1, 3],[0, 0, 0, 0],[0, 3, 1, 2],[0, 3, 2, 1],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[1, 3, 2, 0], + [0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0], + [1, 2, 0, 3],[0, 0, 0, 0],[1, 3, 0, 2],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[2, 3, 0, 1],[2, 3, 1, 0], + [1, 0, 2, 3],[1, 0, 3, 2],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[2, 0, 3, 1],[0, 0, 0, 0],[2, 1, 3, 0], + [0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0], + [2, 0, 1, 3],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[3, 0, 1, 2],[3, 0, 2, 1],[0, 0, 0, 0],[3, 1, 2, 0], + [2, 1, 0, 3],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[3, 1, 0, 2],[0, 0, 0, 0],[3, 2, 0, 1],[3, 2, 1, 0]]; + protected $offsetW; + + public function __construct(Random $random, $octaves){ + parent::__construct($random, $octaves); + $this->offsetW = $random->nextFloat() * 256; + self::$SQRT_3 = sqrt(3); + self::$SQRT_5 = sqrt(5); + self::$F2 = 0.5 * (self::$SQRT_3 - 1); + self::$G2 = (3 - self::$SQRT_3) / 6; + self::$G22 = self::$G2 * 2.0 - 1; + self::$F3 = 1.0 / 3.0; + self::$G3 = 1.0 / 6.0; + self::$F4 = (self::$SQRT_5 - 1.0) / 4.0; + self::$G4 = (5.0 - self::$SQRT_5) / 20.0; + self::$G42 = self::$G4 * 2.0; + self::$G43 = self::$G4 * 3.0; + self::$G44 = self::$G4 * 4.0 - 1.0; + } + + protected static function dot2D($g, $x, $y){ + return $g[0] * $x + $g[1] * $y; + } + + protected static function dot3D($g, $x, $y, $z){ + return $g[0] * $x + $g[1] * $y + $g[2] * $z; + } + + protected static function dot4D($g, $x, $y, $z, $w){ + return $g[0] * $x + $g[1] * $y + $g[2] * $z + $g[3] * $w; + } + + public function getNoise3D($x, $y, $z){ + $x += $this->offsetX; + $y += $this->offsetY; + $z += $this->offsetZ; + + // Skew the input space to determine which simplex cell we're in + $s = ($x + $y + $z) * self::$F3; // Very nice and simple skew factor for 3D + $i = self::floor($x + $s); + $j = self::floor($y + $s); + $k = self::floor($z + $s); + $t = ($i + $j + $k) * self::$G3; + $X0 = $i - $t; // Unskew the cell origin back to (x,y,z) space + $Y0 = $j - $t; + $Z0 = $k - $t; + $x0 = $x - $X0; // The x,y,z distances from the cell origin + $y0 = $y - $Y0; + $z0 = $z - $Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + + // Determine which simplex we are in. + if($x0 >= $y0){ + if($y0 >= $z0){ + $i1 = 1; + $j1 = 0; + $k1 = 0; + $i2 = 1; + $j2 = 1; + $k2 = 0; + }// X Y Z order + elseif($x0 >= $z0){ + $i1 = 1; + $j1 = 0; + $k1 = 0; + $i2 = 1; + $j2 = 0; + $k2 = 1; + }// X Z Y order + else{ + $i1 = 0; + $j1 = 0; + $k1 = 1; + $i2 = 1; + $j2 = 0; + $k2 = 1; + }// Z X Y order + }else{ // x0perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12; + $gi1 = $this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12; + $gi2 = $this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12; + $gi3 = $this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12; + + // Calculate the contribution from the four corners + $t0 = 0.6 - $x0 * $x0 - $y0 * $y0 - $z0 * $z0; + if($t0 < 0){ + $n0 = 0.0; + }else{ + $t0 *= $t0; + $n0 = $t0 * $t0 * self::dot3D(self::$grad3[$gi0], $x0, $y0, $z0); + } + + $t1 = 0.6 - $x1 * $x1 - $y1 * $y1 - $z1 * $z1; + if($t1 < 0){ + $n1 = 0.0; + }else{ + $t1 *= $t1; + $n1 = $t1 * $t1 * self::dot3D(self::$grad3[$gi1], $x1, $y1, $z1); + } + + $t2 = 0.6 - $x2 * $x2 - $y2 * $y2 - $z2 * $z2; + if($t2 < 0){ + $n2 = 0.0; + }else{ + $t2 *= $t2; + $n2 = $t2 * $t2 * self::dot3D(self::$grad3[$gi2], $x2, $y2, $z2); + } + + $t3 = 0.6 - $x3 * $x3 - $y3 * $y3 - $z3 * $z3; + if($t3 < 0){ + $n3 = 0.0; + }else{ + $t3 *= $t3; + $n3 = $t3 * $t3 * self::dot3D(self::$grad3[$gi3], $x3, $y3, $z3); + } + + // Add contributions from each corner to get the noise value. + // The result is scaled to stay just inside [-1,1] + return 32.0 * ($n0 + $n1 + $n2 + $n3); + } + + public function getNoise2D($x, $y){ + $x += $this->offsetX; + $y += $this->offsetY; + + // Skew the input space to determine which simplex cell we're in + $s = ($x + $y) * self::$F2; // Hairy factor for 2D + $i = self::floor($x + $s); + $j = self::floor($y + $s); + $t = ($i + $j) * self::$G2; + $X0 = $i - $t; // Unskew the cell origin back to (x,y) space + $Y0 = $j - $t; + $x0 = $x - $X0; // The x,y distances from the cell origin + $y0 = $y - $Y0; + + // For the 2D case, the simplex shape is an equilateral triangle. + + // Determine which simplex we are in. + if($x0 > $y0){ + $i1 = 1; + $j1 = 0; + }// lower triangle, XY order: (0,0)->(1,0)->(1,1) + else{ + $i1 = 0; + $j1 = 1; + }// upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + $x1 = $x0 - $i1 + self::$G2; // Offsets for middle corner in (x,y) unskewed coords + $y1 = $y0 - $j1 + self::$G2; + $x2 = $x0 + self::$G22; // Offsets for last corner in (x,y) unskewed coords + $y2 = $y0 + self::$G22; + + // Work out the hashed gradient indices of the three simplex corners + $ii = $i & 255; + $jj = $j & 255; + $gi0 = $this->perm[$ii + $this->perm[$jj]] % 12; + $gi1 = $this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12; + $gi2 = $this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12; + + // Calculate the contribution from the three corners + $t0 = 0.5 - $x0 * $x0 - $y0 * $y0; + if($t0 < 0){ + $n0 = 0.0; + }else{ + $t0 *= $t0; + $n0 = $t0 * $t0 * self::dot2D(self::$grad3[$gi0], $x0, $y0); // (x,y) of grad3 used for 2D gradient + } + + $t1 = 0.5 - $x1 * $x1 - $y1 * $y1; + if($t1 < 0){ + $n1 = 0.0; + }else{ + $t1 *= $t1; + $n1 = $t1 * $t1 * self::dot2D(self::$grad3[$gi1], $x1, $y1); + } + + $t2 = 0.5 - $x2 * $x2 - $y2 * $y2; + if($t2 < 0){ + $n2 = 0.0; + }else{ + $t2 *= $t2; + $n2 = $t2 * $t2 * self::dot2D(self::$grad3[$gi2], $x2, $y2); + } + + // Add contributions from each corner to get the noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * ($n0 + $n1 + $n2); + } + + /** + * Computes and returns the 4D simplex noise for the given coordinates in + * 4D space + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param w W coordinate + * @return Noise at given location, from range -1 to 1 + */ + /*public function getNoise4D(x, y, z, w){ + x += offsetX; + y += offsetY; + z += offsetZ; + w += offsetW; + + n0, n1, n2, n3, n4; // Noise contributions from the five corners + + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in + s = (x + y + z + w) * self::$F4; // Factor for 4D skewing + i = floor(x + s); + j = floor(y + s); + k = floor(z + s); + l = floor(w + s); + + t = (i + j + k + l) * self::$G4; // Factor for 4D unskewing + X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space + Y0 = j - t; + Z0 = k - t; + W0 = l - t; + x0 = x - X0; // The x,y,z,w distances from the cell origin + y0 = y - Y0; + z0 = z - Z0; + w0 = w - W0; + + // For the 4D case, the simplex is a 4D shape I won't even try to describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // The method below is a good way of finding the ordering of x,y,z,w and + // then find the correct traversal order for the simplex we’re in. + // First, six pair-wise comparisons are performed between each possible pair + // of the four coordinates, and the results are used to add up binary bits + // for an integer index. + c1 = (x0 > y0) ? 32 : 0; + c2 = (x0 > z0) ? 16 : 0; + c3 = (y0 > z0) ? 8 : 0; + c4 = (x0 > w0) ? 4 : 0; + c5 = (y0 > w0) ? 2 : 0; + c6 = (z0 > w0) ? 1 : 0; + c = c1 + c2 + c3 + c4 + c5 + c6; + i1, j1, k1, l1; // The integer offsets for the second simplex corner + i2, j2, k2, l2; // The integer offsets for the third simplex corner + i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; + j1 = simplex[c][1] >= 3 ? 1 : 0; + k1 = simplex[c][2] >= 3 ? 1 : 0; + l1 = simplex[c][3] >= 3 ? 1 : 0; + + // The number 2 in the "simplex" array is at the second largest coordinate. + i2 = simplex[c][0] >= 2 ? 1 : 0; + j2 = simplex[c][1] >= 2 ? 1 : 0; + k2 = simplex[c][2] >= 2 ? 1 : 0; + l2 = simplex[c][3] >= 2 ? 1 : 0; + + // The number 1 in the "simplex" array is at the second smallest coordinate. + i3 = simplex[c][0] >= 1 ? 1 : 0; + j3 = simplex[c][1] >= 1 ? 1 : 0; + k3 = simplex[c][2] >= 1 ? 1 : 0; + l3 = simplex[c][3] >= 1 ? 1 : 0; + + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + + x1 = x0 - i1 + self::$G4; // Offsets for second corner in (x,y,z,w) coords + y1 = y0 - j1 + self::$G4; + z1 = z0 - k1 + self::$G4; + w1 = w0 - l1 + self::$G4; + + x2 = x0 - i2 + self::$G42; // Offsets for third corner in (x,y,z,w) coords + y2 = y0 - j2 + self::$G42; + z2 = z0 - k2 + self::$G42; + w2 = w0 - l2 + self::$G42; + + x3 = x0 - i3 + self::$G43; // Offsets for fourth corner in (x,y,z,w) coords + y3 = y0 - j3 + self::$G43; + z3 = z0 - k3 + self::$G43; + w3 = w0 - l3 + self::$G43; + + x4 = x0 + self::$G44; // Offsets for last corner in (x,y,z,w) coords + y4 = y0 + self::$G44; + z4 = z0 + self::$G44; + w4 = w0 + self::$G44; + + // Work out the hashed gradient indices of the five simplex corners + ii = i & 255; + jj = j & 255; + kk = k & 255; + ll = l & 255; + + gi0 = $this->perm[ii + $this->perm[jj + $this->perm[kk + $this->perm[ll]]]] % 32; + gi1 = $this->perm[ii + i1 + $this->perm[jj + j1 + $this->perm[kk + k1 + $this->perm[ll + l1]]]] % 32; + gi2 = $this->perm[ii + i2 + $this->perm[jj + j2 + $this->perm[kk + k2 + $this->perm[ll + l2]]]] % 32; + gi3 = $this->perm[ii + i3 + $this->perm[jj + j3 + $this->perm[kk + k3 + $this->perm[ll + l3]]]] % 32; + gi4 = $this->perm[ii + 1 + $this->perm[jj + 1 + $this->perm[kk + 1 + $this->perm[ll + 1]]]] % 32; + + // Calculate the contribution from the five corners + t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; + if(t0 < 0){ + n0 = 0.0; + }else{ + t0 *= t0; + n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); + } + + t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; + if(t1 < 0){ + n1 = 0.0; + }else{ + t1 *= t1; + n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); + } + + t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; + if(t2 < 0){ + n2 = 0.0; + }else{ + t2 *= t2; + n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); + } + + t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; + if(t3 < 0){ + n3 = 0.0; + }else{ + t3 *= t3; + n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); + } + + t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; + if(t4 < 0){ + n4 = 0.0; + }else{ + t4 *= t4; + n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); + } + + // Sum up and scale the result to cover the range [-1,1] + return 27.0 * (n0 + n1 + n2 + n3 + n4); + }*/ +} \ No newline at end of file diff --git a/src/world/generator/noise/OctaveGenerator.php b/src/world/generator/noise/OctaveGenerator.php new file mode 100644 index 000000000..962801bec --- /dev/null +++ b/src/world/generator/noise/OctaveGenerator.php @@ -0,0 +1,108 @@ +octaves = $octaves; + } + + public function setScale($scale){ + $this->setXScale($scale); + $this->setYScale($scale); + $this->setZScale($scale); + } + + public function getXScale(){ + return $this->xScale; + } + + public function setXScale($scale){ + $this->xScale = $scale; + } + + public function getYScale(){ + return $this->yScale; + } + + public function setYScale($scale){ + $this->yScale = $scale; + } + + public function getZScale(){ + return $this->zScale; + } + + public function setZScale($scale){ + $this->zScale = $scale; + } + + public function getOctaves(){ + $array = array(); + foreach($this->octaves as $index => $value){ + $array[$index] = clone $value; + } + return $array; + } + + //1D-noise + public function noise1D($x, $frequency, $amplitude, $normalized = false){ + return $this->noise3D($x, 0, 0, $frequency, $amplitude, $normalized); + } + + //2D-noise + public function noise2D($x, $y, $frequency, $amplitude, $normalized = false){ + return $this->noise3D($x, $y, 0, $frequency, $amplitude, $normalized); + } + + //3D-noise + public function noise3D($x, $y, $z, $frequency, $amplitude, $normalized = false){ + $result = 0; + $amp = 1; + $freq = 1; + $max = 0; + + $x *= $this->xScale; + $y *= $this->yScale; + $z *= $this->zScale; + + foreach($this->octaves as $noiseGenerator){ + $result += $octave->noise($x * $freq, $y * $freq, $z * $freq) * $amp; + $max += $amp; + $freq *= $frequency; + $amp *= $amplitude; + } + if($normalized === true){ + $result /= $max; + } + + return $result; + } + + /*public function generateNoiseOctaves($x, $y, $z, $frequency, $amplitude){ + + }*/ +} \ No newline at end of file diff --git a/src/world/generator/noise/PerlinOctaveGenerator.php b/src/world/generator/noise/PerlinOctaveGenerator.php new file mode 100644 index 000000000..53a79884c --- /dev/null +++ b/src/world/generator/noise/PerlinOctaveGenerator.php @@ -0,0 +1,63 @@ +octaves = array(); + for($o = 0; $o < $octaves; ++$o){ + $this->octaves[$o] = new NoiseGeneratorPerlin($random); + } + } + + /*public function generateNoiseOctaves($x, $y, $z, $sizeX, $sizeY, $sizeZ, $fX, $fY, $fZ){ + $adouble = array_fill(0, $sizeX * $sizeY * $sizeZ, 0.0); + + $d3 = 1.0; + + foreach($this->octaves as $octave){ + $dX = $x * $d3 * $fX; + $dY = $y * $d3 * $fY; + $dZ = $x * $d3 * $fZ; + + $x1 = NoiseGenerator::floor($dX); + $z1 = NoiseGenerator::floor($dZ); + + $dX -= $x1; + $dZ -= $z1; + + $x1 %= 16777216; + $z1 %= 16777216; + //$x1 &= 0xFFFFFF; + //$z1 &= 0xFFFFFF; + + $dX += $x1; + $dZ += $z1; + $octave->populateNoiseArray($adouble, $dX, $dY, $dZ, $sizeX, $sizeY, $sizeZ, $fX * $d3, $fY * $d3, $fZ * $d3, $d3); + $d3 *= 0.5; + } + + return $adouble; + }*/ +} \ No newline at end of file