mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 00:07:30 +00:00
Added base McRegion classes
This commit is contained in:
parent
b2978133f7
commit
e9311f5ceb
@ -73,7 +73,7 @@ class Chunk extends BaseChunk{
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
}
|
||||
|
||||
$sections = [];
|
||||
|
@ -145,9 +145,8 @@ class RegionLoader{
|
||||
$nbt->V = new Byte("V", self::VERSION);
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
//TODO: check type and name
|
||||
//$nbt->GrassMap = new IntArray("GrassMap", array_fill(0, 256, 127));
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
|
163
src/pocketmine/level/format/mcregion/Chunk.php
Normal file
163
src/pocketmine/level/format/mcregion/Chunk.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\generic\BaseChunk;
|
||||
use pocketmine\level\format\generic\EmptyChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class Chunk extends BaseChunk{
|
||||
|
||||
/** @var Compound */
|
||||
protected $nbt;
|
||||
|
||||
public function __construct(LevelProvider $level, Compound $nbt){
|
||||
$this->nbt = $nbt;
|
||||
|
||||
if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof Enum){
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->Entities = new Enum("Entities", []);
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->TileEntities) and $this->nbt->TileEntities instanceof Enum){
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->TileEntities = new Enum("TileEntities", []);
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->TileTicks) and $this->nbt->TileTicks instanceof Enum){
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->Sections) and $this->nbt->Sections instanceof Enum){
|
||||
$this->nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->Sections = new Enum("Sections", []);
|
||||
$this->nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Biomes) or !($this->nbt->Biomes instanceof ByteArray)){
|
||||
$this->nbt->Biomes = new ByteArray("Biomes", str_repeat("\x01", 256));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
}
|
||||
|
||||
/** @var ChunkSection[] $sections */
|
||||
$sections = [];
|
||||
|
||||
$blockLight = $skyLight = $datas = $blocks = [$fill = array_fill(0, 256, ""), $fill, $fill, $fill, $fill, $fill, $fill, $fill];
|
||||
|
||||
$offset = 0;
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
list($blocks[0][$i], $blocks[1][$i], $blocks[2][$i], $blocks[3][$i], $blocks[4][$i], $blocks[5][$i], $blocks[6][$i], $blocks[7][$i]) = str_split(substr($this->nbt->Blocks, $offset << 1, 128), 16);
|
||||
list($datas[0][$i], $datas[1][$i], $datas[2][$i], $datas[3][$i], $datas[4][$i], $datas[5][$i], $datas[6][$i], $datas[7][$i]) = str_split(substr($this->nbt->Data, $offset, 64), 8);
|
||||
list($skyLight[0][$i], $skyLight[1][$i], $skyLight[2][$i], $skyLight[3][$i], $skyLight[4][$i], $skyLight[5][$i], $skyLight[6][$i], $skyLight[7][$i]) = str_split(substr($this->nbt->SkyLight, $offset, 64), 8);
|
||||
list($blockLight[0][$i], $blockLight[1][$i], $blockLight[2][$i], $blockLight[3][$i], $blockLight[4][$i], $blockLight[5][$i], $blockLight[6][$i], $blockLight[7][$i]) = str_split(substr($this->nbt->BlockLight, $offset, 64), 8);
|
||||
$offset += 64;
|
||||
}
|
||||
|
||||
for($Y = 0; $Y < 8; ++$Y){
|
||||
$sections[$Y] = new ChunkSection(
|
||||
$Y,
|
||||
implode($blocks[$Y]),
|
||||
implode($datas[$Y]),
|
||||
implode($skyLight[$Y]),
|
||||
implode($blockLight[$Y])
|
||||
);
|
||||
}
|
||||
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
if(substr_count($sections[$y]->getIdArray(), "\x00") === 4096){
|
||||
$sections[$y] = new EmptyChunkSection($y);
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $sections, $this->nbt->Biomes->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setPopulated($value = 1){
|
||||
$this->nbt->TerrainPopulated = new Byte("TerrainPopulated", $value);
|
||||
}
|
||||
|
||||
public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){
|
||||
$blockId = "";
|
||||
$blockData = "";
|
||||
$blockSkyLight = "";
|
||||
$blockLight = "";
|
||||
$emptySections = [false, false, false, false, false, false, false, false];
|
||||
|
||||
$emptyBlocks = str_repeat("\x00", 4096);
|
||||
$emptyHalf = str_repeat("\x00", 2048);
|
||||
|
||||
foreach($this->sections as $i => $section){
|
||||
if($section instanceof EmptyChunkSection){
|
||||
$blockId .= $emptyBlocks;
|
||||
$blockData .= $emptyHalf;
|
||||
$blockSkyLight .= $emptyHalf;
|
||||
$blockLight .= $emptyHalf;
|
||||
$emptySections[$i] = true;
|
||||
}else{
|
||||
$blockId .= $section->getIdArray();
|
||||
$blockData .= $section->getDataArray();
|
||||
$blockSkyLight .= $section->getSkyLightArray();
|
||||
$blockLight .= $section->getLightArray();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: maxBlockY, biomeMap, biomeTemp
|
||||
|
||||
//TODO: time
|
||||
return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), 0 /*$this->getLevel()->getTime()*/, $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Compound
|
||||
*/
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
}
|
184
src/pocketmine/level/format/mcregion/ChunkSection.php
Normal file
184
src/pocketmine/level/format/mcregion/ChunkSection.php
Normal file
@ -0,0 +1,184 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
class ChunkSection implements \pocketmine\level\format\ChunkSection{
|
||||
|
||||
private $y;
|
||||
private $blocks;
|
||||
private $data;
|
||||
private $blockLight;
|
||||
private $skyLight;
|
||||
|
||||
public function __construct($y, $blocks, $data, $blockLight, $skyLight){
|
||||
$this->y = $y;
|
||||
$this->blocks = $blocks;
|
||||
$this->data = $data;
|
||||
$this->blockLight = $blockLight;
|
||||
$this->skyLight = $skyLight;
|
||||
}
|
||||
|
||||
public function getY(){
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blocks{($z << 8) + ($x << 4) + $y});
|
||||
}
|
||||
|
||||
public function setBlockId($x, $y, $z, $id){
|
||||
$this->blocks{($z << 8) + ($x << 4) + $y} = chr($id);
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$m = ord($this->data{($z << 7) + ($x << 3) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $m >> 4;
|
||||
}else{
|
||||
return $m & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$i = ($z << 7) + ($x << 3) + ($y >> 1);
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
}else{
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlock($x, $y, $z, &$blockId, &$meta = null){
|
||||
$i = ($z << 8) + ($x << 4) + $y;
|
||||
$blockId = ord($this->blocks{$i});
|
||||
$m = ord($this->data{$i >> 1});
|
||||
if(($y & 1) === 0){
|
||||
$meta = $m >> 4;
|
||||
}else{
|
||||
$meta = $m & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null){
|
||||
$i = ($z << 8) + ($x << 4) + $y;
|
||||
|
||||
$changed = false;
|
||||
|
||||
if($blockId !== null){
|
||||
$blockId = chr($blockId);
|
||||
if($this->blocks{$i} !== $blockId){
|
||||
$this->blocks{$i} = $blockId;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($meta !== null){
|
||||
$i >>= 1;
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
if((($old_m & 0xf0) >> 4) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}else{
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f));
|
||||
if(($old_m & 0x0f) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$sl = ord($this->skyLight{($z << 7) + ($x << 3) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $sl >> 4;
|
||||
}else{
|
||||
return $sl & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockSkyLight($x, $y, $z, $level){
|
||||
$i = ($z << 7) + ($x << 3) + ($y >> 1);
|
||||
$old_sl = ord($this->skyLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f));
|
||||
}else{
|
||||
$this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$l = ord($this->blockLight{($z << 7) + ($x << 3) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $l >> 4;
|
||||
}else{
|
||||
return $l & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockLight($x, $y, $z, $level){
|
||||
$i = ($z << 7) + ($x << 3) + ($y >> 1);
|
||||
$old_l = ord($this->blockLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f));
|
||||
}else{
|
||||
$this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockIdColumn($x, $z){
|
||||
return substr($this->blocks, ($z << 8) + ($x << 4), 16);
|
||||
}
|
||||
|
||||
public function getBlockDataColumn($x, $z){
|
||||
return substr($this->data, ($z << 7) + ($x << 3), 8);
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z){
|
||||
return substr($this->skyLight, ($z << 7) + ($x << 3), 8);
|
||||
}
|
||||
|
||||
public function getBlockLightColumn($x, $z){
|
||||
return substr($this->blockLight, ($z << 7) + ($x << 3), 8);
|
||||
}
|
||||
|
||||
public function getIdArray(){
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
public function getDataArray(){
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getSkyLightArray(){
|
||||
return $this->skyLight;
|
||||
}
|
||||
|
||||
public function getLightArray(){
|
||||
return $this->blockLight;
|
||||
}
|
||||
|
||||
}
|
78
src/pocketmine/level/format/mcregion/ChunkSnapshot.php
Normal file
78
src/pocketmine/level/format/mcregion/ChunkSnapshot.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\generic\BaseChunkSnapshot;
|
||||
|
||||
class ChunkSnapshot extends BaseChunkSnapshot{
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blockId{
|
||||
(($y >> 4) << 12) //get section index
|
||||
+ ($y << 8) + ($z << 4) + $x //get block index in section
|
||||
});
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$data = ord($this->blockData{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $data & 0x0F;
|
||||
}else{
|
||||
return $data >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$level = ord($this->skyLight{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $level & 0x0F;
|
||||
}else{
|
||||
return $level >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$level = ord($this->light{
|
||||
(($y >> 4) << 11) //get section index
|
||||
+ ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section
|
||||
});
|
||||
if(($y & 1) === 0){
|
||||
return $level & 0x0F;
|
||||
}else{
|
||||
return $level >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBiome(){
|
||||
return 0; //TODO
|
||||
}
|
||||
|
||||
public function getHighestBlockAt($x, $z){
|
||||
return 127; //TODO
|
||||
}
|
||||
}
|
285
src/pocketmine/level/format/mcregion/McRegion.php
Normal file
285
src/pocketmine/level/format/mcregion/McRegion.php
Normal file
@ -0,0 +1,285 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\format\SimpleChunk;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\Player;
|
||||
|
||||
class McRegion extends BaseLevelProvider{
|
||||
|
||||
/** @var RegionLoader[] */
|
||||
protected $regions = [];
|
||||
|
||||
/** @var Chunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
|
||||
public static function isValid($path){
|
||||
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
||||
|
||||
if($isValid){
|
||||
$files = glob($path . "/region/*.mc*");
|
||||
foreach($files as $f){
|
||||
if(strpos($f, ".mca") !== false){ //Anvil
|
||||
$isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public static function generate($path, $name, $seed, $generator, array $options = []){
|
||||
@mkdir($path, 0777, true);
|
||||
@mkdir($path . "/region", 0777);
|
||||
//TODO, add extra details
|
||||
$levelData = new Compound("Data", [
|
||||
"hardcore" => new Byte("hardcore", 0),
|
||||
"initialized" => new Byte("initialized", 1),
|
||||
"GameType" => new Int("GameType", 0),
|
||||
"generatorVersion" => new Int("generatorVersion", 1), //2 in MCPE
|
||||
"SpawnX" => new Int("SpawnX", 128),
|
||||
"SpawnY" => new Int("SpawnY", 70),
|
||||
"SpawnZ" => new Int("SpawnZ", 128),
|
||||
"version" => new Int("version", 19133),
|
||||
"DayTime" => new Int("DayTime", 0),
|
||||
"LastPlayed" => new Long("LastPlayed", microtime(true) * 1000),
|
||||
"RandomSeed" => new Long("RandomSeed", $seed),
|
||||
"SizeOnDisk" => new Long("SizeOnDisk", 0),
|
||||
"Time" => new Long("Time", 0),
|
||||
"generatorName" => new String("generatorName", Generator::getGeneratorName($generator)),
|
||||
"generatorOptions" => new String("generatorOptions", isset($options["preset"]) ? $options["preset"] : ""),
|
||||
"LevelName" => new String("LevelName", $name),
|
||||
"GameRules" => new Compound("GameRules", [])
|
||||
]);
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setData(new Compound(null, [
|
||||
"Data" => $levelData
|
||||
]));
|
||||
$buffer = $nbt->writeCompressed();
|
||||
@file_put_contents($path . "level.dat", $buffer);
|
||||
}
|
||||
|
||||
public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
|
||||
$x = $chunkX >> 5;
|
||||
$z = $chunkZ >> 5;
|
||||
}
|
||||
|
||||
public function unloadChunks(){
|
||||
$this->chunks = [];
|
||||
}
|
||||
|
||||
public function getGenerator(){
|
||||
return $this->levelData["generatorName"];
|
||||
}
|
||||
|
||||
public function getGeneratorOptions(){
|
||||
return ["preset" => $this->levelData["generatorOptions"]];
|
||||
}
|
||||
|
||||
public function getLoadedChunks(){
|
||||
return $this->chunks;
|
||||
}
|
||||
|
||||
public function isChunkLoaded($x, $z){
|
||||
return isset($this->chunks[Level::chunkHash($x, $z)]);
|
||||
}
|
||||
|
||||
public function saveChunks(){
|
||||
foreach($this->chunks as $chunk){
|
||||
$this->saveChunk($chunk->getX(), $chunk->getZ());
|
||||
}
|
||||
}
|
||||
|
||||
public function loadChunk($chunkX, $chunkZ, $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
return true;
|
||||
}
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$this->level->timings->syncChunkLoadDataTimer->startTiming();
|
||||
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded
|
||||
$this->level->timings->syncChunkLoadDataTimer->stopTiming();
|
||||
|
||||
if($chunk instanceof Chunk){
|
||||
$this->chunks[$index] = $chunk;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk instanceof Chunk){
|
||||
if($safe === true and $this->isChunkLoaded($x, $z)){
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
|
||||
$this->chunks[$index = Level::chunkHash($x, $z)] = null;
|
||||
|
||||
unset($this->chunks[$index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveChunk($x, $z){
|
||||
if($this->isChunkLoaded($x, $z)){
|
||||
$this->getRegion($x >> 5, $z >> 5)->writeChunk($this->getChunk($x, $z));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $x
|
||||
* @param $z
|
||||
*
|
||||
* @return RegionLoader
|
||||
*/
|
||||
protected function getRegion($x, $z){
|
||||
$index = $x . ":" . $z;
|
||||
|
||||
return isset($this->regions[$index]) ? $this->regions[$index] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param bool $create
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ, $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
return $this->chunks[$index];
|
||||
}else{
|
||||
$this->loadChunk($chunkX, $chunkZ, $create);
|
||||
return isset($this->chunks[$index]) ? $this->chunks[$index] : null;
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){
|
||||
return;
|
||||
//TODO!
|
||||
|
||||
|
||||
if($chunk->isGenerated() === false){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false);
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
$region = $this->getRegion($regionX, $regionZ);
|
||||
$region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
$this->loadChunk($chunkX, $chunkZ);
|
||||
}else{
|
||||
$newChunk = $this->getChunk($chunkX, $chunkZ, true);
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
/*$section = new ChunkSection(new Compound(null, [
|
||||
"Y" => new Byte("Y", $y),
|
||||
"Blocks" => new ByteArray("Blocks", $chunk->getSectionIds($y)),
|
||||
"Data" => new ByteArray("Data", $chunk->getSectionData($y)),
|
||||
"SkyLight" => new ByteArray("SkyLight", str_repeat("\xff", 2048)), //TODO
|
||||
"BlockLight" => new ByteArray("BlockLight", str_repeat("\x00", 2048)) //TODO
|
||||
]));
|
||||
$newChunk->setSection($y, $section);*/
|
||||
}
|
||||
if($chunk->isPopulated()){
|
||||
$newChunk->setPopulated(1);
|
||||
}
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $newChunk;
|
||||
$this->saveChunk($chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
public function createChunkSection($Y){
|
||||
return new ChunkSection(
|
||||
$Y,
|
||||
str_repeat("\xff", 4096),
|
||||
$half = str_repeat("\xff", 2048),
|
||||
$half,
|
||||
$half
|
||||
);
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){
|
||||
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
if($chunk instanceof Chunk){
|
||||
return $chunk->isPopulated();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadRegion($x, $z){
|
||||
$index = $x . ":" . $z;
|
||||
if(isset($this->regions[$index])){
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->regions[$index] = new RegionLoader($this, $x, $z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->unloadChunks();
|
||||
foreach($this->regions as $index => $region){
|
||||
$region->close();
|
||||
unset($this->regions[$index]);
|
||||
}
|
||||
}
|
||||
}
|
375
src/pocketmine/level/format/mcregion/RegionLoader.php
Normal file
375
src/pocketmine/level/format/mcregion/RegionLoader.php
Normal file
@ -0,0 +1,375 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\ByteArray;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class RegionLoader{
|
||||
const VERSION = 1;
|
||||
const COMPRESSION_GZIP = 1;
|
||||
const COMPRESSION_ZLIB = 2;
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
protected $filePath;
|
||||
protected $filePointer;
|
||||
protected $lastSector;
|
||||
/** @var LevelProvider */
|
||||
protected $levelProvider;
|
||||
protected $locationTable = [];
|
||||
|
||||
public function __construct(LevelProvider $level, $regionX, $regionZ){
|
||||
$this->x = $regionX;
|
||||
$this->z = $regionZ;
|
||||
$this->levelProvider = $level;
|
||||
$this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mcr";
|
||||
touch($this->filePath);
|
||||
$this->filePointer = fopen($this->filePath, "r+b");
|
||||
flock($this->filePointer, LOCK_EX);
|
||||
stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB
|
||||
stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB
|
||||
if(!file_exists($this->filePath)){
|
||||
$this->createBlank();
|
||||
}else{
|
||||
$this->loadLocationTable();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
if(is_resource($this->filePointer)){
|
||||
$this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
flock($this->filePointer, LOCK_UN);
|
||||
fclose($this->filePointer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isChunkGenerated($index){
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
}
|
||||
|
||||
public function readChunk($x, $z, $generate = true){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($index < 0 or $index >= 4096){
|
||||
//Regenerate chunk due to corruption
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 1;
|
||||
}
|
||||
|
||||
if(!$this->isChunkGenerated($index)){
|
||||
if($generate === true){
|
||||
//Allocate space
|
||||
$this->locationTable[$index][0] = ++$this->lastSector;
|
||||
$this->locationTable[$index][1] = 1;
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt(-1) . chr(self::COMPRESSION_ZLIB), 4096, "\x00", STR_PAD_RIGHT));
|
||||
$this->writeLocationIndex($index);
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
$length = Binary::readInt(fread($this->filePointer, 4));
|
||||
$compression = ord(fgetc($this->filePointer));
|
||||
|
||||
if($length <= 0){ //Not yet generated
|
||||
$this->generateChunk($x, $z);
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
$length = Binary::readInt(fread($this->filePointer, 4));
|
||||
$compression = ord(fgetc($this->filePointer));
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
trigger_error("Corrupted bigger chunk detected", E_USER_WARNING);
|
||||
$this->locationTable[$index][1] = $length >> 12;
|
||||
$this->writeLocationIndex($index);
|
||||
}elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
|
||||
trigger_error("Invalid compression type", E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->readCompressed(fread($this->filePointer, $length - 1), $compression);
|
||||
$chunk = $nbt->getData();
|
||||
|
||||
|
||||
if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Chunk($this->levelProvider, $chunk->Level);
|
||||
}
|
||||
|
||||
public function chunkExists($x, $z){
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
$nbt = new Compound("Level", []);
|
||||
$nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x);
|
||||
$nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z);
|
||||
$nbt->LastUpdate = new Long("LastUpdate", 0);
|
||||
$nbt->LightPopulated = new Byte("LightPopulated", 0);
|
||||
$nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||
$nbt->V = new Byte("V", self::VERSION);
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
|
||||
$nbt->Blocks = new ByteArray("Blocks", str_repeat("\x00", 32768));
|
||||
$nbt->Blocks = new ByteArray("Data", $half = str_repeat("\x00", 16384));
|
||||
$nbt->SkyLight = new ByteArray("SkyLight", $half);
|
||||
$nbt->BlockLight = new ByteArray("BlockLight", $half);
|
||||
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileEntities = new Enum("TileEntities", []);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
$this->saveChunk($x, $z, $nbt);
|
||||
}
|
||||
|
||||
protected function saveChunk($x, $z, Compound $nbt){
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", array("Level" => $nbt)));
|
||||
$chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL);
|
||||
$length = strlen($chunkData) + 1;
|
||||
$sectors = (int) ceil(($length + 4) / 4096);
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($this->locationTable[$index][1] < $sectors){
|
||||
$this->locationTable[$index][0] = $this->lastSector += $sectors; //The GC will clean this shift later
|
||||
}
|
||||
$this->locationTable[$index][1] = $sectors;
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
public function removeChunk($x, $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
}
|
||||
|
||||
public function writeChunk(Chunk $chunk){
|
||||
//TODO
|
||||
$nbt = $chunk->getNBT();
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
foreach($chunk->getSections() as $section){
|
||||
$nbt->Sections[$section->getY()] = new Compound(null, [
|
||||
"Y" => new Byte("Y", $section->getY()),
|
||||
"Blocks" => new ByteArray("Blocks", $section->getIdArray()),
|
||||
"Data" => new ByteArray("Data", $section->getDataArray()),
|
||||
"BlockLight" => new ByteArray("BlockLight", $section->getLightArray()),
|
||||
"SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->Biomes = new ByteArray("Biomes", $chunk->getBiomeIdArray());
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", $chunk->getBiomeColorArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and $entity->closed !== true){
|
||||
$entity->saveNBT();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("Entities", $entities);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
|
||||
$tiles = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt);
|
||||
}
|
||||
|
||||
protected static function getChunkOffset($x, $z){
|
||||
return $x + ($z << 5);
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->writeLocationTable();
|
||||
flock($this->filePointer, LOCK_UN);
|
||||
fclose($this->filePointer);
|
||||
}
|
||||
|
||||
public function doSlowCleanUp(){
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){
|
||||
continue;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
$chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12);
|
||||
$length = Binary::readInt(substr($chunk, 0, 4));
|
||||
if($length <= 1){
|
||||
$this->locationTable[$i] = array(0, 0, 0); //Non-generated chunk, remove it from index
|
||||
}
|
||||
$chunk = zlib_decode(substr($chunk, 5));
|
||||
if(strlen($chunk) <= 1){
|
||||
$this->locationTable[$i] = array(0, 0, 0); //Corrupted chunk, remove it
|
||||
continue;
|
||||
}
|
||||
$chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, 15, 9);
|
||||
$chunk = Binary::writeInt(strlen($chunk)) . $chunk;
|
||||
$sectors = (int) ceil(strlen($chunk) / 4096);
|
||||
if($sectors > $this->locationTable[$i][1]){
|
||||
$this->locationTable[$i][0] = $this->lastSector += $sectors;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
}
|
||||
$this->writeLocationTable();
|
||||
$n = $this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
private function cleanGarbage(){
|
||||
$sectors = [];
|
||||
foreach($this->locationTable as $index => $data){ //Calculate file usage
|
||||
if($data[0] === 0 or $data[1] === 0){
|
||||
$this->locationTable[$index] = array(0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
for($i = 0; $i < $data[1]; ++$i){
|
||||
$sectors[$data[0]] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($sectors) === ($this->lastSector - 2)){ //No collection needed
|
||||
return 0;
|
||||
}
|
||||
|
||||
ksort($sectors);
|
||||
$shift = 0;
|
||||
$lastSector = 1; //First chunk - 1
|
||||
|
||||
fseek($this->filePointer, 8192);
|
||||
$sector = 2;
|
||||
foreach($sectors as $sector => $index){
|
||||
if(($sector - $lastSector) > 1){
|
||||
$shift += $sector - $lastSector - 1;
|
||||
}
|
||||
if($shift > 0){
|
||||
fseek($this->filePointer, $sector << 12);
|
||||
$old = fread($this->filePointer, 4096);
|
||||
fseek($this->filePointer, ($sector - $shift) << 12);
|
||||
fwrite($this->filePointer, $old, 4096);
|
||||
}
|
||||
$this->locationTable[$index][0] -= $shift;
|
||||
$lastSector = $sector;
|
||||
}
|
||||
ftruncate($this->filePointer, ($sector + 1) << 12); //Truncate to the end of file written
|
||||
return $shift;
|
||||
}
|
||||
|
||||
private function loadLocationTable(){
|
||||
fseek($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
$table = fread($this->filePointer, 4 * 1024 * 2);
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = Binary::readInt(substr($table, $i << 2, 4));
|
||||
$this->locationTable[$i] = array(($index & ~0xff) >> 8, $index & 0xff, Binary::readInt(substr($table, 4096 + ($i << 2), 4)));
|
||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function writeLocationTable(){
|
||||
$table = "";
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$table .= Binary::writeInt(($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
|
||||
}
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$table .= Binary::writeInt($this->locationTable[$i][2]);
|
||||
}
|
||||
fseek($this->filePointer, 0);
|
||||
fwrite($this->filePointer, $table, 4096 * 2);
|
||||
}
|
||||
|
||||
private function writeLocationIndex($index){
|
||||
fseek($this->filePointer, $index << 2);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
|
||||
}
|
||||
|
||||
private function createBlank(){
|
||||
fseek($this->filePointer, 0);
|
||||
ftruncate($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
$table = "";
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i] = array(0, 0);
|
||||
$table .= Binary::writeInt(0);
|
||||
}
|
||||
|
||||
$time = time();
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i][2] = $time;
|
||||
$table .= Binary::writeInt($time);
|
||||
}
|
||||
|
||||
fwrite($this->filePointer, $table, 4096 * 2);
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user