Skip to content

Commit 0579402

Browse files
committed
Implement new packet compression
1 parent e6f6c1f commit 0579402

File tree

4 files changed

+91
-117
lines changed

4 files changed

+91
-117
lines changed

MultiCompressor.php

Lines changed: 0 additions & 73 deletions
This file was deleted.

ProxyListener.php

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,41 +27,14 @@ public function onDataPacketReceive(DataPacketReceiveEvent $event): void
2727
$origin = $event->getOrigin();
2828
$packet = $event->getPacket();
2929

30-
switch ($packet->pid()) {
31-
case NetworkStackLatencyPacket::NETWORK_ID:
32-
/** @var NetworkStackLatencyPacket $packet USED FOR PING CALCULATIONS */
33-
if ($packet->timestamp === 0 && $packet->needResponse) {
34-
if (($player = $origin->getPlayer()) !== null && $player->isConnected()) {
35-
$origin->sendDataPacket(NetworkStackLatencyPacket::response(0));
36-
}
37-
$event->cancel();
30+
/** @var NetworkStackLatencyPacket $packet USED FOR PING CALCULATIONS */
31+
if ($packet->pid() == NetworkStackLatencyPacket::NETWORK_ID) {
32+
if ($packet->timestamp === 0 && $packet->needResponse) {
33+
if (($player = $origin->getPlayer()) !== null && $player->isConnected()) {
34+
$origin->sendDataPacket(NetworkStackLatencyPacket::response(0));
3835
}
39-
break;
40-
case RequestNetworkSettingsPacket::NETWORK_ID:
41-
/** @var RequestNetworkSettingsPacket $packet USED TO SIMULATE VANILLA BEHAVIOUR, SINCE IT'S NOT USED BY US */
42-
$multiProtocol = method_exists($origin, 'setProtocolId');
43-
$protocolVersion = $packet->getProtocolVersion();
44-
45-
if (($multiProtocol && !in_array($protocolVersion, ProtocolInfo::ACCEPTED_PROTOCOL, true)) || !$multiProtocol && $protocolVersion !== ProtocolInfo::CURRENT_PROTOCOL) {
46-
$origin->disconnectIncompatibleProtocol($protocolVersion);
47-
return;
48-
}
49-
50-
if ($multiProtocol) {
51-
$origin->setProtocolId($packet->getProtocolVersion());
52-
}
53-
54-
$origin->sendDataPacket(NetworkSettingsPacket::create(
55-
NetworkSettingsPacket::COMPRESS_EVERYTHING,
56-
CompressionAlgorithm::ZLIB,
57-
false,
58-
0,
59-
0
60-
), true);
61-
6236
$event->cancel();
63-
break;
37+
}
6438
}
65-
6639
}
6740
}

ProxyNetworkInterface.php

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace libproxy;
77

88
use Error;
9+
use ErrorException;
910
use Exception;
1011
use libproxy\data\LatencyData;
1112
use libproxy\data\TickSyncPacket;
@@ -17,12 +18,17 @@
1718
use libproxy\protocol\ProxyPacketSerializer;
1819
use pmmp\thread\Thread as NativeThread;
1920
use pmmp\thread\ThreadSafeArray;
21+
use pocketmine\network\mcpe\compression\DecompressionException;
22+
use pocketmine\network\mcpe\compression\ZlibCompressor;
2023
use pocketmine\network\mcpe\convert\TypeConverter;
2124
use pocketmine\network\mcpe\EntityEventBroadcaster;
2225
use pocketmine\network\mcpe\NetworkSession;
2326
use pocketmine\network\mcpe\PacketBroadcaster;
27+
use pocketmine\network\mcpe\protocol\PacketDecodeException;
2428
use pocketmine\network\mcpe\protocol\PacketPool;
29+
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
2530
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
31+
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
2632
use pocketmine\network\mcpe\raklib\PthreadsChannelReader;
2733
use pocketmine\network\mcpe\raklib\PthreadsChannelWriter;
2834
use pocketmine\network\NetworkInterface;
@@ -32,12 +38,16 @@
3238
use pocketmine\Server;
3339
use pocketmine\snooze\SleeperHandlerEntry;
3440
use pocketmine\thread\ThreadCrashException;
41+
use pocketmine\timings\Timings;
3542
use pocketmine\utils\Binary;
3643
use pocketmine\utils\BinaryDataException;
44+
use pocketmine\utils\BinaryStream;
3745
use Socket;
3846
use ThreadedArray;
3947
use WeakMap;
48+
use function base64_encode;
4049
use function bin2hex;
50+
use function ord;
4151
use function socket_close;
4252
use function socket_create_pair;
4353
use function socket_last_error;
@@ -46,6 +56,7 @@
4656
use function strlen;
4757
use function substr;
4858
use function trim;
59+
use function zstd_uncompress;
4960
use const AF_INET;
5061
use const AF_UNIX;
5162
use const SOCK_STREAM;
@@ -214,7 +225,7 @@ private function onPacketReceive(string $buffer): void
214225
break; // might be data arriving from the client after the server has closed the connection
215226
}
216227

217-
$session->handleEncoded($pk->payload);
228+
$this->handleEncoded($session, $pk->payload);
218229
$this->receiveBytes += strlen($pk->payload);
219230
break;
220231
}
@@ -225,6 +236,70 @@ private function onPacketReceive(string $buffer): void
225236
}
226237
}
227238

