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