Compare commits

..

102 Commits
4.5.1 ... 4.8.0

Author SHA1 Message Date
0284e65f60 Release 4.8.0 2022-08-24 20:03:56 +01:00
b0d787b3d3 Update BedrockProtocol for 1.19.21 2022-08-24 19:54:41 +01:00
75eba9c9ed 4.7.4 is next 2022-08-22 19:28:47 +01:00
b5a049d1fe Release 4.7.3 2022-08-22 19:28:43 +01:00
bd9fcffe62 Bump build/php from f292501 to e90ff50 (#5242)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `f292501` to `e90ff50`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](f292501a70...e90ff50310)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:42:17 +01:00
feffbc2c5b Bump phpunit/phpunit from 9.5.21 to 9.5.22 (#5243)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.21 to 9.5.22.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.21...9.5.22)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:33:16 +01:00
53b51c99b4 Bump pocketmine/locale-data from 2.8.6 to 2.8.7 (#5244)
Bumps [pocketmine/locale-data](https://github.com/pmmp/Language) from 2.8.6 to 2.8.7.
- [Release notes](https://github.com/pmmp/Language/releases)
- [Commits](https://github.com/pmmp/Language/compare/2.8.6...2.8.7)

---
updated-dependencies:
- dependency-name: pocketmine/locale-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:32:50 +01:00
5cb77c8365 GiveCommand: fix CS 2022-08-22 00:55:17 +01:00
bf8befc40b Remove dead comment on GiveCommand (#5241) 2022-08-22 00:49:22 +01:00
f75ca312cc Worker: Unstack tasks in a synchronized block
this prevents any tasks still left in the queue on shutdown getting pulled out by the worker when we're attempting to shut it down.
This led to various race conditions, most notably weird cases where PopulationTask would inexplicably find its expected generator state had not been correctly set up.
2022-08-21 21:57:11 +01:00
d144832928 GiveCommand: limit max amount in line with vanilla 2022-08-21 21:19:16 +01:00
709a869045 Vines can now only be placed on full cube blocks (#5053)
fixes #2673
2022-08-21 21:04:24 +01:00
ac056044ce Updated PHPStan baseline 2022-08-21 20:46:38 +01:00
fc8434308b SignText: changed misleading documentation
this looks like a leftover from the days when sign text was handled by the tile directly
2022-08-21 20:45:23 +01:00
5426b41447 InventoryTransaction: prevent client-authoritative item overstacking
this cheat is often used to carry more items in the inventory, wear multiple pieces of armour in one slot, and more.
2022-08-21 20:35:23 +01:00
af2babec23 GiveCommand: do not accept negative amounts 2022-08-21 20:28:39 +01:00
717ab1989a Update setup-php-action to pmmp/setup-php-action@82a44d659b 2022-08-21 18:14:07 +01:00
83db186b6a Updated setup-php-action to pmmp/setup-php-action@e128aee02f 2022-08-20 18:53:08 +01:00
6a4e5aba8b Update setup-php-action to pmmp/setup-php-action@330b4c2940 2022-08-20 18:03:30 +01:00
c13170a00b Avoid implicit integer cast in Normal::pickBiome()
this throws deprecation warnings on PHP 8.1.
2022-08-20 17:16:38 +01:00
98778052bb actions: start building on 8.1 2022-08-20 16:32:36 +01:00
e86e8254a8 Workaround PHPStan "feature" phpstan/phpstan#7701 2022-08-20 16:29:26 +01:00
1b852ac290 bootstrap: do not complain about xdebug if mode is 'off'
if xdebug.mode=off, the performance impact is the same as if xdebug wasn't loaded.
2022-08-19 16:45:40 +01:00
10b799fadb Bump shivammathur/setup-php from 2.21.1 to 2.21.2 (#5238)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.21.1 to 2.21.2.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.21.1...2.21.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 15:51:44 +01:00
bc5008334a Bump pocketmine/locale-data from 2.8.3 to 2.8.6 (#5239)
Bumps [pocketmine/locale-data](https://github.com/pmmp/Language) from 2.8.3 to 2.8.6.
- [Release notes](https://github.com/pmmp/Language/releases)
- [Commits](https://github.com/pmmp/Language/compare/2.8.3...2.8.6)

---
updated-dependencies:
- dependency-name: pocketmine/locale-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 15:51:04 +01:00
575dd47db7 4.7.3 is next 2022-08-16 17:51:26 +01:00
e4a5defabb Release 4.7.2 2022-08-16 17:51:26 +01:00
c9626c610b Skin: Correctly handle errors produced by commented JSON decoder 2022-08-16 17:35:23 +01:00
8fb7fff6b9 Update SECURITY.md 2022-08-16 17:22:22 +01:00
5c8d8ff61f Update SECURITY.md 2022-08-16 17:04:25 +01:00
99b55f7427 actions: use newer php-cs-fixer 2022-08-15 17:26:42 +01:00
dce8bd6d21 CS: Standardize new with braces 2022-08-15 17:16:23 +01:00
8fa81242d6 Sugarcane: fixed support conditions (#5052) 2022-08-15 17:08:26 +01:00
2f4a9469b6 Player: spectator shouldn't able to pick blocks they don't have (#5111)
Jury is out on whether they should be able to pick blocks at all, or be considered to have infinite resources, but this solution has been used in a few other places already anyway, so it can be cleaned up another time.
2022-08-15 16:48:37 +01:00
c5b2488fc1 oh come on... 2022-08-14 20:02:07 +01:00
d62df585f2 4.7.2 is next 2022-08-14 19:56:00 +01:00
19d7c2b552 Release 4.7.1 2022-08-14 19:55:56 +01:00
036e06e889 Revert "Workaround items in blockentity NBT not being processed correctly in 1.19.10"
This reverts commit 2b61c025c2.
2022-08-14 17:25:55 +01:00
9343a0b800 Added build log link to Discord release embed 2022-08-14 17:20:01 +01:00
14b4644b03 Added build_log_url to build_info.json 2022-08-14 17:20:01 +01:00
464b65b25c Bump docker/build-push-action from 3.1.0 to 3.1.1 (#5213)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.1.0...v3.1.1)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-04 15:15:17 +01:00
9898577135 Bump phpstan/phpstan from 1.7.15 to 1.8.0 (#5120)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.7.15 to 1.8.0.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.8.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.7.15...1.8.0)

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

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

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

View File

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

View File

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

View File

@ -0,0 +1,38 @@
name: Notify Discord webhook of release
on:
release:
types:
- published
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.21.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 }}

View File

@ -18,7 +18,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.19.0
uses: shivammathur/setup-php@2.21.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.

View File

@ -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.19.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

View File

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

View File

@ -22,7 +22,8 @@
declare(strict_types=1);
const VERSIONS = [
"8.0"
"8.0",
"8.1"
];
$workflowFile = file_get_contents(__DIR__ . '/main.yml');

View File

@ -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,

View File

@ -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.

View File

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

View File

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

View File

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

42
changelogs/4.6.md Normal file
View File

@ -0,0 +1,42 @@
**For Minecraft: Bedrock Edition 1.19.10**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.6.0
Released 13th July 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.19.10.
- Removed support for older versions.
# 4.6.1
Released 22nd July 2022.
## Tools
- `build/generate-registry-annotations.php` now supports processing single files (useful for PhpStorm file watchers).
## API
- Updated documentation for `AsyncTask`.
## Fixes
- Fixed incorrect items being displayed in item frames.
- Fixed books not showing in lecterns.
- Fixed incorrect damage interval of Wither status effect.
- Fixed incorrect fire ticks when being set on fire by lava (8 seconds in Bedrock instead of 15).
- `Entity->attack()` now cancels damage from `FIRE` and `FIRE_TICK` damage causes if the entity is fireproof.
- Fixed inventory windows getting force-closed when the client attempts to use an enchanting table or anvil.
# 4.6.2
Released 6th August 2022.
## Core
- Improved server-side performance of `PlayerAuthInputPacket` handler.
- Improved client-side performance of `FloatingTextParticle` by using an invisible falling block entity. This offered a roughly 5x performance improvement over using tiny invisible players in local testing.
## Fixes
- Fixed assert failures and debug spam on debug Minecraft clients related to abilities in `AddPlayerPacket`.
- Fixed crash in `ReversePriorityQueue` on PHP 8.1 by adding `#[ReturnTypeWillChange]` attribute.

46
changelogs/4.7.md Normal file
View 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()`.

14
changelogs/4.8.md Normal file
View File

@ -0,0 +1,14 @@
**For Minecraft: Bedrock Edition 1.19.21**
### 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.8.0
Released 9th August 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.19.21.
- Removed support for older versions.

View File

@ -34,8 +34,8 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.8.0+bedrock-1.19.0",
"pocketmine/bedrock-protocol": "~10.0.0+bedrock-1.19.0",
"pocketmine/bedrock-data": "~1.10.0+bedrock-1.19.20",
"pocketmine/bedrock-protocol": "~12.2.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"

144
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "363ffec55c206510b591b54f10138fc0",
"content-hash": "72bfad1350c97a3df225577163d702ea",
"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.2.0+bedrock-1.19.21",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "331fb0eb45c26daadf8cf01a3b6f20e909d7684b"
"reference": "d1b3e83f77e2c6628b64793485260cddc55d92e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/331fb0eb45c26daadf8cf01a3b6f20e909d7684b",
"reference": "331fb0eb45c26daadf8cf01a3b6f20e909d7684b",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/d1b3e83f77e2c6628b64793485260cddc55d92e3",
"reference": "d1b3e83f77e2c6628b64793485260cddc55d92e3",
"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/12.2.0+bedrock-1.19.21"
},
"time": "2022-06-08T01:11:15+00:00"
"time": "2022-08-24T18:47:37+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -536,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",
@ -553,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",
@ -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,29 +1885,29 @@
"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",
"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",
@ -1961,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": [
{
@ -1969,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",
@ -2214,16 +2209,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.20",
"version": "9.5.22",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
"reference": "e329ac6e8744f461518272612a479fde958752fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e329ac6e8744f461518272612a479fde958752fe",
"reference": "e329ac6e8744f461518272612a479fde958752fe",
"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.22"
},
"funding": [
{
@ -2313,7 +2307,7 @@
"type": "github"
}
],
"time": "2022-04-01T12:37:26+00:00"
"time": "2022-08-20T08:25:46+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -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

View File

@ -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){

View File

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

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -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;
}

View File

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

View File

@ -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

View File

@ -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])){

View File

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

View File

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

View File

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

View File

@ -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);
}
/*

View File

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

View File

@ -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());

View File

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

View File

@ -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 */

View File

@ -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{

View File

@ -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");
}
}
/**

View File

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

View File

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

View File

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

View File

@ -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;

View File

@ -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());

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

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

View File

@ -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(),

View File

@ -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;

View File

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

View File

@ -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);

View File

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

View File

@ -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();
}

View File

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

View File

@ -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{

View File

@ -48,7 +48,7 @@ final class WorldCreationOptions{
}
public static function create() : self{
return new self;
return new self();
}
/** @phpstan-return class-string<Generator> */

View File

@ -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){

View File

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

View File

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

View File

@ -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
]
];

View File

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

View File

@ -5,11 +5,6 @@ parameters:
count: 1
path: ../../../src/block/BaseBanner.php
-
message: "#^Property pocketmine\\\\block\\\\tile\\\\TileFactory\\:\\:\\$saveNames \\(array\\<class\\-string\\<pocketmine\\\\block\\\\tile\\\\Tile\\>, string\\>\\) does not accept array\\<class\\-string\\<pocketmine\\\\block\\\\tile\\\\Tile\\>, bool\\|string\\>\\.$#"
count: 1
path: ../../../src/block/tile/TileFactory.php
-
message: "#^Comparison operation \"\\<\" between int\\<1, max\\> and 1 is always false\\.$#"
count: 1
@ -40,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,13 +70,13 @@ 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
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

File diff suppressed because one or more lines are too long

View File

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