Compare commits

...

59 Commits

Author SHA1 Message Date
bd21feffc4 Release 4.18.0-ALPHA1 2023-03-16 21:45:18 +00:00
5b324f695c Merge branch 'stable' into minor-next 2023-03-16 15:04:19 +00:00
9caed10488 update-updater-api: use github.repository_owner to make fork testing of this workflow less obnoxious 2023-03-16 15:03:14 +00:00
83945ff0a0 Do not update release channels if the new build has a lower version ID
this prevents stuff like 5.0.0 beta versions getting overwritten by 4.x beta versions.
2023-03-16 15:02:42 +00:00
ef45180b80 Rename DataPacketPreReceiveEvent -> DataPacketDecodeEvent
thank you @IvanCraft623 for the suggestion
2023-03-16 13:40:37 +00:00
941fd03998 Remove useless code 2023-03-15 22:58:10 +00:00
1af8da3c1f Merge branch 'minor-next' of github.com:pmmp/PocketMine-MP into minor-next 2023-03-15 22:54:05 +00:00
a5985dcf7d Merge branch 'stable' into minor-next 2023-03-15 22:53:44 +00:00
183d1f4038 Implement DataPacketPreReceiveEvent (#5559)
closes #5554

This is called just before the packet is decoded, allowing the event to be used to drop packets from clients without wasting CPU time decoding them. This can be particularly useful for mitigating denial-of-service attacks.
2023-03-15 22:47:19 +00:00
08ee825d91 StandardPacketBroadcaster: Include varint length prefix in length calculation
varints encode 7 bits per byte, so a log with base 128 will tell us how many bytes are required to encode the length of the packet.
2023-03-15 22:41:19 +00:00
337a254768 Use NetworkBroadcastUtils for broadcasting packets
this eradicates all but 4 usages of Server in Entity, which is extremely cool.
2023-03-15 22:28:51 +00:00
a31e3331fd Move Server::broadcastPackets() to NetworkBroadcastUtils::broadcastPackets()
this has no business being in Server, and it also doesn't need to be an instance method, since it never uses $this.
2023-03-15 22:25:23 +00:00
acebbeed16 Added version channels for update.pmmp.io 2023-03-15 20:59:36 +00:00
e0fdbe6eb1 make-release: don't automatically push
this is rather obnoxious when attempting to push test releases to a
fork.
2023-03-15 20:47:01 +00:00
cc8660629b First look at shared EntityEventBroadcaster,
this improves performance in PvP servers and other areas where lots of players or entities exist in one space.

fixes #5622
2023-03-15 18:22:56 +00:00
e7e19abe85 IPv4 and IPv6 RakLibInterface instances now both use the same broadcaster and context
fixes #5625
2023-03-15 17:17:56 +00:00
5f9e0081fd Fixed mushroom block silk-touch drops and block picking behaviour
fixes #5284
2023-03-15 16:36:35 +00:00
b266f45152 Bump build/php from 71b9f9d to a464454 (#5637)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `71b9f9d` to `a464454`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](71b9f9d2d7...a464454d1e)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 13:44:20 +00:00
14f141fab2 NetworkSession: Stop counting DataPacketReceiveEvent in handler timings
we want it to be included in receive timings, but not handler timings. Handler timings should reflect the time spent in the actual session PacketHandler, not in the event.
2023-03-14 19:00:15 +00:00
daff955bc4 Merge remote-tracking branch 'origin/stable' into minor-next 2023-03-14 18:42:14 +00:00
0022d82779 Merge commit 'd376399b7f332384532a82eaf69b9b02dad5bd0c' into minor-next 2023-03-14 18:39:03 +00:00
7cad9be0d2 Bump build/php from b2207cf to 71b9f9d (#5634)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `b2207cf` to `71b9f9d`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](b2207cf70d...71b9f9d2d7)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-14 18:32:10 +00:00
2f862a552a actions: Replace deprecated ::set-output commands 2023-03-14 18:31:25 +00:00
590f6dad08 4.17.1 is next 2023-03-14 18:14:40 +00:00
9564c81582 Release 4.17.0 2023-03-14 18:14:40 +00:00
3de7a8c27f Updated for 1.19.70 2023-03-14 18:08:10 +00:00
d376399b7f Update composer dependencies
bedrock-item-upgrade-schema and bedrock-block-upgrade-schema are now minor-version-locked, to prevent introducing new upgrade schemas not intended for the currently in-use version
previously I'd intended to do this using max schema IDs, but this has proven to be error-prone, so it makes more sense to lock them in using package version constraints instead.
2023-03-14 17:29:25 +00:00
e2071e59c8 actions: update PHP versions 2023-03-13 17:44:10 +00:00
8e280ebb8b RuntimeBlockMapping: avoid unnecessary PacketSerializer usage 2023-03-11 22:16:24 +00:00
fa7c38276c Fixing gigantic clusterfuck with protocol contexts and broadcasting
fixes #5623
2023-03-11 21:54:14 +00:00
b13e97de3d Timings: fixed receivePacket timer showing 2x the actual number of received packets 2023-03-11 19:13:10 +00:00
328b87fc18 Bump phpstan/phpstan from 1.10.4 to 1.10.6 (#5617)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.4 to 1.10.6.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.4...1.10.6)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-10 11:49:34 +00:00
acaa1a9ce1 contributing: update table to reflect branch name changes
these branches were renamed to make them more auto-complete-friendly.
2023-03-07 17:28:31 +00:00
3aec0fa3df 4.16.1 is next 2023-03-07 16:19:51 +00:00
fa131dab12 Release 4.16.0 2023-03-07 16:19:50 +00:00
bb4a82b1e7 Merge branch 'next-minor' into stable 2023-03-07 16:15:57 +00:00
93d844a281 build/make-release: improve support for non-stable release channels 2023-03-07 16:12:27 +00:00
616844696e 4.15.4 is next 2023-03-07 15:33:03 +00:00
71e3e36522 Release 4.15.3 2023-03-07 15:33:03 +00:00
a1b42d419f Merge branch 'stable' into next-minor 2023-03-07 15:24:24 +00:00
ef942a627f actions: drop concurrency group
this is causing builds to get randomly cancelled when multiple branches are pushed at once.
2023-03-07 15:23:12 +00:00
fd8c276bd2 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-03-07 15:15:21 +00:00
9783380d1a Merge branch 'stable' into next-minor 2023-03-07 15:13:39 +00:00
a784d93bfd Update composer dependencies 2023-03-07 15:13:16 +00:00
a05e8b366f Bump phpstan/phpstan from 1.10.3 to 1.10.4 (#5610)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.3 to 1.10.4.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.3...1.10.4)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-07 14:40:08 +00:00
87a2e0460c Ping Discord news subscribers on new releases 2023-03-04 17:09:32 +00:00
4073c3fb39 Update composer dependencies 2023-03-04 16:56:08 +00:00
e227e6d8bf Merge branch 'stable' into next-minor 2023-03-04 16:55:56 +00:00
3aa40829ae Update composer dependencies 2023-03-04 16:55:31 +00:00
035d4b7263 MemoryManager: stringify floats, fixes #5598 2023-03-04 16:47:58 +00:00
3db1492c18 Fix CS again 2023-03-04 16:43:29 +00:00
a523189149 Added separate timings for broadcast and session buffer compression 2023-03-04 16:41:41 +00:00
f8893efb94 Don't bother with global batch compression if there is only 1 recipient
this allows the session to achieve better ratios, and also reduces worker pool spam.
2023-03-04 16:34:00 +00:00
70f1ee3e97 draft-release: set prerelease flag properly 2023-03-04 16:29:26 +00:00
eb2f0ed3d0 4.16.0-BETA3 is next 2023-03-04 16:19:34 +00:00
14e7d3e143 Release 4.16.0-BETA2 2023-03-04 16:19:34 +00:00
6d636fc2c7 4.16.0-BETA2 is next 2023-03-04 16:18:04 +00:00
0edc5f8113 Bump phpunit/phpunit from 9.6.3 to 9.6.4 (#5597)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.6.3 to 9.6.4.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.6.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.6.3...9.6.4)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 14:06:18 +00:00
a382f0fd92 Bump phpstan/phpstan-phpunit from 1.3.8 to 1.3.10 (#5602)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.3.8 to 1.3.10.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.3.8...1.3.10)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 14:05:59 +00:00
39 changed files with 925 additions and 445 deletions

View File

@ -30,20 +30,20 @@ jobs:
id: tag-name
run: |
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
echo ::set-output name=TAG_NAME::$VERSION
echo ::set-output name=MAJOR::$(echo $VERSION | cut -d. -f1)
echo ::set-output name=MINOR::$(echo $VERSION | cut -d. -f1-2)
echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
- name: Download new release information
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
- name: Detect channel
id: channel
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
run: echo CHANNEL=$(jq -r '.channel' new_build_info.json) >> $GITHUB_OUTPUT
- name: Get name of Docker repository name
id: docker-repo-name
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v4.0.0

View File

@ -18,8 +18,9 @@ require dirname(__DIR__, 2) . '/vendor/autoload.php';
/**
* @phpstan-return array<string, mixed>
*/
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl) : array{
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl, int $newsPingRoleId) : array{
return [
"content" => "<@&$newsPingRoleId> New PocketMine-MP release: $version ($channel)",
"embeds" => [
[
"title" => "New PocketMine-MP release: $version ($channel)",
@ -35,11 +36,11 @@ DESCRIPTION,
];
}
if(count($argv) !== 5){
fwrite(STDERR, "Required arguments: github repo, version, API token\n");
if(count($argv) !== 6){
fwrite(STDERR, "Required arguments: github repo, version, API token, webhook URL, ping role ID\n");
exit(1);
}
[, $repo, $tagName, $token, $hookURL] = $argv;
[, $repo, $tagName, $token, $hookURL, $newsPingRoleId] = $argv;
$result = Internet::getURL('https://api.github.com/repos/' . $repo . '/releases/tags/' . $tagName, extraHeaders: [
'Authorization: token ' . $token
@ -86,7 +87,7 @@ $buildLogUrl = $buildInfoJson["build_log_url"];
$description = $releaseInfoJson["body"];
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl);
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl, (int) $newsPingRoleId);
$response = Internet::postURL(
$hookURL,

View File

@ -32,7 +32,7 @@ jobs:
- name: Get actual tag name
id: tag-name
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
- name: Run webhook post script
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }}

View File

@ -40,7 +40,7 @@ jobs:
run: |
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
echo "Build number: $BUILD_NUMBER"
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
echo BUILD_NUMBER=$BUILD_NUMBER >> $GITHUB_OUTPUT
- name: Minify BedrockData JSON files
run: php vendor/pocketmine/bedrock-data/.minify_json.php
@ -51,11 +51,12 @@ jobs:
- name: Get PocketMine-MP release version
id: get-pm-version
run: |
echo ::set-output name=PM_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;')
echo ::set-output name=MCPE_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;')
echo ::set-output name=PM_VERSION_SHORT::$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);')
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
echo ::set-output name=CHANGELOG_SUFFIX::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;')
echo PM_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;') >> $GITHUB_OUTPUT
echo MCPE_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;') >> $GITHUB_OUTPUT
echo PM_VERSION_SHORT=$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);') >> $GITHUB_OUTPUT
echo PM_VERSION_MD=$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);') >> $GITHUB_OUTPUT
echo CHANGELOG_SUFFIX=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;') >> $GITHUB_OUTPUT
echo PRERELEASE=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "false" : "true";') >> $GITHUB_OUTPUT
- name: Generate build info
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} ${{ github.run_id }} > build_info.json
@ -75,6 +76,7 @@ jobs:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}
draft: true
prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }}
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -9,12 +9,11 @@ jobs:
build-php:
name: Prepare PHP
runs-on: ${{ matrix.image }}
concurrency: php-build-${{ matrix.php }}
strategy:
matrix:
image: [ubuntu-20.04]
php: [8.0.27, 8.1.14, 8.2.1]
php: [8.0.28, 8.1.16, 8.2.3]
steps:
- name: Build and prepare PHP cache
@ -33,7 +32,7 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.27, 8.1.14, 8.2.1]
php: [8.0.28, 8.1.16, 8.2.3]
steps:
- uses: actions/checkout@v3
@ -72,7 +71,7 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.27, 8.1.14, 8.2.1]
php: [8.0.28, 8.1.16, 8.2.3]
steps:
- uses: actions/checkout@v3
@ -111,7 +110,7 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.27, 8.1.14, 8.2.1]
php: [8.0.28, 8.1.16, 8.2.3]
steps:
- uses: actions/checkout@v3
@ -152,7 +151,7 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.27, 8.1.14, 8.2.1]
php: [8.0.28, 8.1.16, 8.2.3]
steps:
- uses: actions/checkout@v3

