PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
PocketChunkParser.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
22 namespace pocketmine\level\format;
23 
25 
32  private $location;
33  private $raw = "";
34  private $file;
35  public $sectorLength = 4096; //16 * 16 * 16
36  public $chunkLength = 86016; //21 * $sectorLength
37  public $map = [];
38 
39  public function __construct(){
40  }
41 
42  private function loadLocationTable(){
43  $this->location = [];
44  for($offset = 0; $offset < 0x1000; $offset += 4){
45  $data = Binary::readLInt(substr($this->raw, $offset, 4));
46  $sectors = $data & 0xff;
47  if($sectors === 0){
48  continue;
49  }
50  $sectorLocation = $data >> 8;
51  $this->location[$offset >> 2] = $sectorLocation * $this->sectorLength; //$this->getOffset($X, $Z, $sectors);
52  }
53  }
54 
55  public function loadFile($file){
56  if(file_exists($file . ".gz")){
57  $this->raw = gzinflate(file_get_contents($file . ".gz"));
58  $r = @gzinflate($this->raw);
59  if($r !== false and $r != ""){
60  $this->raw = $r;
61  }
62  @unlink($file . ".gz");
63  file_put_contents($file, $this->raw);
64  }elseif(!file_exists($file)){
65  return false;
66  }else{
67  $this->raw = file_get_contents($file);
68  }
69  $this->file = $file;
70  $this->chunkLength = $this->sectorLength * ord($this->raw{0});
71 
72  return true;
73  }
74 
75  public function loadRaw($raw, $file){
76  $this->file = $file;
77  $this->raw = $raw;
78  $this->chunkLength = $this->sectorLength * ord($this->raw{0});
79 
80  return true;
81  }
82 
83  private function getOffset($X, $Z){
84  return $this->location[$X + ($Z << 5)];
85  }
86 
87  public function getChunk($X, $Z){
88  $X = (int) $X;
89  $Z = (int) $Z;
90 
91  return substr($this->raw, $this->getOffset($X, $Z), $this->chunkLength);
92  }
93 
94  public function writeChunk($X, $Z){
95  $X = (int) $X;
96  $Z = (int) $Z;
97  if(!isset($this->map[$X][$Z])){
98  return false;
99  }
100  $chunk = "";
101  foreach($this->map[$X][$Z] as $section => $data){
102  for($i = 0; $i < 256; ++$i){
103  $chunk .= $data[$i];
104  }
105  }
106 
107  return Binary::writeLInt(strlen($chunk)) . $chunk;
108  }
109 
110  public function parseChunk($X, $Z){
111  $X = (int) $X;
112  $Z = (int) $Z;
113  $offset = $this->getOffset($X, $Z);
114  $len = Binary::readLInt(substr($this->raw, $offset, 4));
115  $offset += 4;
116  $chunk = [
117  0 => [], //Block
118  1 => [], //Data
119  2 => [], //SkyLight
120  3 => [], //BlockLight
121  ];
122  foreach($chunk as $section => &$data){
123  $l = $section === 0 ? 128 : 64;
124  for($i = 0; $i < 256; ++$i){
125  $data[$i] = substr($this->raw, $offset, $l);
126  $offset += $l;
127  }
128  }
129 
130  return $chunk;
131  }
132 
133  public function loadMap(){
134  if($this->raw == ""){
135  return false;
136  }
137  $this->loadLocationTable();
138  for($x = 0; $x < 16; ++$x){
139  $this->map[$x] = [];
140  for($z = 0; $z < 16; ++$z){
141  $this->map[$x][$z] = $this->parseChunk($x, $z);
142  }
143  }
144  $this->raw = "";
145 
146  return true;
147  }
148 
149  public function saveMap($final = false){
150 
151  $fp = fopen($this->file, "r+b");
152  flock($fp, LOCK_EX);
153  foreach($this->map as $x => $d){
154  foreach($d as $z => $chunk){
155  fseek($fp, $this->getOffset($x, $z));
156  fwrite($fp, $this->writeChunk($x, $z), $this->chunkLength);
157  }
158  }
159  flock($fp, LOCK_UN);
160  fclose($fp);
161  $original = filesize($this->file);
162  file_put_contents($this->file . ".gz", gzdeflate(gzdeflate(file_get_contents($this->file), 9), 9)); //Double compression for flat maps
163  $compressed = filesize($this->file . ".gz");
164  if($final === true){
165  @unlink($this->file);
166  }
167  }
168 
169  public function getFloor($x, $z){
170  $X = $x >> 4;
171  $Z = $z >> 4;
172  $aX = $x - ($X << 4);
173  $aZ = $z - ($Z << 4);
174  $index = $aZ + ($aX << 4);
175  for($y = 127; $y <= 0; --$y){
176  if($this->map[$X][$Z][0][$index]{$y} !== "\x00"){
177  break;
178  }
179  }
180 
181  return $y;
182  }
183 
184  public function getBlock($x, $y, $z){
185  $x = (int) $x;
186  $y = (int) $y;
187  $z = (int) $z;
188  $X = $x >> 4;
189  $Z = $z >> 4;
190  $aX = $x - ($X << 4);
191  $aZ = $z - ($Z << 4);
192  $index = $aZ + ($aX << 4);
193  $block = ord($this->map[$X][$Z][0][$index]{$y});
194  $meta = ord($this->map[$X][$Z][1][$index]{$y >> 1});
195  if(($y & 1) === 0){
196  $meta = $meta & 0x0F;
197  }else{
198  $meta = $meta >> 4;
199  }
200 
201  return [$block, $meta];
202  }
203 
204  public function getChunkColumn($X, $Z, $x, $z, $type = 0){
205  $index = $z + ($x << 4);
206 
207  return $this->map[$X][$Z][$type][$index];
208  }
209 
210  public function setBlock($x, $y, $z, $block, $meta = 0){
211  $x = (int) $x;
212  $y = (int) $y;
213  $z = (int) $z;
214  $X = $x >> 4;
215  $Z = $z >> 4;
216  $aX = $x - ($X << 4);
217  $aZ = $z - ($Z << 4);
218  $index = $aZ + ($aX << 4);
219  $this->map[$X][$Z][0][$index]{$y} = chr($block);
220  $old_meta = ord($this->map[$X][$Z][1][$index]{$y >> 1});
221  if(($y & 1) === 0){
222  $meta = ($old_meta & 0xF0) | ($meta & 0x0F);
223  }else{
224  $meta = (($meta << 4) & 0xF0) | ($old_meta & 0x0F);
225  }
226  $this->map[$X][$Z][1][$index]{$y >> 1} = chr($meta);
227  }
228 
229 }