-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Process SkinData asynchronously #6589
Open
Blackjack200
wants to merge
8
commits into
pmmp:stable
Choose a base branch
from
Blackjack200:async-parse-skin
base: stable
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+129
−24
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e45f989
process SkinData asynchronously
Blackjack200 832c9cd
check whether session is online
Blackjack200 2e69cf2
Fix: Prevent skin overwrite when multiple skin change tasks run concu…
Blackjack200 844cc8e
Fix: Correct disconnect message for invalid login UUID
Blackjack200 a8cddf2
fix codestyle...
Blackjack200 30ed339
onCompletion(): replace instanceof check with @var for consistency
Blackjack200 438d7ce
Fix missing handling for invalid base64 data case and adjust other code
Blackjack200 d3b54db
...
Blackjack200 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
|
||
/* | ||
* | ||
* ____ _ _ __ __ _ __ __ ____ | ||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ | ||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | | ||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ | ||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Lesser General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* @author PocketMine Team | ||
* @link http://www.pocketmine.net/ | ||
* | ||
* | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace pocketmine\network\mcpe; | ||
|
||
use pocketmine\entity\InvalidSkinException; | ||
use pocketmine\entity\Skin; | ||
use pocketmine\network\mcpe\convert\TypeConverter; | ||
use pocketmine\network\mcpe\protocol\types\skin\SkinData; | ||
use pocketmine\scheduler\AsyncTask; | ||
use pocketmine\thread\NonThreadSafeValue; | ||
|
||
class ProcessSkinTask extends AsyncTask{ | ||
private const TLS_KEY_ON_COMPLETION = "onCompletion"; | ||
|
||
private ?string $error = "Unknown"; | ||
/** @var NonThreadSafeValue<SkinData> */ | ||
private NonThreadSafeValue $skinData; | ||
|
||
/** | ||
* @param \Closure(?Skin $skin,?string $error) : void $callback | ||
*/ | ||
public function __construct( | ||
SkinData $skinData, | ||
\Closure $callback, | ||
){ | ||
$this->skinData = new NonThreadSafeValue($skinData); | ||
$this->storeLocal(self::TLS_KEY_ON_COMPLETION, $callback); | ||
} | ||
|
||
public function onRun() : void{ | ||
try{ | ||
$skin = TypeConverter::getInstance()->getSkinAdapter()->fromSkinData($this->skinData->deserialize()); | ||
$this->setResult($skin); | ||
$this->error = null; | ||
}catch(\InvalidArgumentException|InvalidSkinException $e){ | ||
$this->error = $e->getMessage(); | ||
} | ||
} | ||
|
||
public function onCompletion() : void{ | ||
/** @var Skin|null $result */ | ||
$result = $this->getResult(); | ||
/** @var \Closure(?Skin $skin,?string $error) : void $callback */ | ||
$callback = $this->fetchLocal(self::TLS_KEY_ON_COMPLETION); | ||
$callback($result, $this->error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would probably make the server to freeze / crash if a malicious client floods tons of skin change requests per tick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the existing
$this->gamePacketLimiter
be sufficient for skin change packets, or would it be better to implement a separate rate limiter specifically forPlayerSkinPacket
to prevent potential flooding?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a separate rate limiter is needed, how should we set the values for
INCOMING_SKIN_PACKET_PER_TICK = 1
andINCOMING_SKIN_PACKET_BUFFER_TICKS = 100
?Would these settings be appropriate, or should we adjust them further?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not. It's supposed to limit all packets, so it has quite generous limits.
More so the issue here is memory overload if the client spams a lot of skin change packets (it could cause a large backlog in the worker pool & hog a lot of memory).
My thought was to limit the number of concurrent
ProcessSkinTask
s per session, but I'm not sure if this is the best way.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Limiting the rate of
PlayerSkinPacket
packets seems easier to implement and would have a similar effect to limiting the number of concurrentProcessSkinTask
.In my preliminary testing, setting
INCOMING_SKIN_PACKET_PER_TICK = 0.1
andINCOMING_SKIN_PACKET_BUFFER_TICKS = 20
seems promising (though it still needs sufficient testing). However, this would require changing the$averagePerTick
inPacketLimiter
from anint
to afloat
or both. Would that be acceptable?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I don't think so. The number of packets isn't the only factor we need to consider. We also have to be aware of how long each packet takes to process.
Under normal conditions the packets might take 10 ms each to process, but an attacker could craft packets that take 10-100x longer to process and still be subject to the same packets-per-tick limits (getting more lag per packet). That's why I said we probably need to limit the number of tasks, rather than the number of packets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from the better methods for now, we could consider handling these potential time-bomb data by spawning new processes, similar to ConsoleReaderChildProcess. This would allow the main thread to control their runtime, using OS-level process killing (with proper abstraction for generalization—I’ve already implemented this in my private project, leveraging opis/closure). However, this approach does come with challenging compatibility issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds overengineered to me.