PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
GenerationManager.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\generator;
23 
27 
29 
30 
31  /*
32  * IPC protocol:
33  * int32 (total length)
34  * byte (packet id)
35  * byte[] (length - 1 bytes)
36  */
37 
38  /*
39  * Direction: Server->Thread
40  * byte[] payload:
41  * string root namespace
42  * byte[] path
43  */
44  const PACKET_ADD_NAMESPACE = 0x00;
45 
46  /*
47  * Direction: Both
48  * If Server->Thread, request chunk generation
49  * If Thread->Server, request chunk contents / loading
50  * byte[] payload:
51  * int32 levelID
52  * int32 chunkX
53  * int32 chunkZ
54  */
55  const PACKET_REQUEST_CHUNK = 0x01;
56 
57  /*
58  * Direction: Both
59  * byte[] payload:
60  * int32 levelID
61  * int32 chunkX
62  * int32 chunkZ
63  * byte className length
64  * byte[] className
65  * byte[] chunk (none if generated flag is not set)
66  */
67  const PACKET_SEND_CHUNK = 0x02;
68 
69  /*
70  * Direction: Server->Thread
71  * byte[] payload:
72  * int32 levelID
73  * int32 seed
74  * string class that extends pocketmine\level\generator\Generator
75  * byte[] serialized options array
76  */
77  const PACKET_OPEN_LEVEL = 0x03;
78 
79  /*
80  * Direction: Server->Thread
81  * byte[] payload:
82  * int32 levelID
83  */
84  const PACKET_CLOSE_LEVEL = 0x04;
85 
86  /*
87  * Direction: Server->Thread
88  * no payload
89  */
90  const PACKET_SHUTDOWN = 0xff;
91 
93  protected $thread;
94 
96  protected $logger;
98  protected $loader;
99 
101  protected $levels = [];
102 
104  protected $requestQueue = [];
105 
107  protected $needsChunk = [];
108 
109  protected $shutdown = false;
110 
116  public function __construct(GenerationThread $thread, \Logger $logger, \ClassLoader $loader){
117  $this->thread = $thread;
118  $this->logger = $logger;
119  $this->loader = $loader;
120  $chunkX = $chunkZ = null;
121 
122  while($this->shutdown !== true){
123  try{
124  if(count($this->requestQueue) > 0){
125  foreach($this->requestQueue as $levelID => $chunks){
126  if(count($chunks) === 0){
127  unset($this->requestQueue[$levelID]);
128  }else{
129  $key = key($chunks);
130  Level::getXZ($key, $chunkX, $chunkZ);
131  unset($this->requestQueue[$levelID][$key]);
132  $this->generateChunk($levelID, $chunkX, $chunkZ);
133  }
134  }
135  }else{
136  $this->readPacket();
137  }
138  }catch(\Exception $e){
139  $this->logger->warning("[Generator Thread] Exception: " . $e->getMessage() . " on file \"" . $e->getFile() . "\" line " . $e->getLine());
140  }
141  }
142  }
143 
144  protected function openLevel($levelID, $seed, $class, array $options){
145  if(!isset($this->levels[$levelID])){
146  $this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options);
147  }
148  }
149 
150  protected function generateChunk($levelID, $chunkX, $chunkZ){
151  if(isset($this->levels[$levelID])){
152  $this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly
153  if(isset($this->levels[$levelID])){
154  foreach($this->levels[$levelID]->getChangedChunks() as $index => $chunk){
155  if($chunk->isPopulated()){
156  $this->sendChunk($levelID, $chunk);
157  $this->levels[$levelID]->cleanChangedChunk($index);
158  }
159  }
160 
161  $this->levels[$levelID]->doGarbageCollection();
162  $this->levels[$levelID]->cleanChangedChunks();
163  }
164  }
165  }
166 
167  protected function closeLevel($levelID){
168  if(isset($this->levels[$levelID])){
169  $this->levels[$levelID]->shutdown();
170  unset($this->levels[$levelID]);
171  }
172  }
173 
174  protected function enqueueChunk($levelID, $chunkX, $chunkZ){
175  if(!isset($this->requestQueue[$levelID])){
176  $this->requestQueue[$levelID] = [];
177  }
178  if(!isset($this->requestQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){
179  $this->requestQueue[$levelID][$index] = 1;
180  }else{
181  $this->requestQueue[$levelID][$index]++;
182  arsort($this->requestQueue[$levelID]);
183  }
184  }
185 
186  protected function receiveChunk($levelID, FullChunk $chunk){
187  if($this->needsChunk[$levelID] !== null){
188  if($this->needsChunk[$levelID][0] === $chunk->getX() and $this->needsChunk[$levelID][1] === $chunk->getZ()){
189  $this->needsChunk[$levelID] = $chunk;
190  }
191  }
192  //TODO: set new received chunks
193  }
194 
202  public function requestChunk($levelID, $chunkX, $chunkZ){
203  $this->needsChunk[$levelID] = [$chunkX, $chunkZ];
204  $binary = chr(self::PACKET_REQUEST_CHUNK) . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ);
205  $this->thread->pushThreadToMainPacket($binary);
206 
207  do{
208  $this->readPacket();
209  }while($this->shutdown !== true and !($this->needsChunk[$levelID] instanceof FullChunk));
210 
211  $chunk = $this->needsChunk[$levelID];
212  $this->needsChunk[$levelID] = null;
213  if($chunk instanceof FullChunk){
214  return $chunk;
215  }else{
216  return null;
217  }
218  }
219 
220  public function sendChunk($levelID, FullChunk $chunk){
221  $binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
222  $this->thread->pushThreadToMainPacket($binary);
223  }
224 
225  protected function readPacket(){
226  if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){
227  $pid = ord($packet{0});
228  $offset = 1;
229  if($pid === self::PACKET_REQUEST_CHUNK){
230  $levelID = Binary::readInt(substr($packet, $offset, 4));
231  $offset += 4;
232  $chunkX = Binary::readInt(substr($packet, $offset, 4));
233  $offset += 4;
234  $chunkZ = Binary::readInt(substr($packet, $offset, 4));
235  $this->enqueueChunk($levelID, $chunkX, $chunkZ);
236  }elseif($pid === self::PACKET_SEND_CHUNK){
237  $levelID = Binary::readInt(substr($packet, $offset, 4));
238  $offset += 4;
239  $len = ord($packet{$offset++});
241  $class = substr($packet, $offset, $len);
242  $offset += $len;
243  $chunk = $class::fromBinary(substr($packet, $offset));
244  $this->receiveChunk($levelID, $chunk);
245  }elseif($pid === self::PACKET_OPEN_LEVEL){
246  $levelID = Binary::readInt(substr($packet, $offset, 4));
247  $offset += 4;
248  $seed = Binary::readInt(substr($packet, $offset, 4));
249  $offset += 4;
250  $len = Binary::readShort(substr($packet, $offset, 2));
251  $offset += 2;
252  $class = substr($packet, $offset, $len);
253  $offset += $len;
254  $options = unserialize(substr($packet, $offset));
255  $this->openLevel($levelID, $seed, $class, $options);
256  }elseif($pid === self::PACKET_CLOSE_LEVEL){
257  $levelID = Binary::readInt(substr($packet, $offset, 4));
258  $this->closeLevel($levelID);
259  }elseif($pid === self::PACKET_ADD_NAMESPACE){
260  $len = Binary::readShort(substr($packet, $offset, 2));
261  $offset += 2;
262  $namespace = substr($packet, $offset, $len);
263  $offset += $len;
264  $path = substr($packet, $offset);
265  $this->loader->addPath($path);
266  }elseif($pid === self::PACKET_SHUTDOWN){
267  foreach($this->levels as $level){
268  $level->shutdown();
269  }
270  $this->levels = [];
271 
272  $this->shutdown = true;
273  }
274  }elseif(count($this->thread->getInternalQueue()) === 0){
275  $this->thread->synchronized(function (){
276  $this->thread->wait(50000);
277  });
278 
279  }
280  }
281 
285  public function getLogger(){
286  return $this->logger;
287  }
288 
289 }
__construct(GenerationThread $thread,\Logger $logger,\ClassLoader $loader)
static readShort($str)
Definition: Binary.php:259
static chunkHash($x, $z)
Definition: Level.php:230