Merge remote-tracking branch 'origin/master' into Entities

This commit is contained in:
Shoghi Cervantes 2014-02-13 09:30:21 +01:00
commit acece47a37
27 changed files with 1348 additions and 540 deletions

View File

@ -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);

View File

@ -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(){

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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!

View File

@ -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;
}

View File

@ -20,7 +20,7 @@
*/
class ProtocolInfo{
abstract class ProtocolInfo{
const CURRENT_PROTOCOL = 14;
@ -96,63 +96,6 @@ class ProtocolInfo{
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",
);
}
/***REM_START***/

View File

@ -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();
}

View File

@ -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;

View File

@ -186,11 +186,170 @@ class RakNetParser{
if(strlen($buffer) < ($length - 1)){
return false;
}
if(isset(ProtocolInfo::$packets[$pid])){
$data = new ProtocolInfo::$packets[$pid];
}else{
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;

View File

@ -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));
if($this->levelData["version"] === 0){
$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){
}else{
$this->levelData["height"] = ord($this->read(1));
if($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
$this->levelData["generator"] = $this->read(Utils::readShort($this->read(2), false));
$this->levelData["generatorSettings"] = unserialize($this->read(Utils::readShort($this->read(2), false)));
}
fseek($this->fp, $this->payloadOffset);
}else{
$this->payloadOffset = ftell($this->fp);
$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])){
}
$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;
}
$info = $this->locationTable[$index];
$this->seek($info[0]);
}
$chunk = @gzopen($this->getChunkPath($X, $Z), "rb");
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);
}
}

View File

@ -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){

View File

@ -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);

View File

@ -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");

View File

@ -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();
}

View File

@ -0,0 +1,131 @@
<?php
/**
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
/***REM_START***/
require_once("LevelGenerator.php");
/***REM_END***/
class NormalGenerator implements LevelGenerator{
private $populators = array();
private $level;
private $random;
private $worldHeight = 65;
private $waterHeight = 63;
private $noiseBase;
private $noiseGen1;
private $noiseGen2;
private $noiseGen3;
private $noiseGen4;
private $noiseGen5;
private $noiseGen6;
public function __construct(array $options = array()){
}
public function getSettings(){
return array();
}
public function init(Level $level, Random $random){
$this->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));
}
}

View File

@ -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(){

File diff suppressed because one or more lines are too long

View File

@ -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);
@ -46,24 +46,24 @@ class WorldGenerator{
}
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(){

View File

@ -19,6 +19,78 @@
*
*/
abstract class NoiseGenerator{
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;
}
}

View File

@ -1,68 +0,0 @@
<?php
/**
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
/***REM_START***/
require_once("NoiseGenerator.php");
/***REM_END***/
class NoiseGeneratorOctaves extends NoiseGenerator{
public $octaves;
private $generatorCollection;
public function __construct(Random $random, $octaves){
$this->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;
}
}

View File

@ -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];
$this->perm[$i] = $random->nextRange(0, 255);
}
for($i = 0; $i < 256; ++$i){
$pos = $random->nextRange(0, 255 - $i) + $i;
$old = $this->perm[$i];
$this->perm[$i] = $this->perm[$pos];
$this->perm[$pos] = $old;
$this->perm[$i + 256] = $this->perm[$i];
}
}
public final function curve($par1, $par2, $par3){
return $par2 + $par1 * ($par3 - $par2);
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 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;
}
$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($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;
}
}
}
public function getNoise2D($x, $y){
return $this->getNoise3D($x, $y, 0);
}
}

View File

@ -0,0 +1,438 @@
<?php
/***REM_START***/
require_once("NoiseGenerator.php");
require_once("NoiseGeneratorPerlin.php");
/***REM_END***/
/**
* Generates simplex-based noise.
* <p>
* This is a modified version of the freely published version in the paper by
* Stefan Gustavson at
* <a href="http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf">
* http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf</a>
*/
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{ // x0<y0
if($y0 < $z0){
$i1 = 0;
$j1 = 0;
$k1 = 1;
$i2 = 0;
$j2 = 1;
$k2 = 1;
}// Z Y X order
elseif($x0 < $z0){
$i1 = 0;
$j1 = 1;
$k1 = 0;
$i2 = 0;
$j2 = 1;
$k2 = 1;
}// Y Z X order
else{
$i1 = 0;
$j1 = 1;
$k1 = 0;
$i2 = 1;
$j2 = 1;
$k2 = 0;
}// Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
$x1 = $x0 - $i1 + self::$G3; // Offsets for second corner in (x,y,z) coords
$y1 = $y0 - $j1 + self::$G3;
$z1 = $z0 - $k1 + self::$G3;
$x2 = $x0 - $i2 + 2.0 * self::$G3; // Offsets for third corner in (x,y,z) coords
$y2 = $y0 - $j2 + 2.0 * self::$G3;
$z2 = $z0 - $k2 + 2.0 * self::$G3;
$x3 = $x0 - 1.0 + 3.0 * self::$G3; // Offsets for last corner in (x,y,z) coords
$y3 = $y0 - 1.0 + 3.0 * self::$G3;
$z3 = $z0 - 1.0 + 3.0 * self::$G3;
// Work out the hashed gradient indices of the four simplex corners
$ii = $i & 255;
$jj = $j & 255;
$kk = $k & 255;
$gi0 = $this->perm[$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 were 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<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// The number 3 in the "simplex" array is at the position of the largest coordinate.
i1 = simplex[c][0] >= 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);
}*/
}

View File

@ -0,0 +1,108 @@
<?php
/**
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
abstract class OctaveGenerator{
protected $octaves;
protected $xScale = 1;
protected $yScale = 1;
protected $zScale = 1;
public function __construct(array $octaves){
$this->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){
}*/
}

View File

@ -0,0 +1,63 @@
<?php
/**
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
/***REM_START***/
require_once("OctaveGenerator.php");
/***REM_END***/
class PerlinOctaveGenerator extends OctaveGenerator{
public function __construct(Random $random, $octaves){
$this->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;
}*/
}