View File

@ -15,23 +15,68 @@ jobs:
- uses: actions/checkout@v3
with:
repository: pmmp/update.pmmp.io
repository: ${{ github.repository_owner }}/update.pmmp.io
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
- name: Get actual tag name
id: tag-name
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
- name: Download new release information
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
- name: Detect channel
- name: Detect channels
id: channel
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
- name: Copy release information
run: |
cp new_build_info.json channels/${{ steps.channel.outputs.CHANNEL }}.json
CHANNEL=$(jq -r '.channel' new_build_info.json)
VERSION=${{ steps.tag-name.outputs.TAG_NAME }}
echo CHANNEL=$CHANNEL >> $GITHUB_OUTPUT
if [ "$CHANNEL" == "stable" ]; then
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
else
echo MAJOR=$(echo $VERSION | cut -d. -f1)-$CHANNEL >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2)-$CHANNEL >> $GITHUB_OUTPUT
fi
- name: Update channel info
run: |
function version_id() {
major=$(echo $1 | cut -d. -f1)
minor=$(echo $1 | cut -d. -f2)
patch=$(echo $1 | cut -d. -f3)
echo $(((major * 1000000) + (minor * 1000) + patch))
}
function update_channel() {
local target_file_name="$1"
local new_file_name="$2"
local old_version_id
local new_version_id
if [ ! -f "$target_file_name" ]; then
echo "Creating channel file: $target_file_name"
cp "$new_file_name" "$target_file_name"
else
old_version_id=$(version_id "$(jq -r '.base_version' "$target_file_name")")
new_version_id=$(version_id "$(jq -r '.base_version' "$new_file_name")")
echo "Old version ID: $old_version_id"
echo "New version ID: $new_version_id"
if [ $new_version_id -ge $old_version_id ]; then #suffixed versions will have the same version ID - assume they'll always be newer
echo "Updating channel file: $target_file_name ($old_version_id -> $new_version_id)"
cp "$new_file_name" "$target_file_name"
else
echo "Version $new_version_id is less than $old_version_id, not updating channel file: $target_file_name"
fi
fi
}
update_channel "channels/${{ steps.channel.outputs.CHANNEL }}.json" "new_build_info.json"
update_channel "channels/${{ steps.channel.outputs.MAJOR }}.json" "new_build_info.json"
update_channel "channels/${{ steps.channel.outputs.MINOR }}.json" "new_build_info.json"
rm new_build_info.json
- name: Commit changes

View File

