pmmp / PocketMine-MP

A server software for Minecraft: Bedrock Edition in PHP
https://pmmp.io
GNU Lesser General Public License v3.0
3.24k stars 1.52k forks source link

High FPS clients spam certain packets on interactions and trigger Game Packets rate limit #5728

Open SkyLake-git opened 1 year ago

SkyLake-git commented 1 year ago

Issue description

Steps to reproduce the issue

  1. Run around while holding down the right click against the block

OS and versions

Plugins

Command output | Plugins (0):

Crashdump, backtrace or other files

[08:41:23.623] [Server thread/ERROR]: [NetworkSession: Lyrica0954] Bad packet (error ID 4107-4e51-e1af): Exceeded rate limit for "Game Packets"
[08:41:23.624] [Server thread/DEBUG]: [NetworkSession: Lyrica0954] pocketmine\network\PacketHandlingException: "Exceeded rate limit for "Game Packets"" (EXCEPTION) in "pmsrc/src/network/mcpe/PacketRateLimiter" at line 59
--- Stack trace ---
  #0 pmsrc/src/network/mcpe/NetworkSession(368): pocketmine\network\mcpe\PacketRateLimiter->decrement()
  #1 pmsrc/src/network/mcpe/raklib/RakLibInterface(198): pocketmine\network\mcpe\NetworkSession->handleEncoded(string[109] S.c`b`.`a......(...t....{m..........x..?G....U7.nT..Qg7..Bx............uX......@)
  #2 pmsrc/vendor/pocketmine/raklib-ipc/src/RakLibToUserThreadMessageReceiver(42): pocketmine\network\mcpe\raklib\RakLibInterface->onPacketReceive(int 0, string[118] ._f..Z.[.....<......~ [j.@q..z..up>.|...W..).{.m..`.&W9.hh...8..&.......e...T.0D)
  #3 pmsrc/src/network/mcpe/raklib/RakLibInterface(122): raklib\server\ipc\RakLibToUserThreadMessageReceiver->handle(object pocketmine\network\mcpe\raklib\RakLibInterface#25827)
  #4 pmsrc/vendor/pocketmine/snooze/src/SleeperHandler(123): pocketmine\network\mcpe\raklib\RakLibInterface->pocketmine\network\mcpe\raklib\{closure}()
  #5 pmsrc/src/TimeTrackingSleeperHandler(58): pocketmine\snooze\SleeperHandler->processNotifications()
  #6 pmsrc/vendor/pocketmine/snooze/src/SleeperHandler(82): pocketmine\TimeTrackingSleeperHandler->processNotifications()
  #7 pmsrc/src/Server(1681): pocketmine\snooze\SleeperHandler->sleepUntil(float 1682815283.6707)
  #8 pmsrc/src/Server(1061): pocketmine\Server->tickProcessor()
  #9 pmsrc/src/PocketMine(339): pocketmine\Server->__construct(object BaseClassLoader#5, object pocketmine\utils\MainLogger#2, string[33] ....\, string[41] .....\plugins\)
  #10 pmsrc/src/PocketMine(362): pocketmine\server()
  #11 pmsrc(11): require(string[77] phar://..../PocketMine-MP.phar/src/PocketMine.php)
--- End of exception information ---

https://user-images.githubusercontent.com/70795425/235330117-525eb1a2-f4a8-47ec-b9f8-298f1e23b085.mp4

https://user-images.githubusercontent.com/70795425/235330329-0abef213-a601-405f-98e5-ccfa383f58ae.mp4

dktapps commented 1 year ago

I'm unable to reproduce this, either on localhost or on test.pmmp.io. This suggests some issue with your client or network connection is causing this.

What's your ping to the target server when this occurs?

SkyLake-git commented 1 year ago

40ms on localhost, 340ms on test.pmmp.io.

SkyLake-git commented 1 year ago

I checked the packets received by the server with the PacketLogger plugin and found that a large number of InventoryTransactionPackets were sent when right-clicking.

https://user-images.githubusercontent.com/70795425/235644031-b27060a9-ae96-46e9-ba00-cfb07b2a3f4d.mp4

PacketLogger plugin:

<?php

declare(strict_types=1);

namespace Lyrica0954\PacketLogger;

use pocketmine\event\Listener;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\plugin\PluginBase;

class Main extends PluginBase implements Listener {

    protected function onEnable(): void{
        $this->getServer()->getPluginManager()->registerEvents($this, $this);
    }

    public function onDataPacketReceive(DataPacketReceiveEvent $event): void{
        $origin = $event->getOrigin();
        $player = $origin->getPlayer();

        if (is_null($player)){
            return;
        }

        $packet = $event->getPacket();

        $player->sendMessage($packet->getName());
    }

}
SkyLake-git commented 1 year ago

And the number of these packets seems to depend on the fps of the client.

In my case it was like this 60fps: 2 packets/tick 150fps: 3 packets/tick 250fps: 4 packets/tick Unlimited(Actually about 600fps): about 20 packets/tick

No packet processing errors occurred until unlimited fps.

https://user-images.githubusercontent.com/70795425/235646594-b0989a80-98c0-4a19-8ace-0e50dfd58ea7.mp4

dktapps commented 1 year ago

Ugh.

I suspected that the rate of this was somehow linked to FPS, but Minecraft on my machine is vsynced and it never occurred to me to disable it.

I don't think there's a good solution for this problem. This limit was imposed to prevent players from attacking the server by spamming costly packets.

Perhaps it's worthwhile linking this issue to the FAQ in doc.pmmp.io. No one needs 600 FPS anyway.

GameParrot commented 1 year ago

Client bug i believe, can confirm with bedrock-protocol relay to the hive. Results in high fps clients being kicked from the hive when holding right click to pillar up. Report it to mojang,

ByNamles commented 1 year ago

Is there any way to solve this ?

dktapps commented 1 year ago

Reduce your FPS cap.

ByNamles commented 1 year ago

Does every player with high fps entering the server have to do this?

ByNamles commented 1 year ago

I keep getting the same error even though I fixed it to 60 fps

ethaniccc commented 11 months ago

I'd like to add that the rate limit is also reached when breaking a block, due to multiple Animate packets being sent per client tick. The higher the frame amount, the more animation packets are sent.

Debug, format is "animation packet at (client tick)":

image Around 4 animations are sent per client tick.

image Around 11 to 12 animations are sent per client tick.

GameParrot commented 11 months ago

i can confirm being kicked from zeqa when holding right click an looking at the ground (500+ fps). i can also get kicked from trying to mine blocks on zeqa.

dktapps commented 4 months ago

Some additional context for posterity:

The Game Packets rate limit was imposed to prevent denial-of-service attacks by spamming packets. It's a problem because the client is essentially attacking the server because of Mojang shitcode. The bulk of the problem comes from decoding the packets, which can be very costly for a variety of reasons, which is why we (currently) kick the player to stop them wasting more of the server CPU time.

Options for solutions currently being considered are to add special rate limit behaviour for InventoryTransactionPacket (and a couple of other packets which are similarly spammy) to drop the excess without decoding, instead of kicking the player. This was suggested to me by @Spajker7. It's a good idea but currently a bit awkward to implement, and I'm not sure it won't cause other problems.

The obvious option (increase the limit) would increase the ability of attackers to take down servers, while also not solving the problem - as faster GPUs become available, max FPS of the client will continue to rise, and the problem will keep recurring with higher and higher limits. Therefore we need a better solution for this problem that isn't just kicking the can down the road.