PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

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

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