PageRenderTime 45ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pocketmine/level/format/leveldb/Chunk.php

https://gitlab.com/Skull3x/ClearSky
PHP | 380 lines | 293 code | 56 blank | 31 comment | 30 complexity | 8527808a4ea5a601cfeb77f6b7b7b64c MD5 | raw file
  1. <?php
  2. namespace pocketmine\level\format\leveldb;
  3. use pocketmine\level\format\generic\BaseFullChunk;
  4. use pocketmine\level\format\LevelProvider;
  5. use pocketmine\nbt\NBT;
  6. use pocketmine\Player;
  7. use pocketmine\utils\Binary;
  8. use pocketmine\utils\BinaryStream;
  9. class Chunk extends BaseFullChunk{
  10. const DATA_LENGTH = 16384 * (2 + 1 + 1 + 1) + 256 + 1024;
  11. protected $isLightPopulated = false;
  12. protected $isPopulated = false;
  13. protected $isGenerated = false;
  14. public function __construct($level, $chunkX, $chunkZ, $terrain, array $entityData = null, array $tileData = null){
  15. $offset = 0;
  16. $blocks = substr($terrain, $offset, 32768);
  17. $offset += 32768;
  18. $data = substr($terrain, $offset, 16384);
  19. $offset += 16384;
  20. $skyLight = substr($terrain, $offset, 16384);
  21. $offset += 16384;
  22. $blockLight = substr($terrain, $offset, 16384);
  23. $offset += 16384;
  24. $heightMap = [];
  25. foreach(unpack("C*", substr($terrain, $offset, 256)) as $c){
  26. $heightMap[] = $c;
  27. }
  28. $offset += 256;
  29. $biomeColors = [];
  30. foreach(unpack("N*", substr($terrain, $offset, 1024)) as $c){
  31. $biomeColors[] = $c;
  32. }
  33. $offset += 1024;
  34. parent::__construct($level, $chunkX, $chunkZ, $blocks, $data, $skyLight, $blockLight, $biomeColors, $heightMap, $entityData === null ? [] : $entityData, $tileData === null ? [] : $tileData);
  35. }
  36. public function getBlockId($x, $y, $z){
  37. return ord($this->blocks{($x << 11) | ($z << 7) | $y});
  38. }
  39. public function setBlockId($x, $y, $z, $id){
  40. $this->blocks{($x << 11) | ($z << 7) | $y} = chr($id);
  41. $this->hasChanged = true;
  42. }
  43. public function getBlockData($x, $y, $z){
  44. $m = ord($this->data{($x << 10) | ($z << 6) | ($y >> 1)});
  45. if(($y & 1) === 0){
  46. return $m & 0x0F;
  47. }else{
  48. return $m >> 4;
  49. }
  50. }
  51. public function setBlockData($x, $y, $z, $data){
  52. $i = ($x << 10) | ($z << 6) | ($y >> 1);
  53. $old_m = ord($this->data{$i});
  54. if(($y & 1) === 0){
  55. $this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
  56. }else{
  57. $this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
  58. }
  59. $this->hasChanged = true;
  60. }
  61. public function getFullBlock($x, $y, $z){
  62. $i = ($x << 11) | ($z << 7) | $y;
  63. if(($y & 1) === 0){
  64. return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0F);
  65. }else{
  66. return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4);
  67. }
  68. }
  69. public function getBlock($x, $y, $z, &$blockId, &$meta = null){
  70. $full = $this->getFullBlock($x, $y, $z);
  71. $blockId = $full >> 4;
  72. $meta = $full & 0x0f;
  73. }
  74. public function setBlock($x, $y, $z, $blockId = null, $meta = null){
  75. $i = ($x << 11) | ($z << 7) | $y;
  76. $changed = false;
  77. if($blockId !== null){
  78. $blockId = chr($blockId);
  79. if($this->blocks{$i} !== $blockId){
  80. $this->blocks{$i} = $blockId;
  81. $changed = true;
  82. }
  83. }
  84. if($meta !== null){
  85. $i >>= 1;
  86. $old_m = ord($this->data{$i});
  87. if(($y & 1) === 0){
  88. $this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f));
  89. if(($old_m & 0x0f) !== $meta){
  90. $changed = true;
  91. }
  92. }else{
  93. $this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f));
  94. if((($old_m & 0xf0) >> 4) !== $meta){
  95. $changed = true;
  96. }
  97. }
  98. }
  99. if($changed){
  100. $this->hasChanged = true;
  101. }
  102. return $changed;
  103. }
  104. public function getBlockSkyLight($x, $y, $z){
  105. $sl = ord($this->skyLight{($x << 10) | ($z << 6) | ($y >> 1)});
  106. if(($y & 1) === 0){
  107. return $sl & 0x0F;
  108. }else{
  109. return $sl >> 4;
  110. }
  111. }
  112. public function setBlockSkyLight($x, $y, $z, $level){
  113. $i = ($x << 10) | ($z << 6) | ($y >> 1);
  114. $old_sl = ord($this->skyLight{$i});
  115. if(($y & 1) === 0){
  116. $this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f));
  117. }else{
  118. $this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f));
  119. }
  120. $this->hasChanged = true;
  121. }
  122. public function getBlockLight($x, $y, $z){
  123. $l = ord($this->blockLight{($x << 10) | ($z << 6) | ($y >> 1)});
  124. if(($y & 1) === 0){
  125. return $l & 0x0F;
  126. }else{
  127. return $l >> 4;
  128. }
  129. }
  130. public function setBlockLight($x, $y, $z, $level){
  131. $i = ($x << 10) | ($z << 6) | ($y >> 1);
  132. $old_l = ord($this->blockLight{$i});
  133. if(($y & 1) === 0){
  134. $this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f));
  135. }else{
  136. $this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f));
  137. }
  138. $this->hasChanged = true;
  139. }
  140. public function getBlockIdColumn($x, $z){
  141. return substr($this->blocks, ($x << 11) + ($z << 7), 128);
  142. }
  143. public function getBlockDataColumn($x, $z){
  144. return substr($this->data, ($x << 10) + ($z << 6), 64);
  145. }
  146. public function getBlockSkyLightColumn($x, $z){
  147. return substr($this->skyLight, ($x << 10) + ($z << 6), 64);
  148. }
  149. public function getBlockLightColumn($x, $z){
  150. return substr($this->blockLight, ($x << 10) + ($z << 6), 64);
  151. }
  152. /**
  153. * @return bool
  154. */
  155. public function isLightPopulated(){
  156. return $this->isLightPopulated;
  157. }
  158. /**
  159. * @param int $value
  160. */
  161. public function setLightPopulated($value = 1){
  162. $this->isLightPopulated = (bool) $value;
  163. }
  164. /**
  165. * @return bool
  166. */
  167. public function isPopulated(){
  168. return $this->isPopulated;
  169. }
  170. /**
  171. * @param int $value
  172. */
  173. public function setPopulated($value = 1){
  174. $this->isPopulated = (bool) $value;
  175. }
  176. /**
  177. * @return bool
  178. */
  179. public function isGenerated(){
  180. return $this->isGenerated;
  181. }
  182. /**
  183. * @param int $value
  184. */
  185. public function setGenerated($value = 1){
  186. $this->isGenerated = (bool) $value;
  187. }
  188. public static function fromFastBinary($data, LevelProvider $provider = null){
  189. return self::fromBinary($data, $provider);
  190. }
  191. /**
  192. * @param string $data
  193. * @param LevelProvider $provider
  194. *
  195. * @return Chunk
  196. */
  197. public static function fromBinary($data, LevelProvider $provider = null){
  198. try{
  199. $chunkX = Binary::readLInt(substr($data, 0, 4));
  200. $chunkZ = Binary::readLInt(substr($data, 4, 4));
  201. $chunkData = substr($data, 8, -1);
  202. $flags = ord(substr($data, -1));
  203. $entities = null;
  204. $tiles = null;
  205. $extraData = [];
  206. if($provider instanceof LevelDB){
  207. $nbt = new NBT(NBT::LITTLE_ENDIAN);
  208. $entityData = $provider->getDatabase()->get(substr($data, 0, 8) . LevelDB::ENTRY_ENTITIES);
  209. if($entityData !== false and strlen($entityData) > 0){
  210. $nbt->read($entityData, true);
  211. $entities = $nbt->getData();
  212. if(!is_array($entities)){
  213. $entities = [$entities];
  214. }
  215. }
  216. $tileData = $provider->getDatabase()->get(substr($data, 0, 8) . LevelDB::ENTRY_TILES);
  217. if($tileData !== false and strlen($tileData) > 0){
  218. $nbt->read($tileData, true);
  219. $tiles = $nbt->getData();
  220. if(!is_array($tiles)){
  221. $tiles = [$tiles];
  222. }
  223. }
  224. $tileData = $provider->getDatabase()->get(substr($data, 0, 8) . LevelDB::ENTRY_EXTRA_DATA);
  225. if($tileData !== false and strlen($tileData) > 0){
  226. $stream = new BinaryStream($tileData);
  227. $count = $stream->getInt();
  228. for($i = 0; $i < $count; ++$i){
  229. $key = $stream->getInt();
  230. $value = $stream->getShort(false);
  231. $extraData[$key] = $value;
  232. }
  233. }
  234. }
  235. $chunk = new Chunk($provider instanceof LevelProvider ? $provider : LevelDB::class, $chunkX, $chunkZ, $chunkData, $entities, $tiles);
  236. if($flags & 0x01){
  237. $chunk->setGenerated();
  238. }
  239. if($flags & 0x02){
  240. $chunk->setPopulated();
  241. }
  242. if($flags & 0x04){
  243. $chunk->setLightPopulated();
  244. }
  245. return $chunk;
  246. }catch(\Exception $e){
  247. return null;
  248. }
  249. }
  250. public function toFastBinary(){
  251. return $this->toBinary(false);
  252. }
  253. public function toBinary($saveExtra = false){
  254. $chunkIndex = LevelDB::chunkIndex($this->getX(), $this->getZ());
  255. $provider = $this->getProvider();
  256. if($saveExtra and $provider instanceof LevelDB){
  257. $nbt = new NBT(NBT::LITTLE_ENDIAN);
  258. $entities = [];
  259. foreach($this->getEntities() as $entity){
  260. if(!($entity instanceof Player) and !$entity->closed){
  261. $entity->saveNBT();
  262. $entities[] = $entity->namedtag;
  263. }
  264. }
  265. if(count($entities) > 0){
  266. $nbt->setData($entities);
  267. $provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_ENTITIES, $nbt->write());
  268. }else{
  269. $provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_ENTITIES);
  270. }
  271. $tiles = [];
  272. foreach($this->getTiles() as $tile){
  273. if(!$tile->closed){
  274. $tile->saveNBT();
  275. $tiles[] = $tile->namedtag;
  276. }
  277. }
  278. if(count($tiles) > 0){
  279. $nbt->setData($tiles);
  280. $provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_TILES, $nbt->write());
  281. }else{
  282. $provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_TILES);
  283. }
  284. if(count($this->getBlockExtraDataArray()) > 0){
  285. $extraData = new BinaryStream();
  286. $extraData->putInt(count($this->getBlockExtraDataArray()));
  287. foreach($this->getBlockExtraDataArray() as $key => $value){
  288. $extraData->putInt($key);
  289. $extraData->putShort($value);
  290. }
  291. $provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_EXTRA_DATA, $extraData->getBuffer());
  292. }else{
  293. $provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_EXTRA_DATA);
  294. }
  295. }
  296. $heightmap = pack("C*", ...$this->getHeightMapArray());
  297. $biomeColors = pack("N*", ...$this->getBiomeColorArray());
  298. return $chunkIndex .
  299. $this->getBlockIdArray() .
  300. $this->getBlockDataArray() .
  301. $this->getBlockSkyLightArray() .
  302. $this->getBlockLightArray() .
  303. $heightmap .
  304. $biomeColors . chr(
  305. ($this->isLightPopulated() ? 0x04 : 0) | ($this->isPopulated() ? 0x02 : 0) | ($this->isGenerated() ? 0x01 : 0)
  306. );
  307. }
  308. /**
  309. * @param int $chunkX
  310. * @param int $chunkZ
  311. * @param LevelProvider $provider
  312. *
  313. * @return Chunk
  314. */
  315. public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){
  316. try{
  317. $chunk = new Chunk($provider instanceof LevelProvider ? $provider : LevelDB::class, $chunkX, $chunkZ, str_repeat("\x00", self::DATA_LENGTH));
  318. $chunk->skyLight = str_repeat("\xff", 16384);
  319. return $chunk;
  320. }catch(\Exception $e){
  321. return null;
  322. }
  323. }
  324. }