@ -21,7 +21,7 @@ Larger contributions like feature additions should be preceded by a [Change Prop
## Choosing a target branch
PocketMine-MP has three primary branches of development.
| Type of change | `stable` | `next-minor` | `next-major` |
| Type of change | `stable` | `minor-next` | `major-next` |
|:---------------|:--------:|:------------:|:------------:|
| Bug fixes | ✔️ | ✔️ | ✔️ |
| Improvements to API docs | ✔️ | ✔️ | ✔️ |

View File

@ -36,11 +36,12 @@ use function fwrite;
use function getopt;
use function is_string;
use function max;
use function preg_match;
use function preg_replace;
use function sleep;
use function sprintf;
use function str_pad;
use function strlen;
use function strtolower;
use function system;
use const STDERR;
use const STDIN;
@ -102,22 +103,43 @@ function main() : void{
$filteredOpts[$optName] = $optValue;
}
$channel = $filteredOpts["channel"] ?? null;
if(isset($filteredOpts["current"])){
$currentVer = new VersionString($filteredOpts["current"]);
}else{
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
}
if(isset($filteredOpts["next"])){
$nextVer = new VersionString($filteredOpts["next"]);
$nextVer = isset($filteredOpts["next"]) ? new VersionString($filteredOpts["next"]) : null;
$suffix = $currentVer->getSuffix();
if($suffix !== ""){
if($channel === "stable"){
fwrite(STDERR, "error: cannot release a suffixed build into the stable channel\n");
exit(1);
}
if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
echo "error: invalid current version suffix \"$suffix\"; aborting\n";
exit(1);
}
$nextVer ??= new VersionString(sprintf(
"%u.%u.%u-%s%u",
$currentVer->getMajor(),
$currentVer->getMinor(),
$currentVer->getPatch(),
$matches[1],
((int) $matches[2]) + 1
));
$channel ??= strtolower($matches[1]);
}else{
$nextVer = new VersionString(sprintf(
$nextVer ??= new VersionString(sprintf(
"%u.%u.%u",
$currentVer->getMajor(),
$currentVer->getMinor(),
$currentVer->getPatch() + 1
));
$channel ??= "stable";
}
$channel = $filteredOpts["channel"] ?? VersionInfo::BUILD_CHANNEL;
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
echo "$currentVer will be published on release channel \"$channel\".\n";
@ -137,9 +159,6 @@ function main() : void{
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
echo "pushing changes in 5 seconds\n";
sleep(5);
systemWrapper('git push origin HEAD ' . $currentVer->getBaseVersion(), "failed to push changes to remote");
}
main();

View File

@ -28,4 +28,11 @@ Released 24th February 2023.
- Accept Minecraft: Bedrock Edition 1.19.63 (identical protocol to 1.19.62, but different version due to Mojang mixup).
## Fixes
- Fixed `World Population` timer sometimes not being stopped, causing strange results in timings reports.
- Fixed `World Population` timer sometimes not being stopped, causing strange results in timings reports.
# 4.15.3
Released 7th March 2023.
## Fixes
- Fixed `/dumpmemory` crash when any object contained an `INF` or `NaN` float value.
- Updated RakLib for security fixes.

View File

@ -7,6 +7,8 @@ Plugin developers should **only** update their required API to this version if y
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.16.0-BETA1
Released 4th March 2023.
## General
- Added granular timings for packet encode, similar to the existing timings for packet decode.
- Timings now covers several areas of the network system which were previously not counted by network timings, but were counted by total timings. This provides a better insight into the performance of the network system.
@ -35,3 +37,9 @@ Plugin developers should **only** update their required API to this version if y
- Reduced and/or eliminated most usages of `PacketBatch`, since it only appeared as a throwaway object and was therefore wasting performance.
- `Compressor` now exposes `getCompressionThreshold()` instead of `willCompress()`, which allows determining whether a batch will be compressed without allocating it.
- Added `pocketmine\data\bedrock\BedrockDataFiles`, an auto-generated class of constants with the file paths of all BedrockData files. This makes it easier to locate usages, detect unused files and avoid typos.
# 4.16.0-BETA2
Released 4th March 2023.
## General
- Fixed incorrect release channel for 4.16.0-BETA1.

41
changelogs/4.16.md Normal file
View File

@ -0,0 +1,41 @@
**For Minecraft: Bedrock Edition 1.19.62**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.16.0
Released 7th March 2023.
## General
- Added granular timings for packet encode, similar to the existing timings for packet decode.
- Split `Player Network Send - Compression` timings into two timers, one for `Session Buffer` compression and one for `Broadcast` compression.
- Timings now covers several areas of the network system which were previously not counted by network timings, but were counted by total timings. This provides a better insight into the performance of the network system.
## Performance
- Improved performance of packet batch handling by avoiding unnecessary object allocations.
- Improved performance of packet broadcasting when the broadcast size is below the batch threshold. Previously, the packets would be encoded once by every recipient, but now they are encoded once and then added to the send buffer of each session in their raw form.
- This change mostly affects servers with larger maps, where players are more widely distributed.
- Improved performance of packet broadcasting when the broadcast has only one recipient (allow the session to compress the packet with the rest of its buffer).
## Build system
- Added a new script `build/generate-bedrockdata-path-consts.php`, which must be run whenever BedrockData is updated. This script generates a class of constants with the file paths of all BedrockData files.
## API
### `pocketmine\entity`
- The following new API methods have been added:
- `public Entity->getGravity() : float` - returns the entity's gravity acceleration in blocks/tick^2
- `public Entity->setGravity(float $gravity) : void` - sets the entity's gravity acceleration in blocks/tick^2
## Internals
- Now uses [`pocketmine/bedrock-data` 2.0.0](https://github.com/pmmp/BedrockData/releases/tag/2.0.0+bedrock-1.19.60).
- This version is now used by both PM4 and PM5, reducing maintenance burden.
- Now uses [`pocketmine/bedrock-protocol` 19.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/19.3.0+bedrock-1.19.62).
- This version provides new APIs for handling packet batches which enabled improving performance and adding new features, such as detailed packet encode timings.
- Crafting recipes and creative inventory data are now loaded from `recipes/legacy_recipes.json` and `recipes/legacy_creativeitems.json` respectively. Previously, these were loaded from BedrockData directly, but BedrockData 2.0 now uses a format which can't be supported in 4.x without BC breaks.
- Added dependencies on [`pocketmine/bedrock-block-upgrade-schema`](https://github.com/pmmp/BedrockBlockUpgradeSchema) and [`pocketmine/bedrock-item-upgrade-schema`](https://github.com/pmmp/BedrockItemUpgradeSchema). These provide mapping files no longer present in BedrockData 2.0.
- Reduced and/or eliminated most usages of `PacketBatch`, since it only appeared as a throwaway object and was therefore wasting performance.
- `Compressor` now exposes `getCompressionThreshold()` instead of `willCompress()`, which allows determining whether a batch will be compressed without allocating it.
- Added `pocketmine\data\bedrock\BedrockDataFiles`, an auto-generated class of constants with the file paths of all BedrockData files. This makes it easier to locate usages, detect unused files and avoid typos.

14
changelogs/4.17.md Normal file
View File

@ -0,0 +1,14 @@
**For Minecraft: Bedrock Edition 1.19.70**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.17.0
Released 14th March 2023.
## General
- Added support for Minecraft: Bedrock Edition 1.19.70.
- Removed support for older versions.

45
changelogs/4.18-alpha.md Normal file
View File

@ -0,0 +1,45 @@
**For Minecraft: Bedrock Edition 1.19.70**
### Note about API versions
Plugins which don't touch the `pocketmine\network\mcpe` namespace are compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
### Alpha release warning
Alpha releases are **experimental**. Features introduced in these releases are subject to change or removal.
APIs which existed **prior** to this version will continue to work as normal, so plugins which use them will continue to work.
### Highlights
This version makes changes to the internal network system to improve server performance and reduce memory usage.
While these changes don't affect non-internal API, they are still significant enough to warrant a new minor version, as they may break plugins which use the internal network API (not recommended).
# 4.18.0-ALPHA1
Released 16th March 2023.
## General
- Improved server performance in congested areas of the world (lots of players and/or entities in the same area).
## API
### `pocketmine\event\server`
- The following new classes have been added:
- `DataPacketDecodeEvent` - called before a packet is decoded by a `NetworkSession`; useful to mitigate DoS attacks if PocketMine-MP hasn't been patched against new bugs yet
## Internals
- Introduced new system for broadcasting entity events to network sessions.
- This change improves performance when lots of players and/or entities are in the same area.
- New interface `EntityEventBroadcaster` and class `StandardEntityEventBroadcaster` have been added to implement this.
- All entity-specific `on*()` and `sync*()` methods have been removed from `NetworkSession` (BC break).
- `NetworkSession` now accepts an `EntityEventBroadcaster` instance in its constructor.
- `NetworkBroadcastUtils::broadcastEntityEvent()` can be used to efficiently broadcast events to unique broadcasters shared by several network sessions.
- All network sessions now share the same `PacketSerializerContext` and `PacketBroadcaster` by default.
- Previously, every session had its own context, meaning that broadcast optimisations were not used, causing significant performance losses compared to 3.x.
- This change improves performance in congested areas by allowing reuse of previously encoded packet buffers for all sessions sharing the same context.
- Packet broadcasts are automatically encoded separately per unique `PacketSerializerContext` instance. This allows, for example, a multi-version fork to have a separate context for each protocol version, to ensure maximum broadcast efficiency while encoding different packets for different versions.
- `PacketSerializerContext` is now passed in `NetworkSession::__construct()`, instead of being created by the session.
- `StandardPacketBroadcaster` is now locked to a single `PacketSerializer` context, reducing complexity.
- Introduced `NetworkBroadcastUtils::broadcastPackets()`, replacing `Server->broadcastPackets()`.
- `Server->broadcastPackets()` has been deprecated. It will be removed in a future version.

View File

@ -34,10 +34,10 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-block-upgrade-schema": "^1.0.0",
"pocketmine/bedrock-data": "~2.0.0+bedrock-1.19.60",
"pocketmine/bedrock-item-upgrade-schema": "^1.0.0",
"pocketmine/bedrock-protocol": "~19.3.0+bedrock-1.19.62",
"pocketmine/bedrock-block-upgrade-schema": "~1.1.1+bedrock-1.19.70",
"pocketmine/bedrock-data": "~2.1.1+bedrock-1.19.70",
"pocketmine/bedrock-item-upgrade-schema": "~1.1.0+bedrock-1.19.70",
"pocketmine/bedrock-protocol": "~20.0.0+bedrock-1.19.70",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
@ -56,7 +56,7 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "1.10.3",
"phpstan/phpstan": "1.10.6",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"

146
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e771a9cf116389fef15848801d410c36",
"content-hash": "1d0c1d2fe668d85ae87110a1e3cfac05",
"packages": [
{
"name": "adhocore/json-comment",
@ -250,16 +250,16 @@
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "1.0.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "a05ce434eb7f8c11058d26833bc975fe635b23b4"
"reference": "e0540343e649a92126a1d4071ec401a811416c76"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/a05ce434eb7f8c11058d26833bc975fe635b23b4",
"reference": "a05ce434eb7f8c11058d26833bc975fe635b23b4",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/e0540343e649a92126a1d4071ec401a811416c76",
"reference": "e0540343e649a92126a1d4071ec401a811416c76",
"shasum": ""
},
"type": "library",
@ -270,22 +270,22 @@
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/1.0.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/1.1.1"
},
"time": "2023-02-01T21:09:54+00:00"
"time": "2023-03-08T23:45:59+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.0.0+bedrock-1.19.60",
"version": "2.1.1+bedrock-1.19.70",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "957e49b2381641af29f595e4f32ded3e76ce4723"
"reference": "cba0567bcb25f987f2712092f8d662056719e82d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/957e49b2381641af29f595e4f32ded3e76ce4723",
"reference": "957e49b2381641af29f595e4f32ded3e76ce4723",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/cba0567bcb25f987f2712092f8d662056719e82d",
"reference": "cba0567bcb25f987f2712092f8d662056719e82d",
"shasum": ""
},
"type": "library",
@ -296,22 +296,22 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/2.0.0+bedrock-1.19.60"
"source": "https://github.com/pmmp/BedrockData/tree/2.1.1+bedrock-1.19.70"
},
"time": "2023-02-23T21:25:04+00:00"
"time": "2023-03-14T18:03:19+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
"version": "1.0.0",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
"reference": "7e53f77ea34ba30b1f94d3c24e64e19d3c4296e7"
"reference": "aab89a1f121a0c127557a4a0cf981330301c9c45"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/7e53f77ea34ba30b1f94d3c24e64e19d3c4296e7",
"reference": "7e53f77ea34ba30b1f94d3c24e64e19d3c4296e7",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/aab89a1f121a0c127557a4a0cf981330301c9c45",
"reference": "aab89a1f121a0c127557a4a0cf981330301c9c45",
"shasum": ""
},
"type": "library",
@ -322,22 +322,22 @@
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.0.0"
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.1.0"
},
"time": "2023-02-01T22:50:02+00:00"
"time": "2023-03-08T22:27:13+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "19.3.0+bedrock-1.19.62",
"version": "20.0.0+bedrock-1.19.70",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "a5bf4753c7f30f781c4541918e238f5bb637e7ad"
"reference": "4892a5020187da805d7b46ab522d8185b0283726"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a5bf4753c7f30f781c4541918e238f5bb637e7ad",
"reference": "a5bf4753c7f30f781c4541918e238f5bb637e7ad",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/4892a5020187da805d7b46ab522d8185b0283726",
"reference": "4892a5020187da805d7b46ab522d8185b0283726",
"shasum": ""
},
"require": {
@ -351,7 +351,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.9.13",
"phpstan/phpstan": "1.10.1",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5"
@ -369,9 +369,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/19.3.0+bedrock-1.19.62"
"source": "https://github.com/pmmp/BedrockProtocol/tree/20.0.0+bedrock-1.19.70"
},
"time": "2023-02-19T16:11:03+00:00"
"time": "2023-03-14T17:06:38+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -780,16 +780,16 @@
},
{
"name": "pocketmine/raklib",
"version": "0.14.5",
"version": "0.14.6",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "85b4e5cb7117d37e010eeadb3ff53b21276c6f48"
"reference": "aeca667d5ecc4cc18fded612f29e3511bbf62f42"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/85b4e5cb7117d37e010eeadb3ff53b21276c6f48",
"reference": "85b4e5cb7117d37e010eeadb3ff53b21276c6f48",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/aeca667d5ecc4cc18fded612f29e3511bbf62f42",
"reference": "aeca667d5ecc4cc18fded612f29e3511bbf62f42",
"shasum": ""
},
"require": {
@ -801,7 +801,7 @@
"pocketmine/log": "^0.3.0 || ^0.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.7.7",
"phpstan/phpstan": "1.9.17",
"phpstan/phpstan-strict-rules": "^1.0"
},
"type": "library",
@ -817,9 +817,9 @@
"description": "A RakNet server implementation written in PHP",
"support": {
"issues": "https://github.com/pmmp/RakLib/issues",
"source": "https://github.com/pmmp/RakLib/tree/0.14.5"
"source": "https://github.com/pmmp/RakLib/tree/0.14.6"
},
"time": "2022-08-25T16:16:44+00:00"
"time": "2023-03-07T15:10:23+00:00"
},
{
"name": "pocketmine/raklib-ipc",
@ -1658,16 +1658,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.11.0",
"version": "1.11.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"shasum": ""
},
"require": {
@ -1705,7 +1705,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
},
"funding": [
{
@ -1713,20 +1713,20 @@
"type": "tidelift"
}
],
"time": "2022-03-03T13:19:32+00:00"
"time": "2023-03-08T13:26:56+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.15.3",
"version": "v4.15.4",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039"
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"shasum": ""
},
"require": {
@ -1767,9 +1767,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
},
"time": "2023-01-16T22:05:37+00:00"
"time": "2023-03-05T19:49:14+00:00"
},
{
"name": "phar-io/manifest",
@ -1884,16 +1884,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.3",
"version": "1.10.6",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "5419375b5891add97dc74be71e6c1c34baaddf64"
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64",
"reference": "5419375b5891add97dc74be71e6c1c34baaddf64",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
"shasum": ""
},
"require": {
@ -1923,7 +1923,7 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.10.3"
"source": "https://github.com/phpstan/phpstan/tree/1.10.6"
},
"funding": [
{
@ -1939,20 +1939,20 @@
"type": "tidelift"
}
],
"time": "2023-02-25T14:47:13+00:00"
"time": "2023-03-09T16:55:12+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.3.9",
"version": "1.3.10",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "34ee324a2b8fcab680fbb3f3f3d6c86389df35ba"
"reference": "4cc5c6cc38e56bce7ea47c4091814e516d172dc3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/34ee324a2b8fcab680fbb3f3f3d6c86389df35ba",
"reference": "34ee324a2b8fcab680fbb3f3f3d6c86389df35ba",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4cc5c6cc38e56bce7ea47c4091814e516d172dc3",
"reference": "4cc5c6cc38e56bce7ea47c4091814e516d172dc3",
"shasum": ""
},
"require": {
@ -1989,9 +1989,9 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.9"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.10"
},
"time": "2023-02-28T13:04:23+00:00"
"time": "2023-03-02T10:25:13+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
@ -2044,16 +2044,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.25",
"version": "9.2.26",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954"
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954",
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"shasum": ""
},
"require": {
@ -2075,8 +2075,8 @@
"phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-pcov": "*",
"ext-xdebug": "*"
"ext-pcov": "PHP extension that provides line coverage",
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
},
"type": "library",
"extra": {
@ -2109,7 +2109,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
},
"funding": [
{
@ -2117,7 +2117,7 @@
"type": "github"
}
],
"time": "2023-02-25T05:32:00+00:00"
"time": "2023-03-06T12:58:08+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -2362,16 +2362,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.4",
"version": "9.6.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d"
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d",
"reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5",
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5",
"shasum": ""
},
"require": {
@ -2404,8 +2404,8 @@
"sebastian/version": "^3.0.2"
},
"suggest": {
"ext-soap": "*",
"ext-xdebug": "*"
"ext-soap": "To be able to generate mocks based on WSDL files",
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
},
"bin": [
"phpunit"
@ -2444,7 +2444,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5"
},
"funding": [
{
@ -2460,7 +2460,7 @@
"type": "tidelift"
}
],
"time": "2023-02-27T13:06:37+00:00"
"time": "2023-03-09T06:34:10+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -49,6 +49,7 @@ use function ini_get;
use function ini_set;
use function intdiv;
use function is_array;
use function is_float;
use function is_object;
use function is_resource;
use function is_string;
@ -519,6 +520,8 @@ class MemoryManager{
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
}elseif(is_resource($from)){
$data = "(resource) " . print_r($from, true);
}elseif(is_float($from)){
$data = "(float) $from";
}else{
$data = $from;
}

