mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-16 14:25:05 +00:00
Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
b5a049d1fe | |||
bd9fcffe62 | |||
feffbc2c5b | |||
53b51c99b4 | |||
5cb77c8365 | |||
bf8befc40b | |||
f75ca312cc | |||
d144832928 | |||
709a869045 | |||
ac056044ce | |||
fc8434308b | |||
5426b41447 | |||
af2babec23 | |||
717ab1989a | |||
83db186b6a | |||
6a4e5aba8b | |||
c13170a00b | |||
98778052bb | |||
e86e8254a8 | |||
1b852ac290 | |||
10b799fadb | |||
bc5008334a | |||
575dd47db7 | |||
e4a5defabb | |||
c9626c610b | |||
8fb7fff6b9 | |||
5c8d8ff61f | |||
99b55f7427 | |||
dce8bd6d21 | |||
8fa81242d6 | |||
2f4a9469b6 | |||
c5b2488fc1 | |||
d62df585f2 | |||
19d7c2b552 | |||
036e06e889 | |||
9343a0b800 | |||
14b4644b03 | |||
464b65b25c | |||
15586ed80e | |||
0f8ad8ecf7 | |||
82b9afef77 | |||
2fc84f6c67 | |||
566f5935a3 | |||
44e4dabf6e | |||
8acc535218 | |||
e9a1cb7ce5 | |||
a21419d120 | |||
c2b599166c | |||
df7a1fcba6 | |||
d77a95e4af | |||
5c72807b16 | |||
5c6927e16c |
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.1.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.1.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.1.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.1.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.2
|
||||
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.repository }} ${{ 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.21.0
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
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.
|
||||
|
24
.github/workflows/main.yml
vendored
24
.github/workflows/main.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.22, 8.1.9]
|
||||
|
||||
steps:
|
||||
- name: Build and prepare PHP cache
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -31,13 +31,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.22, 8.1.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -69,13 +69,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.22, 8.1.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -107,7 +107,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.22, 8.1.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -115,7 +115,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -147,13 +147,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.22, 8.1.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -195,10 +195,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.21.0
|
||||
uses: shivammathur/setup-php@2.21.2
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer:3.2
|
||||
tools: php-cs-fixer:3.8
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff --ansi
|
||||
|
3
.github/workflows/update-php-versions.php
vendored
3
.github/workflows/update-php-versions.php
vendored
@ -22,7 +22,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const VERSIONS = [
|
||||
"8.0"
|
||||
"8.0",
|
||||
"8.1"
|
||||
];
|
||||
|
||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||
|
@ -70,6 +70,10 @@ BODY,
|
||||
'scope' => 'namespaced',
|
||||
'include' => ['@all'],
|
||||
],
|
||||
'new_with_braces' => [
|
||||
'named_class' => true,
|
||||
'anonymous_class' => false,
|
||||
],
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
|
@ -7,10 +7,11 @@ GitHub is public and anyone can see the issues you post on the issue tracker, in
|
||||
|
||||
**WARNING: You may put live servers at risk by reporting a vulnerability on the GitHub issue tracker.**
|
||||
|
||||
**Contact us** by sending an email to [**team@pmmp.io**](mailto:team@pmmp.io?subject=Security%20Vulnerability%20in%20PocketMine-MP). Include the following information:
|
||||
**Contact us** by sending an email to [**security@pmmp.io**](mailto:security@pmmp.io). Include the following information:
|
||||
|
||||
- Version of PocketMine-MP
|
||||
- Detailed description of the vulnerability (e.g. how to exploit it, what the effects are)
|
||||
- Your GitHub username, if you wish to be credited for reporting the problem in the security advisory
|
||||
|
||||
Please note that we can't guarantee a reply to every email.
|
||||
|
||||
|
@ -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";
|
||||
|
Submodule build/php updated: f292501a70...e90ff50310
46
changelogs/4.7.md
Normal file
46
changelogs/4.7.md
Normal file
@ -0,0 +1,46 @@
|
||||
**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).
|
||||
|
||||
# 4.7.2
|
||||
Released 16th August 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed crash when processing player skins with invalid geometry data.
|
||||
- Fixed spectator players being able to pick blocks using mousewheel click.
|
||||
- Improved supporting requirements for sugarcane.
|
||||
|
||||
# 4.7.3
|
||||
Released 22nd August 2022.
|
||||
|
||||
## General
|
||||
- Added complete translations for Spanish and Vietnamese.
|
||||
- All continuous integration (static analysis, unit tests, integration tests) are now performed on PHP 8.1 as well as 8.0.
|
||||
- InventoryTransaction now verifies that stack sizes of items after the transaction don't exceed the maximum stack size of the item type or the containing inventory.
|
||||
|
||||
## Fixes
|
||||
- Fixed Normal generator crash on PHP 8.1.
|
||||
- Fixed a race condition during async worker shutdown that could lead to tasks executing in the wrong order. This (very rarely) led to a crash in `PopulationTask` due to its preceding `GeneratorRegisterTask` not being executed.
|
||||
- Fixed `/give` accepting negative amounts or amounts larger than 32767 (vanilla max).
|
||||
- Fixed placement conditions for vines (no longer able to be placed on the side of cacti).
|
||||
- Fixed incorrect documentation of `SignText::__construct()`.
|
@ -34,8 +34,8 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-data": "~1.9.0+bedrock-1.19.10",
|
||||
"pocketmine/bedrock-protocol": "~11.0.0+bedrock-1.19.10",
|
||||
"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",
|
||||
|
64
composer.lock
generated
64
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": "f9d81230e16fff707deb5cbe7ff37376",
|
||||
"content-hash": "80afa24adf37096a23643e051d6128ce",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -245,16 +245,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "1.9.0+bedrock-1.19.10",
|
||||
"version": "1.10.0+bedrock-1.19.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "ecd798a3e7ead50b7da73141bbb0c4ba14dd76a1"
|
||||
"reference": "43610f6749f22d15ede6b60ed5402bdeff47453e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/ecd798a3e7ead50b7da73141bbb0c4ba14dd76a1",
|
||||
"reference": "ecd798a3e7ead50b7da73141bbb0c4ba14dd76a1",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/43610f6749f22d15ede6b60ed5402bdeff47453e",
|
||||
"reference": "43610f6749f22d15ede6b60ed5402bdeff47453e",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -265,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.10"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.20"
|
||||
},
|
||||
"time": "2022-07-12T19:33:21+00:00"
|
||||
"time": "2022-08-09T17:44:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "11.0.4+bedrock-1.19.10",
|
||||
"version": "12.0.0+bedrock-1.19.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "1c87aa1187bc7a31b4fc3e1c0f3e22251c2e6eab"
|
||||
"reference": "c2778039544fa0c7c5bd3af7963149e7552f4215"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/1c87aa1187bc7a31b4fc3e1c0f3e22251c2e6eab",
|
||||
"reference": "1c87aa1187bc7a31b4fc3e1c0f3e22251c2e6eab",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c2778039544fa0c7c5bd3af7963149e7552f4215",
|
||||
"reference": "c2778039544fa0c7c5bd3af7963149e7552f4215",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -312,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/11.0.4+bedrock-1.19.10"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/bedrock-1.19.20"
|
||||
},
|
||||
"time": "2022-07-24T19:22:18+00:00"
|
||||
"time": "2022-08-09T17:57:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -532,16 +532,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "2.8.3",
|
||||
"version": "2.8.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "113c115a3b8976917eb22b74dccab464831b6483"
|
||||
"reference": "e115d3d64a508065f1cedad1be55528906308456"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/113c115a3b8976917eb22b74dccab464831b6483",
|
||||
"reference": "113c115a3b8976917eb22b74dccab464831b6483",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/e115d3d64a508065f1cedad1be55528906308456",
|
||||
"reference": "e115d3d64a508065f1cedad1be55528906308456",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -549,9 +549,9 @@
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/2.8.3"
|
||||
"source": "https://github.com/pmmp/Language/tree/2.8.7"
|
||||
},
|
||||
"time": "2022-05-11T13:51:37+00:00"
|
||||
"time": "2022-08-21T20:37:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
@ -1891,23 +1891,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.15",
|
||||
"version": "9.2.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
|
||||
"reference": "2593003befdcc10db5e213f9f28814f5aa8ac073"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2593003befdcc10db5e213f9f28814f5aa8ac073",
|
||||
"reference": "2593003befdcc10db5e213f9f28814f5aa8ac073",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
"nikic/php-parser": "^4.14",
|
||||
"php": ">=7.3",
|
||||
"phpunit/php-file-iterator": "^3.0.3",
|
||||
"phpunit/php-text-template": "^2.0.2",
|
||||
@ -1956,7 +1956,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.16"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1964,7 +1964,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-07T09:28:20+00:00"
|
||||
"time": "2022-08-20T05:26:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -2209,16 +2209,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.21",
|
||||
"version": "9.5.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1"
|
||||
"reference": "e329ac6e8744f461518272612a479fde958752fe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e32b76be457de00e83213528f6bb37e2a38fcb1",
|
||||
"reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e329ac6e8744f461518272612a479fde958752fe",
|
||||
"reference": "e329ac6e8744f461518272612a479fde958752fe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2295,7 +2295,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.21"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.22"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2307,7 +2307,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-06-19T12:14:25+00:00"
|
||||
"time": "2022-08-20T08:25:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
@ -1,4 +1,5 @@
|
||||
includes:
|
||||
- tests/phpstan/analyse-for-current-php-version.neon.php
|
||||
- tests/phpstan/configs/actual-problems.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/impossible-generics.neon
|
||||
|
@ -37,6 +37,7 @@ namespace pocketmine {
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function function_exists;
|
||||
use function getcwd;
|
||||
use function phpversion;
|
||||
use function preg_match;
|
||||
@ -160,7 +161,7 @@ namespace pocketmine {
|
||||
if(PHP_DEBUG !== 0){
|
||||
$logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
|
||||
}
|
||||
if(extension_loaded("xdebug")){
|
||||
if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){
|
||||
$logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
|
||||
}
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.6.2";
|
||||
public const BASE_VERSION = "4.7.3";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
|
@ -98,7 +98,7 @@ class Sugarcane extends Flowable{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->isTransparent() && !$down->isSameType($this)){
|
||||
if(!$this->isValidSupport($down)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
@ -122,9 +122,10 @@ class Sugarcane extends Flowable{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->isSameType($this)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}elseif($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::SAND || $down->getId() === BlockLegacyIds::PODZOL){
|
||||
}elseif($this->isValidSupport($down)){
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
if($down->getSide($side) instanceof Water){
|
||||
$sideBlock = $down->getSide($side);
|
||||
if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
}
|
||||
@ -132,4 +133,15 @@ class Sugarcane extends Flowable{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isValidSupport(Block $block) : bool{
|
||||
$id = $block->getId();
|
||||
//TODO: rooted dirt, moss block
|
||||
return $block->isSameType($this)
|
||||
|| $id === BlockLegacyIds::GRASS
|
||||
|| $id === BlockLegacyIds::DIRT
|
||||
|| $id === BlockLegacyIds::PODZOL
|
||||
|| $id === BlockLegacyIds::MYCELIUM
|
||||
|| $id === BlockLegacyIds::SAND;
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class Vine extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockClicked->isSolid() || Facing::axis($face) === Axis::Y){
|
||||
if(!$blockClicked->isFullCube() || Facing::axis($face) === Axis::Y){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ class SignText{
|
||||
private array $lines;
|
||||
|
||||
/**
|
||||
* @param string[]|null $lines index-sensitive; omitting an index will leave it unchanged
|
||||
* @param string[]|null $lines index-sensitive; keys 0-3 will be used, regardless of array order
|
||||
*
|
||||
* @throws \InvalidArgumentException if the array size is greater than 4
|
||||
* @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array
|
||||
|
@ -75,7 +75,11 @@ class GiveCommand extends VanillaCommand{
|
||||
if(!isset($args[2])){
|
||||
$item->setCount($item->getMaxStackSize());
|
||||
}else{
|
||||
$item->setCount((int) $args[2]);
|
||||
$count = $this->getBoundedInt($sender, $args[2], 1, 32767);
|
||||
if($count === null){
|
||||
return true;
|
||||
}
|
||||
$item->setCount($count);
|
||||
}
|
||||
|
||||
if(isset($args[3])){
|
||||
|
@ -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"]),
|
||||
|
@ -1466,7 +1466,7 @@ abstract class Entity{
|
||||
$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
|
||||
|
@ -28,7 +28,6 @@ use pocketmine\utils\Limits;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function strlen;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
|
||||
@ -68,9 +67,10 @@ final class Skin{
|
||||
}
|
||||
|
||||
if($geometryData !== ""){
|
||||
$decodedGeometry = (new CommentedJsonDecoder())->decode($geometryData);
|
||||
if($decodedGeometry === false){
|
||||
throw new InvalidSkinException("Invalid geometry data (" . json_last_error_msg() . ")");
|
||||
try{
|
||||
$decodedGeometry = (new CommentedJsonDecoder())->decode($geometryData);
|
||||
}catch(\RuntimeException $e){
|
||||
throw new InvalidSkinException("Invalid geometry data: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -35,7 +35,7 @@ final class StringToEffectParser extends StringToTParser{
|
||||
use SingletonTrait;
|
||||
|
||||
private static function make() : self{
|
||||
$result = new self;
|
||||
$result = new self();
|
||||
|
||||
$result->register("absorption", fn() => VanillaEffects::ABSORPTION());
|
||||
$result->register("blindness", fn() => VanillaEffects::BLINDNESS());
|
||||
|
@ -31,7 +31,7 @@ class HandlerListManager{
|
||||
private static ?self $globalInstance = null;
|
||||
|
||||
public static function global() : self{
|
||||
return self::$globalInstance ?? (self::$globalInstance = new self);
|
||||
return self::$globalInstance ?? (self::$globalInstance = new self());
|
||||
}
|
||||
|
||||
/** @var HandlerList[] classname => HandlerList */
|
||||
|
@ -42,6 +42,9 @@ class DropItemAction extends InventoryAction{
|
||||
if($this->targetItem->isNull()){
|
||||
throw new TransactionValidationException("Cannot drop an empty itemstack");
|
||||
}
|
||||
if($this->targetItem->getCount() > $this->targetItem->getMaxStackSize()){
|
||||
throw new TransactionValidationException("Target item exceeds item type max stack size");
|
||||
}
|
||||
}
|
||||
|
||||
public function onPreExecute(Player $source) : bool{
|
||||
|
@ -70,6 +70,12 @@ class SlotChangeAction extends InventoryAction{
|
||||
if(!$this->inventory->getItem($this->inventorySlot)->equalsExact($this->sourceItem)){
|
||||
throw new TransactionValidationException("Slot does not contain expected original item");
|
||||
}
|
||||
if($this->targetItem->getCount() > $this->targetItem->getMaxStackSize()){
|
||||
throw new TransactionValidationException("Target item exceeds item type max stack size");
|
||||
}
|
||||
if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){
|
||||
throw new TransactionValidationException("Target item exceeds inventory max stack size");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -41,7 +41,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
use SingletonTrait;
|
||||
|
||||
private static function make() : self{
|
||||
$result = new self;
|
||||
$result = new self();
|
||||
|
||||
foreach(DyeColor::getAll() as $color){
|
||||
$prefix = fn(string $name) => $color->name() . "_" . $name;
|
||||
|
@ -35,7 +35,7 @@ final class StringToEnchantmentParser extends StringToTParser{
|
||||
use SingletonTrait;
|
||||
|
||||
private static function make() : self{
|
||||
$result = new self;
|
||||
$result = new self();
|
||||
|
||||
$result->register("blast_protection", fn() => VanillaEnchantments::BLAST_PROTECTION());
|
||||
$result->register("efficiency", fn() => VanillaEnchantments::EFFICIENCY());
|
||||
|
@ -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{
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\block\tile\Spawnable;
|
||||
use pocketmine\data\bedrock\EffectIdMap;
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
@ -59,7 +58,6 @@ use pocketmine\network\mcpe\handler\PreSpawnPacketHandler;
|
||||
use pocketmine\network\mcpe\handler\ResourcePacksPacketHandler;
|
||||
use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
@ -756,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{
|
||||
@ -834,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));
|
||||
}
|
||||
}
|
||||
@ -961,22 +959,6 @@ class NetworkSession{
|
||||
try{
|
||||
$this->queueCompressed($promise);
|
||||
$onCompletion();
|
||||
|
||||
//TODO: HACK! we send the full tile data here, due to a bug in 1.19.10 which causes items in tiles
|
||||
//(item frames, lecterns) to not load properly when they are sent in a chunk via the classic chunk
|
||||
//sending mechanism. We workaround this bug by sending only bare essential data in LevelChunkPacket
|
||||
//(enough to create the tiles, since BlockActorDataPacket can't create tiles by itself) and then
|
||||
//send the actual tile properties here.
|
||||
//TODO: maybe we can stuff these packets inside the cached batch alongside LevelChunkPacket?
|
||||
$chunk = $currentWorld->getChunk($chunkX, $chunkZ);
|
||||
if($chunk !== null){
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if(!($tile instanceof Spawnable)){
|
||||
continue;
|
||||
}
|
||||
$this->sendDataPacket(BlockActorDataPacket::create(BlockPosition::fromVector3($tile->getPosition()), $tile->getSerializedSpawnCompound()));
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
$world->timings->syncChunkSend->stopTiming();
|
||||
}
|
||||
|
@ -890,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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,13 +165,13 @@ class LoginPacketHandler extends PacketHandler{
|
||||
if(!is_array($claims["extraData"])){
|
||||
throw new PacketHandlingException("'extraData' key should be an array");
|
||||
}
|
||||
$mapper = new \JsonMapper;
|
||||
$mapper = new \JsonMapper();
|
||||
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bExceptionOnUndefinedProperty = true;
|
||||
try{
|
||||
/** @var AuthenticationData $extraData */
|
||||
$extraData = $mapper->map($claims["extraData"], new AuthenticationData);
|
||||
$extraData = $mapper->map($claims["extraData"], new AuthenticationData());
|
||||
}catch(\JsonMapper_Exception $e){
|
||||
throw PacketHandlingException::wrap($e);
|
||||
}
|
||||
@ -193,12 +193,12 @@ class LoginPacketHandler extends PacketHandler{
|
||||
throw PacketHandlingException::wrap($e);
|
||||
}
|
||||
|
||||
$mapper = new \JsonMapper;
|
||||
$mapper = new \JsonMapper();
|
||||
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bExceptionOnUndefinedProperty = true;
|
||||
try{
|
||||
$clientData = $mapper->map($clientDataClaims, new ClientData);
|
||||
$clientData = $mapper->map($clientDataClaims, new ClientData());
|
||||
}catch(\JsonMapper_Exception $e){
|
||||
throw PacketHandlingException::wrap($e);
|
||||
}
|
||||
|
@ -98,9 +98,10 @@ 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());
|
||||
|
@ -84,8 +84,8 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
|
||||
$this->sleeper = new SleeperNotifier();
|
||||
|
||||
$mainToThreadBuffer = new \Threaded;
|
||||
$threadToMainBuffer = new \Threaded;
|
||||
$mainToThreadBuffer = new \Threaded();
|
||||
$threadToMainBuffer = new \Threaded();
|
||||
|
||||
$this->rakLib = new RakLibServer(
|
||||
$this->server->getLogger(),
|
||||
|
@ -24,11 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe\serializer;
|
||||
|
||||
use pocketmine\block\tile\Spawnable;
|
||||
use pocketmine\block\tile\Tile;
|
||||
use pocketmine\block\tile\TileFactory;
|
||||
use pocketmine\data\bedrock\BiomeIds;
|
||||
use pocketmine\data\bedrock\LegacyBiomeIdToStringIdMap;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
@ -41,7 +38,6 @@ use pocketmine\world\format\PalettedBlockArray;
|
||||
use pocketmine\world\format\SubChunk;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function str_repeat;
|
||||
|
||||
final class ChunkSerializer{
|
||||
@ -129,19 +125,9 @@ final class ChunkSerializer{
|
||||
|
||||
public static function serializeTiles(Chunk $chunk) : string{
|
||||
$stream = new BinaryStream();
|
||||
$nbtSerializer = new NetworkNbtSerializer();
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile instanceof Spawnable){
|
||||
//TODO: HACK! we send only the bare essentials to create a tile in the chunk itself, due to a bug in
|
||||
//1.19.10 which causes items in tiles (item frames, lecterns) to not load properly when they are sent in
|
||||
//a chunk via the classic chunk sending mechanism. We workaround this bug by sendingBlockActorDataPacket
|
||||
//in NetworkSession to set the actual tile properties after sending the LevelChunkPacket.
|
||||
$nbt = CompoundTag::create()
|
||||
->setString(Tile::TAG_ID, TileFactory::getInstance()->getSaveId(get_class($tile)))
|
||||
->setInt(Tile::TAG_X, $tile->getPosition()->getFloorX())
|
||||
->setInt(Tile::TAG_Y, $tile->getPosition()->getFloorY())
|
||||
->setInt(Tile::TAG_Z, $tile->getPosition()->getFloorZ());
|
||||
$stream->put($nbtSerializer->write(new TreeRoot($nbt)));
|
||||
$stream->put($tile->getSerializedSpawnCompound()->getEncodedNbt());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class PermissionManager{
|
||||
|
||||
public static function getInstance() : PermissionManager{
|
||||
if(self::$instance === null){
|
||||
self::$instance = new self;
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
|
@ -1558,7 +1558,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
$ev = new PlayerBlockPickEvent($this, $block, $item);
|
||||
$existingSlot = $this->inventory->first($item);
|
||||
if($existingSlot === -1 && $this->hasFiniteResources()){
|
||||
if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
|
@ -163,7 +163,7 @@ class AsyncPool{
|
||||
throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once");
|
||||
}
|
||||
|
||||
$task->progressUpdates = new \Threaded;
|
||||
$task->progressUpdates = new \Threaded();
|
||||
$task->setSubmitted();
|
||||
|
||||
$this->getWorker($worker)->stack($task);
|
||||
|
@ -56,7 +56,9 @@ abstract class Worker extends \Worker{
|
||||
$this->isKilled = true;
|
||||
|
||||
if(!$this->isShutdown()){
|
||||
while($this->unstack() !== null);
|
||||
$this->synchronized(function() : void{
|
||||
while($this->unstack() !== null);
|
||||
});
|
||||
$this->notify();
|
||||
$this->shutdown();
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ trait SingletonTrait{
|
||||
private static $instance = null;
|
||||
|
||||
private static function make() : self{
|
||||
return new self;
|
||||
return new self();
|
||||
}
|
||||
|
||||
public static function getInstance() : self{
|
||||
|
@ -48,7 +48,7 @@ final class WorldCreationOptions{
|
||||
}
|
||||
|
||||
public static function create() : self{
|
||||
return new self;
|
||||
return new self();
|
||||
}
|
||||
|
||||
/** @phpstan-return class-string<Generator> */
|
||||
|
@ -123,6 +123,7 @@ class Normal extends Generator{
|
||||
private function pickBiome(int $x, int $z) : Biome{
|
||||
$hash = $x * 2345803 ^ $z * 9236449 ^ $this->seed;
|
||||
$hash *= $hash + 223;
|
||||
$hash = (int) $hash;
|
||||
$xNoise = $hash >> 20 & 3;
|
||||
$zNoise = $hash >> 22 & 3;
|
||||
if($xNoise == 3){
|
||||
|
32
tests/phpstan/analyse-for-current-php-version.neon.php
Normal file
32
tests/phpstan/analyse-for-current-php-version.neon.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This hack works around a "feature" in PHPStan which locks in analysis version based on platform.php in composer.json
|
||||
* See https://github.com/phpstan/phpstan/issues/7701 for details
|
||||
*/
|
||||
return [
|
||||
'parameters' => [
|
||||
'phpVersion' => PHP_VERSION_ID
|
||||
]
|
||||
];
|
@ -35,11 +35,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/handler/InGamePacketHandler.php
|
||||
|
||||
-
|
||||
message: "#^Negated boolean expression is always true\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/handler/InGamePacketHandler.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\raklib\\\\PthreadsChannelWriter\\:\\:\\$buffer is never read, only written\\.$#"
|
||||
count: 1
|
||||
@ -80,3 +75,8 @@ parameters:
|
||||
count: 2
|
||||
path: ../../../src/world/format/io/region/RegionLoader.php
|
||||
|
||||
-
|
||||
message: "#^Casting to int something that's already int\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/normal/Normal.php
|
||||
|
||||
|
@ -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