PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
RakLibInterface.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 
25 namespace pocketmine\network;
26 
46 use pocketmine\network\protocol\Info as ProtocolInfo;
80 use raklib\protocol\EncapsulatedPacket;
81 use raklib\RakLib;
82 use raklib\server\RakLibServer;
83 use raklib\server\ServerHandler;
84 use raklib\server\ServerInstance;
85 
86 class RakLibInterface implements ServerInstance, SourceInterface{
87 
89  private $packetPool;
90 
91  private $server;
93  private $players = [];
94 
96  private $identifiers;
97 
99  private $identifiersACK = [];
100 
102  private $interface;
103 
104  private $upload = 0;
105  private $download = 0;
106 
107  private $internalThreaded;
108  private $externalThreaded;
109 
110  public function __construct(Server $server){
111 
112  $this->registerPackets();
113 
114  $this->server = $server;
115  $this->identifiers = new \SplObjectStorage();
116 
117  $this->internalThreaded = new \Threaded();
118  $this->externalThreaded = new \Threaded();
119 
120  $server = new RakLibServer($this->internalThreaded, $this->externalThreaded, $this->server->getLogger(), $this->server->getLoader(), $this->server->getPort(), $this->server->getIp() === "" ? "0.0.0.0" : $this->server->getIp());
121  $this->interface = new ServerHandler($server, $this);
122  $this->setName($this->server->getMotd());
123  }
124 
125  public function doTick(){
126  $this->interface->sendTick();
127  }
128 
129  public function process(){
130  $work = false;
131  if($this->interface->handlePacket()){
132  $work = true;
133  while($this->interface->handlePacket()){
134  }
135  }
136 
137  $this->doTick();
138 
139  return $work;
140  }
141 
142  public function closeSession($identifier, $reason){
143  if(isset($this->players[$identifier])){
144  $player = $this->players[$identifier];
145  $this->identifiers->detach($player);
146  unset($this->players[$identifier]);
147  unset($this->identifiersACK[$identifier]);
148  $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", $reason);
149  }
150  }
151 
152  public function close(Player $player, $reason = "unknown reason"){
153  if(isset($this->identifiers[$player])){
154  unset($this->players[$this->identifiers[$player]]);
155  unset($this->identifiersACK[$this->identifiers[$player]]);
156  $this->interface->closeSession($this->identifiers[$player], $reason);
157  $this->identifiers->detach($player);
158  }
159  }
160 
161  public function shutdown(){
162  $this->interface->shutdown();
163  }
164 
165  public function emergencyShutdown(){
166  $this->interface->emergencyShutdown();
167  }
168 
169  public function openSession($identifier, $address, $port, $clientID){
170  $player = new Player($this, null, $address, $port);
171  $this->players[$identifier] = $player;
172  $this->identifiersACK[$identifier] = 0;
173  $this->identifiers->attach($player, $identifier);
174  $this->server->addPlayer($identifier, $player);
175  }
176 
177  public function handleEncapsulated($identifier, EncapsulatedPacket $packet, $flags){
178  if(isset($this->players[$identifier])){
179  try{
180  $pk = $this->getPacket($packet->buffer);
181  $pk->decode();
182  $this->players[$identifier]->handleDataPacket($pk);
183  }catch(\Exception $e){
184  if(\pocketmine\DEBUG > 1){
185  $logger = $this->server->getLogger();
186  if($logger instanceof MainLogger){
187  $logger->debug("Packet " . get_class($pk) . " 0x" . bin2hex($packet->buffer));
188  $logger->logException($e);
189  }
190  }
191 
192  $this->interface->blockAddress($this->players[$identifier]->getAddress(), 5);
193  }
194  }
195  }
196 
197  public function blockAddress($address, $timeout = 300){
198  $this->interface->blockAddress($address, $timeout);
199  }
200 
201  public function handleRaw($address, $port, $payload){
202  $this->server->handlePacket($address, $port, $payload);
203  }
204 
205  public function putRaw($address, $port, $payload){
206  $this->interface->sendRaw($address, $port, $payload);
207  }
208 
209  public function notifyACK($identifier, $identifierACK){
210  if(isset($this->players[$identifier])){
211  $this->players[$identifier]->handleACK($identifierACK);
212  }
213  }
214 
215  public function setName($name){
216  $this->interface->sendOption("name", "MCCPP;Demo;$name");
217  }
218 
219  public function setPortCheck($name){
220  $this->interface->sendOption("portChecking", (bool) $name);
221  }
222 
223  public function handleOption($name, $value){
224  if($name === "bandwidth"){
225  $v = unserialize($value);
226  $this->upload = $v["up"];
227  $this->download = $v["down"];
228  }
229  }
230 
231  public function getUploadUsage(){
232  return $this->upload;
233  }
234 
235  public function getDownloadUsage(){
236  return $this->download;
237  }
238 
239  public function putPacket(Player $player, DataPacket $packet, $needACK = false, $immediate = false){
240  if(isset($this->identifiers[$player])){
241  $identifier = $this->identifiers[$player];
242  $pk = null;
243  if(!$packet->isEncoded){
244  $packet->encode();
245  }elseif(!$needACK){
246  if(!isset($packet->__encapsulatedPacket)){
247  $packet->__encapsulatedPacket = new CachedEncapsulatedPacket;
248  $packet->__encapsulatedPacket->identifierACK = null;
249  $packet->__encapsulatedPacket->buffer = $packet->buffer;
250  $packet->__encapsulatedPacket->reliability = 2;
251  }
252  $pk = $packet->__encapsulatedPacket;
253  }
254 
255  if($pk === null){
256  $pk = new EncapsulatedPacket();
257  $pk->buffer = $packet->buffer;
258  $pk->reliability = 2;
259  if($needACK === true){
260  $pk->identifierACK = $this->identifiersACK[$identifier]++;
261  }
262  }
263 
264  $this->interface->sendEncapsulated($identifier, $pk, ($needACK === true ? RakLib::FLAG_NEED_ACK : 0) | ($immediate === true ? RakLib::PRIORITY_IMMEDIATE : RakLib::PRIORITY_NORMAL));
265 
266  return $pk->identifierACK;
267  }
268 
269  return null;
270  }
271 
272  public function registerPacket($id, $class){
273  $this->packetPool[$id] = $class;
274  }
275 
281  public function getPacketFromPool($id){
283  $class = $this->packetPool[$id];
284  if($class !== null){
285  return new $class;
286  }
287  return null;
288  }
289 
290  private function registerPackets(){
291  $this->packetPool = new \SplFixedArray(256);
292 
293  $this->registerPacket(ProtocolInfo::LOGIN_PACKET, LoginPacket::class);
294  $this->registerPacket(ProtocolInfo::LOGIN_STATUS_PACKET, LoginStatusPacket::class);
295  $this->registerPacket(ProtocolInfo::MESSAGE_PACKET, MessagePacket::class);
296  $this->registerPacket(ProtocolInfo::SET_TIME_PACKET, SetTimePacket::class);
297  $this->registerPacket(ProtocolInfo::START_GAME_PACKET, StartGamePacket::class);
298  $this->registerPacket(ProtocolInfo::ADD_MOB_PACKET, AddMobPacket::class);
299  $this->registerPacket(ProtocolInfo::ADD_PLAYER_PACKET, AddPlayerPacket::class);
300  $this->registerPacket(ProtocolInfo::REMOVE_PLAYER_PACKET, RemovePlayerPacket::class);
301  $this->registerPacket(ProtocolInfo::ADD_ENTITY_PACKET, AddEntityPacket::class);
302  $this->registerPacket(ProtocolInfo::REMOVE_ENTITY_PACKET, RemoveEntityPacket::class);
303  $this->registerPacket(ProtocolInfo::ADD_ITEM_ENTITY_PACKET, AddItemEntityPacket::class);
304  $this->registerPacket(ProtocolInfo::TAKE_ITEM_ENTITY_PACKET, TakeItemEntityPacket::class);
305  $this->registerPacket(ProtocolInfo::MOVE_ENTITY_PACKET, MoveEntityPacket::class);
306  $this->registerPacket(ProtocolInfo::ROTATE_HEAD_PACKET, RotateHeadPacket::class);
307  $this->registerPacket(ProtocolInfo::MOVE_PLAYER_PACKET, MovePlayerPacket::class);
308  $this->registerPacket(ProtocolInfo::REMOVE_BLOCK_PACKET, RemoveBlockPacket::class);
309  $this->registerPacket(ProtocolInfo::UPDATE_BLOCK_PACKET, UpdateBlockPacket::class);
310  $this->registerPacket(ProtocolInfo::ADD_PAINTING_PACKET, AddPaintingPacket::class);
311  $this->registerPacket(ProtocolInfo::EXPLODE_PACKET, ExplodePacket::class);
312  $this->registerPacket(ProtocolInfo::LEVEL_EVENT_PACKET, LevelEventPacket::class);
313  $this->registerPacket(ProtocolInfo::TILE_EVENT_PACKET, TileEventPacket::class);
314  $this->registerPacket(ProtocolInfo::ENTITY_EVENT_PACKET, EntityEventPacket::class);
315  $this->registerPacket(ProtocolInfo::PLAYER_EQUIPMENT_PACKET, PlayerEquipmentPacket::class);
316  $this->registerPacket(ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET, PlayerArmorEquipmentPacket::class);
317  $this->registerPacket(ProtocolInfo::INTERACT_PACKET, InteractPacket::class);
318  $this->registerPacket(ProtocolInfo::USE_ITEM_PACKET, UseItemPacket::class);
319  $this->registerPacket(ProtocolInfo::PLAYER_ACTION_PACKET, PlayerActionPacket::class);
320  $this->registerPacket(ProtocolInfo::HURT_ARMOR_PACKET, HurtArmorPacket::class);
321  $this->registerPacket(ProtocolInfo::SET_ENTITY_DATA_PACKET, SetEntityDataPacket::class);
322  $this->registerPacket(ProtocolInfo::SET_ENTITY_MOTION_PACKET, SetEntityMotionPacket::class);
323  $this->registerPacket(ProtocolInfo::SET_HEALTH_PACKET, SetHealthPacket::class);
324  $this->registerPacket(ProtocolInfo::SET_SPAWN_POSITION_PACKET, SetSpawnPositionPacket::class);
325  $this->registerPacket(ProtocolInfo::ANIMATE_PACKET, AnimatePacket::class);
326  $this->registerPacket(ProtocolInfo::RESPAWN_PACKET, RespawnPacket::class);
327  $this->registerPacket(ProtocolInfo::SEND_INVENTORY_PACKET, SendInventoryPacket::class);
328  $this->registerPacket(ProtocolInfo::DROP_ITEM_PACKET, DropItemPacket::class);
329  $this->registerPacket(ProtocolInfo::CONTAINER_OPEN_PACKET, ContainerOpenPacket::class);
330  $this->registerPacket(ProtocolInfo::CONTAINER_CLOSE_PACKET, ContainerClosePacket::class);
331  $this->registerPacket(ProtocolInfo::CONTAINER_SET_SLOT_PACKET, ContainerSetSlotPacket::class);
332  $this->registerPacket(ProtocolInfo::CONTAINER_SET_DATA_PACKET, ContainerSetDataPacket::class);
333  $this->registerPacket(ProtocolInfo::CONTAINER_SET_CONTENT_PACKET, ContainerSetContentPacket::class);
334  $this->registerPacket(ProtocolInfo::CHAT_PACKET, ChatPacket::class);
335  $this->registerPacket(ProtocolInfo::ADVENTURE_SETTINGS_PACKET, AdventureSettingsPacket::class);
336  $this->registerPacket(ProtocolInfo::ENTITY_DATA_PACKET, EntityDataPacket::class);
337  $this->registerPacket(ProtocolInfo::UNLOAD_CHUNK_PACKET, UnloadChunkPacket::class);
338  $this->registerPacket(ProtocolInfo::SET_DIFFICULTY_PACKET, SetDifficultyPacket::class);
339  }
340 
341  private function getPacket($buffer){
342  $pid = ord($buffer{0});
343 
344  if(($data = $this->getPacketFromPool($pid)) === null){
345  $data = new UnknownPacket();
346  $data->packetID = $pid;
347  }
348  $data->setBuffer(substr($buffer, 1));
349 
350  return $data;
351  }
352 }
putPacket(Player $player, DataPacket $packet, $needACK=false, $immediate=false)
close(Player $player, $reason="unknown reason")