View File

@ -43,7 +43,6 @@ use pocketmine\event\player\PlayerCreationEvent;
use pocketmine\event\player\PlayerDataSaveEvent;
use pocketmine\event\player\PlayerLoginEvent;
use pocketmine\event\server\CommandEvent;
use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\event\server\QueryRegenerateEvent;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Language;
@ -54,13 +53,19 @@ use pocketmine\network\mcpe\compression\CompressBatchPromise;
use pocketmine\network\mcpe\compression\CompressBatchTask;
use pocketmine\network\mcpe\compression\Compressor;
use pocketmine\network\mcpe\compression\ZlibCompressor;
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
use pocketmine\network\mcpe\encryption\EncryptionContext;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\PacketBroadcaster;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\raklib\RakLibInterface;
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
use pocketmine\network\mcpe\StandardPacketBroadcaster;
use pocketmine\network\Network;
use pocketmine\network\NetworkInterfaceStartException;
use pocketmine\network\query\DedicatedQueryNetworkInterface;
@ -1169,10 +1174,18 @@ class Server{
return !$anyWorldFailedToLoad;
}
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
private function startupPrepareConnectableNetworkInterfaces(
string $ip,
int $port,
bool $ipV6,
bool $useQuery,
PacketBroadcaster $packetBroadcaster,
EntityEventBroadcaster $entityEventBroadcaster,
PacketSerializerContext $packetSerializerContext
) : bool{
$prettyIp = $ipV6 ? "[$ip]" : $ip;
try{
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6));
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext));
}catch(NetworkInterfaceStartException $e){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
$ip,
@ -1198,11 +1211,15 @@ class Server{
private function startupPrepareNetworkInterfaces() : bool{
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
$packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
$packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext);
$entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster);
if(
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery) ||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext) ||
(
$this->configGroup->getConfigBool("enable-ipv6", true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery)
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext)
)
){
return false;
@ -1334,46 +1351,10 @@ class Server{
/**
* @param Player[] $players
* @param ClientboundPacket[] $packets
* @deprecated
*/
public function broadcastPackets(array $players, array $packets) : bool{
if(count($packets) === 0){
throw new \InvalidArgumentException("Cannot broadcast empty list of packets");
}
return Timings::$broadcastPackets->time(function() use ($players, $packets) : bool{
/** @var NetworkSession[] $recipients */
$recipients = [];
foreach($players as $player){
if($player->isConnected()){
$recipients[] = $player->getNetworkSession();
}
}
if(count($recipients) === 0){
return false;
}
$ev = new DataPacketSendEvent($recipients, $packets);
$ev->call();
if($ev->isCancelled()){
return false;
}
$recipients = $ev->getTargets();
/** @var PacketBroadcaster[] $broadcasters */
$broadcasters = [];
/** @var NetworkSession[][] $broadcasterTargets */
$broadcasterTargets = [];
foreach($recipients as $recipient){
$broadcaster = $recipient->getBroadcaster();
$broadcasters[spl_object_id($broadcaster)] = $broadcaster;
$broadcasterTargets[spl_object_id($broadcaster)][] = $recipient;
}
foreach($broadcasters as $broadcaster){
$broadcaster->broadcastPackets($broadcasterTargets[spl_object_id($broadcaster)], $packets);
}
return true;
});
return NetworkBroadcastUtils::broadcastPackets($players, $packets);
}
/**
@ -1381,9 +1362,10 @@ class Server{
*
* @param bool|null $sync Compression on the main thread (true) or workers (false). Default is automatic (null).
*/
public function prepareBatch(PacketBatch $stream, Compressor $compressor, ?bool $sync = null) : CompressBatchPromise{
public function prepareBatch(PacketBatch $stream, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise{
$timings ??= Timings::$playerNetworkSendCompress;
try{
Timings::$playerNetworkSendCompress->startTiming();
$timings->startTiming();
$buffer = $stream->getBuffer();
@ -1402,7 +1384,7 @@ class Server{
return $promise;
}finally{
Timings::$playerNetworkSendCompress->stopTiming();
$timings->stopTiming();
}
}

View File

@ -31,9 +31,9 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.16.0-BETA1";
public const BASE_VERSION = "4.18.0-ALPHA1";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";
public const BUILD_CHANNEL = "alpha";
private function __construct(){
//NOOP

View File

@ -34,7 +34,7 @@ class RedMushroomBlock extends Opaque{
protected MushroomBlockType $mushroomBlockType;
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
$this->mushroomBlockType = MushroomBlockType::PORES();
$this->mushroomBlockType = MushroomBlockType::ALL_CAP();
parent::__construct($idInfo, $name, $breakInfo);
}
@ -42,6 +42,11 @@ class RedMushroomBlock extends Opaque{
return MushroomBlockTypeIdMap::getInstance()->toId($this->mushroomBlockType);
}
protected function writeStateToItemMeta() : int{
//these blocks always drop as all-cap, but may exist in other forms in the inventory (particularly creative)
return BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_CAP;
}
public function readStateFromData(int $id, int $stateMeta) : void{
$type = MushroomBlockTypeIdMap::getInstance()->fromId($stateMeta);
if($type === null){

View File

@ -44,6 +44,8 @@ use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\AddActorPacket;
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
@ -785,7 +787,7 @@ abstract class Entity{
$this->spawnTo($player);
}
}else{
$this->server->broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
$this->id,
$this->getOffsetPosition($this->location),
$this->location->pitch,
@ -800,7 +802,7 @@ abstract class Entity{
}
protected function broadcastMotion() : void{
$this->server->broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
}
public function getGravity() : float{
@ -1537,7 +1539,7 @@ abstract class Entity{
$id = spl_object_id($player);
if(isset($this->hasSpawned[$id])){
if($send){
$player->getNetworkSession()->onEntityRemoved($this);
$player->getNetworkSession()->getEntityEventBroadcaster()->onEntityRemoved([$player->getNetworkSession()], $this);
}
unset($this->hasSpawned[$id]);
}
@ -1548,9 +1550,11 @@ abstract class Entity{
* player moves, viewers will once again be able to see the entity.
*/
public function despawnFromAll() : void{
foreach($this->hasSpawned as $player){
$this->despawnFrom($player);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->hasSpawned,
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEntityRemoved($recipients, $this)
);
$this->hasSpawned = [];
}
/**
@ -1624,9 +1628,7 @@ abstract class Entity{
$targets = $targets ?? $this->hasSpawned;
$data = $data ?? $this->getAllNetworkData();
foreach($targets as $p){
$p->getNetworkSession()->syncActorData($this, $data);
}
NetworkBroadcastUtils::broadcastEntityEvent($targets, fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->syncActorData($recipients, $this, $data));
}
/**
@ -1680,7 +1682,7 @@ abstract class Entity{
* @param Player[]|null $targets
*/
public function broadcastAnimation(Animation $animation, ?array $targets = null) : void{
$this->server->broadcastPackets($targets ?? $this->getViewers(), $animation->encode());
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $animation->encode());
}
/**
@ -1689,7 +1691,7 @@ abstract class Entity{
*/
public function broadcastSound(Sound $sound, ?array $targets = null) : void{
if(!$this->silent){
$this->server->broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
}
}

View File

@ -47,6 +47,8 @@ use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
@ -174,7 +176,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
* @param Player[]|null $targets
*/
public function sendSkin(?array $targets = null) : void{
$this->server->broadcastPackets($targets ?? $this->hasSpawned, [
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->hasSpawned, [
PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin))
]);
}
@ -189,9 +191,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
public function emote(string $emoteId) : void{
foreach($this->getViewers() as $player){
$player->getNetworkSession()->onEmote($this, $emoteId);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEmote($recipients, $this, $emoteId)
);
}
public function getHungerManager() : HungerManager{
@ -270,11 +273,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->xpManager = new ExperienceManager($this);
$this->inventory = new PlayerInventory($this);
$syncHeldItem = function() : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
}
};
$syncHeldItem = fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this)
);
$this->inventory->getListeners()->add(new CallbackInventoryListener(
function(Inventory $unused, int $slot, Item $unused2) use ($syncHeldItem) : void{
if($slot === $this->inventory->getHeldItemIndex()){
@ -315,11 +317,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
if($offHand !== null){
$this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand));
}
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(function() : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobOffHandItemChange($this);
}
}));
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobOffHandItemChange($recipients, $this)
)));
$enderChestInventoryTag = $nbt->getListTag(self::TAG_ENDER_CHEST_INVENTORY);
if($enderChestInventoryTag !== null){
@ -333,11 +334,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
$this->inventory->setHeldItemIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0));
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
}
});
$this->inventory->getHeldItemIndexChangeListeners()->add(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this)
));
$this->hungerManager->setFood((float) $nbt->getInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood()));
$this->hungerManager->setExhaustion($nbt->getFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion()));
@ -490,11 +490,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
protected function sendSpawnPacket(Player $player) : void{
$networkSession = $player->getNetworkSession();
if(!($this instanceof Player)){
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
$networkSession->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
}
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
$networkSession->sendDataPacket(AddPlayerPacket::create(
$this->getUniqueId(),
$this->getName(),
$this->getId(),
@ -524,11 +525,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
$player->getNetworkSession()->onMobArmorChange($this);
$player->getNetworkSession()->onMobOffHandItemChange($this);
$entityEventBroadcaster = $networkSession->getEntityEventBroadcaster();
$entityEventBroadcaster->onMobArmorChange([$networkSession], $this);
$entityEventBroadcaster->onMobOffHandItemChange([$networkSession], $this);
if(!($this instanceof Player)){
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
$networkSession->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
}
}

View File

@ -50,6 +50,8 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
@ -143,13 +145,10 @@ abstract class Living extends Entity{
$this->armorInventory = new ArmorInventory($this);
//TODO: load/save armor inventory contents
$this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
function(Inventory $unused) : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobArmorChange($this);
}
}
));
$this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this)
)));
$health = $this->getMaxHealth();
@ -850,7 +849,8 @@ abstract class Living extends Entity{
protected function sendSpawnPacket(Player $player) : void{
parent::sendSpawnPacket($player);
$player->getNetworkSession()->onMobArmorChange($this);
$networkSession = $player->getNetworkSession();
$networkSession->getEntityEventBroadcaster()->onMobArmorChange([$networkSession], $this);
}
protected function syncNetworkData(EntityMetadataCollection $properties) : void{

View File

@ -35,6 +35,8 @@ use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
@ -328,9 +330,10 @@ class ItemEntity extends Entity{
return;
}
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
);
$inventory = $ev->getInventory();
if($inventory !== null){

View File

@ -33,6 +33,8 @@ use pocketmine\event\entity\ProjectileHitEvent;
use pocketmine\item\VanillaItems;
use pocketmine\math\RayTraceResult;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
@ -196,9 +198,10 @@ class Arrow extends Projectile{
return;
}
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
}
NetworkBroadcastUtils::broadcastEntityEvent(
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
);
$ev->getInventory()?->addItem($ev->getItem());
$this->flagForDespawn();

View File

@ -0,0 +1,54 @@
<?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\event\server;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
use pocketmine\network\mcpe\NetworkSession;
/**
* Called before a packet is decoded and handled by the network session.
* Cancelling this event will drop the packet without decoding it, minimizing wasted CPU time.
*/
class DataPacketDecodeEvent extends ServerEvent implements Cancellable{
use CancellableTrait;
public function __construct(
private NetworkSession $origin,
private int $packetId,
private string $packetBuffer
){}
public function getOrigin() : NetworkSession{
return $this->origin;
}
public function getPacketId() : int{
return $this->packetId;
}
public function getPacketBuffer() : string{
return $this->packetBuffer;
}
}

View File

@ -0,0 +1,93 @@
<?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\Attribute;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\Living;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
/**
* This class allows broadcasting entity events to many viewers on the server network.
*/
interface EntityEventBroadcaster{
/**
* @param NetworkSession[] $recipients
* @param Attribute[] $attributes
*/
public function syncAttributes(array $recipients, Living $entity, array $attributes) : void;
/**
* @param NetworkSession[] $recipients
* @param MetadataProperty[] $properties
*
* @phpstan-param array<int, MetadataProperty> $properties
*/
public function syncActorData(array $recipients, Entity $entity, array $properties) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onEntityEffectAdded(array $recipients, Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onEntityRemoved(array $recipients, Entity $entity) : void;
/**
* TODO: expand this to more than just humans
*
* @param NetworkSession[] $recipients
*/
public function onMobMainHandItemChange(array $recipients,Human $mob) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onMobOffHandItemChange(array $recipients, Human $mob) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onMobArmorChange(array $recipients, Living $mob) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onPickUpItem(array $recipients, Entity $collector, Entity $pickedUp) : void;
/**
* @param NetworkSession[] $recipients
*/
public function onEmote(array $recipients, Human $from, string $emoteId) : void;
}

View File

@ -0,0 +1,103 @@
<?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\event\server\DataPacketSendEvent;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\player\Player;
use pocketmine\timings\Timings;
use function count;
use function spl_object_id;
final class NetworkBroadcastUtils{
private function __construct(){
//NOOP
}
/**
* @param Player[] $recipients
* @param ClientboundPacket[] $packets
*/
public static function broadcastPackets(array $recipients, array $packets) : bool{
if(count($packets) === 0){
throw new \InvalidArgumentException("Cannot broadcast empty list of packets");
}
return Timings::$broadcastPackets->time(function() use ($recipients, $packets) : bool{
/** @var NetworkSession[] $sessions */
$sessions = [];
foreach($recipients as $player){
if($player->isConnected()){
$sessions[] = $player->getNetworkSession();
}
}
if(count($sessions) === 0){
return false;
}
$ev = new DataPacketSendEvent($sessions, $packets);
$ev->call();
if($ev->isCancelled()){
return false;
}
$sessions = $ev->getTargets();
/** @var PacketBroadcaster[] $uniqueBroadcasters */
$uniqueBroadcasters = [];
/** @var NetworkSession[][] $broadcasterTargets */
$broadcasterTargets = [];
foreach($sessions as $recipient){
$broadcaster = $recipient->getBroadcaster();
$uniqueBroadcasters[spl_object_id($broadcaster)] = $broadcaster;
$broadcasterTargets[spl_object_id($broadcaster)][spl_object_id($recipient)] = $recipient;
}
foreach($uniqueBroadcasters as $broadcaster){
$broadcaster->broadcastPackets($broadcasterTargets[spl_object_id($broadcaster)], $packets);
}
return true;
});
}
/**
* @param Player[] $recipients
* @phpstan-param \Closure(EntityEventBroadcaster, array<int, NetworkSession>) : void $callback
*/
public static function broadcastEntityEvent(array $recipients, \Closure $callback) : void{
$uniqueBroadcasters = [];
$broadcasterTargets = [];
foreach($recipients as $recipient){
$session = $recipient->getNetworkSession();
$broadcaster = $session->getEntityEventBroadcaster();
$uniqueBroadcasters[spl_object_id($broadcaster)] = $broadcaster;
$broadcasterTargets[spl_object_id($broadcaster)][spl_object_id($session)] = $session;
}
foreach($uniqueBroadcasters as $k => $broadcaster){
$callback($broadcaster, $broadcasterTargets[$k]);
}
}
}

View File

@ -23,13 +23,9 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe;
use pocketmine\data\bedrock\EffectIdMap;
use pocketmine\entity\Attribute;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\Living;
use pocketmine\event\player\PlayerDuplicateLoginEvent;
use pocketmine\event\server\DataPacketDecodeEvent;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\form\Form;
@ -43,7 +39,6 @@ use pocketmine\network\mcpe\cache\ChunkCache;
use pocketmine\network\mcpe\compression\CompressBatchPromise;
use pocketmine\network\mcpe\compression\Compressor;
use pocketmine\network\mcpe\compression\DecompressionException;
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\encryption\DecryptionException;
@ -62,10 +57,6 @@ use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\DisconnectPacket;
use pocketmine\network\mcpe\protocol\EmotePacket;
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
@ -74,19 +65,16 @@ use pocketmine\network\mcpe\protocol\PacketDecodeException;
use pocketmine\network\mcpe\protocol\PacketPool;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\ServerboundPacket;
use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket;
use pocketmine\network\mcpe\protocol\SetActorDataPacket;
use pocketmine\network\mcpe\protocol\SetDifficultyPacket;
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
use pocketmine\network\mcpe\protocol\SetTimePacket;
use pocketmine\network\mcpe\protocol\SetTitlePacket;
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\ToastRequestPacket;
use pocketmine\network\mcpe\protocol\TransferPacket;
@ -98,16 +86,10 @@ use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\network\mcpe\protocol\UpdateAdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\NetworkSessionManager;
use pocketmine\network\PacketHandlingException;
use pocketmine\permission\DefaultPermissionNames;
@ -135,7 +117,6 @@ use function hrtime;
use function in_array;
use function intdiv;
use function json_encode;
use function ksort;
use function min;
use function strcasecmp;
use function strlen;
@ -144,7 +125,6 @@ use function substr;
use function time;
use function ucfirst;
use const JSON_THROW_ON_ERROR;
use const SORT_NUMERIC;
class NetworkSession{
private const INCOMING_PACKET_BATCH_PER_TICK = 2; //usually max 1 per tick, but transactions may arrive separately
@ -188,8 +168,6 @@ class NetworkSession{
private bool $forceAsyncCompression = true;
private bool $enableCompression = false; //disabled until handshake completed
private PacketSerializerContext $packetSerializerContext;
private ?InventoryManager $invManager = null;
/**
@ -202,8 +180,10 @@ class NetworkSession{
private Server $server,
private NetworkSessionManager $manager,
private PacketPool $packetPool,
private PacketSerializerContext $packetSerializerContext,
private PacketSender $sender,
private PacketBroadcaster $broadcaster,
private EntityEventBroadcaster $entityEventBroadcaster,
private Compressor $compressor,
private string $ip,
private int $port
@ -212,9 +192,6 @@ class NetworkSession{
$this->compressedQueue = new \SplQueue();
//TODO: allow this to be injected
$this->packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
$this->disposeHooks = new ObjectSet();
$this->connectTime = time();
@ -278,10 +255,10 @@ class NetworkSession{
$effectManager = $this->player->getEffects();
$effectManager->getEffectAddHooks()->add($effectAddHook = function(EffectInstance $effect, bool $replacesOldEffect) : void{
$this->onEntityEffectAdded($this->player, $effect, $replacesOldEffect);
$this->entityEventBroadcaster->onEntityEffectAdded([$this], $this->player, $effect, $replacesOldEffect);
});
$effectManager->getEffectRemoveHooks()->add($effectRemoveHook = function(EffectInstance $effect) : void{
$this->onEntityEffectRemoved($this->player, $effect);
$this->entityEventBroadcaster->onEntityEffectRemoved([$this], $this->player, $effect);
});
$this->disposeHooks->add(static function() use ($effectManager, $effectAddHook, $effectRemoveHook) : void{
$effectManager->getEffectAddHooks()->remove($effectAddHook);
@ -431,32 +408,45 @@ class NetworkSession{
throw new PacketHandlingException("Unexpected non-serverbound packet");
}
$timings = Timings::getDecodeDataPacketTimings($packet);
$timings = Timings::getReceiveDataPacketTimings($packet);
$timings->startTiming();
try{
$stream = PacketSerializer::decoder($buffer, 0, $this->packetSerializerContext);
try{
$packet->decode($stream);
}catch(PacketDecodeException $e){
throw PacketHandlingException::wrap($e);
}
if(!$stream->feof()){
$remains = substr($stream->getBuffer(), $stream->getOffset());
$this->logger->debug("Still " . strlen($remains) . " bytes unread in " . $packet->getName() . ": " . bin2hex($remains));
}
}finally{
$timings->stopTiming();
}
$timings = Timings::getHandleDataPacketTimings($packet);
$timings->startTiming();
try{
//TODO: I'm not sure DataPacketReceiveEvent should be included in the handler timings, but it needs to be
//included for now to ensure the receivePacket timings are counted the way they were before
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
$ev->call();
if($ev->isCancelled()){
return;
}
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
$decodeTimings->startTiming();
try{
$stream = PacketSerializer::decoder($buffer, 0, $this->packetSerializerContext);
try{
$packet->decode($stream);
}catch(PacketDecodeException $e){
throw PacketHandlingException::wrap($e);
}
if(!$stream->feof()){
$remains = substr($stream->getBuffer(), $stream->getOffset());
$this->logger->debug("Still " . strlen($remains) . " bytes unread in " . $packet->getName() . ": " . bin2hex($remains));
}
}finally{
$decodeTimings->stopTiming();
}
$ev = new DataPacketReceiveEvent($this, $packet);
$ev->call();
if(!$ev->isCancelled() && ($this->handler === null || !$packet->handle($this->handler))){
$this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer()));
if(!$ev->isCancelled()){
$handlerTimings = Timings::getHandleDataPacketTimings($packet);
$handlerTimings->startTiming();
try{
if($this->handler === null || !$packet->handle($this->handler)){
$this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer()));
}
}finally{
$handlerTimings->stopTiming();
}
}
}finally{
$timings->stopTiming();
@ -528,7 +518,7 @@ class NetworkSession{
PacketBatch::encodeRaw($stream, $this->sendBuffer);
if($this->enableCompression){
$promise = $this->server->prepareBatch(new PacketBatch($stream->getBuffer()), $this->compressor, $syncMode);
$promise = $this->server->prepareBatch(new PacketBatch($stream->getBuffer()), $this->compressor, $syncMode, Timings::$playerNetworkSendCompressSessionBuffer);
}else{
$promise = new CompressBatchPromise();
$promise->resolve($stream->getBuffer());
@ -545,6 +535,8 @@ class NetworkSession{
public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; }
public function getEntityEventBroadcaster() : EntityEventBroadcaster{ return $this->entityEventBroadcaster; }
public function getCompressor() : Compressor{
return $this->compressor;
}
@ -815,7 +807,7 @@ class NetworkSession{
}
public function onServerRespawn() : void{
$this->syncAttributes($this->player, $this->player->getAttributeMap()->getAll());
$this->entityEventBroadcaster->syncAttributes([$this], $this->player, $this->player->getAttributeMap()->getAll());
$this->player->sendData(null);
$this->syncAbilities($this->player);
@ -924,41 +916,6 @@ class NetworkSession{
));
}
/**
* @param Attribute[] $attributes
*/
public function syncAttributes(Living $entity, array $attributes) : void{
if(count($attributes) > 0){
$this->sendDataPacket(UpdateAttributesPacket::create($entity->getId(), array_map(function(Attribute $attr) : NetworkAttribute{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
}, $attributes), 0));
}
}
/**
* @param MetadataProperty[] $properties
* @phpstan-param array<int, MetadataProperty> $properties
*/
public function syncActorData(Entity $entity, array $properties) : void{
//TODO: HACK! as of 1.18.10, the client responds differently to the same data ordered in different orders - for
//example, sending HEIGHT in the list before FLAGS when unsetting the SWIMMING flag results in a hitbox glitch
ksort($properties, SORT_NUMERIC);
$this->sendDataPacket(SetActorDataPacket::create($entity->getId(), $properties, new PropertySyncData([], []), 0));
}
public function onEntityEffectAdded(Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void{
//TODO: we may need yet another effect <=> ID map in the future depending on protocol changes
$this->sendDataPacket(MobEffectPacket::add($entity->getId(), $replacesOldEffect, EffectIdMap::getInstance()->toId($effect->getType()), $effect->getAmplifier(), $effect->isVisible(), $effect->getDuration()));
}
public function onEntityEffectRemoved(Living $entity, EffectInstance $effect) : void{
$this->sendDataPacket(MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType())));
}
public function onEntityRemoved(Entity $entity) : void{
$this->sendDataPacket(RemoveActorPacket::create($entity->getId()));
}
public function syncAvailableCommands() : void{
$commandData = [];
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
@ -1094,36 +1051,6 @@ class NetworkSession{
return $this->invManager;
}
/**
* TODO: expand this to more than just humans
*/
public function onMobMainHandItemChange(Human $mob) : void{
//TODO: we could send zero for slot here because remote players don't need to know which slot was selected
$inv = $mob->getInventory();
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
}
public function onMobOffHandItemChange(Human $mob) : void{
$inv = $mob->getOffHandInventory();
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, 0, ContainerIds::OFFHAND));
}
public function onMobArmorChange(Living $mob) : void{
$inv = $mob->getArmorInventory();
$converter = TypeConverter::getInstance();
$this->sendDataPacket(MobArmorEquipmentPacket::create(
$mob->getId(),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots()))
));
}
public function onPlayerPickUpItem(Player $collector, Entity $pickedUp) : void{
$this->sendDataPacket(TakeItemActorPacket::create($collector->getId(), $pickedUp->getId()));
}
/**
* @param Player[] $players
*/
@ -1167,10 +1094,6 @@ class NetworkSession{
$this->sendDataPacket(SetTitlePacket::setAnimationTimes($fadeIn, $stay, $fadeOut));
}
public function onEmote(Human $from, string $emoteId) : void{
$this->sendDataPacket(EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER));
}
public function onToastNotification(string $title, string $body) : void{
$this->sendDataPacket(ToastRequestPacket::create($title, $body));
}
@ -1210,7 +1133,7 @@ class NetworkSession{
$this->player->doChunkRequests();
$dirtyAttributes = $this->player->getAttributeMap()->needSend();
$this->syncAttributes($this->player, $dirtyAttributes);
$this->entityEventBroadcaster->syncAttributes([$this], $this->player, $dirtyAttributes);
foreach($dirtyAttributes as $attribute){
//TODO: we might need to send these to other players in the future
//if that happens, this will need to become more complex than a flag on the attribute itself

View File

@ -0,0 +1,143 @@
<?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\data\bedrock\EffectIdMap;
use pocketmine\entity\Attribute;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\Living;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\EmotePacket;
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\SetActorDataPacket;
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute;
use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use function array_map;
use function count;
use function ksort;
use const SORT_NUMERIC;
final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
public function __construct(
private StandardPacketBroadcaster $broadcaster
){}
/**
* @param NetworkSession[] $recipients
*/
private function sendDataPacket(array $recipients, ClientboundPacket $packet) : void{
$this->broadcaster->broadcastPackets($recipients, [$packet]);
}
public function syncAttributes(array $recipients, Living $entity, array $attributes) : void{
if(count($attributes) > 0){
$this->sendDataPacket($recipients, UpdateAttributesPacket::create(
$entity->getId(),
array_map(fn(Attribute $attr) => new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []), $attributes),
0
));
}
}
public function syncActorData(array $recipients, Entity $entity, array $properties) : void{
//TODO: HACK! as of 1.18.10, the client responds differently to the same data ordered in different orders - for
//example, sending HEIGHT in the list before FLAGS when unsetting the SWIMMING flag results in a hitbox glitch
ksort($properties, SORT_NUMERIC);
$this->sendDataPacket($recipients, SetActorDataPacket::create($entity->getId(), $properties, new PropertySyncData([], []), 0));
}
public function onEntityEffectAdded(array $recipients, Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void{
//TODO: we may need yet another effect <=> ID map in the future depending on protocol changes
$this->sendDataPacket($recipients, MobEffectPacket::add(
$entity->getId(),
$replacesOldEffect,
EffectIdMap::getInstance()->toId($effect->getType()),
$effect->getAmplifier(),
$effect->isVisible(),
$effect->getDuration()
));
}
public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void{
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType())));
}
public function onEntityRemoved(array $recipients, Entity $entity) : void{
$this->sendDataPacket($recipients, RemoveActorPacket::create($entity->getId()));
}
public function onMobMainHandItemChange(array $recipients, Human $mob) : void{
//TODO: we could send zero for slot here because remote players don't need to know which slot was selected
$inv = $mob->getInventory();
$this->sendDataPacket($recipients, MobEquipmentPacket::create(
$mob->getId(),
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())),
$inv->getHeldItemIndex(),
$inv->getHeldItemIndex(),
ContainerIds::INVENTORY
));
}
public function onMobOffHandItemChange(array $recipients, Human $mob) : void{
$inv = $mob->getOffHandInventory();
$this->sendDataPacket($recipients, MobEquipmentPacket::create(
$mob->getId(),
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))),
0,
0,
ContainerIds::OFFHAND
));
}
public function onMobArmorChange(array $recipients, Living $mob) : void{
$inv = $mob->getArmorInventory();
$converter = TypeConverter::getInstance();
$this->sendDataPacket($recipients, MobArmorEquipmentPacket::create(
$mob->getId(),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())),
ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots()))
));
}
public function onPickUpItem(array $recipients, Entity $collector, Entity $pickedUp) : void{
$this->sendDataPacket($recipients, TakeItemActorPacket::create($collector->getId(), $pickedUp->getId()));
}
public function onEmote(array $recipients, Human $from, string $emoteId) : void{
$this->sendDataPacket($recipients, EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER));
}
}

