mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-16 06:15:09 +00:00
Compare commits
66 Commits
Author | SHA1 | Date | |
---|---|---|---|
19d7c2b552 | |||
036e06e889 | |||
9343a0b800 | |||
14b4644b03 | |||
464b65b25c | |||
15586ed80e | |||
0f8ad8ecf7 | |||
82b9afef77 | |||
2fc84f6c67 | |||
566f5935a3 | |||
44e4dabf6e | |||
8acc535218 | |||
e9a1cb7ce5 | |||
a21419d120 | |||
c2b599166c | |||
df7a1fcba6 | |||
d77a95e4af | |||
5c72807b16 | |||
5c6927e16c | |||
9abbb85a93 | |||
554182b2cb | |||
d669a6f0c7 | |||
5d9f783037 | |||
01ca14c314 | |||
608c6ed6db | |||
c26631d06d | |||
b75bc61a64 | |||
3724479be3 | |||
eb916fe43d | |||
5e3b3a0700 | |||
e10a624444 | |||
b20e04539d | |||
4852f8029a | |||
c4f85e526b | |||
6cee428287 | |||
bcba064d69 | |||
86647683bc | |||
64f0e58e60 | |||
62f21516d1 | |||
c553f7cf06 | |||
fec89b7803 | |||
2b61c025c2 | |||
c7133bc2e6 | |||
baf75089f5 | |||
705df7d508 | |||
75d7adfb2d | |||
9d535e2917 | |||
3ccd288afd | |||
06655bee78 | |||
0ad2985247 | |||
269b6ed16a | |||
f031c3c602 | |||
f3e09dd7d5 | |||
68e704bf97 | |||
9898577135 | |||
784d602600 | |||
15c99cfe77 | |||
d5fa0a2fc5 | |||
0da9260994 | |||
df2d1fd4f9 | |||
9f65fb5f90 | |||
caa4b78a3f | |||
14352a05bc | |||
bb5b52d998 | |||
5e22b70b6d | |||
02513818a9 |
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -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
|
||||
|
100
.github/workflows/discord-release-embed.php
vendored
Normal file
100
.github/workflows/discord-release-embed.php
vendored
Normal 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);
|
||||
}
|
38
.github/workflows/discord-release-notify.yml
vendored
Normal file
38
.github/workflows/discord-release-notify.yml
vendored
Normal 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 }}
|
6
.github/workflows/draft-release.yml
vendored
6
.github/workflows/draft-release.yml
vendored
@ -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.
|
||||
|
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -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
|
||||
|
3
.github/workflows/support.yml
vendored
3
.github/workflows/support.yml
vendored
@ -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)
|
||||
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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]);
|
||||
}
|
||||
|
Submodule build/php updated: 11103498ca...f292501a70
@ -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
42
changelogs/4.6.md
Normal 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
23
changelogs/4.7.md
Normal 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).
|
@ -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
118
composer.lock
generated
@ -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",
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"]),
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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{
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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{
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)];
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
@ -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");
|
||||
}
|
||||
|
Reference in New Issue
Block a user