239+
/**
240+
* @throws PacketHandlingException
241+
*/
242+
public function handleEncoded(NetworkSession $session, string $payload): void
243+
{
244+
if (!(fn() => $this->connected)->call($session)) {
245+
return;
246+
}
247+
248+
Timings::$playerNetworkReceive->startTiming();
249+
try {
250+
(fn() => $this->packetBatchLimiter->decrement())->call($session);
251+
252+
if (strlen($payload) < 1) {
253+
throw new PacketHandlingException("No bytes in payload");
254+
}
255+
256+
Timings::$playerNetworkReceiveDecompress->startTiming();
257+
$compressionType = ord($payload[0]);
258+
$compressed = substr($payload, 1);
259+
260+
try {
261+
$decompressed = match ($compressionType) {
262+
CompressionAlgorithm::NONE => $compressed,
263+
CompressionAlgorithm::ZLIB => $session->getCompressor()->decompress($compressed),
264+
CompressionAlgorithm::NONE - 1 => ($d = zstd_uncompress($compressed)) === false ? throw new DecompressionException("Failed to decompress packet") : $d,
265+
default => throw new PacketHandlingException("Packet compressed with unexpected compression type $compressionType")
266+
};
267+
} catch (ErrorException|DecompressionException $e) {
268+
$session->getLogger()->debug("Failed to decompress packet: " . base64_encode($compressed));
269+
throw PacketHandlingException::wrap($e, "Compressed packet batch decode error");
270+
} finally {
271+
Timings::$playerNetworkReceiveDecompress->stopTiming();
272+
}
273+
274+
try {
275+
$stream = new BinaryStream($decompressed);
276+
$count = 0;
277+
foreach (PacketBatch::decodeRaw($stream) as $buffer) {
278+
(fn() => $this->gamePacketLimiter->decrement())->call($session);
279+
if (++$count > 100) {
280+
throw new PacketHandlingException("Too many packets in batch");
281+
}
282+
$packet = PacketPool::getInstance()->getPacket($buffer);
283+
if ($packet === null) {
284+
$session->getLogger()->debug("Unknown packet: " . base64_encode($buffer));
285+
throw new PacketHandlingException("Unknown packet received");
286+
}
287+
try {
288+
$session->handleDataPacket($packet, $buffer);
289+
} catch (PacketHandlingException $e) {
290+
$session->getLogger()->debug($packet->getName() . ": " . base64_encode($buffer));
291+
throw PacketHandlingException::wrap($e, "Error processing " . $packet->getName());
292+
}
293+
}
294+
} catch (PacketDecodeException|BinaryDataException $e) {
295+
$session->getLogger()->logException($e);
296+
throw PacketHandlingException::wrap($e, "Packet batch decode error");
297+
}
298+
} finally {
299+
Timings::$playerNetworkReceive->stopTiming();
300+
}
301+
}
302+
228303
public function tick(): void
229304
{
230305
if (!$this->proxy->isRunning()) {
@@ -295,20 +370,13 @@ public function createSession(int $socketId, string $ip, int $port): NetworkSess
295370
new ProxyPacketSender($socketId, $this),
296371
$this->packetBroadcaster,
297372
$this->entityEventBroadcaster,
298-
MultiCompressor::getInstance(),
373+
ZlibCompressor::getInstance(),
299374
TypeConverter::getInstance(),
300375
$ip,
301376
$port
302377
);
303378

304379
$this->sessions[$socketId] = $session;
305-
306-
// Set the LoginPacketHandler, since compression is handled by the proxy
307-
(function (): void {
308-
/** @noinspection PhpUndefinedFieldInspection */
309-
$this->onSessionStartSuccess();
310-
})->call($session);
311-
312380
return $session;
313381
}
314382

ProxyThread.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
use pocketmine\snooze\SleeperHandlerEntry;
1212
use pocketmine\thread\log\AttachableThreadSafeLogger;
1313
use pocketmine\thread\Thread;
14+
use pocketmine\utils\Utils;
1415
use RuntimeException;
1516
use Socket;
1617
use function gc_enable;
1718
use function ini_set;
18-
use function register_shutdown_function;
1919
use function socket_bind;
2020
use function socket_create;
2121
use function socket_last_error;
@@ -134,10 +134,16 @@ private function createServerSocket(): Socket
134134
if (!socket_listen($serverSocket, 10)) {
135135
throw new RuntimeException("Failed to listen to socket: " . socket_strerror(socket_last_error($serverSocket)));
136136
}
137-
if (!socket_set_option($serverSocket, SOL_SOCKET, SO_SNDBUF, 8 * 1024 * 1024) || !socket_set_option($serverSocket, SOL_SOCKET, SO_RCVBUF, 8 * 1024 * 1024) || !socket_set_option($serverSocket, SOL_TCP, TCP_NODELAY, 1)) {
137+
if (!socket_set_option($serverSocket, SOL_TCP, TCP_NODELAY, 1)) {
138138
throw new RuntimeException("Failed to set option on socket: " . socket_strerror(socket_last_error($serverSocket)));
139139
}
140140

141+
if (Utils::getOS() !== Utils::OS_MACOS) {
142+
if (!socket_set_option($serverSocket, SOL_SOCKET, SO_SNDBUF, 8 * 1024 * 1024) || !socket_set_option($serverSocket, SOL_SOCKET, SO_RCVBUF, 8 * 1024 * 1024)) {
143+
throw new RuntimeException("Failed to set option on socket: " . socket_strerror(socket_last_error($serverSocket)));
144+
}
145+
}
146+
141147
return $serverSocket;
142148
}
143149
}

0 commit comments

Comments
 (0)