View File

@ -25,60 +25,65 @@ namespace pocketmine\network\mcpe;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\utils\BinaryStream;
use function count;
use function log;
use function spl_object_id;
use function strlen;
final class StandardPacketBroadcaster implements PacketBroadcaster{
public function __construct(private Server $server){}
public function __construct(
private Server $server,
private PacketSerializerContext $protocolContext
){}
public function broadcastPackets(array $recipients, array $packets) : void{
$packetBufferTotalLengths = [];
$packetBuffers = [];
$compressors = [];
/** @var NetworkSession[][][] $targetMap */
$targetMap = [];
/** @var NetworkSession[][] $targetsByCompressor */
$targetsByCompressor = [];
foreach($recipients as $recipient){
$serializerContext = $recipient->getPacketSerializerContext();
$bufferId = spl_object_id($serializerContext);
if(!isset($packetBuffers[$bufferId])){
$packetBufferTotalLengths[$bufferId] = 0;
$packetBuffers[$bufferId] = [];
foreach($packets as $packet){
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($serializerContext), $packet);
$packetBufferTotalLengths[$bufferId] += strlen($buffer);
$packetBuffers[$bufferId][] = $buffer;
}
if($recipient->getPacketSerializerContext() !== $this->protocolContext){
throw new \InvalidArgumentException("Only recipients with the same protocol context as the broadcaster can be broadcast to by this broadcaster");
}
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
$compressor = $recipient->getCompressor();
$compressors[spl_object_id($compressor)] = $compressor;
$targetMap[$bufferId][spl_object_id($compressor)][] = $recipient;
$targetsByCompressor[spl_object_id($compressor)][] = $recipient;
}
foreach($targetMap as $bufferId => $compressorMap){
foreach($compressorMap as $compressorId => $compressorTargets){
$compressor = $compressors[$compressorId];
$totalLength = 0;
$packetBuffers = [];
foreach($packets as $packet){
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($this->protocolContext), $packet);
//varint length prefix + packet buffer
$totalLength += (((int) log(strlen($buffer), 128)) + 1) + strlen($buffer);
$packetBuffers[] = $buffer;
}
$threshold = $compressor->getCompressionThreshold();
if($threshold !== null && $packetBufferTotalLengths[$bufferId] >= $threshold){
//do not prepare shared batch unless we're sure it will be compressed
$stream = new BinaryStream();
PacketBatch::encodeRaw($stream, $packetBuffers[$bufferId]);
$batchBuffer = $stream->getBuffer();
foreach($targetsByCompressor as $compressorId => $compressorTargets){
$compressor = $compressors[$compressorId];
$promise = $this->server->prepareBatch(new PacketBatch($batchBuffer), $compressor);
foreach($compressorTargets as $target){
$target->queueCompressed($promise);
}
}else{
foreach($compressorTargets as $target){
foreach($packetBuffers[$bufferId] as $packetBuffer){
$target->addToSendBuffer($packetBuffer);
}
$threshold = $compressor->getCompressionThreshold();
if(count($compressorTargets) > 1 && $threshold !== null && $totalLength >= $threshold){
//do not prepare shared batch unless we're sure it will be compressed
$stream = new BinaryStream();
PacketBatch::encodeRaw($stream, $packetBuffers);
$batchBuffer = $stream->getBuffer();
$promise = $this->server->prepareBatch(new PacketBatch($batchBuffer), $compressor, timings: Timings::$playerNetworkSendCompressBroadcast);
foreach($compressorTargets as $target){
$target->queueCompressed($promise);
}
}else{
foreach($compressorTargets as $target){
foreach($packetBuffers as $packetBuffer){
$target->addToSendBuffer($packetBuffer);
}
}
}

View File

@ -29,8 +29,7 @@ use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\data\bedrock\LegacyBlockIdToStringIdMap;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\Filesystem;
use pocketmine\utils\SingletonTrait;
@ -55,14 +54,14 @@ final class RuntimeBlockMapping{
}
public function __construct(string $canonicalBlockStatesFile, string $r12ToCurrentBlockMapFile){
$stream = PacketSerializer::decoder(
Filesystem::fileGetContents($canonicalBlockStatesFile),
0,
new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary())
);
$stream = new BinaryStream(Filesystem::fileGetContents($canonicalBlockStatesFile));
$list = [];
$nbtReader = new NetworkNbtSerializer();
while(!$stream->feof()){
$list[] = $stream->getNbtCompoundRoot();
$offset = $stream->getOffset();
$blockState = $nbtReader->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
$stream->setOffset($offset);
$list[] = $blockState;
}
$this->bedrockKnownStates = $list;
@ -73,14 +72,10 @@ final class RuntimeBlockMapping{
$legacyIdMap = LegacyBlockIdToStringIdMap::getInstance();
/** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */
$legacyStateMap = [];
$legacyStateMapReader = PacketSerializer::decoder(
Filesystem::fileGetContents($r12ToCurrentBlockMapFile),
0,
new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary())
);
$legacyStateMapReader = new BinaryStream(Filesystem::fileGetContents($r12ToCurrentBlockMapFile));
$nbtReader = new NetworkNbtSerializer();
while(!$legacyStateMapReader->feof()){
$id = $legacyStateMapReader->getString();
$id = $legacyStateMapReader->get($legacyStateMapReader->getUnsignedVarInt());
$meta = $legacyStateMapReader->getLShort();
$offset = $legacyStateMapReader->getOffset();

View File

@ -25,7 +25,6 @@ namespace pocketmine\network\mcpe\handler;
use pocketmine\entity\InvalidSkinException;
use pocketmine\event\player\PlayerPreLoginEvent;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\KnownTranslationKeys;
use pocketmine\network\mcpe\auth\ProcessLoginTask;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
@ -33,7 +32,6 @@ use pocketmine\network\mcpe\JwtException;
use pocketmine\network\mcpe\JwtUtils;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\LoginPacket;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\types\login\AuthenticationData;
use pocketmine\network\mcpe\protocol\types\login\ClientData;
use pocketmine\network\mcpe\protocol\types\login\ClientDataToSkinDataHelper;
@ -45,7 +43,6 @@ use pocketmine\player\XboxLivePlayerInfo;
use pocketmine\Server;
use Ramsey\Uuid\Uuid;
use function is_array;
use function preg_match;
/**
* Handles the initial login phase of the session. This handler is used as the initial state.
@ -73,26 +70,6 @@ class LoginPacketHandler extends PacketHandler{
$clientData = $this->parseClientData($packet->clientDataJwt);
//TODO: REMOVE THIS
//Mojang forgot to bump the protocol version when they changed protocol in 1.19.62. Check the game version instead.
if(preg_match('/^(\d+)\.(\d+)\.(\d+)/', $clientData->GameVersion, $matches) !== 1){
throw new PacketHandlingException("Invalid game version format, expected at least 3 digits");
}
$major = (int) $matches[1];
$minor = (int) $matches[2];
$patch = (int) $matches[3];
if($major === 1 && $minor === 19 && $patch < 62){
$this->session->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_FAILED_CLIENT), true);
//This pocketmine disconnect message will only be seen by the console (PlayStatusPacket causes the messages to be shown for the client)
$this->session->disconnect(
$this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_disconnect_incompatibleProtocol("$packet->protocol (< v1.19.62)")),
false
);
return true;
}
try{
$skin = SkinAdapterSingleton::get()->fromSkinData(ClientDataToSkinDataHelper::fromClientData($clientData));
}catch(\InvalidArgumentException | InvalidSkinException $e){

View File

@ -114,7 +114,7 @@ class PreSpawnPacketHandler extends PacketHandler{
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs());
$this->session->getLogger()->debug("Sending attributes");
$this->session->syncAttributes($this->player, $this->player->getAttributeMap()->getAll());
$this->session->getEntityEventBroadcaster()->syncAttributes([$this->session], $this->player, $this->player->getAttributeMap()->getAll());
$this->session->getLogger()->debug("Sending available commands");
$this->session->syncAvailableCommands();
@ -125,7 +125,7 @@ class PreSpawnPacketHandler extends PacketHandler{
$this->session->getLogger()->debug("Sending effects");
foreach($this->player->getEffects()->all() as $effect){
$this->session->onEntityEffectAdded($this->player, $effect, false);
$this->session->getEntityEventBroadcaster()->onEntityEffectAdded([$this->session], $this->player, $effect, false);
}
$this->session->getLogger()->debug("Sending actor metadata");

View File

@ -71,9 +71,6 @@ final class SessionStartPacketHandler extends PacketHandler{
}
protected function isCompatibleProtocol(int $protocolVersion) : bool{
//TODO: REMOVE THIS
//1.19.63 released with an unchanged protocol, but a bumped protocol version, since they forgot to do it in the
//previous release. Since they are functionally identical, we can accept both.
return $protocolVersion === ProtocolInfo::CURRENT_PROTOCOL || $protocolVersion === 568;
return $protocolVersion === ProtocolInfo::CURRENT_PROTOCOL;
}
}

View File

@ -26,11 +26,12 @@ namespace pocketmine\network\mcpe\raklib;
use pocketmine\network\AdvancedNetworkInterface;
use pocketmine\network\mcpe\compression\ZlibCompressor;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\PacketBroadcaster;
use pocketmine\network\mcpe\protocol\PacketPool;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\StandardPacketBroadcaster;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\Network;
use pocketmine\network\NetworkInterfaceStartException;
use pocketmine\network\PacketHandlingException;
@ -78,10 +79,16 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
private SleeperNotifier $sleeper;
private PacketBroadcaster $broadcaster;
private PacketBroadcaster $packetBroadcaster;
private EntityEventBroadcaster $entityEventBroadcaster;
private PacketSerializerContext $packetSerializerContext;
public function __construct(Server $server, string $ip, int $port, bool $ipV6){
public function __construct(Server $server, string $ip, int $port, bool $ipV6, PacketBroadcaster $packetBroadcaster, EntityEventBroadcaster $entityEventBroadcaster, PacketSerializerContext $packetSerializerContext){
$this->server = $server;
$this->packetBroadcaster = $packetBroadcaster;
$this->packetSerializerContext = $packetSerializerContext;
$this->entityEventBroadcaster = $entityEventBroadcaster;
$this->rakServerId = mt_rand(0, PHP_INT_MAX);
$this->sleeper = new SleeperNotifier();
@ -105,8 +112,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->interface = new UserToRakLibThreadMessageSender(
new PthreadsChannelWriter($mainToThreadBuffer)
);
$this->broadcaster = new StandardPacketBroadcaster($this->server);
}
public function start() : void{
@ -166,8 +171,10 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->server,
$this->network->getSessionManager(),
PacketPool::getInstance(),
$this->packetSerializerContext,
new RakLibPacketSender($sessionId, $this),
$this->broadcaster,
$this->packetBroadcaster,
$this->entityEventBroadcaster,
ZlibCompressor::getInstance(), //TODO: this shouldn't be hardcoded, but we might need the RakNet protocol version to select it
$address,
$port

View File

@ -52,6 +52,10 @@ abstract class Timings{
public static $playerNetworkSend;
/** @var TimingsHandler */
public static $playerNetworkSendCompress;
public static TimingsHandler $playerNetworkSendCompressBroadcast;
public static TimingsHandler $playerNetworkSendCompressSessionBuffer;
/** @var TimingsHandler */
public static $playerNetworkSendEncrypt;
/** @var TimingsHandler */
@ -153,6 +157,8 @@ abstract class Timings{
self::$playerNetworkSend = new TimingsHandler("Player Network Send");
self::$playerNetworkSendCompress = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Player Network Send - Compression", self::$playerNetworkSend);
self::$playerNetworkSendCompressBroadcast = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Player Network Send - Compression (Broadcast)", self::$playerNetworkSendCompress);
self::$playerNetworkSendCompressSessionBuffer = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Player Network Send - Compression (Session Buffer)", self::$playerNetworkSendCompress);
self::$playerNetworkSendEncrypt = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Player Network Send - Encryption", self::$playerNetworkSend);
self::$playerNetworkReceive = new TimingsHandler("Player Network Receive");

View File

@ -65,6 +65,7 @@ use pocketmine\math\Vector3;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\NetworkBroadcastUtils;
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
@ -685,7 +686,7 @@ class World implements ChunkManager{
$this->broadcastPacketToViewers($pos, $e);
}
}else{
$this->server->broadcastPackets($this->filterViewersForPosition($pos, $players), $pk);
NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $players), $pk);
}
}
}
@ -711,7 +712,7 @@ class World implements ChunkManager{
$this->broadcastPacketToViewers($pos, $e);
}
}else{
$this->server->broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk);
NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk);
}
}
}
@ -1021,7 +1022,7 @@ class World implements ChunkManager{
World::getXZ($index, $chunkX, $chunkZ);
$chunkPlayers = $this->getChunkPlayers($chunkX, $chunkZ);
if(count($chunkPlayers) > 0){
$this->server->broadcastPackets($chunkPlayers, $entries);
NetworkBroadcastUtils::broadcastPackets($chunkPlayers, $entries);
}
}

View File

@ -490,11 +490,6 @@ parameters:
count: 1
path: ../../../src/command/defaults/TimingsCommand.php
-
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
count: 2
path: ../../../src/console/ConsoleReader.php
-
message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#"
count: 1
@ -650,21 +645,6 @@ parameters:
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:onEntityEffectAdded\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:onEntityEffectRemoved\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAttributes\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAbilities\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 2
@ -685,6 +665,21 @@ parameters:
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:onEntityEffectAdded\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:onEntityEffectRemoved\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:syncAttributes\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#2 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\PreSpawnPacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1