PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/src/classes/ChunkParser.class.php

https://github.com/domanicYL/PocketMine-MP
PHP | 240 lines | 195 code | 20 blank | 25 comment | 19 complexity | 60219e56924679346ee9b0c2a4bce18d MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /*
  3. -
  4. / \
  5. / \
  6. / PocketMine \
  7. / MP \
  8. |\ @shoghicp /|
  9. |. \ / .|
  10. | .. \ / .. |
  11. | .. | .. |
  12. | .. | .. |
  13. \ | /
  14. \ | /
  15. \ | /
  16. \ | /
  17. This program is free software: you can redistribute it and/or modify
  18. it under the terms of the GNU Lesser General Public License as published by
  19. the Free Software Foundation, either version 3 of the License, or
  20. (at your option) any later version.
  21. */
  22. class ChunkParser{
  23. private $location, $raw = b"", $file;
  24. var $sectorLength = 4096; //16 * 16 * 16
  25. var $chunkLength = 86016; //21 * $sectorLength
  26. var $map;
  27. function __construct(){
  28. $map = array();
  29. }
  30. private function loadLocationTable(){
  31. $this->location = array();
  32. console("[DEBUG] Loading Chunk Location table...", true, true, 2);
  33. $chunkCnt = 0;
  34. for($offset = 0; $offset < 0x1000; $offset += 4){
  35. $data = substr($this->raw, $offset, 4);
  36. $sectors = ord($data{0});
  37. if($sectors === 0){
  38. continue;
  39. }
  40. $x = ord($data{1});
  41. $z = ord($data{2});
  42. $X = $chunkCnt % 16;
  43. $Z = $chunkCnt >> 4;
  44. //$unused = ord($data{3});
  45. if(!isset($this->location[$X])){
  46. $this->location[$X] = array();
  47. }
  48. $this->location[$X][$Z] = $this->getOffset($X, $Z, $sectors);
  49. ++$chunkCnt;
  50. }
  51. }
  52. public function loadFile($file){
  53. if(ZLIB_EXTENSION === true and file_exists($file.".gz")){
  54. $this->raw = gzinflate(file_get_contents($file.".gz"));
  55. $r = @gzinflate($this->raw);
  56. if($r !== false and $r != ""){
  57. $this->raw = $r;
  58. }
  59. @unlink($file.".gz");
  60. file_put_contents($file, $this->raw);
  61. }elseif(!file_exists($file)){
  62. return false;
  63. }else{
  64. $this->raw = file_get_contents($file);
  65. }
  66. $this->file = $file;
  67. $this->chunkLength = $this->sectorLength * ord($this->raw{0});
  68. return true;
  69. }
  70. public function loadRaw($raw, $file){
  71. $this->file = $file;
  72. $this->raw = $raw;
  73. $this->chunkLength = $this->sectorLength * ord($this->raw{0});
  74. return true;
  75. }
  76. private function getOffsetPosition($X, $Z){
  77. $data = substr($this->raw, ($X << 2) + ($Z << 7), 4); //$X * 4 + $Z * 128
  78. return array(ord($data{0}), ord($data{1}), ord($data{2}), ord($data{3}));
  79. }
  80. private function getOffset($X, $Z, $sectors = 21){
  81. return 0x1000 + (($X * $sectors) << 12) + (($Z * $sectors) << 16);
  82. }
  83. private function getOffsetLocation($X, $Z){
  84. return $X << 2 + $Z << 7;
  85. }
  86. public function getChunk($X, $Z){
  87. $X = (int) $X;
  88. $Z = (int) $Z;
  89. return substr($this->raw, $this->getOffset($X, $Z), $this->chunkLength);
  90. }
  91. public function writeChunk($X, $Z){
  92. $X = (int) $X;
  93. $Z = (int) $Z;
  94. if(!isset($this->map[$X][$Z])){
  95. return false;
  96. }
  97. $chunk = "";
  98. foreach($this->map[$X][$Z] as $section => $data){
  99. for($i = 0; $i < 256; ++$i){
  100. $chunk .= $data[$i];
  101. }
  102. }
  103. return Utils::writeLInt(strlen($chunk)).$chunk;
  104. }
  105. public function parseChunk($X, $Z){
  106. $X = (int) $X;
  107. $Z = (int) $Z;
  108. $offset = $this->location[$X][$Z];
  109. $len = Utils::readLInt(substr($this->raw, $offset, 4));
  110. $offset += 4;
  111. $chunk = array(
  112. 0 => array(), //Block
  113. 1 => array(), //Data
  114. 2 => array(), //SkyLight
  115. 3 => array(), //BlockLight
  116. );
  117. foreach($chunk as $section => &$data){
  118. $l = $section === 0 ? 128:64;
  119. for($i = 0; $i < 256; ++$i){
  120. $data[$i] = substr($this->raw, $offset, $l);
  121. $offset += $l;
  122. }
  123. }
  124. return $chunk;
  125. }
  126. public function loadMap(){
  127. if($this->raw == ""){
  128. return false;
  129. }
  130. $this->loadLocationTable();
  131. console("[DEBUG] Loading chunks...", true, true, 2);
  132. for($x = 0; $x < 16; ++$x){
  133. $this->map[$x] = array();
  134. for($z = 0; $z < 16; ++$z){
  135. $this->map[$x][$z] = $this->parseChunk($x, $z);
  136. }
  137. }
  138. $this->raw = b"";
  139. console("[DEBUG] Chunks loaded!", true, true, 2);
  140. }
  141. public function saveMap($final = false){
  142. console("[DEBUG] Saving chunks...", true, true, 2);
  143. $fp = fopen($this->file, "r+b");
  144. flock($fp, LOCK_EX);
  145. foreach($this->map as $x => $d){
  146. foreach($d as $z => $chunk){
  147. fseek($fp, $this->location[$x][$z]);
  148. fwrite($fp, $this->writeChunk($x, $z), $this->chunkLength);
  149. }
  150. }
  151. flock($fp, LOCK_UN);
  152. fclose($fp);
  153. if(ZLIB_EXTENSION === true){
  154. $original = filesize($this->file);
  155. file_put_contents($this->file .".gz", gzdeflate(gzdeflate(file_get_contents($this->file),9),9)); //Double compression for flat maps
  156. $compressed = filesize($this->file .".gz");
  157. console("[DEBUG] Saved chunks.dat.gz with ".round(($compressed/$original)*100, 2)."% (".round($compressed/1024, 2)."KB) of the original size", true, true, 2);
  158. if($final === true){
  159. @unlink($this->file);
  160. }
  161. }
  162. }
  163. public function getFloor($x, $z){
  164. $X = $x >> 4;
  165. $Z = $z >> 4;
  166. $aX = $x - ($X << 4);
  167. $aZ = $z - ($Z << 4);
  168. $index = $aZ + ($aX << 4);
  169. for($y = 127; $y <= 0; --$y){
  170. if($this->map[$X][$Z][0][$index]{$y} !== "\x00"){
  171. break;
  172. }
  173. }
  174. return $y;
  175. }
  176. public function getBlock($x, $y, $z){
  177. $x = (int) $x;
  178. $y = (int) $y;
  179. $z = (int) $z;
  180. $X = $x >> 4;
  181. $Z = $z >> 4;
  182. $aX = $x - ($X << 4);
  183. $aZ = $z - ($Z << 4);
  184. $index = $aZ + ($aX << 4);
  185. $block = ord($this->map[$X][$Z][0][$index]{$y});
  186. $meta = ord($this->map[$X][$Z][1][$index]{$y >> 1});
  187. if(($y & 1) === 0){
  188. $meta = $meta & 0x0F;
  189. }else{
  190. $meta = $meta >> 4;
  191. }
  192. return array($block, $meta);
  193. }
  194. public function getChunkColumn($X, $Z, $x, $z, $type = 0){
  195. $index = $z + ($x << 4);
  196. return $this->map[$X][$Z][$type][$index];
  197. }
  198. public function setBlock($x, $y, $z, $block, $meta = 0){
  199. $x = (int) $x;
  200. $y = (int) $y;
  201. $z = (int) $z;
  202. $X = $x >> 4;
  203. $Z = $z >> 4;
  204. $aX = $x - ($X << 4);
  205. $aZ = $z - ($Z << 4);
  206. $index = $aZ + ($aX << 4);
  207. $this->map[$X][$Z][0][$index]{$y} = chr($block);
  208. $old_meta = ord($this->map[$X][$Z][1][$index]{$y >> 1});
  209. if(($y & 1) === 0){
  210. $meta = ($old_meta & 0xF0) | ($meta & 0x0F);
  211. }else{
  212. $meta = (($meta << 4) & 0xF0) | ($old_meta & 0x0F);
  213. }
  214. $this->map[$X][$Z][1][$index]{$y >> 1} = chr($meta);
  215. }
  216. }