Compare commits

..

66 Commits
4.5.1 ... 4.7.1

Author SHA1 Message Date
19d7c2b552 Release 4.7.1 2022-08-14 19:55:56 +01:00
036e06e889 Revert "Workaround items in blockentity NBT not being processed correctly in 1.19.10"
This reverts commit 2b61c025c2.
2022-08-14 17:25:55 +01:00
9343a0b800 Added build log link to Discord release embed 2022-08-14 17:20:01 +01:00
14b4644b03 Added build_log_url to build_info.json 2022-08-14 17:20:01 +01:00
464b65b25c Bump docker/build-push-action from 3.1.0 to 3.1.1 (#5213)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-12 21:11:11 +01:00
15586ed80e Fix CS 2022-08-12 21:09:15 +01:00
0f8ad8ecf7 Update permission doc output format 2022-08-12 20:47:38 +01:00
82b9afef77 Allow generating RST permission summaries, to be used on doc.pmmp.io 2022-08-12 18:00:52 +01:00
2fc84f6c67 ItemFactory: treat durables with negative meta as unknown items
fixes #5117
2022-08-12 17:24:43 +01:00
566f5935a3 CraftingManagerFromDataHelper: do not register recipes with unknown outputs
fixes #5093

we don't need to check the inputs, since unknown input items shouldn't be obtainable anyway.
2022-08-12 17:19:47 +01:00
44e4dabf6e Fixed Turtle Master potions giving no effects 2022-08-12 17:05:08 +01:00
8acc535218 ffs 2022-08-09 19:27:54 +01:00
e9a1cb7ce5 4.7.1 is next 2022-08-09 19:24:02 +01:00
a21419d120 Release 4.7.0 2022-08-09 19:24:01 +01:00
c2b599166c Added new shiny webhook for Discord release notifications 2022-08-09 19:21:36 +01:00
df7a1fcba6 Changes for 1.19.20 2022-08-09 19:06:05 +01:00
d77a95e4af actions/draft-release: bake the full changelog blob URL into the release notes, to ensure it works properly in emails and embeds 2022-08-06 15:49:04 +01:00
5c72807b16 Bump shivammathur/setup-php from 2.21.0 to 2.21.1 (#5199)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.21.0 to 2.21.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.21.0...2.21.1)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  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>
2022-08-06 15:39:36 +01:00
5c6927e16c 4.6.3 is next 2022-08-06 15:35:47 +01:00
9abbb85a93 Release 4.6.2 2022-08-06 15:35:47 +01:00
554182b2cb Update composer dependencies 2022-08-06 15:27:11 +01:00
d669a6f0c7 ReversePriorityQueue: add ReturnTypeWillChange attribute
it's doubtful any plugin dev is extending this, but nonetheless, we can't change it in a patch.
2022-07-27 03:51:06 +01:00
5d9f783037 InGamePacketHandler: do not update player rotation if it didn't change
setRotation() does an alarmingly large amount of work...
2022-07-24 21:07:35 +01:00
01ca14c314 InGamePacketHandler: avoid processing movement if position is unchanged since last tick 2022-07-24 21:00:12 +01:00
608c6ed6db Improved suboptimal code in Player::handleMovement() 2022-07-24 20:51:28 +01:00
c26631d06d InGamePacketHandler: avoid useless object allocations when forceMoveSync=false (99.9% of the time) 2022-07-24 20:44:27 +01:00
b75bc61a64 InGamePacketHandler: don't bother checking for flag changes if the flag fields are identical
we don't need to check this on a bit by bit level if the integers are the same.

this saves 2-3 microseconds per packet on my machine, which doesn't sound like much, but it adds up when there are lots of players.
2022-07-24 20:35:49 +01:00
3724479be3 InGamePacketHandler: improve performance of input flag resolving 2022-07-24 20:33:35 +01:00
eb916fe43d Use a falling block entity to improve client side performance of FloatingTextParticle (#4714)
Performance tests show that this has a considerable client-side performance advantage over using players. In my local tests, using 1000 floating texts in a 10x10x10 area, I observed an FPS increase from 1.5 to 8.0.
2022-07-24 18:22:21 +01:00
5e3b3a0700 Fix assert spam on debug clients 2022-07-24 17:51:02 +01:00
e10a624444 4.6.2 is next 2022-07-22 19:35:10 +01:00
b20e04539d Release 4.6.1 2022-07-22 19:34:57 +01:00
4852f8029a AsyncTask: update documentation 2022-07-21 23:26:46 +01:00
c4f85e526b Bump shivammathur/setup-php from 2.20.0 to 2.21.0 (#5181)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.20.0 to 2.21.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.20.0...2.21.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-20 15:19:21 +01:00
6cee428287 Bump docker/build-push-action from 3.0.0 to 3.1.0 (#5182)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-20 15:18:38 +01:00
bcba064d69 Bump build/php from 1110349 to f292501 (#5180)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `1110349` to `f292501`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](11103498ca...f292501a70)

---
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>
2022-07-20 15:16:34 +01:00
86647683bc fix CS again 2022-07-19 20:35:34 +01:00
64f0e58e60 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2022-07-19 20:17:15 +01:00
62f21516d1 build/generate-registry-annotations.php: allow processing a single file
this is useful for automatically invoking the script via a PhpStorm file watcher.
2022-07-19 20:17:07 +01:00
c553f7cf06 build/generate-registry-annotations.php: write to stderr on error 2022-07-19 20:15:44 +01:00
fec89b7803 Lava burns entities for only 8 seconds in Bedrock (#5173) 2022-07-17 20:50:15 +01:00
2b61c025c2 Workaround items in blockentity NBT not being processed correctly in 1.19.10
closes #5154

this hack sends only the bare essential data to create the tiles in LevelChunkPacket,
and then separately sending the full tile data using BlockActorDataPacket afterwards.

This is necessary because the client doesn't handle items correctly in NBT when chunks are sent without using the SubChunkRequest system.
In 4.6 this is observed with incorrect items shown in item frames; in 5.0 it's seen with items simply not showing up at all (difference due to modernization of the serialization format in 5.0).
2022-07-14 21:54:01 +01:00
c7133bc2e6 InGamePacketHandler: don't kick the player out of inventory windows on actor events
this is sent when the player crafts something using an anvil.
2022-07-14 20:36:11 +01:00
baf75089f5 Entity: cancel fire damage for fireproof entities 2022-07-14 19:53:25 +01:00
Ali
705df7d508 EffectManager: remove redundant check (#5153) 2022-07-14 17:56:18 +01:00
75d7adfb2d WitherEffect: fixed incorrect damage interval 2022-07-14 16:05:35 +01:00
9d535e2917 4.6.1 is next 2022-07-13 01:28:42 +01:00
3ccd288afd Release 4.6.0 2022-07-13 01:28:37 +01:00
06655bee78 Updated to 1.19.10 2022-07-13 00:59:49 +01:00
0ad2985247 Update documentation for Item::__construct() 2022-07-06 23:54:29 +01:00
269b6ed16a FallableTrait: fixed logic for block replacement
closes #5126

I don't know why it wasn't done this way to begin with. FallingBlock always used canBeReplaced()...
2022-07-06 16:16:49 +01:00
f031c3c602 Updated NBT dependency 2022-07-06 15:19:19 +01:00
f3e09dd7d5 Bump shivammathur/setup-php from 2.19.1 to 2.20.0 (#5135)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.19.1 to 2.20.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.19.1...2.20.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-06 13:28:46 +01:00
68e704bf97 Bump shivammathur/setup-php from 2.19.0 to 2.19.1 (#5098)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.19.0 to 2.19.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.19.0...2.19.1)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  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>
2022-07-04 15:15:17 +01:00
9898577135 Bump phpstan/phpstan from 1.7.15 to 1.8.0 (#5120)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.7.15 to 1.8.0.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.8.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.7.15...1.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-30 16:09:03 +01:00
784d602600 Fixed ItemBreakParticle using untranslated internal ID/meta for network data
this caused it to display particles for incorrect items. It may also have been possibly responsible for client crashes.
2022-06-29 14:01:39 +01:00
15c99cfe77 4.5.3 is next 2022-06-29 02:18:50 +01:00
d5fa0a2fc5 Release 4.5.2 2022-06-29 02:18:50 +01:00
0da9260994 Updated composer dependencies 2022-06-29 02:13:32 +01:00
df2d1fd4f9 of course there were two bugs on one line ... 2022-06-24 01:40:26 +01:00
9f65fb5f90 Fixed top-side skulls with no-drop flag set being treated as unknown blocks 2022-06-24 01:31:11 +01:00
caa4b78a3f Update composer dependencies 2022-06-21 20:21:02 +01:00
14352a05bc reword support bot message 2022-06-11 15:54:44 +01:00
bb5b52d998 Player: fix terrain getting redrawn when moving in noclip mode 2022-06-09 13:48:29 +01:00
5e22b70b6d this is a joke ... 2022-06-08 14:56:25 +01:00
02513818a9 4.5.2 is next 2022-06-08 02:50:34 +01:00
39 changed files with 650 additions and 273 deletions

View File

@ -46,7 +46,7 @@ jobs:
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
- name: Build image for tag
uses: docker/build-push-action@v3.0.0
uses: docker/build-push-action@v3.1.1
with:
push: true
context: ./pocketmine-mp
@ -59,7 +59,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.0.0
uses: docker/build-push-action@v3.1.1
with:
push: true
context: ./pocketmine-mp
@ -72,7 +72,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.0.0
uses: docker/build-push-action@v3.1.1
with:
push: true
context: ./pocketmine-mp
@ -85,7 +85,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.0.0
uses: docker/build-push-action@v3.1.1
with:
push: true
context: ./pocketmine-mp

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace pocketmine;
use pocketmine\utils\Internet;
use function dirname;
use function fwrite;
use function is_array;
use function json_decode;
use function json_encode;
use const JSON_THROW_ON_ERROR;
use const STDERR;
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{
return [
"embeds" => [
[
"title" => "New PocketMine-MP release: $version ($channel)",
"description" => <<<DESCRIPTION
$description
[Details]($detailsUrl) | [Source Code]($sourceUrl) | [Build Log]($buildLogUrl) | [Download]($pharDownloadUrl)
DESCRIPTION,
"url" => $detailsUrl,
"color" => $channel === "stable" ? 0x57ab5a : 0xc69026
]
]
];
}
if(count($argv) !== 5){
fwrite(STDERR, "Required arguments: github repo, version, API token\n");
exit(1);
}
[, $repo, $tagName, $token, $hookURL] = $argv;
$result = Internet::getURL('https://api.github.com/repos/' . $repo . '/releases/tags/' . $tagName, extraHeaders: [
'Authorization: token ' . $token
]);
if($result === null){
fwrite(STDERR, "failed to access GitHub API\n");
return;
}
if($result->getCode() !== 200){
fwrite(STDERR, "Error accessing GitHub API: " . $result->getCode() . "\n");
fwrite(STDERR, $result->getBody() . "\n");
exit(1);
}
$releaseInfoJson = json_decode($result->getBody(), true, JSON_THROW_ON_ERROR);
if(!is_array($releaseInfoJson)){
fwrite(STDERR, "Invalid release JSON returned from GitHub API\n");
exit(1);
}
$buildInfoPath = 'https://github.com/' . $repo . '/releases/download/' . $tagName . '/build_info.json';
$buildInfoResult = Internet::getURL($buildInfoPath, extraHeaders: [
'Authorization: token ' . $token
]);
if($buildInfoResult === null){
fwrite(STDERR, "missing build_info.json\n");
exit(1);
}
if($buildInfoResult->getCode() !== 200){
fwrite(STDERR, "error accessing build_info.json: " . $buildInfoResult->getCode() . "\n");
fwrite(STDERR, $buildInfoResult->getBody() . "\n");
exit(1);
}
$buildInfoJson = json_decode($buildInfoResult->getBody(), true, JSON_THROW_ON_ERROR);
if(!is_array($buildInfoJson)){
fwrite(STDERR, "invalid build_info.json\n");
exit(1);
}
$detailsUrl = $buildInfoJson["details_url"];
$sourceUrl = $buildInfoJson["source_url"];
$pharDownloadUrl = $buildInfoJson["download_url"];
$buildLogUrl = $buildInfoJson["build_log_url"];
$description = $releaseInfoJson["body"];
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl);
$response = Internet::postURL(
$hookURL,
json_encode($discordPayload, JSON_THROW_ON_ERROR),
extraHeaders: ['Content-Type: application/json']
);
if($response?->getCode() !== 204){
fwrite(STDERR, "failed to send Discord webhook\n");
fwrite(STDERR, $response?->getBody() ?? "no response body\n");
exit(1);
}

View File

@ -0,0 +1,38 @@
name: Notify Discord webhook of release
on:
release:
types:
- published
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.21.1
with:
php-version: 8.0
- name: Restore Composer package cache
uses: actions/cache@v3
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
- name: Get actual tag name
id: tag-name
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
- name: Run webhook post script
run: php .github/workflows/discord-release-embed.php ${{ github.repo }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }}

View File

@ -18,7 +18,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.19.0
uses: shivammathur/setup-php@2.21.1
with:
php-version: 8.0
@ -57,7 +57,7 @@ jobs:
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
- 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 }} > build_info.json
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
- name: Upload release artifacts
uses: actions/upload-artifact@v3
@ -80,4 +80,4 @@ jobs:
body: |
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
Please see the [changelogs](/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.

View File

@ -195,7 +195,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.19.0
uses: shivammathur/setup-php@2.21.1
with:
php-version: 8.0
tools: php-cs-fixer:3.2

View File

@ -16,9 +16,10 @@ jobs:
Hi, we only accept **bug reports** on this issue tracker, but this issue looks like a support request.
Instead of creating a bug report, try the following:
Instead of creating an issue, try the following:
- Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io)

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
if(count($argv) !== 5){
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number>");
if(count($argv) !== 6){
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number> <github actions run ID>\n");
exit(1);
}
@ -40,4 +40,5 @@ echo json_encode([
"details_url" => "https://github.com/$argv[3]/releases/tag/$argv[2]",
"download_url" => "https://github.com/$argv[3]/releases/download/$argv[2]/PocketMine-MP.phar",
"source_url" => "https://github.com/$argv[3]/tree/$argv[2]",
"build_log_url" => "https://github.com/$argv[3]/actions/runs/$argv[5]",
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";

View File

@ -29,7 +29,9 @@ use function count;
use function dirname;
use function file_get_contents;
use function file_put_contents;
use function fwrite;
use function implode;
use function is_dir;
use function ksort;
use function mb_strtoupper;
use function preg_match;
@ -39,7 +41,8 @@ use function substr;
use const SORT_STRING;
if(count($argv) !== 2){
die("Provide a path to process");
fwrite(STDERR, "Provide a path to process\n");
exit(1);
}
/**
@ -80,30 +83,24 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
return implode("\n", $lines);
}
require dirname(__DIR__) . '/vendor/autoload.php';
/** @var string $file */
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
if(substr($file, -4) !== ".php"){
continue;
}
function processFile(string $file) : void{
$contents = file_get_contents($file);
if($contents === false){
throw new \RuntimeException("Failed to get contents of $file");
}
if(preg_match("/(*ANYCRLF)^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match('/(*ANYCRLF)^((final|abstract)\s+)?class /m', $contents) !== 1){
continue;
return;
}
$shortClassName = basename($file, ".php");
$className = $matches[1] . "\\" . $shortClassName;
if(!class_exists($className)){
continue;
return;
}
$reflect = new \ReflectionClass($className);
$docComment = $reflect->getDocComment();
if($docComment === false || preg_match("/(*ANYCRLF)^\s*\*\s*@generate-registry-docblock$/m", $docComment) !== 1){
continue;
return;
}
echo "Found registry in $file\n";
@ -117,3 +114,18 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
echo "No changes made to file $file\n";
}
}
require dirname(__DIR__) . '/vendor/autoload.php';
if(is_dir($argv[1])){
/** @var string $file */
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
if(substr($file, -4) !== ".php"){
continue;
}
processFile($file);
}
}else{
processFile($argv[1]);
}

View File

@ -19,3 +19,10 @@ Released 8th June 2022.
## Fixes
- Fixed commands defined in `pocketmine.yml` `aliases` not passing the correct arguments.
- Updated BedrockProtocol to fix command argument types displayed on client-side command suggestions.
# 4.5.2
Released 29th June 2022.
## Fixes
- Fixed terrain getting redrawn when flying in spectator mode (or when using `Player->setHasBlockCollision(false)`).
- Fixed skulls with the `noDrops` flag set being treated as unknown blocks.

42
changelogs/4.6.md Normal file
View File

@ -0,0 +1,42 @@
**For Minecraft: Bedrock Edition 1.19.10**
### 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.6.0
Released 13th July 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.19.10.
- Removed support for older versions.
# 4.6.1
Released 22nd July 2022.
## Tools
- `build/generate-registry-annotations.php` now supports processing single files (useful for PhpStorm file watchers).
## API
- Updated documentation for `AsyncTask`.
## Fixes
- Fixed incorrect items being displayed in item frames.
- Fixed books not showing in lecterns.
- Fixed incorrect damage interval of Wither status effect.
- Fixed incorrect fire ticks when being set on fire by lava (8 seconds in Bedrock instead of 15).
- `Entity->attack()` now cancels damage from `FIRE` and `FIRE_TICK` damage causes if the entity is fireproof.
- Fixed inventory windows getting force-closed when the client attempts to use an enchanting table or anvil.
# 4.6.2
Released 6th August 2022.
## Core
- Improved server-side performance of `PlayerAuthInputPacket` handler.
- Improved client-side performance of `FloatingTextParticle` by using an invisible falling block entity. This offered a roughly 5x performance improvement over using tiny invisible players in local testing.
## Fixes
- Fixed assert failures and debug spam on debug Minecraft clients related to abilities in `AddPlayerPacket`.
- Fixed crash in `ReversePriorityQueue` on PHP 8.1 by adding `#[ReturnTypeWillChange]` attribute.

23
changelogs/4.7.md Normal file
View File

@ -0,0 +1,23 @@
**For Minecraft: Bedrock Edition 1.19.20**
### 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.7.0
Released 9th August 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.19.20.
- Removed support for older versions.
# 4.7.1
Released 14th August 2022.
## Fixes
- Fixed server crash when loading items from disk which have negative meta values.
- Fixed Turtle Master potions not giving any effects.
- Unimplemented items are no longer craftable.
- Fixed incorrect items appearing in item frames (due to an obsolete workaround for 1.19.10).

View File

@ -34,8 +34,8 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.8.0+bedrock-1.19.0",
"pocketmine/bedrock-protocol": "~10.0.0+bedrock-1.19.0",
"pocketmine/bedrock-data": "~1.10.0+bedrock-1.19.20",
"pocketmine/bedrock-protocol": "~12.0.0+bedrock-1.19.20",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
@ -53,7 +53,7 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "1.7.8",
"phpstan/phpstan": "1.8.2",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"

118
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": "363ffec55c206510b591b54f10138fc0",
"content-hash": "80afa24adf37096a23643e051d6128ce",
"packages": [
{
"name": "adhocore/json-comment",
@ -63,26 +63,26 @@
},
{
"name": "brick/math",
"version": "0.9.3",
"version": "0.10.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae"
"reference": "de846578401f4e58f911b3afeb62ced56365ed87"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae",
"reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae",
"url": "https://api.github.com/repos/brick/math/zipball/de846578401f4e58f911b3afeb62ced56365ed87",
"reference": "de846578401f4e58f911b3afeb62ced56365ed87",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.1 || ^8.0"
"php": "^7.4 || ^8.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0",
"vimeo/psalm": "4.9.2"
"phpunit/phpunit": "^9.0",
"vimeo/psalm": "4.25.0"
},
"type": "library",
"autoload": {
@ -107,19 +107,15 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.9.3"
"source": "https://github.com/brick/math/tree/0.10.1"
},
"funding": [
{
"url": "https://github.com/BenMorel",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/brick/math",
"type": "tidelift"
}
],
"time": "2021-08-15T20:50:18+00:00"
"time": "2022-08-01T22:54:31+00:00"
},
{
"name": "fgrosse/phpasn1",
@ -249,16 +245,16 @@
},
{
"name": "pocketmine/bedrock-data",
"version": "1.8.0+bedrock-1.19.0",
"version": "1.10.0+bedrock-1.19.20",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "e654d0a6e5dfd55f8546097ea629c528a70c30ee"
"reference": "43610f6749f22d15ede6b60ed5402bdeff47453e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e654d0a6e5dfd55f8546097ea629c528a70c30ee",
"reference": "e654d0a6e5dfd55f8546097ea629c528a70c30ee",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/43610f6749f22d15ede6b60ed5402bdeff47453e",
"reference": "43610f6749f22d15ede6b60ed5402bdeff47453e",
"shasum": ""
},
"type": "library",
@ -269,22 +265,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/bedrock-1.19.0"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.20"
},
"time": "2022-06-07T16:20:20+00:00"
"time": "2022-08-09T17:44:22+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "10.0.1+bedrock-1.19.0",
"version": "12.0.0+bedrock-1.19.20",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "331fb0eb45c26daadf8cf01a3b6f20e909d7684b"
"reference": "c2778039544fa0c7c5bd3af7963149e7552f4215"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/331fb0eb45c26daadf8cf01a3b6f20e909d7684b",
"reference": "331fb0eb45c26daadf8cf01a3b6f20e909d7684b",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c2778039544fa0c7c5bd3af7963149e7552f4215",
"reference": "c2778039544fa0c7c5bd3af7963149e7552f4215",
"shasum": ""
},
"require": {
@ -298,7 +294,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.7.11",
"phpstan/phpstan": "1.8.0",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5"
@ -316,9 +312,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/10.0.1+bedrock-1.19.0"
"source": "https://github.com/pmmp/BedrockProtocol/tree/bedrock-1.19.20"
},
"time": "2022-06-08T01:11:15+00:00"
"time": "2022-08-09T17:57:29+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -685,16 +681,16 @@
},
{
"name": "pocketmine/nbt",
"version": "0.3.2",
"version": "0.3.3",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe"
"reference": "f4321be50df1a18b9f4e94d428a2e68a6e2ac2b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/3e0d9ef6b6c5fb45e3745a121296e75631b3eefe",
"reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/f4321be50df1a18b9f4e94d428a2e68a6e2ac2b4",
"reference": "f4321be50df1a18b9f4e94d428a2e68a6e2ac2b4",
"shasum": ""
},
"require": {
@ -704,7 +700,7 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "1.2.0",
"phpstan/phpstan": "1.7.7",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5"
},
@ -721,9 +717,9 @@
"description": "PHP library for working with Named Binary Tags",
"support": {
"issues": "https://github.com/pmmp/NBT/issues",
"source": "https://github.com/pmmp/NBT/tree/0.3.2"
"source": "https://github.com/pmmp/NBT/tree/0.3.3"
},
"time": "2021-12-16T01:02:37+00:00"
"time": "2022-07-06T14:13:26+00:00"
},
{
"name": "pocketmine/raklib",
@ -930,20 +926,20 @@
},
{
"name": "ramsey/uuid",
"version": "4.3.1",
"version": "4.4.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28"
"reference": "373f7bacfcf3de038778ff27dcce5672ddbf4c8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28",
"reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/373f7bacfcf3de038778ff27dcce5672ddbf4c8a",
"reference": "373f7bacfcf3de038778ff27dcce5672ddbf4c8a",
"shasum": ""
},
"require": {
"brick/math": "^0.8 || ^0.9",
"brick/math": "^0.8 || ^0.9 || ^0.10",
"ext-ctype": "*",
"ext-json": "*",
"php": "^8.0",
@ -959,7 +955,6 @@
"doctrine/annotations": "^1.8",
"ergebnis/composer-normalize": "^2.15",
"mockery/mockery": "^1.3",
"moontoast/math": "^1.1",
"paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2",
"php-mock/php-mock-mockery": "^1.3",
@ -1008,7 +1003,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.3.1"
"source": "https://github.com/ramsey/uuid/tree/4.4.0"
},
"funding": [
{
@ -1020,7 +1015,7 @@
"type": "tidelift"
}
],
"time": "2022-03-27T21:42:02+00:00"
"time": "2022-08-05T17:58:37+00:00"
},
{
"name": "symfony/polyfill-php81",
@ -1737,16 +1732,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.7.8",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "2bf3d43015d56abac4d002a4d2d6c3a7d6fa627a"
"reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/2bf3d43015d56abac4d002a4d2d6c3a7d6fa627a",
"reference": "2bf3d43015d56abac4d002a4d2d6c3a7d6fa627a",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c53312ecc575caf07b0e90dee43883fdf90ca67c",
"reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c",
"shasum": ""
},
"require": {
@ -1772,7 +1767,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.7.8"
"source": "https://github.com/phpstan/phpstan/tree/1.8.2"
},
"funding": [
{
@ -1792,7 +1787,7 @@
"type": "tidelift"
}
],
"time": "2022-06-01T13:43:17+00:00"
"time": "2022-07-20T09:57:31+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
@ -1848,21 +1843,21 @@
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "1.2.3",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43"
"reference": "543675a9be82d4befb9ca0bd8cdc9d211665037f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
"reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/543675a9be82d4befb9ca0bd8cdc9d211665037f",
"reference": "543675a9be82d4befb9ca0bd8cdc9d211665037f",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.6.3"
"phpstan/phpstan": "^1.7.15"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
@ -1890,9 +1885,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.2.3"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.3.0"
},
"time": "2022-05-04T15:20:40+00:00"
"time": "2022-06-24T06:47:20+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -2214,16 +2209,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.20",
"version": "9.5.21",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
"reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e32b76be457de00e83213528f6bb37e2a38fcb1",
"reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1",
"shasum": ""
},
"require": {
@ -2257,7 +2252,6 @@
"sebastian/version": "^3.0.2"
},
"require-dev": {
"ext-pdo": "*",
"phpspec/prophecy-phpunit": "^2.0.1"
},
"suggest": {
@ -2301,7 +2295,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.21"
},
"funding": [
{
@ -2313,7 +2307,7 @@
"type": "github"
}
],
"time": "2022-04-01T12:37:26+00:00"
"time": "2022-06-19T12:14:25+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.5.1";
public const BASE_VERSION = "4.7.1";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";

View File

@ -94,7 +94,8 @@ class Lava extends Liquid{
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
$entity->attack($ev);
$ev = new EntityCombustByBlockEvent($this, $entity, 15);
//in java burns entities for 15 seconds - seems to be a parity issue in bedrock
$ev = new EntityCombustByBlockEvent($this, $entity, 8);
$ev->call();
if(!$ev->isCancelled()){
$entity->setOnFire($ev->getDuration());

View File

@ -56,7 +56,8 @@ class Skull extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = $stateMeta === 1 ? Facing::UP : BlockDataSerializer::readHorizontalFacing($stateMeta);
$facingMeta = $stateMeta & 0x7;
$this->facing = $facingMeta === 1 ? Facing::UP : BlockDataSerializer::readHorizontalFacing($facingMeta);
$this->noDrops = ($stateMeta & BlockLegacyMetadata::SKULL_FLAG_NO_DROPS) !== 0;
}

View File

@ -24,9 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\Block;
use pocketmine\block\BlockLegacyIds;
use pocketmine\block\Fire;
use pocketmine\block\Liquid;
use pocketmine\block\VanillaBlocks;
use pocketmine\entity\Location;
use pocketmine\entity\object\FallingBlock;
@ -50,7 +47,7 @@ trait FallableTrait{
public function onNearbyBlockChange() : void{
$pos = $this->getPosition();
$down = $pos->getWorld()->getBlock($pos->getSide(Facing::DOWN));
if($down->getId() === BlockLegacyIds::AIR || $down instanceof Liquid || $down instanceof Fire){
if($down->canBeReplaced()){
$pos->getWorld()->setBlock($pos, VanillaBlocks::AIR());
$block = $this;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\crafting;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use function array_map;
@ -33,6 +34,23 @@ use function json_decode;
final class CraftingManagerFromDataHelper{
/**
* @param Item[] $items
*/
private static function containsUnknownOutputs(array $items) : bool{
$factory = ItemFactory::getInstance();
foreach($items as $item){
if($item->hasAnyDamageValue()){
throw new \InvalidArgumentException("Recipe outputs must not have wildcard meta values");
}
if(!$factory->isRegistered($item->getId(), $item->getMeta())){
return true;
}
}
return false;
}
public static function make(string $filePath) : CraftingManager{
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true);
if(!is_array($recipes)){
@ -52,9 +70,13 @@ final class CraftingManagerFromDataHelper{
if($recipeType === null){
continue;
}
$output = array_map($itemDeserializerFunc, $recipe["output"]);
if(self::containsUnknownOutputs($output)){
continue;
}
$result->registerShapelessRecipe(new ShapelessRecipe(
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"]),
$output,
$recipeType
));
}
@ -62,10 +84,14 @@ final class CraftingManagerFromDataHelper{
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
continue;
}
$output = array_map($itemDeserializerFunc, $recipe["output"]);
if(self::containsUnknownOutputs($output)){
continue;
}
$result->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"],
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"])
$output
));
}
foreach($recipes["smelting"] as $recipe){
@ -79,19 +105,30 @@ final class CraftingManagerFromDataHelper{
if($furnaceType === null){
continue;
}
$output = Item::jsonDeserialize($recipe["output"]);
if(self::containsUnknownOutputs([$output])){
continue;
}
$result->getFurnaceRecipeManager($furnaceType)->register(new FurnaceRecipe(
Item::jsonDeserialize($recipe["output"]),
$output,
Item::jsonDeserialize($recipe["input"]))
);
}
foreach($recipes["potion_type"] as $recipe){
$output = Item::jsonDeserialize($recipe["output"]);
if(self::containsUnknownOutputs([$output])){
continue;
}
$result->registerPotionTypeRecipe(new PotionTypeRecipe(
Item::jsonDeserialize($recipe["input"]),
Item::jsonDeserialize($recipe["ingredient"]),
Item::jsonDeserialize($recipe["output"])
$output
));
}
foreach($recipes["potion_container_change"] as $recipe){
if(!ItemFactory::getInstance()->isRegistered($recipe["output_item_id"])){
continue;
}
$result->registerPotionContainerChangeRecipe(new PotionContainerChangeRecipe(
$recipe["input_item_id"],
Item::jsonDeserialize($recipe["ingredient"]),

View File

@ -522,6 +522,9 @@ abstract class Entity{
}
public function attack(EntityDamageEvent $source) : void{
if($this->isFireProof() && ($source->getCause() === EntityDamageEvent::CAUSE_FIRE || $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK)){
$source->cancel();
}
$source->call();
if($source->isCancelled()){
return;
@ -1461,8 +1464,9 @@ abstract class Entity{
$this->location->pitch,
$this->location->yaw,
$this->location->yaw, //TODO: head yaw
$this->location->yaw, //TODO: body yaw (wtf mojang?)
array_map(function(Attribute $attr) : NetworkAttribute{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
}, $this->attributeMap->getAll()),
$this->getAllNetworkData(),
[] //TODO: entity links

View File

@ -48,9 +48,9 @@ use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
@ -58,11 +58,15 @@ use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\GameMode;
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\types\UpdateAbilitiesPacketLayer;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\world\sound\TotemUseSound;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use function array_fill;
use function array_filter;
use function array_key_exists;
use function array_merge;
@ -471,7 +475,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
$this->getUniqueId(),
$this->getName(),
$this->getId(), //TODO: actor unique ID
$this->getId(),
"",
$this->location->asVector3(),
@ -482,7 +485,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
GameMode::SURVIVAL,
$this->getAllNetworkData(),
AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->getId()), //TODO
UpdateAbilitiesPacket::create(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
new UpdateAbilitiesPacketLayer(
UpdateAbilitiesPacketLayer::LAYER_BASE,
array_fill(0, UpdateAbilitiesPacketLayer::NUMBER_OF_ABILITIES, false),
0.0,
0.0
)
]),
[], //TODO: entity links
"", //device ID (we intentionally don't send this - secvuln)
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)

View File

@ -85,14 +85,11 @@ class EffectManager{
$index = spl_object_id($effectType);
if(isset($this->effects[$index])){
$effect = $this->effects[$index];
$hasExpired = $effect->hasExpired();
$ev = new EntityEffectRemoveEvent($this->entity, $effect);
$ev->call();
if($ev->isCancelled()){
if($hasExpired && !$ev->getEffect()->hasExpired()){ //altered duration of an expired effect to make it not get removed
foreach($this->effectAddHooks as $hook){
$hook($ev->getEffect(), true);
}
foreach($this->effectAddHooks as $hook){
$hook($ev->getEffect(), true);
}
return;
}

View File

@ -30,7 +30,7 @@ use pocketmine\event\entity\EntityDamageEvent;
class WitherEffect extends Effect{
public function canTick(EffectInstance $instance) : bool{
if(($interval = (50 >> $instance->getAmplifier())) > 0){
if(($interval = (40 >> $instance->getAmplifier())) > 0){
return ($instance->getDuration() % $interval) === 0;
}
return true;

View File

@ -100,8 +100,9 @@ class Item implements \JsonSerializable{
* Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register
* into the index.
*
* NOTE: This should NOT BE USED for creating items to set into an inventory. Use {@link ItemFactory#get} for that
* NOTE: This should NOT BE USED for creating items to set into an inventory. Use VanillaItems for that
* purpose.
* @see VanillaItems
*/
public function __construct(ItemIdentifier $identifier, string $name = "Unknown"){
$this->identifier = $identifier;

View File

@ -472,7 +472,7 @@ class ItemFactory{
if(isset($this->list[$offset = self::getListOffset($id, $meta)])){
$item = clone $this->list[$offset];
}elseif(isset($this->list[$zero = self::getListOffset($id, 0)]) && $this->list[$zero] instanceof Durable){
if($meta <= $this->list[$zero]->getMaxDurability()){
if($meta >= 0 && $meta <= $this->list[$zero]->getMaxDurability()){
$item = clone $this->list[$zero];
$item->setDamage($meta);
}else{

View File

@ -185,13 +185,16 @@ final class PotionType{
new EffectInstance(VanillaEffects::WITHER(), 800, 1)
]),
new self("turtle_master", "Turtle Master", fn() => [
//TODO
new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 3),
new EffectInstance(VanillaEffects::RESISTANCE(), 20 * 20, 2),
]),
new self("long_turtle_master", "Long Turtle Master", fn() => [
//TODO
new EffectInstance(VanillaEffects::SLOWNESS(), 40 * 20, 3),
new EffectInstance(VanillaEffects::RESISTANCE(), 40 * 20, 2),
]),
new self("strong_turtle_master", "Strong Turtle Master", fn() => [
//TODO
new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 5),
new EffectInstance(VanillaEffects::RESISTANCE(), 20 * 20, 3),
]),
new self("slow_falling", "Slow Falling", fn() => [
//TODO

View File

@ -30,6 +30,7 @@ use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\protocol\LevelChunkPacket;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\ChunkPosition;
use pocketmine\network\mcpe\serializer\ChunkSerializer;
use pocketmine\scheduler\AsyncTask;
use pocketmine\world\format\Chunk;
@ -71,7 +72,7 @@ class ChunkRequestTask extends AsyncTask{
$subCount = ChunkSerializer::getSubChunkCount($chunk) + ChunkSerializer::LOWER_PADDING_SIZE;
$encoderContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
$payload = ChunkSerializer::serializeFullChunk($chunk, RuntimeBlockMapping::getInstance(), $encoderContext, $this->tiles);
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create($this->chunkX, $this->chunkZ, $subCount, false, null, $payload))->getBuffer()));
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $subCount, false, null, $payload))->getBuffer()));
}
public function onError() : void{

View File

@ -57,7 +57,6 @@ use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\handler\PreSpawnPacketHandler;
use pocketmine\network\mcpe\handler\ResourcePacksPacketHandler;
use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
@ -93,6 +92,7 @@ use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\command\CommandData;
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;
@ -100,9 +100,13 @@ 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\types\UpdateAbilitiesPacketLayer;
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;
use pocketmine\permission\DefaultPermissions;
use pocketmine\player\GameMode;
use pocketmine\player\Player;
@ -250,8 +254,8 @@ class NetworkSession{
$permissionHooks = $this->player->getPermissionRecalculationCallbacks();
$permissionHooks->add($permHook = function() : void{
$this->logger->debug("Syncing available commands and adventure settings due to permission recalculation");
$this->syncAdventureSettings($this->player);
$this->logger->debug("Syncing available commands and abilities/permissions due to permission recalculation");
$this->syncAbilities($this->player);
$this->syncAvailableCommands();
});
$this->disposeHooks->add(static function() use ($permissionHooks, $permHook) : void{
@ -716,7 +720,7 @@ class NetworkSession{
$this->syncAttributes($this->player, $this->player->getAttributeMap()->getAll());
$this->player->sendData(null);
$this->syncAdventureSettings($this->player);
$this->syncAbilities($this->player);
$this->invManager->syncAll();
$this->setHandler(new InGamePacketHandler($this->player, $this, $this->invManager));
}
@ -750,7 +754,7 @@ class NetworkSession{
}
public function syncViewAreaCenterPoint(Vector3 $newPos, int $viewDistance) : void{
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create(BlockPosition::fromVector3($newPos), $viewDistance * 16)); //blocks, not chunks >.>
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create(BlockPosition::fromVector3($newPos), $viewDistance * 16, [])); //blocks, not chunks >.>
}
public function syncPlayerSpawnPoint(Position $newSpawn) : void{
@ -766,37 +770,60 @@ class NetworkSession{
public function syncGameMode(GameMode $mode, bool $isRollback = false) : void{
$this->sendDataPacket(SetPlayerGameTypePacket::create(TypeConverter::getInstance()->coreGameModeToProtocol($mode)));
if($this->player !== null){
$this->syncAdventureSettings($this->player);
$this->syncAbilities($this->player);
$this->syncAdventureSettings(); //TODO: we might be able to do this with the abilities packet alone
}
if(!$isRollback && $this->invManager !== null){
$this->invManager->syncCreative();
}
}
/**
* TODO: make this less specialized
*/
public function syncAdventureSettings(Player $for) : void{
public function syncAbilities(Player $for) : void{
$isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR);
$pk = AdventureSettingsPacket::create(
0,
$isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL,
0,
//ALL of these need to be set for the base layer, otherwise the client will cry
$boolAbilities = [
UpdateAbilitiesPacketLayer::ABILITY_ALLOW_FLIGHT => $for->getAllowFlight(),
UpdateAbilitiesPacketLayer::ABILITY_FLYING => $for->isFlying(),
UpdateAbilitiesPacketLayer::ABILITY_NO_CLIP => !$for->hasBlockCollision(),
UpdateAbilitiesPacketLayer::ABILITY_OPERATOR => $isOp,
UpdateAbilitiesPacketLayer::ABILITY_TELEPORT => $for->hasPermission(DefaultPermissionNames::COMMAND_TELEPORT),
UpdateAbilitiesPacketLayer::ABILITY_INVULNERABLE => $for->isCreative(),
UpdateAbilitiesPacketLayer::ABILITY_MUTED => false,
UpdateAbilitiesPacketLayer::ABILITY_WORLD_BUILDER => false,
UpdateAbilitiesPacketLayer::ABILITY_INFINITE_RESOURCES => !$for->hasFiniteResources(),
UpdateAbilitiesPacketLayer::ABILITY_LIGHTNING => false,
UpdateAbilitiesPacketLayer::ABILITY_BUILD => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_MINE => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_DOORS_AND_SWITCHES => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_OPEN_CONTAINERS => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_ATTACK_PLAYERS => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_ATTACK_MOBS => !$for->isSpectator(),
];
$this->sendDataPacket(UpdateAbilitiesPacket::create(
$isOp ? CommandPermissions::OPERATOR : CommandPermissions::NORMAL,
$isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER,
0,
$for->getId()
);
$for->getId(),
[
//TODO: dynamic flying speed! FINALLY!!!!!!!!!!!!!!!!!
new UpdateAbilitiesPacketLayer(UpdateAbilitiesPacketLayer::LAYER_BASE, $boolAbilities, 0.05, 0.1),
]
));
}
$pk->setFlag(AdventureSettingsPacket::WORLD_IMMUTABLE, $for->isSpectator());
$pk->setFlag(AdventureSettingsPacket::NO_PVP, $for->isSpectator());
$pk->setFlag(AdventureSettingsPacket::AUTO_JUMP, $for->hasAutoJump());
$pk->setFlag(AdventureSettingsPacket::ALLOW_FLIGHT, $for->getAllowFlight());
$pk->setFlag(AdventureSettingsPacket::NO_CLIP, !$for->hasBlockCollision());
$pk->setFlag(AdventureSettingsPacket::FLYING, $for->isFlying());
//TODO: permission flags
$this->sendDataPacket($pk);
public function syncAdventureSettings() : void{
if($this->player === null){
throw new \LogicException("Cannot sync adventure settings for a player that is not yet created");
}
//everything except auto jump is handled via UpdateAbilitiesPacket
$this->sendDataPacket(UpdateAdventureSettingsPacket::create(
noAttackingMobs: false,
noAttackingPlayers: false,
worldImmutable: false,
showNameTags: true,
autoJump: $this->player->hasAutoJump()
));
}
/**
@ -805,7 +832,7 @@ class NetworkSession{
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());
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
}, $attributes), 0));
}
}

View File

@ -145,6 +145,11 @@ class InGamePacketHandler extends PacketHandler{
/** @var UseItemTransactionData|null */
protected $lastRightClickData = null;
protected ?Vector3 $lastPlayerAuthInputPosition = null;
protected ?float $lastPlayerAuthInputYaw = null;
protected ?float $lastPlayerAuthInputPitch = null;
protected ?int $lastPlayerAuthInputFlags = null;
/** @var bool */
public $forceMoveSync = false;
@ -168,9 +173,10 @@ class InGamePacketHandler extends PacketHandler{
return true;
}
private function resolveOnOffInputFlags(PlayerAuthInputPacket $packet, int $startFlag, int $stopFlag) : ?bool{
$enabled = $packet->hasFlag($startFlag);
if($enabled !== $packet->hasFlag($stopFlag)){
private function resolveOnOffInputFlags(int $inputFlags, int $startFlag, int $stopFlag) : ?bool{
$enabled = ($inputFlags & (1 << $startFlag)) !== 0;
$disabled = ($inputFlags & (1 << $stopFlag)) !== 0;
if($enabled !== $disabled){
return $enabled;
}
//neither flag was set, or both were set
@ -179,51 +185,68 @@ class InGamePacketHandler extends PacketHandler{
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
$rawPos = $packet->getPosition();
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $packet->getYaw(), $packet->getHeadYaw(), $packet->getPitch()] as $float){
$rawYaw = $packet->getYaw();
$rawPitch = $packet->getPitch();
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $rawYaw, $packet->getHeadYaw(), $rawPitch] as $float){
if(is_infinite($float) || is_nan($float)){
$this->session->getLogger()->debug("Invalid movement received, contains NAN/INF components");
return false;
}
}
$yaw = fmod($packet->getYaw(), 360);
$pitch = fmod($packet->getPitch(), 360);
if($yaw < 0){
$yaw += 360;
if($rawYaw !== $this->lastPlayerAuthInputYaw || $rawPitch !== $this->lastPlayerAuthInputPitch){
$this->lastPlayerAuthInputYaw = $rawYaw;
$this->lastPlayerAuthInputPitch = $rawPitch;
$yaw = fmod($rawYaw, 360);
$pitch = fmod($rawPitch, 360);
if($yaw < 0){
$yaw += 360;
}
$this->player->setRotation($yaw, $pitch);
}
$this->player->setRotation($yaw, $pitch);
$curPos = $this->player->getLocation();
$hasMoved = $this->lastPlayerAuthInputPosition === null || !$this->lastPlayerAuthInputPosition->equals($rawPos);
$newPos = $rawPos->round(4)->subtract(0, 1.62, 0);
if($this->forceMoveSync && $newPos->distanceSquared($curPos) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->session->getLogger()->debug("Got outdated pre-teleport movement, received " . $newPos . ", expected " . $curPos);
//Still getting movements from before teleport, ignore them
return false;
if($this->forceMoveSync && $hasMoved){
$curPos = $this->player->getLocation();
if($newPos->distanceSquared($curPos) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->session->getLogger()->debug("Got outdated pre-teleport movement, received " . $newPos . ", expected " . $curPos);
//Still getting movements from before teleport, ignore them
return false;
}
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
$this->forceMoveSync = false;
}
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
$this->forceMoveSync = false;
$inputFlags = $packet->getInputFlags();
if($inputFlags !== $this->lastPlayerAuthInputFlags){
$this->lastPlayerAuthInputFlags = $inputFlags;
$sneaking = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING);
$sprinting = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
$swimming = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
$gliding = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$mismatch =
($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
($swimming !== null && !$this->player->toggleSwim($swimming)) |
($gliding !== null && !$this->player->toggleGlide($gliding));
if((bool) $mismatch){
$this->player->sendData([$this->player]);
$sneaking = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING);
$sprinting = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
$swimming = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
$gliding = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$mismatch =
($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
($swimming !== null && !$this->player->toggleSwim($swimming)) |
($gliding !== null && !$this->player->toggleGlide($gliding));
if((bool) $mismatch){
$this->player->sendData([$this->player]);
}
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
$this->player->jump();
}
}
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
$this->player->jump();
}
if(!$this->forceMoveSync){
if(!$this->forceMoveSync && $hasMoved){
$this->lastPlayerAuthInputPosition = $rawPos;
//TODO: this packet has WAYYYYY more useful information that we're not using
$this->player->handleMovement($newPos);
}
@ -269,7 +292,6 @@ class InGamePacketHandler extends PacketHandler{
//TODO HACK: EATING_ITEM is sent back to the server when the server sends it for other players (1.14 bug, maybe earlier)
return $packet->actorRuntimeId === ActorEvent::EATING_ITEM;
}
$this->player->removeCurrentWindow();
switch($packet->eventId){
case ActorEvent::EATING_ITEM: //TODO: ignore this and handle it server-side
@ -868,7 +890,14 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleModalFormResponse(ModalFormResponsePacket $packet) : bool{
return $this->player->onFormSubmit($packet->formId, self::stupid_json_decode($packet->formData, true));
if($packet->cancelReason !== null){
//TODO: make APIs for this to allow plugins to use this information
return $this->player->onFormSubmit($packet->formId, null);
}elseif($packet->formData !== null){
return $this->player->onFormSubmit($packet->formId, self::stupid_json_decode($packet->formData, true));
}else{
throw new PacketHandlingException("Expected either formData or cancelReason to be set in ModalFormResponsePacket");
}
}
/**
@ -979,7 +1008,7 @@ class InGamePacketHandler extends PacketHandler{
}
if($isFlying !== $this->player->isFlying()){
if(!$this->player->toggleFlight($isFlying)){
$this->session->syncAdventureSettings($this->player);
$this->session->syncAbilities($this->player);
}
}

View File

@ -98,16 +98,18 @@ class PreSpawnPacketHandler extends PacketHandler{
false,
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
Uuid::fromString(Uuid::NIL),
false,
[],
0,
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries()
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries(),
));
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getAvailableActorIdentifiers());
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs());
$this->session->syncAttributes($this->player, $this->player->getAttributeMap()->getAll());
$this->session->syncAvailableCommands();
$this->session->syncAdventureSettings($this->player);
$this->session->syncAbilities($this->player);
$this->session->syncAdventureSettings();
foreach($this->player->getEffects()->all() as $effect){
$this->session->onEntityEffectAdded($this->player, $effect, false);
}

View File

@ -421,7 +421,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
public function setAllowFlight(bool $value) : void{
if($this->allowFlight !== $value){
$this->allowFlight = $value;
$this->getNetworkSession()->syncAdventureSettings($this);
$this->getNetworkSession()->syncAbilities($this);
}
}
@ -432,7 +432,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
public function setHasBlockCollision(bool $value) : void{
if($this->blockCollision !== $value){
$this->blockCollision = $value;
$this->getNetworkSession()->syncAdventureSettings($this);
$this->getNetworkSession()->syncAbilities($this);
}
}
@ -444,7 +444,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
if($this->flying !== $value){
$this->flying = $value;
$this->resetFallDistance();
$this->getNetworkSession()->syncAdventureSettings($this);
$this->getNetworkSession()->syncAbilities($this);
}
}
@ -455,7 +455,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
public function setAutoJump(bool $value) : void{
if($this->autoJump !== $value){
$this->autoJump = $value;
$this->getNetworkSession()->syncAdventureSettings($this);
$this->getNetworkSession()->syncAdventureSettings();
}
}
@ -1180,7 +1180,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
return;
}
$oldPos = $this->getLocation();
$oldPos = $this->location;
$distanceSquared = $newPos->distanceSquared($oldPos);
$revert = false;
@ -1198,7 +1198,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
* asking for help if you suffer the consequences of messing with this.
*/
$this->logger->debug("Moved too fast, reverting movement");
$this->logger->debug("Old position: " . $this->location->asVector3() . ", new position: " . $newPos);
$this->logger->debug("Old position: " . $oldPos->asVector3() . ", new position: " . $newPos);
$revert = true;
}elseif(!$this->getWorld()->isInLoadedTerrain($newPos)){
$revert = true;
@ -1206,9 +1206,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
}
if(!$revert && $distanceSquared != 0){
$dx = $newPos->x - $this->location->x;
$dy = $newPos->y - $this->location->y;
$dz = $newPos->z - $this->location->z;
$dx = $newPos->x - $oldPos->x;
$dy = $newPos->y - $oldPos->y;
$dz = $newPos->z - $oldPos->z;
$this->move($dx, $dy, $dz);
}
@ -2339,6 +2339,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
parent::syncNetworkData($properties);
$properties->setGenericFlag(EntityMetadataFlags::ACTION, $this->startAction > -1);
$properties->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, $this->hasBlockCollision());
$properties->setPlayerFlag(PlayerMetadataFlags::SLEEP, $this->sleeping !== null);
$properties->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $this->sleeping !== null ? BlockPosition::fromVector3($this->sleeping) : new BlockPosition(0, 0, 0));

View File

@ -33,19 +33,31 @@ use function spl_object_id;
/**
* Class used to run async tasks in other threads.
*
* An AsyncTask does not have its own thread. It is queued into an AsyncPool and executed if there is an async worker
* with no AsyncTask running. Therefore, an AsyncTask SHOULD NOT execute for more than a few seconds. For tasks that
* run for a long time or infinitely, start another thread instead.
* An AsyncTask is run by a thread pool of reusable threads, and doesn't have its own dedicated thread. A thread is
* usually chosen from the pool at random to run the task (though a specific thread in the pool may be selected
* manually, if needed).
* Reusing threads this way has a much lower performance cost than starting an entirely new thread for every operation.
* AsyncTasks are therefore suitable for brief CPU-bound tasks, such as world generation, compression/decompression of
* data, etc.
*
* AsyncTask SHOULD NOT be used for I/O-bound tasks, such as network I/O, file I/O, database I/O, etc. The server's
* central AsyncPool is used for things like compressing network packets for sending, so using AsyncTask for I/O will
* slow the whole server down, stall chunk loading, etc.
*
* An AsyncTask SHOULD NOT run for more than a few seconds. For tasks that run for a long time or indefinitely, create
* a dedicated thread instead.
*
* The Server instance is not accessible inside {@link AsyncTask::onRun()}. It can only be accessed in the main server
* thread, e.g. during {@link AsyncTask::onCompletion()} or {@link AsyncTask::onProgressUpdate()}. This means that
* whatever you do in onRun() must be able to work without the Server instance.
*
* WARNING: Any non-Threaded objects WILL BE SERIALIZED when assigned to members of AsyncTasks or other Threaded object.
* If later accessed from said Threaded object, you will be operating on a COPY OF THE OBJECT, NOT THE ORIGINAL OBJECT.
* If you want to store non-serializable objects to access when the task completes, store them using
* {@link AsyncTask::storeLocal}.
*
* WARNING: As of pthreads v3.1.6, arrays are converted to Volatile objects when assigned as members of Threaded objects.
* WARNING: Arrays are converted to Volatile objects when assigned as members of Threaded objects.
* Keep this in mind when using arrays stored as members of your AsyncTask.
*
* WARNING: Do not call PocketMine-MP API methods from other Threads!!
*/
abstract class AsyncTask extends \Threaded{
/**

View File

@ -38,6 +38,7 @@ class ReversePriorityQueue extends \SplPriorityQueue{
*
* @return int
*/
#[\ReturnTypeWillChange]
public function compare($priority1, $priority2){
//TODO: this will crash if non-numeric priorities are used
return (int) -($priority1 - $priority2);

View File

@ -23,25 +23,20 @@ declare(strict_types=1);
namespace pocketmine\world\particle;
use pocketmine\block\VanillaBlocks;
use pocketmine\entity\Entity;
use pocketmine\entity\Skin;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\protocol\AddActorPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\ByteMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\IntMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty;
use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use Ramsey\Uuid\Uuid;
use function str_repeat;
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
class FloatingTextParticle implements Particle{
//TODO: HACK!
@ -94,39 +89,34 @@ class FloatingTextParticle implements Particle{
}
if(!$this->invisible){
$uuid = Uuid::uuid4();
$name = $this->title . ($this->text !== "" ? "\n" . $this->text : "");
$p[] = PlayerListPacket::add([PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin("Standard_Custom", str_repeat("\x00", 8192))))]);
$actorFlags = (
1 << EntityMetadataFlags::IMMOBILE
);
$actorMetadata = [
EntityMetadataProperties::FLAGS => new LongMetadataProperty($actorFlags),
EntityMetadataProperties::SCALE => new FloatMetadataProperty(0.01) //zero causes problems on debug builds
EntityMetadataProperties::SCALE => new FloatMetadataProperty(0.01), //zero causes problems on debug builds
EntityMetadataProperties::BOUNDING_BOX_WIDTH => new FloatMetadataProperty(0.0),
EntityMetadataProperties::BOUNDING_BOX_HEIGHT => new FloatMetadataProperty(0.0),
EntityMetadataProperties::NAMETAG => new StringMetadataProperty($name),
EntityMetadataProperties::VARIANT => new IntMetadataProperty(RuntimeBlockMapping::getInstance()->toRuntimeId(VanillaBlocks::AIR()->getFullId())),
EntityMetadataProperties::ALWAYS_SHOW_NAMETAG => new ByteMetadataProperty(1),
];
$p[] = AddPlayerPacket::create(
$uuid,
$name,
$p[] = AddActorPacket::create(
$this->entityId, //TODO: actor unique ID
$this->entityId,
"",
$pos, //TODO: check offset
EntityIds::FALLING_BLOCK,
$pos, //TODO: check offset (0.49?)
null,
0,
0,
0,
ItemStackWrapper::legacy(ItemStack::null()),
GameMode::SURVIVAL,
$actorMetadata,
AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->entityId),
0,
[],
"",
DeviceOS::UNKNOWN
$actorMetadata,
[]
);
$p[] = PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($uuid)]);
}
return $p;

View File

@ -25,6 +25,7 @@ namespace pocketmine\world\particle;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
use pocketmine\network\mcpe\protocol\types\ParticleIds;
@ -32,6 +33,7 @@ class ItemBreakParticle implements Particle{
public function __construct(private Item $item){}
public function encode(Vector3 $pos) : array{
return [LevelEventPacket::standardParticle(ParticleIds::ITEM_BREAK, ($this->item->getId() << 16) | $this->item->getMeta(), $pos)];
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId($this->item->getId(), $this->item->getMeta());
return [LevelEventPacket::standardParticle(ParticleIds::ITEM_BREAK, ($id << 16) | $meta, $pos)];
}
}

View File

@ -131,7 +131,7 @@ parameters:
path: ../../../src/block/DragonEgg.php
-
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int\\<0, max\\> given\\.$#"
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int\\<0, 255\\> given\\.$#"
count: 1
path: ../../../src/block/DragonEgg.php
@ -335,21 +335,6 @@ parameters:
count: 3
path: ../../../src/block/Mycelium.php
-
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
count: 1
path: ../../../src/block/RedMushroom.php
-
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
count: 1
path: ../../../src/block/RedMushroom.php
-
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
count: 1
path: ../../../src/block/RedMushroom.php
-
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#"
count: 1
@ -701,7 +686,7 @@ parameters:
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAdventureSettings\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAbilities\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 2
path: ../../../src/network/mcpe/NetworkSession.php

View File

@ -5,11 +5,6 @@ parameters:
count: 1
path: ../../../src/block/BaseBanner.php
-
message: "#^Property pocketmine\\\\block\\\\tile\\\\TileFactory\\:\\:\\$saveNames \\(array\\<class\\-string\\<pocketmine\\\\block\\\\tile\\\\Tile\\>, string\\>\\) does not accept array\\<class\\-string\\<pocketmine\\\\block\\\\tile\\\\Tile\\>, bool\\|string\\>\\.$#"
count: 1
path: ../../../src/block/tile/TileFactory.php
-
message: "#^Comparison operation \"\\<\" between int\\<1, max\\> and 1 is always false\\.$#"
count: 1
@ -80,11 +75,6 @@ parameters:
count: 1
path: ../../../src/thread/Worker.php
-
message: "#^Dead catch \\- JsonException is never thrown in the try block\\.$#"
count: 1
path: ../../../src/utils/Config.php
-
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
count: 2

File diff suppressed because one or more lines are too long

View File

@ -35,20 +35,32 @@ use function fopen;
use function fwrite;
use function getcwd;
use function ksort;
use function str_repeat;
use function str_replace;
use function strlen;
use function strtolower;
use const SORT_STRING;
use const STDERR;
require dirname(__DIR__) . '/vendor/autoload.php';
if(count($argv) > 2){
fwrite(STDERR, "Required arguments: md|rst\n");
exit(1);
}
$format = $argv[1] ?? "md";
if($format !== "md" && $format !== "rst"){
fwrite(STDERR, "Invalid format, expected either \"md\" or \"rst\"\n");
exit(1);
}
function markdownify(string $name) : string{
return str_replace(['.', '`', ' '], ['', '', '-'], strtolower($name));
}
DefaultPermissions::registerCorePermissions();
$cwd = Utils::assumeNotFalse(getcwd());
$output = Path::join($cwd, "core-permissions.md");
$output = Path::join($cwd, "core-permissions.$format");
echo "Writing output to $output\n";
$doc = fopen($output, "wb");
if($doc === false){
@ -59,36 +71,92 @@ if($doc === false){
$permissions = PermissionManager::getInstance()->getPermissions();
ksort($permissions, SORT_STRING);
fwrite($doc, "# PocketMine-MP Core Permissions\n");
fwrite($doc, "Generated from PocketMine-MP " . VersionInfo::VERSION()->getFullVersion() . "\n");
$title = "List of " . VersionInfo::NAME . " core permissions";
if($format === "md"){
fwrite($doc, "# $title\n");
}else{
fwrite($doc, ".. _corepermissions:\n\n");
fwrite($doc, "$title\n");
fwrite($doc, str_repeat("=", strlen($title)) . "\n\n");
}
fwrite($doc, "Generated from " . VersionInfo::NAME . " " . VersionInfo::VERSION()->getFullVersion() . "\n");
fwrite($doc, "\n");
fwrite($doc, "| Name | Description | Implied permissions |\n");
fwrite($doc, "|:-----|:------------|:-------------------:|\n");
if($format === "md"){
fwrite($doc, "| Name | Description | Implied permissions |\n");
fwrite($doc, "|:-----|:------------|:-------------------:|\n");
}else{
fwrite($doc, ".. list-table::\n");
fwrite($doc, " :header-rows: 1\n\n");
fwrite($doc, " * - Name\n");
fwrite($doc, " - Description\n");
fwrite($doc, " - Implied permissions\n");
fwrite($doc, "\n");
}
foreach($permissions as $permission){
$link = count($permission->getChildren()) === 0 ? "N/A" : "[Jump](#" . markdownify("Permissions implied by `" . $permission->getName() . "`") . ")";
fwrite($doc, "| `" . $permission->getName() . "` | " . $permission->getDescription() . " | $link |\n");
if($format === "md"){
$link = count($permission->getChildren()) === 0 ? "N/A" : "[Jump](#" . markdownify("Permissions implied by `" . $permission->getName() . "`") . ")";
fwrite($doc, "| `" . $permission->getName() . "` | " . $permission->getDescription() . " | $link |\n");
}else{
fwrite($doc, " * - ``" . $permission->getName() . "``\n");
fwrite($doc, " - " . $permission->getDescription() . "\n");
if(count($permission->getChildren()) === 0){
fwrite($doc, " - N/A\n");
}else{
fwrite($doc, " - :ref:`Jump<permissions_implied_by_" . $permission->getName() . ">`\n");
}
}
}
fwrite($doc, "\n\n");
fwrite($doc, "## Implied permissions\n");
fwrite($doc, "Some permissions automatically grant (or deny) other permissions by default when granted. These are referred to as **implied permissions**.<br>\n");
fwrite($doc, "Permissions may imply permissions which in turn imply other permissions (e.g. `pocketmine.group.operator` implies `pocketmine.group.user`, which in turn implies `pocketmine.command.help`).<br>\n");
fwrite($doc, "Implied permissions can be overridden by explicit permissions from elsewhere.<br>\n");
fwrite($doc, "**Note:** When explicitly denied, implied permissions are inverted. This means that \"granted\" becomes \"denied\" and vice versa.\n");
$title = "Implied permissions";
if($format === "md"){
fwrite($doc, "## $title\n");
}else{
fwrite($doc, "$title\n");
fwrite($doc, str_repeat("-", strlen($title)) . "\n\n");
}
$newline = $format === "md" ? "<br>\n" : "\n\n";
$code = $format === "md" ? "`" : "``";
fwrite($doc, "Some permissions automatically grant (or deny) other permissions by default when granted. These are referred to as **implied permissions**.$newline");
fwrite($doc, "Permissions may imply permissions which in turn imply other permissions (e.g. {$code}pocketmine.group.operator{$code} implies {$code}pocketmine.group.user{$code}, which in turn implies {$code}pocketmine.command.help{$code}).$newline");
fwrite($doc, "Implied permissions can be overridden by explicit permissions from elsewhere.$newline");
fwrite($doc, "**Note:** When explicitly denied, implied permissions are inverted. This means that \"granted\" becomes \"denied\" and vice versa.$newline");
fwrite($doc, "\n\n");
foreach($permissions as $permission){
if(count($permission->getChildren()) === 0){
continue;
}
fwrite($doc, "### Permissions implied by `" . $permission->getName() . "`\n");
$title = "Permissions implied by " . $code . $permission->getName() . $code;
if($format === "md"){
fwrite($doc, "### $title\n");
}else{
fwrite($doc, ".. _permissions_implied_by_" . $permission->getName() . ":\n\n");
fwrite($doc, "$title\n");
fwrite($doc, str_repeat("~", strlen($title)) . "\n\n");
}
fwrite($doc, "Users granted this permission will also be granted/denied the following permissions implicitly:\n\n");
fwrite($doc, "| Name | Type |\n");
fwrite($doc, "|:-----|:----:|\n");
$children = $permission->getChildren();
ksort($children, SORT_STRING);
foreach(Utils::stringifyKeys($children) as $childName => $isGranted){
fwrite($doc, "| `$childName` | " . ($isGranted ? "Granted" : "Denied") . " |\n");
if($format === "md"){
fwrite($doc, "| Name | Type |\n");
fwrite($doc, "|:-----|:----:|\n");
$children = $permission->getChildren();
ksort($children, SORT_STRING);
foreach(Utils::stringifyKeys($children) as $childName => $isGranted){
fwrite($doc, "| `$childName` | " . ($isGranted ? "Granted" : "Denied") . " |\n");
}
}else{
fwrite($doc, ".. list-table::\n");
fwrite($doc, " :header-rows: 1\n\n");
fwrite($doc, " * - Name\n");
fwrite($doc, " - Type\n");
$children = $permission->getChildren();
ksort($children, SORT_STRING);
foreach(Utils::stringifyKeys($children) as $childName => $isGranted){
fwrite($doc, " * - ``$childName``\n");
fwrite($doc, " - " . ($isGranted ? "Granted" : "Denied") . "\n");
}
}
fwrite($doc, "\n");
}