mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-12 20:35:11 +00:00
Compare commits
287 Commits
4.12.3
...
4.18.0-ALP
Author | SHA1 | Date | |
---|---|---|---|
bd21feffc4 | |||
5b324f695c | |||
9caed10488 | |||
83945ff0a0 | |||
ef45180b80 | |||
941fd03998 | |||
1af8da3c1f | |||
a5985dcf7d | |||
183d1f4038 | |||
08ee825d91 | |||
337a254768 | |||
a31e3331fd | |||
acebbeed16 | |||
e0fdbe6eb1 | |||
cc8660629b | |||
e7e19abe85 | |||
5f9e0081fd | |||
b266f45152 | |||
14f141fab2 | |||
daff955bc4 | |||
0022d82779 | |||
7cad9be0d2 | |||
2f862a552a | |||
590f6dad08 | |||
9564c81582 | |||
3de7a8c27f | |||
d376399b7f | |||
e2071e59c8 | |||
8e280ebb8b | |||
fa7c38276c | |||
b13e97de3d | |||
328b87fc18 | |||
acaa1a9ce1 | |||
3aec0fa3df | |||
fa131dab12 | |||
bb4a82b1e7 | |||
93d844a281 | |||
616844696e | |||
71e3e36522 | |||
a1b42d419f | |||
ef942a627f | |||
fd8c276bd2 | |||
9783380d1a | |||
a784d93bfd | |||
a05e8b366f | |||
87a2e0460c | |||
4073c3fb39 | |||
e227e6d8bf | |||
3aa40829ae | |||
035d4b7263 | |||
3db1492c18 | |||
a523189149 | |||
f8893efb94 | |||
70f1ee3e97 | |||
eb2f0ed3d0 | |||
14e7d3e143 | |||
6d636fc2c7 | |||
a39f61a33d | |||
aaec21f544 | |||
0edc5f8113 | |||
a382f0fd92 | |||
0fcd2e7894 | |||
369e0855a7 | |||
a6cf39b94e | |||
17afd38274 | |||
8f024cb382 | |||
e7209679fb | |||
da054736b1 | |||
d92173cded | |||
308cdb6863 | |||
ae50b952f1 | |||
f44946cb49 | |||
f704bfb63a | |||
9acb4d64db | |||
8234360c8d | |||
6a64486f55 | |||
6ec778d0af | |||
75bb4f8da6 | |||
efdd7a186d | |||
c4ecb3d128 | |||
b574d49d36 | |||
47e9ecd257 | |||
799739fe86 | |||
59a04c971f | |||
168af31fd7 | |||
871bd169a8 | |||
4dbcd714bd | |||
d5e92b4ae6 | |||
2a3288c4f9 | |||
9cdb641936 | |||
b56b35b10d | |||
324bc27b5a | |||
71aad310c6 | |||
38828e2b42 | |||
9a6d7b505c | |||
1e3b025916 | |||
396d64c60b | |||
d7a0f5362e | |||
c5dcd268ad | |||
910c4c4b24 | |||
2fd6e769e6 | |||
69155015c9 | |||
6854830b6e | |||
2c413768a5 | |||
fbaf8e3fc8 | |||
c62845e92a | |||
c7930ce9ec | |||
475888b031 | |||
40b90bb722 | |||
5a4550a4fc | |||
7bbc04e6de | |||
3ba662f64f | |||
5d7b99daf4 | |||
e47627f565 | |||
39207c7992 | |||
41ab698f93 | |||
e45a6d8311 | |||
8912a97be7 | |||
8d2a9ce67c | |||
811352e2ef | |||
981385cf4a | |||
cfa1e7486a | |||
3c46bf01c6 | |||
4562cfb85b | |||
cb1aac3cd4 | |||
3dd1a14fb7 | |||
63c3127248 | |||
96c32d24ba | |||
d64a9d8b52 | |||
92c29b8172 | |||
0ac9584bbb | |||
fe12e8d944 | |||
7529953f0a | |||
00dbb6855a | |||
7eca3e8081 | |||
b58d7fc82a | |||
ceea8220a9 | |||
18013e9551 | |||
bd3e9e1cad | |||
6173471cca | |||
644881372d | |||
a12aac71fd | |||
608fcd6cd7 | |||
ce9b25e97a | |||
f948cb0086 | |||
6c52723d97 | |||
d5b7bf77b0 | |||
b1a5b02d3a | |||
74e052de51 | |||
a5397d55fe | |||
65ef929d22 | |||
441919c5e3 | |||
448aeec780 | |||
78aea5c34c | |||
d7f40f75d2 | |||
b47035fbab | |||
f0925ff9dc | |||
d9324b9951 | |||
1d9336ed67 | |||
d37142af4b | |||
7c068101b7 | |||
217f9aea02 | |||
2f5e08067d | |||
a8556dff02 | |||
664089861a | |||
edb8f19a0c | |||
6c0254c1eb | |||
0bb9fb09cc | |||
ab21fcdd67 | |||
ad6a423d12 | |||
b03df4f1e6 | |||
0a2a6e2b3a | |||
0eb751c1c9 | |||
b59b1e491e | |||
95e8c68fde | |||
7e16f9be8f | |||
768650cee0 | |||
c2c529e2da | |||
289e86e899 | |||
7d59bafd83 | |||
1bbe053848 | |||
2ed48c8469 | |||
d786ed5ebf | |||
a9f06fc5f4 | |||
dff3f45d22 | |||
1e17d86421 | |||
ba18a81e88 | |||
329c2a6c0f | |||
39218017ca | |||
fc487b17be | |||
91ac47ecba | |||
f4a1d69075 | |||
cbeae906e1 | |||
b25e8e26f0 | |||
a79be994de | |||
e26c8b9e9f | |||
4e9c3e101d | |||
d295e1be54 | |||
2f3fcef97c | |||
4df1f7f502 | |||
d74719704e | |||
c5056e0a43 | |||
a47aa50477 | |||
5021096bdd | |||
f7930a3a0b | |||
bb7df60a4d | |||
992cb06da6 | |||
bb3f87f862 | |||
d2eddf9d33 | |||
81ca0c8fbf | |||
fc77b14760 | |||
52b6f1a492 | |||
0233e74f4f | |||
dd355c58d8 | |||
267032cff9 | |||
d4b8c47a65 | |||
4a3d9f8f83 | |||
1c96e7936c | |||
6cecd690b2 | |||
91e38d1f97 | |||
653178c1fd | |||
eb06535ed1 | |||
f9bcc8e862 | |||
ece49f011c | |||
f43ca405d4 | |||
d146175d27 | |||
3baa5ab712 | |||
e647e8c933 | |||
c02bead12b | |||
e9ca25c1cb | |||
a513cca582 | |||
9a47c1d401 | |||
433f5451d7 | |||
64e505defb | |||
33d1755eae | |||
fc63c54116 | |||
db07976aab | |||
0e4b79ea77 | |||
588c9b114b | |||
b095f606c1 | |||
b312e93176 | |||
172ce659b8 | |||
61933624d2 | |||
0d31b25fba | |||
90beaeaeb4 | |||
0d169b4e80 | |||
b1c0eae1f6 | |||
80832ff763 | |||
f7d0d16eb3 | |||
6375139d0b | |||
567bd8abb5 | |||
3d038b28ff | |||
0c9b6a6797 | |||
639f089c55 | |||
9010b2743c | |||
4c91c4aaf1 | |||
17125ce0e3 | |||
3987ee6cb2 | |||
51a684c0ea | |||
43e69041fc | |||
2a33c9ed3b | |||
b03733442b | |||
9c9929ff39 | |||
566a8a261f | |||
a9e5f92958 | |||
ee7d4728d8 | |||
923dcec4e7 | |||
aebcfc516f | |||
99faeb8d05 | |||
b2017c8462 | |||
bf44edd179 | |||
aa374083d8 | |||
8c0d3943d8 | |||
d02c6668b2 | |||
880d01daea | |||
50b70708fb | |||
c5d716dc9d | |||
95d0a3bf41 | |||
cf707e15c2 | |||
1308cda5c2 | |||
4357c110c8 | |||
fed2a6d917 | |||
1d4b6dc66e | |||
174c9a48f5 | |||
3984d220bb | |||
0b497654f2 | |||
d476a4c1aa |
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@ -6,6 +6,12 @@ updates:
|
|||||||
interval: daily
|
interval: daily
|
||||||
time: "10:00"
|
time: "10:00"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
#only allow patch updates for locale-data - this has to be updated manually due to codegen
|
||||||
|
- dependency-name: pocketmine/locale-data
|
||||||
|
update-types:
|
||||||
|
- "version-update:semver-major"
|
||||||
|
- "version-update:semver-minor"
|
||||||
|
|
||||||
- package-ecosystem: gitsubmodule
|
- package-ecosystem: gitsubmodule
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
18
.github/workflows/build-docker-image.yml
vendored
18
.github/workflows/build-docker-image.yml
vendored
@ -30,23 +30,23 @@ jobs:
|
|||||||
id: tag-name
|
id: tag-name
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
||||||
echo ::set-output name=TAG_NAME::$VERSION
|
echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT
|
||||||
echo ::set-output name=MAJOR::$(echo $VERSION | cut -d. -f1)
|
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
|
||||||
echo ::set-output name=MINOR::$(echo $VERSION | cut -d. -f1-2)
|
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Download new release information
|
- name: Download new release information
|
||||||
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
|
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
|
||||||
|
|
||||||
- name: Detect channel
|
- name: Detect channel
|
||||||
id: channel
|
id: channel
|
||||||
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
|
run: echo CHANNEL=$(jq -r '.channel' new_build_info.json) >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Get name of Docker repository name
|
- name: Get name of Docker repository name
|
||||||
id: docker-repo-name
|
id: docker-repo-name
|
||||||
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
|
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Build image for tag
|
- name: Build image for tag
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build image for major tag
|
- name: Build image for major tag
|
||||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
@ -72,7 +72,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build image for minor tag
|
- name: Build image for minor tag
|
||||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
@ -85,7 +85,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build image for latest tag
|
- name: Build image for latest tag
|
||||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
|
11
.github/workflows/discord-release-embed.php
vendored
11
.github/workflows/discord-release-embed.php
vendored
@ -18,8 +18,9 @@ require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
|||||||
/**
|
/**
|
||||||
* @phpstan-return array<string, mixed>
|
* @phpstan-return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl) : array{
|
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl, int $newsPingRoleId) : array{
|
||||||
return [
|
return [
|
||||||
|
"content" => "<@&$newsPingRoleId> New PocketMine-MP release: $version ($channel)",
|
||||||
"embeds" => [
|
"embeds" => [
|
||||||
[
|
[
|
||||||
"title" => "New PocketMine-MP release: $version ($channel)",
|
"title" => "New PocketMine-MP release: $version ($channel)",
|
||||||
@ -35,11 +36,11 @@ DESCRIPTION,
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count($argv) !== 5){
|
if(count($argv) !== 6){
|
||||||
fwrite(STDERR, "Required arguments: github repo, version, API token\n");
|
fwrite(STDERR, "Required arguments: github repo, version, API token, webhook URL, ping role ID\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
[, $repo, $tagName, $token, $hookURL] = $argv;
|
[, $repo, $tagName, $token, $hookURL, $newsPingRoleId] = $argv;
|
||||||
|
|
||||||
$result = Internet::getURL('https://api.github.com/repos/' . $repo . '/releases/tags/' . $tagName, extraHeaders: [
|
$result = Internet::getURL('https://api.github.com/repos/' . $repo . '/releases/tags/' . $tagName, extraHeaders: [
|
||||||
'Authorization: token ' . $token
|
'Authorization: token ' . $token
|
||||||
@ -86,7 +87,7 @@ $buildLogUrl = $buildInfoJson["build_log_url"];
|
|||||||
|
|
||||||
$description = $releaseInfoJson["body"];
|
$description = $releaseInfoJson["body"];
|
||||||
|
|
||||||
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl);
|
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl, (int) $newsPingRoleId);
|
||||||
|
|
||||||
$response = Internet::postURL(
|
$response = Internet::postURL(
|
||||||
$hookURL,
|
$hookURL,
|
||||||
|
6
.github/workflows/discord-release-notify.yml
vendored
6
.github/workflows/discord-release-notify.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PHP and tools
|
- name: Setup PHP and tools
|
||||||
uses: shivammathur/setup-php@2.22.0
|
uses: shivammathur/setup-php@2.24.0
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.0
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get actual tag name
|
- name: Get actual tag name
|
||||||
id: tag-name
|
id: tag-name
|
||||||
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Run webhook post script
|
- name: Run webhook post script
|
||||||
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
|
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }}
|
||||||
|
17
.github/workflows/draft-release.yml
vendored
17
.github/workflows/draft-release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@2.22.0
|
uses: shivammathur/setup-php@2.24.0
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.0
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
|
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
|
||||||
echo "Build number: $BUILD_NUMBER"
|
echo "Build number: $BUILD_NUMBER"
|
||||||
echo ::set-output name=BUILD_NUMBER::$BUILD_NUMBER
|
echo BUILD_NUMBER=$BUILD_NUMBER >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Minify BedrockData JSON files
|
- name: Minify BedrockData JSON files
|
||||||
run: php vendor/pocketmine/bedrock-data/.minify_json.php
|
run: php vendor/pocketmine/bedrock-data/.minify_json.php
|
||||||
@ -51,10 +51,12 @@ jobs:
|
|||||||
- name: Get PocketMine-MP release version
|
- name: Get PocketMine-MP release version
|
||||||
id: get-pm-version
|
id: get-pm-version
|
||||||
run: |
|
run: |
|
||||||
echo ::set-output name=PM_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;')
|
echo PM_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;') >> $GITHUB_OUTPUT
|
||||||
echo ::set-output name=MCPE_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;')
|
echo MCPE_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;') >> $GITHUB_OUTPUT
|
||||||
echo ::set-output name=PM_VERSION_SHORT::$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);')
|
echo PM_VERSION_SHORT=$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);') >> $GITHUB_OUTPUT
|
||||||
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
|
echo PM_VERSION_MD=$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);') >> $GITHUB_OUTPUT
|
||||||
|
echo CHANGELOG_SUFFIX=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;') >> $GITHUB_OUTPUT
|
||||||
|
echo PRERELEASE=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "false" : "true";') >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Generate build info
|
- name: Generate build info
|
||||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} ${{ github.run_id }} > build_info.json
|
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
|
||||||
@ -74,10 +76,11 @@ jobs:
|
|||||||
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
|
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
|
||||||
commit: ${{ github.sha }}
|
commit: ${{ github.sha }}
|
||||||
draft: true
|
draft: true
|
||||||
|
prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }}
|
||||||
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||||
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
body: |
|
body: |
|
||||||
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
|
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
|
||||||
|
|
||||||
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.
|
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 }}${{ steps.get-pm-version.outputs.CHANGELOG_SUFFIX }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
|
||||||
|
30
.github/workflows/main.yml
vendored
30
.github/workflows/main.yml
vendored
@ -13,14 +13,15 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
image: [ubuntu-20.04]
|
image: [ubuntu-20.04]
|
||||||
php: [8.0.23, 8.1.10]
|
php: [8.0.28, 8.1.16, 8.2.3]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Build and prepare PHP cache
|
- name: Build and prepare PHP cache
|
||||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
install-path: "./bin"
|
install-path: "./bin"
|
||||||
|
pm-version-major: "4"
|
||||||
|
|
||||||
phpstan:
|
phpstan:
|
||||||
name: PHPStan analysis
|
name: PHPStan analysis
|
||||||
@ -31,16 +32,17 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image: [ubuntu-20.04]
|
image: [ubuntu-20.04]
|
||||||
php: [8.0.23, 8.1.10]
|
php: [8.0.28, 8.1.16, 8.2.3]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
install-path: "./bin"
|
install-path: "./bin"
|
||||||
|
pm-version-major: "4"
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
run: curl -sS https://getcomposer.org/installer | php
|
run: curl -sS https://getcomposer.org/installer | php
|
||||||
@ -69,16 +71,17 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image: [ubuntu-20.04]
|
image: [ubuntu-20.04]
|
||||||
php: [8.0.23, 8.1.10]
|
php: [8.0.28, 8.1.16, 8.2.3]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
install-path: "./bin"
|
install-path: "./bin"
|
||||||
|
pm-version-major: "4"
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
run: curl -sS https://getcomposer.org/installer | php
|
run: curl -sS https://getcomposer.org/installer | php
|
||||||
@ -107,7 +110,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image: [ubuntu-20.04]
|
image: [ubuntu-20.04]
|
||||||
php: [8.0.23, 8.1.10]
|
php: [8.0.28, 8.1.16, 8.2.3]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -115,10 +118,11 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
install-path: "./bin"
|
install-path: "./bin"
|
||||||
|
pm-version-major: "4"
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
run: curl -sS https://getcomposer.org/installer | php
|
run: curl -sS https://getcomposer.org/installer | php
|
||||||
@ -147,16 +151,17 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image: [ubuntu-20.04]
|
image: [ubuntu-20.04]
|
||||||
php: [8.0.23, 8.1.10]
|
php: [8.0.28, 8.1.16, 8.2.3]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
install-path: "./bin"
|
install-path: "./bin"
|
||||||
|
pm-version-major: "4"
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
run: curl -sS https://getcomposer.org/installer | php
|
run: curl -sS https://getcomposer.org/installer | php
|
||||||
@ -180,6 +185,9 @@ jobs:
|
|||||||
- name: Regenerate KnownTranslation APIs
|
- name: Regenerate KnownTranslation APIs
|
||||||
run: php build/generate-known-translation-apis.php
|
run: php build/generate-known-translation-apis.php
|
||||||
|
|
||||||
|
- name: Regenerate BedrockData available files constants
|
||||||
|
run: php build/generate-bedrockdata-path-consts.php
|
||||||
|
|
||||||
- name: Verify code is unchanged
|
- name: Verify code is unchanged
|
||||||
run: |
|
run: |
|
||||||
git diff
|
git diff
|
||||||
@ -195,7 +203,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PHP and tools
|
- name: Setup PHP and tools
|
||||||
uses: shivammathur/setup-php@2.22.0
|
uses: shivammathur/setup-php@2.24.0
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.0
|
||||||
tools: php-cs-fixer:3.11
|
tools: php-cs-fixer:3.11
|
||||||
|
3
.github/workflows/update-php-versions.php
vendored
3
.github/workflows/update-php-versions.php
vendored
@ -23,7 +23,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
const VERSIONS = [
|
const VERSIONS = [
|
||||||
"8.0",
|
"8.0",
|
||||||
"8.1"
|
"8.1",
|
||||||
|
"8.2"
|
||||||
];
|
];
|
||||||
|
|
||||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||||
|
59
.github/workflows/update-updater-api.yml
vendored
59
.github/workflows/update-updater-api.yml
vendored
@ -15,23 +15,68 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: pmmp/update.pmmp.io
|
repository: ${{ github.repository_owner }}/update.pmmp.io
|
||||||
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
|
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
|
||||||
|
|
||||||
- name: Get actual tag name
|
- name: Get actual tag name
|
||||||
id: tag-name
|
id: tag-name
|
||||||
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Download new release information
|
- name: Download new release information
|
||||||
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
|
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
|
||||||
|
|
||||||
- name: Detect channel
|
- name: Detect channels
|
||||||
id: channel
|
id: channel
|
||||||
run: echo ::set-output name=CHANNEL::$(jq -r '.channel' new_build_info.json)
|
|
||||||
|
|
||||||
- name: Copy release information
|
|
||||||
run: |
|
run: |
|
||||||
cp new_build_info.json channels/${{ steps.channel.outputs.CHANNEL }}.json
|
CHANNEL=$(jq -r '.channel' new_build_info.json)
|
||||||
|
VERSION=${{ steps.tag-name.outputs.TAG_NAME }}
|
||||||
|
echo CHANNEL=$CHANNEL >> $GITHUB_OUTPUT
|
||||||
|
if [ "$CHANNEL" == "stable" ]; then
|
||||||
|
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
|
||||||
|
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo MAJOR=$(echo $VERSION | cut -d. -f1)-$CHANNEL >> $GITHUB_OUTPUT
|
||||||
|
echo MINOR=$(echo $VERSION | cut -d. -f1-2)-$CHANNEL >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Update channel info
|
||||||
|
run: |
|
||||||
|
function version_id() {
|
||||||
|
major=$(echo $1 | cut -d. -f1)
|
||||||
|
minor=$(echo $1 | cut -d. -f2)
|
||||||
|
patch=$(echo $1 | cut -d. -f3)
|
||||||
|
echo $(((major * 1000000) + (minor * 1000) + patch))
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_channel() {
|
||||||
|
local target_file_name="$1"
|
||||||
|
local new_file_name="$2"
|
||||||
|
|
||||||
|
local old_version_id
|
||||||
|
local new_version_id
|
||||||
|
|
||||||
|
if [ ! -f "$target_file_name" ]; then
|
||||||
|
echo "Creating channel file: $target_file_name"
|
||||||
|
cp "$new_file_name" "$target_file_name"
|
||||||
|
else
|
||||||
|
old_version_id=$(version_id "$(jq -r '.base_version' "$target_file_name")")
|
||||||
|
new_version_id=$(version_id "$(jq -r '.base_version' "$new_file_name")")
|
||||||
|
|
||||||
|
echo "Old version ID: $old_version_id"
|
||||||
|
echo "New version ID: $new_version_id"
|
||||||
|
|
||||||
|
if [ $new_version_id -ge $old_version_id ]; then #suffixed versions will have the same version ID - assume they'll always be newer
|
||||||
|
echo "Updating channel file: $target_file_name ($old_version_id -> $new_version_id)"
|
||||||
|
cp "$new_file_name" "$target_file_name"
|
||||||
|
else
|
||||||
|
echo "Version $new_version_id is less than $old_version_id, not updating channel file: $target_file_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
update_channel "channels/${{ steps.channel.outputs.CHANNEL }}.json" "new_build_info.json"
|
||||||
|
update_channel "channels/${{ steps.channel.outputs.MAJOR }}.json" "new_build_info.json"
|
||||||
|
update_channel "channels/${{ steps.channel.outputs.MINOR }}.json" "new_build_info.json"
|
||||||
rm new_build_info.json
|
rm new_build_info.json
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
|
@ -21,7 +21,7 @@ Larger contributions like feature additions should be preceded by a [Change Prop
|
|||||||
## Choosing a target branch
|
## Choosing a target branch
|
||||||
PocketMine-MP has three primary branches of development.
|
PocketMine-MP has three primary branches of development.
|
||||||
|
|
||||||
| Type of change | `stable` | `next-minor` | `next-major` |
|
| Type of change | `stable` | `minor-next` | `major-next` |
|
||||||
|:---------------|:--------:|:------------:|:------------:|
|
|:---------------|:--------:|:------------:|:------------:|
|
||||||
| Bug fixes | ✔️ | ✔️ | ✔️ |
|
| Bug fixes | ✔️ | ✔️ | ✔️ |
|
||||||
| Improvements to API docs | ✔️ | ✔️ | ✔️ |
|
| Improvements to API docs | ✔️ | ✔️ | ✔️ |
|
||||||
|
128
build/generate-bedrockdata-path-consts.php
Normal file
128
build/generate-bedrockdata-path-consts.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\build\generate_bedrockdata_path_consts;
|
||||||
|
|
||||||
|
use Symfony\Component\Filesystem\Path;
|
||||||
|
use function dirname;
|
||||||
|
use function fclose;
|
||||||
|
use function fopen;
|
||||||
|
use function fwrite;
|
||||||
|
use function is_file;
|
||||||
|
use function scandir;
|
||||||
|
use function str_replace;
|
||||||
|
use function strtoupper;
|
||||||
|
use const PHP_EOL;
|
||||||
|
use const pocketmine\BEDROCK_DATA_PATH;
|
||||||
|
use const SCANDIR_SORT_ASCENDING;
|
||||||
|
use const STDERR;
|
||||||
|
|
||||||
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
function constantify(string $permissionName) : string{
|
||||||
|
return strtoupper(str_replace([".", "-"], "_", $permissionName));
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = scandir(BEDROCK_DATA_PATH, SCANDIR_SORT_ASCENDING);
|
||||||
|
if($files === false){
|
||||||
|
fwrite(STDERR, "Couldn't find any files in " . BEDROCK_DATA_PATH . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$consts = [];
|
||||||
|
|
||||||
|
foreach($files as $file){
|
||||||
|
if($file === '.' || $file === '..'){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if($file[0] === '.'){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$path = Path::join(BEDROCK_DATA_PATH, $file);
|
||||||
|
if(!is_file($path)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach([
|
||||||
|
'README.md',
|
||||||
|
'LICENSE',
|
||||||
|
'composer.json',
|
||||||
|
] as $ignored){
|
||||||
|
if($file === $ignored){
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$consts[] = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = fopen(dirname(__DIR__) . '/src/data/bedrock/BedrockDataFiles.php', 'wb');
|
||||||
|
if($output === false){
|
||||||
|
fwrite(STDERR, "Couldn't open output file" . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fwrite($output, <<<'HEADER'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\data\bedrock;
|
||||||
|
|
||||||
|
use const pocketmine\BEDROCK_DATA_PATH;
|
||||||
|
|
||||||
|
final class BedrockDataFiles{
|
||||||
|
private function __construct(){
|
||||||
|
//NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HEADER
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($consts as $constName => $fileName){
|
||||||
|
fwrite($output, "\tpublic const " . constantify($fileName) . " = BEDROCK_DATA_PATH . '/$fileName';\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite($output, "}\n");
|
||||||
|
fclose($output);
|
||||||
|
|
||||||
|
echo "Done. Don't forget to run CS fixup after generating code.\n";
|
@ -36,8 +36,8 @@ use function ksort;
|
|||||||
use function mb_strtoupper;
|
use function mb_strtoupper;
|
||||||
use function preg_match;
|
use function preg_match;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
|
use function str_ends_with;
|
||||||
use function str_replace;
|
use function str_replace;
|
||||||
use function substr;
|
|
||||||
use const SORT_STRING;
|
use const SORT_STRING;
|
||||||
use const STDERR;
|
use const STDERR;
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
|
|||||||
if(is_dir($argv[1])){
|
if(is_dir($argv[1])){
|
||||||
/** @var string $file */
|
/** @var string $file */
|
||||||
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
|
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
|
||||||
if(substr($file, -4) !== ".php"){
|
if(!str_ends_with($file, ".php")){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\build\make_release;
|
namespace pocketmine\build\make_release;
|
||||||
|
|
||||||
|
use pocketmine\utils\Filesystem;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
use pocketmine\utils\VersionString;
|
use pocketmine\utils\VersionString;
|
||||||
use pocketmine\VersionInfo;
|
use pocketmine\VersionInfo;
|
||||||
@ -30,17 +31,17 @@ use function array_keys;
|
|||||||
use function array_map;
|
use function array_map;
|
||||||
use function dirname;
|
use function dirname;
|
||||||
use function fgets;
|
use function fgets;
|
||||||
use function file_get_contents;
|
|
||||||
use function file_put_contents;
|
use function file_put_contents;
|
||||||
use function fwrite;
|
use function fwrite;
|
||||||
use function getopt;
|
use function getopt;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
use function max;
|
use function max;
|
||||||
|
use function preg_match;
|
||||||
use function preg_replace;
|
use function preg_replace;
|
||||||
use function sleep;
|
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
use function str_pad;
|
use function str_pad;
|
||||||
use function strlen;
|
use function strlen;
|
||||||
|
use function strtolower;
|
||||||
use function system;
|
use function system;
|
||||||
use const STDERR;
|
use const STDERR;
|
||||||
use const STDIN;
|
use const STDIN;
|
||||||
@ -50,7 +51,7 @@ use const STR_PAD_LEFT;
|
|||||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||||
|
|
||||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
|
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
|
||||||
$versionInfo = Utils::assumeNotFalse(file_get_contents($versionInfoPath), $versionInfoPath . " should always exist");
|
$versionInfo = Filesystem::fileGetContents($versionInfoPath);
|
||||||
$versionInfo = preg_replace(
|
$versionInfo = preg_replace(
|
||||||
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
||||||
'$1const BASE_VERSION = "' . $newVersion . '";',
|
'$1const BASE_VERSION = "' . $newVersion . '";',
|
||||||
@ -102,22 +103,43 @@ function main() : void{
|
|||||||
$filteredOpts[$optName] = $optValue;
|
$filteredOpts[$optName] = $optValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$channel = $filteredOpts["channel"] ?? null;
|
||||||
if(isset($filteredOpts["current"])){
|
if(isset($filteredOpts["current"])){
|
||||||
$currentVer = new VersionString($filteredOpts["current"]);
|
$currentVer = new VersionString($filteredOpts["current"]);
|
||||||
}else{
|
}else{
|
||||||
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
|
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
|
||||||
}
|
}
|
||||||
if(isset($filteredOpts["next"])){
|
|
||||||
$nextVer = new VersionString($filteredOpts["next"]);
|
$nextVer = isset($filteredOpts["next"]) ? new VersionString($filteredOpts["next"]) : null;
|
||||||
|
|
||||||
|
$suffix = $currentVer->getSuffix();
|
||||||
|
if($suffix !== ""){
|
||||||
|
if($channel === "stable"){
|
||||||
|
fwrite(STDERR, "error: cannot release a suffixed build into the stable channel\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
|
||||||
|
echo "error: invalid current version suffix \"$suffix\"; aborting\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$nextVer ??= new VersionString(sprintf(
|
||||||
|
"%u.%u.%u-%s%u",
|
||||||
|
$currentVer->getMajor(),
|
||||||
|
$currentVer->getMinor(),
|
||||||
|
$currentVer->getPatch(),
|
||||||
|
$matches[1],
|
||||||
|
((int) $matches[2]) + 1
|
||||||
|
));
|
||||||
|
$channel ??= strtolower($matches[1]);
|
||||||
}else{
|
}else{
|
||||||
$nextVer = new VersionString(sprintf(
|
$nextVer ??= new VersionString(sprintf(
|
||||||
"%u.%u.%u",
|
"%u.%u.%u",
|
||||||
$currentVer->getMajor(),
|
$currentVer->getMajor(),
|
||||||
$currentVer->getMinor(),
|
$currentVer->getMinor(),
|
||||||
$currentVer->getPatch() + 1
|
$currentVer->getPatch() + 1
|
||||||
));
|
));
|
||||||
|
$channel ??= "stable";
|
||||||
}
|
}
|
||||||
$channel = $filteredOpts["channel"] ?? VersionInfo::BUILD_CHANNEL;
|
|
||||||
|
|
||||||
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
|
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
|
||||||
echo "$currentVer will be published on release channel \"$channel\".\n";
|
echo "$currentVer will be published on release channel \"$channel\".\n";
|
||||||
@ -137,9 +159,6 @@ function main() : void{
|
|||||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
|
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
|
||||||
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
|
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
|
||||||
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
|
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
|
||||||
echo "pushing changes in 5 seconds\n";
|
|
||||||
sleep(5);
|
|
||||||
systemWrapper('git push origin HEAD ' . $currentVer->getBaseVersion(), "failed to push changes to remote");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
Submodule build/php updated: 6b605ed7c4...a464454d1e
@ -38,3 +38,82 @@ Released 28th December 2022.
|
|||||||
- Fixed unauthenticated connections taking up player count slots, preventing players from joining.
|
- Fixed unauthenticated connections taking up player count slots, preventing players from joining.
|
||||||
- Fixed a possible crash in `World->tickChunk()` when plugins unload chunks during some events.
|
- Fixed a possible crash in `World->tickChunk()` when plugins unload chunks during some events.
|
||||||
- `/gamemode` will now report a failure to change game mode if the player is already in the requested game mode.
|
- `/gamemode` will now report a failure to change game mode if the player is already in the requested game mode.
|
||||||
|
|
||||||
|
# 4.12.4
|
||||||
|
Released 3rd January 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Added workarounds for an active exploit being used to deny service to servers.
|
||||||
|
|
||||||
|
# 4.12.5
|
||||||
|
Released 6th January 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Removed a workaround for an old client bug in custom form responses. The code contained a denial-of-service vulnerability.
|
||||||
|
|
||||||
|
# 4.12.6
|
||||||
|
Released 7th January 2023.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- Added a new security measure to `NetworkSession` to detect and ban players who flood the server with packets.
|
||||||
|
|
||||||
|
# 4.12.7
|
||||||
|
Released 8th January 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed players getting kicked when the server lags for too long.
|
||||||
|
- Fixed players getting kicked when a debugging session is active and a breakpoint is hit.
|
||||||
|
|
||||||
|
# 4.12.8
|
||||||
|
Released 9th January 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed players getting kicked during PvP.
|
||||||
|
- Fixed players randomly getting kicked on Windows (improper rate limit handling wrt. 15ms timer resolution).
|
||||||
|
|
||||||
|
# 4.12.9
|
||||||
|
Released 16th January 2023.
|
||||||
|
|
||||||
|
## Improvements
|
||||||
|
### Timings
|
||||||
|
- Added new timers:
|
||||||
|
- `Server Mid-Tick Processing` - time spent processing Snooze interrupts between ticks (e.g. incoming network packets)
|
||||||
|
- `Server Tick Update Cycle` - time spent processing regular per-tick updates (e.g. entity movement, world updates, etc.) (`Server->tick()`)
|
||||||
|
- `Full Server Tick` timer now counts the total of `Server Mid-Tick Processing` and `Server Tick Update Cycle`, which generates more accurate performance metrics.
|
||||||
|
- Previously, this timer only counted the time spent during regular per-tick updates, and the time recorded by `Server Mid-Tick Processing` was not included in the report at all.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed blocks such as pressure plates being able to be placed without the correct supporting blocks if the clicked block was solid.
|
||||||
|
- Pressure plates now self-destruct when the block below them is removed.
|
||||||
|
- Fixed being unable to place blocks by clicking on the side of a bell (when the click doesn't result in ringing the bell).
|
||||||
|
- Fixed various rotation-aware blocks (e.g. stairs) behaving incorrectly when placed by clicking on the side of a replaceable block (e.g. tall grass).
|
||||||
|
- Fixed banners being able to be placed on top of blocks such as skulls.
|
||||||
|
- Fixed server-side collision boxes of walls and glass (which should connect, but didn't). Note that wall connections still don't show client side - this just fixes the collision boxes.
|
||||||
|
- Fixed `PlayerInteractEvent` with `LEFT_CLICK` sometimes firing before `BlockBreakEvent` when breaking blocks.
|
||||||
|
|
||||||
|
## Other changes
|
||||||
|
- Increased packet batch budget for player sessions.
|
||||||
|
|
||||||
|
# 4.12.10
|
||||||
|
Released 18th January 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed reported server load not including the time spent processing Snooze interrupts between ticks (e.g. incoming network packets).
|
||||||
|
- Fixed `Connection Handler` entry in timings report not including time spent receiving packets.
|
||||||
|
|
||||||
|
## Note about server load & performance
|
||||||
|
This version will report higher apparent server load than previous versions. The actual performance of the server is unchanged; the previous reported load was inaccurate.
|
||||||
|
These bugs have been present for nearly 5 years (ever since the first introduction of Snooze in 3.0.0).
|
||||||
|
|
||||||
|
# 4.12.11
|
||||||
|
Released 22nd January 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Code is now tested and analysed using PHP 8.2 in addition to 8.1 and 8.0.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed pthreads 5.0.0 incorrectly being treated as compatible.
|
||||||
|
- Fixed deprecation errors on PHP 8.2.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- Updated documentation in `PlayerPreLoginEvent`.
|
||||||
|
94
changelogs/4.13-beta.md
Normal file
94
changelogs/4.13-beta.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.50**
|
||||||
|
|
||||||
|
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
|
||||||
|
|
||||||
|
### 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.13.0-BETA1
|
||||||
|
Released 18th January 2023.
|
||||||
|
|
||||||
|
## Gameplay
|
||||||
|
- Death message is now shown on the death screen when a player dies.
|
||||||
|
- Armour damage is now only increased if the armour reduced the damage taken.
|
||||||
|
- Implemented Swift Sneak enchantment.
|
||||||
|
- Fixed incorrect collision box calculation of walls and glass/bars when connected. Note: Client-side, wall connections are still broken; this only fixes projectile flight server-side.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Improved performance of chunk selection for chunk random ticking using a cache. This improves performance of chunk random ticking by 10-20%.
|
||||||
|
|
||||||
|
## Localization
|
||||||
|
- Added localized description for the `/dumpmemory` command.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
- Added the following new core permissions:
|
||||||
|
- `pocketmine.command.effect.other` - allows the player to use the `/effect` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.effect.self` - allows the player to use the `/effect` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.enchant.other` - allows the player to use the `/enchant` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.enchant.self` - allows the player to use the `/enchant` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.gamemode.other` - allows the player to use the `/gamemode` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.gamemode.self` - allows the player to use the `/gamemode` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.give.other` - allows the player to use the `/give` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.give.self` - allows the player to use the `/give` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.spawnpoint.other` - allows the player to use the `/spawnpoint` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.spawnpoint.self` - allows the player to use the `/spawnpoint` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.teleport.other` - allows the player to use the `/teleport` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.teleport.self` - allows the player to use the `/teleport` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.title.other` - allows the player to use the `/title` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.title.self` - allows the player to use the `/title` command on themselves (default operator only)
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- Decoupled `Player->sendMessage()` and `Player->sendTranslation()`.
|
||||||
|
- Refactored resource pack loading in `ResourcePackManager` to make it easier to understand.
|
||||||
|
- Client-aware translation processing has been moved to `NetworkSession` due to being client-specific.
|
||||||
|
- Replaced hardcoded strings with constants in various places.
|
||||||
|
- `NetworkSession` destructive cleanup is now deferred to the next session tick. This fixes various `InventoryManager` crashes when kicking players during events.
|
||||||
|
- Updated code using `strpos()` to use `str_starts_with()`, `str_ends_with()` and `str_contains()` where appropriate.
|
||||||
|
- Added documentation for some internal methods.
|
||||||
|
|
||||||
|
## API
|
||||||
|
### `pocketmine\command`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `protected VanillaCommand->fetchPermittedPlayerTarget(...) : ?Player` - fetches a player target according to the given sender permissions, or null if not found or not permitted
|
||||||
|
|
||||||
|
### `pocketmine\entity`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public Living->getDisplayName() : string` - the name of the entity to be shown in death messages, commands etc.
|
||||||
|
|
||||||
|
### `pocketmine\event\world`
|
||||||
|
- The following new classes have been added:
|
||||||
|
- `WorldSoundEvent` - called when a sound is played in a world
|
||||||
|
- `WorldParticleEvent` - called when a particle is spawned in a world
|
||||||
|
|
||||||
|
### `pocketmine\item`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public Item->onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool` - called when a player interacts with an entity with this item in hand
|
||||||
|
|
||||||
|
### `pocketmine\lang`
|
||||||
|
- `Language->translate()` and `Language->translateString()` no longer parse nested translation in the "base text". This was never intended behaviour, and didn't work beyond the first level anyway.
|
||||||
|
|
||||||
|
### `pocketmine\player`
|
||||||
|
- The following new interfaces have been added:
|
||||||
|
- `PlayerDataProvider` - implemented by classes which want to offer storage for player data
|
||||||
|
- The following new classes have been added:
|
||||||
|
- `DatFilePlayerDataProvider` - the default player data provider, which stores `.dat` files in the `players` folder
|
||||||
|
- `PlayerDataLoadException` - thrown when an error occurs while loading player data
|
||||||
|
- `PlayerDataSaveException` - thrown when an error occurs while saving player data
|
||||||
|
- The following API methods have been deprecated:
|
||||||
|
- `Player->sendTranslation()` - use `Player->sendMessage()` instead with a `Translatable` message
|
||||||
|
|
||||||
|
### `pocketmine\resourcepacks`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public ResourcePackManager->setResourceStack(list<ResourcePack> $resourceStack) : void` - sets the list of resource packs to be applied by players
|
||||||
|
- `public ResourcePackManager->setPackEncryptionKey(string $id, ?string $key) : void` - sets the encryption key to be used for a resource pack
|
||||||
|
|
||||||
|
### `pocketmine\utils`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public static Filesystem::fileGetContents(...) : string` - a wrapper around `file_get_contents()` which throws an exception on failure
|
||||||
|
|
||||||
|
### `pocketmine\world`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public World->requestSafeSpawn(?Vector3 $spawn = null) : Promise<Position>` - an async version of `getSafeSpawn()` which generates all the needed chunks before returning
|
94
changelogs/4.13.md
Normal file
94
changelogs/4.13.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.50**
|
||||||
|
|
||||||
|
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
|
||||||
|
|
||||||
|
### 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.13.0
|
||||||
|
Released 30th January 2023.
|
||||||
|
|
||||||
|
## Gameplay
|
||||||
|
- Death message is now shown on the death screen when a player dies.
|
||||||
|
- Armour damage is now only increased if the armour reduced the damage taken.
|
||||||
|
- Implemented Swift Sneak enchantment.
|
||||||
|
- Fixed incorrect collision box calculation of walls and glass/bars when connected. Note: Client-side, wall connections are still broken; this only fixes projectile flight server-side.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Improved performance of chunk selection for chunk random ticking using a cache. This improves performance of chunk random ticking by 10-20%.
|
||||||
|
|
||||||
|
## Localization
|
||||||
|
- Added localized description for the `/dumpmemory` command.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
- Added the following new core permissions:
|
||||||
|
- `pocketmine.command.effect.other` - allows the player to use the `/effect` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.effect.self` - allows the player to use the `/effect` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.enchant.other` - allows the player to use the `/enchant` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.enchant.self` - allows the player to use the `/enchant` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.gamemode.other` - allows the player to use the `/gamemode` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.gamemode.self` - allows the player to use the `/gamemode` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.give.other` - allows the player to use the `/give` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.give.self` - allows the player to use the `/give` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.spawnpoint.other` - allows the player to use the `/spawnpoint` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.spawnpoint.self` - allows the player to use the `/spawnpoint` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.teleport.other` - allows the player to use the `/teleport` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.teleport.self` - allows the player to use the `/teleport` command on themselves (default operator only)
|
||||||
|
- `pocketmine.command.title.other` - allows the player to use the `/title` command on other players (default operator only)
|
||||||
|
- `pocketmine.command.title.self` - allows the player to use the `/title` command on themselves (default operator only)
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- Decoupled `Player->sendMessage()` and `Player->sendTranslation()`.
|
||||||
|
- Refactored resource pack loading in `ResourcePackManager` to make it easier to understand.
|
||||||
|
- Client-aware translation processing has been moved to `NetworkSession` due to being client-specific.
|
||||||
|
- Replaced hardcoded strings with constants in various places.
|
||||||
|
- `NetworkSession` destructive cleanup is now deferred to the next session tick. This fixes various `InventoryManager` crashes when kicking players during events.
|
||||||
|
- Updated code using `strpos()` to use `str_starts_with()`, `str_ends_with()` and `str_contains()` where appropriate.
|
||||||
|
- Added documentation for some internal methods.
|
||||||
|
|
||||||
|
## API
|
||||||
|
### `pocketmine\command`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `protected VanillaCommand->fetchPermittedPlayerTarget(...) : ?Player` - fetches a player target according to the given sender permissions, or null if not found or not permitted
|
||||||
|
|
||||||
|
### `pocketmine\entity`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public Living->getDisplayName() : string` - the name of the entity to be shown in death messages, commands etc.
|
||||||
|
|
||||||
|
### `pocketmine\event\world`
|
||||||
|
- The following new classes have been added:
|
||||||
|
- `WorldSoundEvent` - called when a sound is played in a world
|
||||||
|
- `WorldParticleEvent` - called when a particle is spawned in a world
|
||||||
|
|
||||||
|
### `pocketmine\item`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public Item->onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool` - called when a player interacts with an entity with this item in hand
|
||||||
|
|
||||||
|
### `pocketmine\lang`
|
||||||
|
- `Language->translate()` and `Language->translateString()` no longer parse nested translation in the "base text". This was never intended behaviour, and didn't work beyond the first level anyway.
|
||||||
|
|
||||||
|
### `pocketmine\player`
|
||||||
|
- The following new interfaces have been added:
|
||||||
|
- `PlayerDataProvider` - implemented by classes which want to offer storage for player data
|
||||||
|
- The following new classes have been added:
|
||||||
|
- `DatFilePlayerDataProvider` - the default player data provider, which stores `.dat` files in the `players` folder
|
||||||
|
- `PlayerDataLoadException` - thrown when an error occurs while loading player data
|
||||||
|
- `PlayerDataSaveException` - thrown when an error occurs while saving player data
|
||||||
|
- The following API methods have been deprecated:
|
||||||
|
- `Player->sendTranslation()` - use `Player->sendMessage()` instead with a `Translatable` message
|
||||||
|
|
||||||
|
### `pocketmine\resourcepacks`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public ResourcePackManager->setResourceStack(list<ResourcePack> $resourceStack) : void` - sets the list of resource packs to be applied by players
|
||||||
|
- `public ResourcePackManager->setPackEncryptionKey(string $id, ?string $key) : void` - sets the encryption key to be used for a resource pack
|
||||||
|
|
||||||
|
### `pocketmine\utils`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public static Filesystem::fileGetContents(...) : string` - a wrapper around `file_get_contents()` which throws an exception on failure
|
||||||
|
|
||||||
|
### `pocketmine\world`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public World->requestSafeSpawn(?Vector3 $spawn = null) : Promise<Position>` - an async version of `getSafeSpawn()` which generates all the needed chunks before returning
|
21
changelogs/4.14.md
Normal file
21
changelogs/4.14.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.60**
|
||||||
|
|
||||||
|
### 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.14.0
|
||||||
|
Released 8th February 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.19.60.
|
||||||
|
- Removed support for older versions.
|
||||||
|
|
||||||
|
# 4.14.1
|
||||||
|
Released 15th February 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed all players getting kicked with `Receiving packets too fast` if a server tick takes longer than 5 seconds (e.g. because of autosave or GC).
|
||||||
|
- Fixed players getting kicked when linking with entities.
|
38
changelogs/4.15.md
Normal file
38
changelogs/4.15.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.62**
|
||||||
|
|
||||||
|
### Note about API versions
|
||||||
|
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||||
|
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||||
|
|
||||||
|
# 4.15.0
|
||||||
|
Released 17th February 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.19.62.
|
||||||
|
- Removed support for older versions.
|
||||||
|
|
||||||
|
# 4.15.1
|
||||||
|
Released 21st February 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed dropped items not despawning when in non-ticking chunks.
|
||||||
|
- Fixed dropped items not despawning if an infinite pickup delay is set.
|
||||||
|
- Fixed infinite despawn delay (never despawn) being ignored for dropped items.
|
||||||
|
|
||||||
|
# 4.15.2
|
||||||
|
Released 24th February 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Accept Minecraft: Bedrock Edition 1.19.63 (identical protocol to 1.19.62, but different version due to Mojang mixup).
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed `World Population` timer sometimes not being stopped, causing strange results in timings reports.
|
||||||
|
|
||||||
|
# 4.15.3
|
||||||
|
Released 7th March 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed `/dumpmemory` crash when any object contained an `INF` or `NaN` float value.
|
||||||
|
- Updated RakLib for security fixes.
|
45
changelogs/4.16-beta.md
Normal file
45
changelogs/4.16-beta.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.62**
|
||||||
|
|
||||||
|
### Note about API versions
|
||||||
|
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||||
|
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||||
|
|
||||||
|
# 4.16.0-BETA1
|
||||||
|
Released 4th March 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added granular timings for packet encode, similar to the existing timings for packet decode.
|
||||||
|
- Timings now covers several areas of the network system which were previously not counted by network timings, but were counted by total timings. This provides a better insight into the performance of the network system.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Improved performance of packet batch handling by avoiding unnecessary object allocations.
|
||||||
|
- Improved performance of packet broadcasting when the broadcast size is below the batch threshold. Previously, the packets would be encoded once by every recipient, but now they are encoded once and then added to the send buffer of each session in their raw form.
|
||||||
|
- This change mostly affects servers with larger maps, where players are more widely distributed.
|
||||||
|
|
||||||
|
## Build system
|
||||||
|
- Added a new script `build/generate-bedrockdata-path-consts.php`, which must be run whenever BedrockData is updated. This script generates a class of constants with the file paths of all BedrockData files.
|
||||||
|
|
||||||
|
## API
|
||||||
|
### `pocketmine\entity`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public Entity->getGravity() : float` - returns the entity's gravity acceleration in blocks/tick^2
|
||||||
|
- `public Entity->setGravity(float $gravity) : void` - sets the entity's gravity acceleration in blocks/tick^2
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- Now uses [`pocketmine/bedrock-data` 2.0.0](https://github.com/pmmp/BedrockData/releases/tag/2.0.0+bedrock-1.19.60).
|
||||||
|
- This version is now used by both PM4 and PM5, reducing maintenance burden.
|
||||||
|
- Now uses [`pocketmine/bedrock-protocol` 19.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/19.3.0+bedrock-1.19.62).
|
||||||
|
- This version provides new APIs for handling packet batches which enabled improving performance and adding new features, such as detailed packet encode timings.
|
||||||
|
- Crafting recipes and creative inventory data are now loaded from `recipes/legacy_recipes.json` and `recipes/legacy_creativeitems.json` respectively. Previously, these were loaded from BedrockData directly, but BedrockData 2.0 now uses a format which can't be supported in 4.x without BC breaks.
|
||||||
|
- Added dependencies on [`pocketmine/bedrock-block-upgrade-schema`](https://github.com/pmmp/BedrockBlockUpgradeSchema) and [`pocketmine/bedrock-item-upgrade-schema`](https://github.com/pmmp/BedrockItemUpgradeSchema). These provide mapping files no longer present in BedrockData 2.0.
|
||||||
|
- Reduced and/or eliminated most usages of `PacketBatch`, since it only appeared as a throwaway object and was therefore wasting performance.
|
||||||
|
- `Compressor` now exposes `getCompressionThreshold()` instead of `willCompress()`, which allows determining whether a batch will be compressed without allocating it.
|
||||||
|
- Added `pocketmine\data\bedrock\BedrockDataFiles`, an auto-generated class of constants with the file paths of all BedrockData files. This makes it easier to locate usages, detect unused files and avoid typos.
|
||||||
|
|
||||||
|
# 4.16.0-BETA2
|
||||||
|
Released 4th March 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Fixed incorrect release channel for 4.16.0-BETA1.
|
41
changelogs/4.16.md
Normal file
41
changelogs/4.16.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.62**
|
||||||
|
|
||||||
|
### Note about API versions
|
||||||
|
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||||
|
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||||
|
|
||||||
|
# 4.16.0
|
||||||
|
Released 7th March 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added granular timings for packet encode, similar to the existing timings for packet decode.
|
||||||
|
- Split `Player Network Send - Compression` timings into two timers, one for `Session Buffer` compression and one for `Broadcast` compression.
|
||||||
|
- Timings now covers several areas of the network system which were previously not counted by network timings, but were counted by total timings. This provides a better insight into the performance of the network system.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Improved performance of packet batch handling by avoiding unnecessary object allocations.
|
||||||
|
- Improved performance of packet broadcasting when the broadcast size is below the batch threshold. Previously, the packets would be encoded once by every recipient, but now they are encoded once and then added to the send buffer of each session in their raw form.
|
||||||
|
- This change mostly affects servers with larger maps, where players are more widely distributed.
|
||||||
|
- Improved performance of packet broadcasting when the broadcast has only one recipient (allow the session to compress the packet with the rest of its buffer).
|
||||||
|
|
||||||
|
## Build system
|
||||||
|
- Added a new script `build/generate-bedrockdata-path-consts.php`, which must be run whenever BedrockData is updated. This script generates a class of constants with the file paths of all BedrockData files.
|
||||||
|
|
||||||
|
## API
|
||||||
|
### `pocketmine\entity`
|
||||||
|
- The following new API methods have been added:
|
||||||
|
- `public Entity->getGravity() : float` - returns the entity's gravity acceleration in blocks/tick^2
|
||||||
|
- `public Entity->setGravity(float $gravity) : void` - sets the entity's gravity acceleration in blocks/tick^2
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- Now uses [`pocketmine/bedrock-data` 2.0.0](https://github.com/pmmp/BedrockData/releases/tag/2.0.0+bedrock-1.19.60).
|
||||||
|
- This version is now used by both PM4 and PM5, reducing maintenance burden.
|
||||||
|
- Now uses [`pocketmine/bedrock-protocol` 19.3.0](https://github.com/pmmp/BedrockProtocol/releases/tag/19.3.0+bedrock-1.19.62).
|
||||||
|
- This version provides new APIs for handling packet batches which enabled improving performance and adding new features, such as detailed packet encode timings.
|
||||||
|
- Crafting recipes and creative inventory data are now loaded from `recipes/legacy_recipes.json` and `recipes/legacy_creativeitems.json` respectively. Previously, these were loaded from BedrockData directly, but BedrockData 2.0 now uses a format which can't be supported in 4.x without BC breaks.
|
||||||
|
- Added dependencies on [`pocketmine/bedrock-block-upgrade-schema`](https://github.com/pmmp/BedrockBlockUpgradeSchema) and [`pocketmine/bedrock-item-upgrade-schema`](https://github.com/pmmp/BedrockItemUpgradeSchema). These provide mapping files no longer present in BedrockData 2.0.
|
||||||
|
- Reduced and/or eliminated most usages of `PacketBatch`, since it only appeared as a throwaway object and was therefore wasting performance.
|
||||||
|
- `Compressor` now exposes `getCompressionThreshold()` instead of `willCompress()`, which allows determining whether a batch will be compressed without allocating it.
|
||||||
|
- Added `pocketmine\data\bedrock\BedrockDataFiles`, an auto-generated class of constants with the file paths of all BedrockData files. This makes it easier to locate usages, detect unused files and avoid typos.
|
14
changelogs/4.17.md
Normal file
14
changelogs/4.17.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.70**
|
||||||
|
|
||||||
|
### Note about API versions
|
||||||
|
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||||
|
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||||
|
|
||||||
|
# 4.17.0
|
||||||
|
Released 14th March 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.19.70.
|
||||||
|
- Removed support for older versions.
|
45
changelogs/4.18-alpha.md
Normal file
45
changelogs/4.18-alpha.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
**For Minecraft: Bedrock Edition 1.19.70**
|
||||||
|
|
||||||
|
### Note about API versions
|
||||||
|
Plugins which don't touch the `pocketmine\network\mcpe` namespace are compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||||
|
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||||
|
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||||
|
|
||||||
|
### Alpha release warning
|
||||||
|
Alpha releases are **experimental**. Features introduced in these releases are subject to change or removal.
|
||||||
|
|
||||||
|
APIs which existed **prior** to this version will continue to work as normal, so plugins which use them will continue to work.
|
||||||
|
|
||||||
|
### Highlights
|
||||||
|
This version makes changes to the internal network system to improve server performance and reduce memory usage.
|
||||||
|
|
||||||
|
While these changes don't affect non-internal API, they are still significant enough to warrant a new minor version, as they may break plugins which use the internal network API (not recommended).
|
||||||
|
|
||||||
|
# 4.18.0-ALPHA1
|
||||||
|
Released 16th March 2023.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Improved server performance in congested areas of the world (lots of players and/or entities in the same area).
|
||||||
|
|
||||||
|
## API
|
||||||
|
### `pocketmine\event\server`
|
||||||
|
- The following new classes have been added:
|
||||||
|
- `DataPacketDecodeEvent` - called before a packet is decoded by a `NetworkSession`; useful to mitigate DoS attacks if PocketMine-MP hasn't been patched against new bugs yet
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- Introduced new system for broadcasting entity events to network sessions.
|
||||||
|
- This change improves performance when lots of players and/or entities are in the same area.
|
||||||
|
- New interface `EntityEventBroadcaster` and class `StandardEntityEventBroadcaster` have been added to implement this.
|
||||||
|
- All entity-specific `on*()` and `sync*()` methods have been removed from `NetworkSession` (BC break).
|
||||||
|
- `NetworkSession` now accepts an `EntityEventBroadcaster` instance in its constructor.
|
||||||
|
- `NetworkBroadcastUtils::broadcastEntityEvent()` can be used to efficiently broadcast events to unique broadcasters shared by several network sessions.
|
||||||
|
- All network sessions now share the same `PacketSerializerContext` and `PacketBroadcaster` by default.
|
||||||
|
- Previously, every session had its own context, meaning that broadcast optimisations were not used, causing significant performance losses compared to 3.x.
|
||||||
|
- This change improves performance in congested areas by allowing reuse of previously encoded packet buffers for all sessions sharing the same context.
|
||||||
|
- Packet broadcasts are automatically encoded separately per unique `PacketSerializerContext` instance. This allows, for example, a multi-version fork to have a separate context for each protocol version, to ensure maximum broadcast efficiency while encoding different packets for different versions.
|
||||||
|
- `PacketSerializerContext` is now passed in `NetworkSession::__construct()`, instead of being created by the session.
|
||||||
|
- `StandardPacketBroadcaster` is now locked to a single `PacketSerializer` context, reducing complexity.
|
||||||
|
- Introduced `NetworkBroadcastUtils::broadcastPackets()`, replacing `Server->broadcastPackets()`.
|
||||||
|
- `Server->broadcastPackets()` has been deprecated. It will be removed in a future version.
|
@ -34,14 +34,16 @@
|
|||||||
"adhocore/json-comment": "^1.1",
|
"adhocore/json-comment": "^1.1",
|
||||||
"fgrosse/phpasn1": "^2.3",
|
"fgrosse/phpasn1": "^2.3",
|
||||||
"netresearch/jsonmapper": "^4.0",
|
"netresearch/jsonmapper": "^4.0",
|
||||||
"pocketmine/bedrock-data": "~1.13.0+bedrock-1.19.50",
|
"pocketmine/bedrock-block-upgrade-schema": "~1.1.1+bedrock-1.19.70",
|
||||||
"pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50",
|
"pocketmine/bedrock-data": "~2.1.1+bedrock-1.19.70",
|
||||||
|
"pocketmine/bedrock-item-upgrade-schema": "~1.1.0+bedrock-1.19.70",
|
||||||
|
"pocketmine/bedrock-protocol": "~20.0.0+bedrock-1.19.70",
|
||||||
"pocketmine/binaryutils": "^0.2.1",
|
"pocketmine/binaryutils": "^0.2.1",
|
||||||
"pocketmine/callback-validator": "^1.0.2",
|
"pocketmine/callback-validator": "^1.0.2",
|
||||||
"pocketmine/classloader": "^0.2.0",
|
"pocketmine/classloader": "^0.2.0",
|
||||||
"pocketmine/color": "^0.2.0",
|
"pocketmine/color": "^0.3.0",
|
||||||
"pocketmine/errorhandler": "^0.6.0",
|
"pocketmine/errorhandler": "^0.6.0",
|
||||||
"pocketmine/locale-data": "~2.11.0",
|
"pocketmine/locale-data": "~2.18.0",
|
||||||
"pocketmine/log": "^0.4.0",
|
"pocketmine/log": "^0.4.0",
|
||||||
"pocketmine/log-pthreads": "^0.4.0",
|
"pocketmine/log-pthreads": "^0.4.0",
|
||||||
"pocketmine/math": "^0.4.0",
|
"pocketmine/math": "^0.4.0",
|
||||||
@ -54,7 +56,7 @@
|
|||||||
"webmozart/path-util": "^2.3"
|
"webmozart/path-util": "^2.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "1.9.4",
|
"phpstan/phpstan": "1.10.6",
|
||||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||||
"phpunit/phpunit": "^9.2"
|
"phpunit/phpunit": "^9.2"
|
||||||
|
374
composer.lock
generated
374
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "393c7921d03d080d3ef3b836f90b4415",
|
"content-hash": "1d0c1d2fe668d85ae87110a1e3cfac05",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/json-comment",
|
"name": "adhocore/json-comment",
|
||||||
@ -249,17 +249,43 @@
|
|||||||
"time": "2022-12-08T20:46:14+00:00"
|
"time": "2022-12-08T20:46:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/bedrock-data",
|
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||||
"version": "1.13.0+bedrock-1.19.50",
|
"version": "1.1.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/BedrockData.git",
|
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||||
"reference": "57337ddc9433a0e245a1ce48c51af05f0573d58d"
|
"reference": "e0540343e649a92126a1d4071ec401a811416c76"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/57337ddc9433a0e245a1ce48c51af05f0573d58d",
|
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/e0540343e649a92126a1d4071ec401a811416c76",
|
||||||
"reference": "57337ddc9433a0e245a1ce48c51af05f0573d58d",
|
"reference": "e0540343e649a92126a1d4071ec401a811416c76",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"CC0-1.0"
|
||||||
|
],
|
||||||
|
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||||
|
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/1.1.1"
|
||||||
|
},
|
||||||
|
"time": "2023-03-08T23:45:59+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pocketmine/bedrock-data",
|
||||||
|
"version": "2.1.1+bedrock-1.19.70",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/pmmp/BedrockData.git",
|
||||||
|
"reference": "cba0567bcb25f987f2712092f8d662056719e82d"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/cba0567bcb25f987f2712092f8d662056719e82d",
|
||||||
|
"reference": "cba0567bcb25f987f2712092f8d662056719e82d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -270,22 +296,48 @@
|
|||||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.50"
|
"source": "https://github.com/pmmp/BedrockData/tree/2.1.1+bedrock-1.19.70"
|
||||||
},
|
},
|
||||||
"time": "2022-11-30T16:19:59+00:00"
|
"time": "2023-03-14T18:03:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/bedrock-protocol",
|
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||||
"version": "17.1.0+bedrock-1.19.50",
|
"version": "1.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||||
"reference": "c572706cf5e3202718dd35a35dd30fe08cd671de"
|
"reference": "aab89a1f121a0c127557a4a0cf981330301c9c45"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de",
|
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/aab89a1f121a0c127557a4a0cf981330301c9c45",
|
||||||
"reference": "c572706cf5e3202718dd35a35dd30fe08cd671de",
|
"reference": "aab89a1f121a0c127557a4a0cf981330301c9c45",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"CC0-1.0"
|
||||||
|
],
|
||||||
|
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
||||||
|
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.1.0"
|
||||||
|
},
|
||||||
|
"time": "2023-03-08T22:27:13+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pocketmine/bedrock-protocol",
|
||||||
|
"version": "20.0.0+bedrock-1.19.70",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||||
|
"reference": "4892a5020187da805d7b46ab522d8185b0283726"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/4892a5020187da805d7b46ab522d8185b0283726",
|
||||||
|
"reference": "4892a5020187da805d7b46ab522d8185b0283726",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -293,13 +345,13 @@
|
|||||||
"netresearch/jsonmapper": "^4.0",
|
"netresearch/jsonmapper": "^4.0",
|
||||||
"php": "^8.0",
|
"php": "^8.0",
|
||||||
"pocketmine/binaryutils": "^0.2.0",
|
"pocketmine/binaryutils": "^0.2.0",
|
||||||
"pocketmine/color": "^0.2.0",
|
"pocketmine/color": "^0.2.0 || ^0.3.0",
|
||||||
"pocketmine/math": "^0.3.0 || ^0.4.0",
|
"pocketmine/math": "^0.3.0 || ^0.4.0",
|
||||||
"pocketmine/nbt": "^0.3.0",
|
"pocketmine/nbt": "^0.3.0",
|
||||||
"ramsey/uuid": "^4.1"
|
"ramsey/uuid": "^4.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "1.9.3",
|
"phpstan/phpstan": "1.10.1",
|
||||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||||
"phpunit/phpunit": "^9.5"
|
"phpunit/phpunit": "^9.5"
|
||||||
@ -317,9 +369,9 @@
|
|||||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/17.1.0+bedrock-1.19.50"
|
"source": "https://github.com/pmmp/BedrockProtocol/tree/20.0.0+bedrock-1.19.70"
|
||||||
},
|
},
|
||||||
"time": "2022-12-15T20:34:49+00:00"
|
"time": "2023-03-14T17:06:38+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/binaryutils",
|
"name": "pocketmine/binaryutils",
|
||||||
@ -460,24 +512,24 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/color",
|
"name": "pocketmine/color",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/Color.git",
|
"url": "https://github.com/pmmp/Color.git",
|
||||||
"reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2"
|
"reference": "8cb346d0a21ad3287cc8d7175e4b643416607249"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/Color/zipball/09be6ea6d76f2e33d6813c39d29c22c46c17e1d2",
|
"url": "https://api.github.com/repos/pmmp/Color/zipball/8cb346d0a21ad3287cc8d7175e4b643416607249",
|
||||||
"reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2",
|
"reference": "8cb346d0a21ad3287cc8d7175e4b643416607249",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0"
|
"php": "^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "0.12.59",
|
"phpstan/phpstan": "1.9.4",
|
||||||
"phpstan/phpstan-strict-rules": "^0.12.2"
|
"phpstan/phpstan-strict-rules": "^1.2.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -492,9 +544,9 @@
|
|||||||
"description": "Color handling library used by PocketMine-MP and related projects",
|
"description": "Color handling library used by PocketMine-MP and related projects",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/Color/issues",
|
"issues": "https://github.com/pmmp/Color/issues",
|
||||||
"source": "https://github.com/pmmp/Color/tree/0.2.0"
|
"source": "https://github.com/pmmp/Color/tree/0.3.0"
|
||||||
},
|
},
|
||||||
"time": "2020-12-11T01:24:32+00:00"
|
"time": "2022-12-18T19:49:21+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/errorhandler",
|
"name": "pocketmine/errorhandler",
|
||||||
@ -537,16 +589,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/locale-data",
|
"name": "pocketmine/locale-data",
|
||||||
"version": "2.11.0",
|
"version": "2.18.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/Language.git",
|
"url": "https://github.com/pmmp/Language.git",
|
||||||
"reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53"
|
"reference": "da25bfe9ee4822a84feb9b7e620c56ad4000aed0"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/4b33d8fa53eda53d9662a7478806ebae2e4a5c53",
|
"url": "https://api.github.com/repos/pmmp/Language/zipball/da25bfe9ee4822a84feb9b7e620c56ad4000aed0",
|
||||||
"reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53",
|
"reference": "da25bfe9ee4822a84feb9b7e620c56ad4000aed0",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -554,9 +606,9 @@
|
|||||||
"description": "Language resources used by PocketMine-MP",
|
"description": "Language resources used by PocketMine-MP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/Language/issues",
|
"issues": "https://github.com/pmmp/Language/issues",
|
||||||
"source": "https://github.com/pmmp/Language/tree/2.11.0"
|
"source": "https://github.com/pmmp/Language/tree/2.18.3"
|
||||||
},
|
},
|
||||||
"time": "2022-11-25T14:24:34+00:00"
|
"time": "2023-01-17T21:43:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/log",
|
"name": "pocketmine/log",
|
||||||
@ -728,16 +780,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/raklib",
|
"name": "pocketmine/raklib",
|
||||||
"version": "0.14.5",
|
"version": "0.14.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/RakLib.git",
|
"url": "https://github.com/pmmp/RakLib.git",
|
||||||
"reference": "85b4e5cb7117d37e010eeadb3ff53b21276c6f48"
|
"reference": "aeca667d5ecc4cc18fded612f29e3511bbf62f42"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/85b4e5cb7117d37e010eeadb3ff53b21276c6f48",
|
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/aeca667d5ecc4cc18fded612f29e3511bbf62f42",
|
||||||
"reference": "85b4e5cb7117d37e010eeadb3ff53b21276c6f48",
|
"reference": "aeca667d5ecc4cc18fded612f29e3511bbf62f42",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -749,7 +801,7 @@
|
|||||||
"pocketmine/log": "^0.3.0 || ^0.4.0"
|
"pocketmine/log": "^0.3.0 || ^0.4.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "1.7.7",
|
"phpstan/phpstan": "1.9.17",
|
||||||
"phpstan/phpstan-strict-rules": "^1.0"
|
"phpstan/phpstan-strict-rules": "^1.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -765,9 +817,9 @@
|
|||||||
"description": "A RakNet server implementation written in PHP",
|
"description": "A RakNet server implementation written in PHP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||||
"source": "https://github.com/pmmp/RakLib/tree/0.14.5"
|
"source": "https://github.com/pmmp/RakLib/tree/0.14.6"
|
||||||
},
|
},
|
||||||
"time": "2022-08-25T16:16:44+00:00"
|
"time": "2023-03-07T15:10:23+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/raklib-ipc",
|
"name": "pocketmine/raklib-ipc",
|
||||||
@ -852,42 +904,53 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ramsey/collection",
|
"name": "ramsey/collection",
|
||||||
"version": "1.2.2",
|
"version": "1.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ramsey/collection.git",
|
"url": "https://github.com/ramsey/collection.git",
|
||||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a"
|
"reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
"url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4",
|
||||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
"reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.3 || ^8",
|
"php": "^7.4 || ^8.0",
|
||||||
"symfony/polyfill-php81": "^1.23"
|
"symfony/polyfill-php81": "^1.23"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"captainhook/captainhook": "^5.3",
|
"captainhook/plugin-composer": "^5.3",
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
|
"ergebnis/composer-normalize": "^2.28.3",
|
||||||
"ergebnis/composer-normalize": "^2.6",
|
"fakerphp/faker": "^1.21",
|
||||||
"fakerphp/faker": "^1.5",
|
"hamcrest/hamcrest-php": "^2.0",
|
||||||
"hamcrest/hamcrest-php": "^2",
|
"jangregor/phpstan-prophecy": "^1.0",
|
||||||
"jangregor/phpstan-prophecy": "^0.8",
|
"mockery/mockery": "^1.5",
|
||||||
"mockery/mockery": "^1.3",
|
"php-parallel-lint/php-console-highlighter": "^1.0",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.3",
|
||||||
|
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
|
||||||
"phpspec/prophecy-phpunit": "^2.0",
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
"phpstan/extension-installer": "^1",
|
"phpstan/extension-installer": "^1.2",
|
||||||
"phpstan/phpstan": "^0.12.32",
|
"phpstan/phpstan": "^1.9",
|
||||||
"phpstan/phpstan-mockery": "^0.12.5",
|
"phpstan/phpstan-mockery": "^1.1",
|
||||||
"phpstan/phpstan-phpunit": "^0.12.11",
|
"phpstan/phpstan-phpunit": "^1.3",
|
||||||
"phpunit/phpunit": "^8.5 || ^9",
|
"phpunit/phpunit": "^9.5",
|
||||||
"psy/psysh": "^0.10.4",
|
"psalm/plugin-mockery": "^1.1",
|
||||||
"slevomat/coding-standard": "^6.3",
|
"psalm/plugin-phpunit": "^0.18.4",
|
||||||
"squizlabs/php_codesniffer": "^3.5",
|
"ramsey/coding-standard": "^2.0.3",
|
||||||
"vimeo/psalm": "^4.4"
|
"ramsey/conventional-commits": "^1.3",
|
||||||
|
"vimeo/psalm": "^5.4"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"captainhook": {
|
||||||
|
"force-install": true
|
||||||
|
},
|
||||||
|
"ramsey/conventional-commits": {
|
||||||
|
"configFile": "conventional-commits.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Ramsey\\Collection\\": "src/"
|
"Ramsey\\Collection\\": "src/"
|
||||||
@ -915,7 +978,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/ramsey/collection/issues",
|
"issues": "https://github.com/ramsey/collection/issues",
|
||||||
"source": "https://github.com/ramsey/collection/tree/1.2.2"
|
"source": "https://github.com/ramsey/collection/tree/1.3.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -927,27 +990,27 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-10-10T03:01:02+00:00"
|
"time": "2022-12-27T19:12:24+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ramsey/uuid",
|
"name": "ramsey/uuid",
|
||||||
"version": "4.6.0",
|
"version": "4.7.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ramsey/uuid.git",
|
"url": "https://github.com/ramsey/uuid.git",
|
||||||
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f"
|
"reference": "433b2014e3979047db08a17a205f410ba3869cf2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/ad63bc700e7d021039e30ce464eba384c4a1d40f",
|
"url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
|
||||||
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f",
|
"reference": "433b2014e3979047db08a17a205f410ba3869cf2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10",
|
"brick/math": "^0.8.8 || ^0.9 || ^0.10",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"php": "^8.0",
|
"php": "^8.0",
|
||||||
"ramsey/collection": "^1.0"
|
"ramsey/collection": "^1.2 || ^2.0"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
"rhumsaa/uuid": "self.version"
|
"rhumsaa/uuid": "self.version"
|
||||||
@ -1007,7 +1070,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/ramsey/uuid/issues",
|
"issues": "https://github.com/ramsey/uuid/issues",
|
||||||
"source": "https://github.com/ramsey/uuid/tree/4.6.0"
|
"source": "https://github.com/ramsey/uuid/tree/4.7.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1019,20 +1082,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-11-05T23:03:38+00:00"
|
"time": "2023-01-12T18:13:24+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/filesystem",
|
"name": "symfony/filesystem",
|
||||||
"version": "v5.4.13",
|
"version": "v5.4.21",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/filesystem.git",
|
"url": "https://github.com/symfony/filesystem.git",
|
||||||
"reference": "ac09569844a9109a5966b9438fc29113ce77cf51"
|
"reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51",
|
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
|
||||||
"reference": "ac09569844a9109a5966b9438fc29113ce77cf51",
|
"reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1067,7 +1130,7 @@
|
|||||||
"description": "Provides basic utilities for the filesystem",
|
"description": "Provides basic utilities for the filesystem",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/filesystem/tree/v5.4.13"
|
"source": "https://github.com/symfony/filesystem/tree/v5.4.21"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1083,7 +1146,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-09-21T19:53:16+00:00"
|
"time": "2023-02-14T08:03:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
@ -1525,30 +1588,30 @@
|
|||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
"name": "doctrine/instantiator",
|
"name": "doctrine/instantiator",
|
||||||
"version": "1.4.1",
|
"version": "1.5.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/instantiator.git",
|
"url": "https://github.com/doctrine/instantiator.git",
|
||||||
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
|
"reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
|
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
|
||||||
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
|
"reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1 || ^8.0"
|
"php": "^7.1 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^9",
|
"doctrine/coding-standard": "^9 || ^11",
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"ext-phar": "*",
|
"ext-phar": "*",
|
||||||
"phpbench/phpbench": "^0.16 || ^1",
|
"phpbench/phpbench": "^0.16 || ^1",
|
||||||
"phpstan/phpstan": "^1.4",
|
"phpstan/phpstan": "^1.4",
|
||||||
"phpstan/phpstan-phpunit": "^1",
|
"phpstan/phpstan-phpunit": "^1",
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
||||||
"vimeo/psalm": "^4.22"
|
"vimeo/psalm": "^4.30 || ^5.4"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -1575,7 +1638,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/instantiator/issues",
|
"issues": "https://github.com/doctrine/instantiator/issues",
|
||||||
"source": "https://github.com/doctrine/instantiator/tree/1.4.1"
|
"source": "https://github.com/doctrine/instantiator/tree/1.5.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1591,20 +1654,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-03-03T08:28:38+00:00"
|
"time": "2022-12-30T00:15:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myclabs/deep-copy",
|
"name": "myclabs/deep-copy",
|
||||||
"version": "1.11.0",
|
"version": "1.11.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||||
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
|
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
|
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||||
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
|
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1642,7 +1705,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
|
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1650,20 +1713,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-03-03T13:19:32+00:00"
|
"time": "2023-03-08T13:26:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v4.15.2",
|
"version": "v4.15.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
|
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
|
||||||
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1704,9 +1767,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
|
||||||
},
|
},
|
||||||
"time": "2022-11-12T15:38:23+00:00"
|
"time": "2023-03-05T19:49:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phar-io/manifest",
|
"name": "phar-io/manifest",
|
||||||
@ -1821,16 +1884,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "1.9.4",
|
"version": "1.10.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2"
|
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
|
||||||
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
"reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1860,7 +1923,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan/tree/1.9.4"
|
"source": "https://github.com/phpstan/phpstan/tree/1.10.6"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1876,25 +1939,25 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-17T13:33:52+00:00"
|
"time": "2023-03-09T16:55:12+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-phpunit",
|
"name": "phpstan/phpstan-phpunit",
|
||||||
"version": "1.3.3",
|
"version": "1.3.10",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||||
"reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7"
|
"reference": "4cc5c6cc38e56bce7ea47c4091814e516d172dc3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/54a24bd23e9e80ee918cdc24f909d376c2e273f7",
|
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4cc5c6cc38e56bce7ea47c4091814e516d172dc3",
|
||||||
"reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7",
|
"reference": "4cc5c6cc38e56bce7ea47c4091814e516d172dc3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"phpstan/phpstan": "^1.9.3"
|
"phpstan/phpstan": "^1.10"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"phpunit/phpunit": "<7.0"
|
"phpunit/phpunit": "<7.0"
|
||||||
@ -1926,31 +1989,32 @@
|
|||||||
"description": "PHPUnit extensions and rules for PHPStan",
|
"description": "PHPUnit extensions and rules for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.3"
|
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.10"
|
||||||
},
|
},
|
||||||
"time": "2022-12-21T15:25:00+00:00"
|
"time": "2023-03-02T10:25:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-strict-rules",
|
"name": "phpstan/phpstan-strict-rules",
|
||||||
"version": "1.4.4",
|
"version": "1.5.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||||
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6"
|
"reference": "b7dd96a5503919a43b3cd06a2dced9d4252492f2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6",
|
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b7dd96a5503919a43b3cd06a2dced9d4252492f2",
|
||||||
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6",
|
"reference": "b7dd96a5503919a43b3cd06a2dced9d4252492f2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"phpstan/phpstan": "^1.8.6"
|
"phpstan/phpstan": "^1.10"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"nikic/php-parser": "^4.13.0",
|
"nikic/php-parser": "^4.13.0",
|
||||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^1.1",
|
||||||
"phpstan/phpstan-phpunit": "^1.0",
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
"phpunit/phpunit": "^9.5"
|
"phpunit/phpunit": "^9.5"
|
||||||
},
|
},
|
||||||
@ -1974,29 +2038,29 @@
|
|||||||
"description": "Extra strict and opinionated rules for PHPStan",
|
"description": "Extra strict and opinionated rules for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4"
|
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.0"
|
||||||
},
|
},
|
||||||
"time": "2022-09-21T11:38:17+00:00"
|
"time": "2023-02-21T10:17:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.22",
|
"version": "9.2.26",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
"reference": "e4bf60d2220b4baaa0572986b5d69870226b06df"
|
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e4bf60d2220b4baaa0572986b5d69870226b06df",
|
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||||
"reference": "e4bf60d2220b4baaa0572986b5d69870226b06df",
|
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"nikic/php-parser": "^4.14",
|
"nikic/php-parser": "^4.15",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpunit/php-file-iterator": "^3.0.3",
|
"phpunit/php-file-iterator": "^3.0.3",
|
||||||
"phpunit/php-text-template": "^2.0.2",
|
"phpunit/php-text-template": "^2.0.2",
|
||||||
@ -2011,8 +2075,8 @@
|
|||||||
"phpunit/phpunit": "^9.3"
|
"phpunit/phpunit": "^9.3"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-pcov": "*",
|
"ext-pcov": "PHP extension that provides line coverage",
|
||||||
"ext-xdebug": "*"
|
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
@ -2045,7 +2109,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.22"
|
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2053,7 +2117,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-18T16:40:55+00:00"
|
"time": "2023-03-06T12:58:08+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-file-iterator",
|
"name": "phpunit/php-file-iterator",
|
||||||
@ -2298,20 +2362,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "9.5.27",
|
"version": "9.6.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "a2bc7ffdca99f92d959b3f2270529334030bba38"
|
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2bc7ffdca99f92d959b3f2270529334030bba38",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5",
|
||||||
"reference": "a2bc7ffdca99f92d959b3f2270529334030bba38",
|
"reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"doctrine/instantiator": "^1.3.1",
|
"doctrine/instantiator": "^1.3.1 || ^2",
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
@ -2340,8 +2404,8 @@
|
|||||||
"sebastian/version": "^3.0.2"
|
"sebastian/version": "^3.0.2"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-soap": "*",
|
"ext-soap": "To be able to generate mocks based on WSDL files",
|
||||||
"ext-xdebug": "*"
|
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
|
||||||
},
|
},
|
||||||
"bin": [
|
"bin": [
|
||||||
"phpunit"
|
"phpunit"
|
||||||
@ -2349,7 +2413,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "9.5-dev"
|
"dev-master": "9.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -2380,7 +2444,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.27"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2396,7 +2460,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-09T07:31:23+00:00"
|
"time": "2023-03-09T06:34:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/cli-parser",
|
"name": "sebastian/cli-parser",
|
||||||
@ -2764,16 +2828,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/environment",
|
"name": "sebastian/environment",
|
||||||
"version": "5.1.4",
|
"version": "5.1.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||||
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
|
"reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
|
||||||
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
"reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -2815,7 +2879,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
|
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2823,7 +2887,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-04-03T09:37:03+00:00"
|
"time": "2023-02-03T06:03:51+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/exporter",
|
"name": "sebastian/exporter",
|
||||||
@ -3137,16 +3201,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/recursion-context",
|
"name": "sebastian/recursion-context",
|
||||||
"version": "4.0.4",
|
"version": "4.0.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/recursion-context.git",
|
"url": "https://github.com/sebastianbergmann/recursion-context.git",
|
||||||
"reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
|
"reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
|
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
|
||||||
"reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
|
"reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -3185,10 +3249,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Provides functionality to recursively process PHP variables",
|
"description": "Provides functionality to recursively process PHP variables",
|
||||||
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
|
"homepage": "https://github.com/sebastianbergmann/recursion-context",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
|
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
|
"source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -3196,7 +3260,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-10-26T13:17:30+00:00"
|
"time": "2023-02-03T06:07:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/resource-operations",
|
"name": "sebastian/resource-operations",
|
||||||
@ -3255,16 +3319,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/type",
|
"name": "sebastian/type",
|
||||||
"version": "3.2.0",
|
"version": "3.2.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/type.git",
|
"url": "https://github.com/sebastianbergmann/type.git",
|
||||||
"reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e"
|
"reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
|
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
|
||||||
"reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
|
"reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -3299,7 +3363,7 @@
|
|||||||
"homepage": "https://github.com/sebastianbergmann/type",
|
"homepage": "https://github.com/sebastianbergmann/type",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/type/issues",
|
"issues": "https://github.com/sebastianbergmann/type/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/type/tree/3.2.0"
|
"source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -3307,7 +3371,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-09-12T14:47:03+00:00"
|
"time": "2023-02-03T06:13:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/version",
|
"name": "sebastian/version",
|
||||||
|
4898
resources/legacy_creativeitems.json
Normal file
4898
resources/legacy_creativeitems.json
Normal file
File diff suppressed because it is too large
Load Diff
49988
resources/legacy_recipes.json
Normal file
49988
resources/legacy_recipes.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -37,4 +37,6 @@ define('pocketmine\PATH', dirname(__DIR__) . '/');
|
|||||||
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
|
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
|
||||||
define('pocketmine\BEDROCK_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-data/');
|
define('pocketmine\BEDROCK_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-data/');
|
||||||
define('pocketmine\LOCALE_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/locale-data/');
|
define('pocketmine\LOCALE_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/locale-data/');
|
||||||
|
define('pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-block-upgrade-schema/');
|
||||||
|
define('pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-item-upgrade-schema/');
|
||||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');
|
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');
|
||||||
|
@ -49,6 +49,7 @@ use function ini_get;
|
|||||||
use function ini_set;
|
use function ini_set;
|
||||||
use function intdiv;
|
use function intdiv;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
|
use function is_float;
|
||||||
use function is_object;
|
use function is_object;
|
||||||
use function is_resource;
|
use function is_resource;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
@ -69,6 +70,10 @@ use const JSON_UNESCAPED_SLASHES;
|
|||||||
use const SORT_NUMERIC;
|
use const SORT_NUMERIC;
|
||||||
|
|
||||||
class MemoryManager{
|
class MemoryManager{
|
||||||
|
private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND;
|
||||||
|
private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2;
|
||||||
|
private const DEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND;
|
||||||
|
|
||||||
private int $memoryLimit;
|
private int $memoryLimit;
|
||||||
private int $globalMemoryLimit;
|
private int $globalMemoryLimit;
|
||||||
private int $checkRate;
|
private int $checkRate;
|
||||||
@ -113,20 +118,12 @@ class MemoryManager{
|
|||||||
if($m <= 0){
|
if($m <= 0){
|
||||||
$defaultMemory = 0;
|
$defaultMemory = 0;
|
||||||
}else{
|
}else{
|
||||||
switch(mb_strtoupper($matches[2])){
|
$defaultMemory = match(mb_strtoupper($matches[2])){
|
||||||
case "K":
|
"K" => intdiv($m, 1024),
|
||||||
$defaultMemory = intdiv($m, 1024);
|
"M" => $m,
|
||||||
break;
|
"G" => $m * 1024,
|
||||||
case "M":
|
default => $m,
|
||||||
$defaultMemory = $m;
|
};
|
||||||
break;
|
|
||||||
case "G":
|
|
||||||
$defaultMemory = $m * 1024;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$defaultMemory = $m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,11 +136,11 @@ class MemoryManager{
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024;
|
$this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024;
|
||||||
$this->checkRate = $config->getPropertyInt("memory.check-rate", 20);
|
$this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE);
|
||||||
$this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true);
|
$this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true);
|
||||||
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", 30);
|
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
|
||||||
|
|
||||||
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", 36000);
|
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEFAULT_TICKS_PER_GC);
|
||||||
$this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true);
|
$this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true);
|
||||||
$this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true);
|
$this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true);
|
||||||
|
|
||||||
@ -523,6 +520,8 @@ class MemoryManager{
|
|||||||
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
|
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||||
}elseif(is_resource($from)){
|
}elseif(is_resource($from)){
|
||||||
$data = "(resource) " . print_r($from, true);
|
$data = "(resource) " . print_r($from, true);
|
||||||
|
}elseif(is_float($from)){
|
||||||
|
$data = "(float) $from";
|
||||||
}else{
|
}else{
|
||||||
$data = $from;
|
$data = $from;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ namespace pocketmine {
|
|||||||
if(substr_count($pthreads_version, ".") < 2){
|
if(substr_count($pthreads_version, ".") < 2){
|
||||||
$pthreads_version = "0.$pthreads_version";
|
$pthreads_version = "0.$pthreads_version";
|
||||||
}
|
}
|
||||||
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") > 0){
|
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") >= 0){
|
||||||
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
|
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
280
src/Server.php
280
src/Server.php
@ -43,27 +43,29 @@ use pocketmine\event\player\PlayerCreationEvent;
|
|||||||
use pocketmine\event\player\PlayerDataSaveEvent;
|
use pocketmine\event\player\PlayerDataSaveEvent;
|
||||||
use pocketmine\event\player\PlayerLoginEvent;
|
use pocketmine\event\player\PlayerLoginEvent;
|
||||||
use pocketmine\event\server\CommandEvent;
|
use pocketmine\event\server\CommandEvent;
|
||||||
use pocketmine\event\server\DataPacketSendEvent;
|
|
||||||
use pocketmine\event\server\QueryRegenerateEvent;
|
use pocketmine\event\server\QueryRegenerateEvent;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\lang\Language;
|
use pocketmine\lang\Language;
|
||||||
use pocketmine\lang\LanguageNotFoundException;
|
use pocketmine\lang\LanguageNotFoundException;
|
||||||
use pocketmine\lang\Translatable;
|
use pocketmine\lang\Translatable;
|
||||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
|
||||||
use pocketmine\nbt\NbtDataException;
|
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
use pocketmine\nbt\TreeRoot;
|
|
||||||
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
||||||
use pocketmine\network\mcpe\compression\CompressBatchTask;
|
use pocketmine\network\mcpe\compression\CompressBatchTask;
|
||||||
use pocketmine\network\mcpe\compression\Compressor;
|
use pocketmine\network\mcpe\compression\Compressor;
|
||||||
use pocketmine\network\mcpe\compression\ZlibCompressor;
|
use pocketmine\network\mcpe\compression\ZlibCompressor;
|
||||||
|
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
|
||||||
use pocketmine\network\mcpe\encryption\EncryptionContext;
|
use pocketmine\network\mcpe\encryption\EncryptionContext;
|
||||||
|
use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||||
use pocketmine\network\mcpe\NetworkSession;
|
use pocketmine\network\mcpe\NetworkSession;
|
||||||
use pocketmine\network\mcpe\PacketBroadcaster;
|
use pocketmine\network\mcpe\PacketBroadcaster;
|
||||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||||
|
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||||
use pocketmine\network\mcpe\raklib\RakLibInterface;
|
use pocketmine\network\mcpe\raklib\RakLibInterface;
|
||||||
|
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\StandardPacketBroadcaster;
|
||||||
use pocketmine\network\Network;
|
use pocketmine\network\Network;
|
||||||
use pocketmine\network\NetworkInterfaceStartException;
|
use pocketmine\network\NetworkInterfaceStartException;
|
||||||
use pocketmine\network\query\DedicatedQueryNetworkInterface;
|
use pocketmine\network\query\DedicatedQueryNetworkInterface;
|
||||||
@ -72,9 +74,13 @@ use pocketmine\network\query\QueryInfo;
|
|||||||
use pocketmine\network\upnp\UPnPNetworkInterface;
|
use pocketmine\network\upnp\UPnPNetworkInterface;
|
||||||
use pocketmine\permission\BanList;
|
use pocketmine\permission\BanList;
|
||||||
use pocketmine\permission\DefaultPermissions;
|
use pocketmine\permission\DefaultPermissions;
|
||||||
|
use pocketmine\player\DatFilePlayerDataProvider;
|
||||||
use pocketmine\player\GameMode;
|
use pocketmine\player\GameMode;
|
||||||
use pocketmine\player\OfflinePlayer;
|
use pocketmine\player\OfflinePlayer;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\player\PlayerDataLoadException;
|
||||||
|
use pocketmine\player\PlayerDataProvider;
|
||||||
|
use pocketmine\player\PlayerDataSaveException;
|
||||||
use pocketmine\player\PlayerInfo;
|
use pocketmine\player\PlayerInfo;
|
||||||
use pocketmine\plugin\PharPluginLoader;
|
use pocketmine\plugin\PharPluginLoader;
|
||||||
use pocketmine\plugin\Plugin;
|
use pocketmine\plugin\Plugin;
|
||||||
@ -105,17 +111,18 @@ use pocketmine\utils\SignalHandler;
|
|||||||
use pocketmine\utils\Terminal;
|
use pocketmine\utils\Terminal;
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
use pocketmine\world\format\Chunk;
|
|
||||||
use pocketmine\world\format\io\WorldProviderManager;
|
use pocketmine\world\format\io\WorldProviderManager;
|
||||||
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
||||||
use pocketmine\world\generator\Generator;
|
use pocketmine\world\generator\Generator;
|
||||||
use pocketmine\world\generator\GeneratorManager;
|
use pocketmine\world\generator\GeneratorManager;
|
||||||
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
||||||
|
use pocketmine\world\Position;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use pocketmine\world\WorldCreationOptions;
|
use pocketmine\world\WorldCreationOptions;
|
||||||
use pocketmine\world\WorldManager;
|
use pocketmine\world\WorldManager;
|
||||||
use Ramsey\Uuid\UuidInterface;
|
use Ramsey\Uuid\UuidInterface;
|
||||||
use Symfony\Component\Filesystem\Path;
|
use Symfony\Component\Filesystem\Path;
|
||||||
|
use function array_fill;
|
||||||
use function array_sum;
|
use function array_sum;
|
||||||
use function base64_encode;
|
use function base64_encode;
|
||||||
use function cli_set_process_title;
|
use function cli_set_process_title;
|
||||||
@ -124,7 +131,6 @@ use function count;
|
|||||||
use function date;
|
use function date;
|
||||||
use function fclose;
|
use function fclose;
|
||||||
use function file_exists;
|
use function file_exists;
|
||||||
use function file_get_contents;
|
|
||||||
use function file_put_contents;
|
use function file_put_contents;
|
||||||
use function filemtime;
|
use function filemtime;
|
||||||
use function fopen;
|
use function fopen;
|
||||||
@ -161,12 +167,9 @@ use function time;
|
|||||||
use function touch;
|
use function touch;
|
||||||
use function trim;
|
use function trim;
|
||||||
use function yaml_parse;
|
use function yaml_parse;
|
||||||
use function zlib_decode;
|
|
||||||
use function zlib_encode;
|
|
||||||
use const DIRECTORY_SEPARATOR;
|
use const DIRECTORY_SEPARATOR;
|
||||||
use const PHP_EOL;
|
use const PHP_EOL;
|
||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
use const ZLIB_ENCODING_GZIP;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that manages everything
|
* The class that manages everything
|
||||||
@ -184,9 +187,30 @@ class Server{
|
|||||||
public const DEFAULT_PORT_IPV6 = 19133;
|
public const DEFAULT_PORT_IPV6 = 19133;
|
||||||
public const DEFAULT_MAX_VIEW_DISTANCE = 16;
|
public const DEFAULT_MAX_VIEW_DISTANCE = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worlds, network, commands and most other things are polled this many times per second on average.
|
||||||
|
* Between ticks, the server will sleep to ensure that the average tick rate is maintained.
|
||||||
|
* It may wake up between ticks if a Snooze notification source is triggered (e.g. to process network packets).
|
||||||
|
*/
|
||||||
|
public const TARGET_TICKS_PER_SECOND = 20;
|
||||||
|
/**
|
||||||
|
* The average time between ticks, in seconds.
|
||||||
|
*/
|
||||||
|
public const TARGET_SECONDS_PER_TICK = 1 / self::TARGET_TICKS_PER_SECOND;
|
||||||
|
public const TARGET_NANOSECONDS_PER_TICK = 1_000_000_000 / self::TARGET_TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TPS threshold below which the server will generate log warnings.
|
||||||
|
*/
|
||||||
|
private const TPS_OVERLOAD_WARNING_THRESHOLD = self::TARGET_TICKS_PER_SECOND * 0.6;
|
||||||
|
|
||||||
|
private const TICKS_PER_WORLD_CACHE_CLEAR = 5 * self::TARGET_TICKS_PER_SECOND;
|
||||||
|
private const TICKS_PER_TPS_OVERLOAD_WARNING = 5 * self::TARGET_TICKS_PER_SECOND;
|
||||||
|
private const TICKS_PER_STATS_REPORT = 300 * self::TARGET_TICKS_PER_SECOND;
|
||||||
|
|
||||||
private static ?Server $instance = null;
|
private static ?Server $instance = null;
|
||||||
|
|
||||||
private SleeperHandler $tickSleeper;
|
private TimeTrackingSleeperHandler $tickSleeper;
|
||||||
|
|
||||||
private BanList $banByName;
|
private BanList $banByName;
|
||||||
|
|
||||||
@ -202,7 +226,7 @@ class Server{
|
|||||||
|
|
||||||
private PluginManager $pluginManager;
|
private PluginManager $pluginManager;
|
||||||
|
|
||||||
private float $profilingTickRate = 20;
|
private float $profilingTickRate = self::TARGET_TICKS_PER_SECOND;
|
||||||
|
|
||||||
private UpdateChecker $updater;
|
private UpdateChecker $updater;
|
||||||
|
|
||||||
@ -212,10 +236,10 @@ class Server{
|
|||||||
private int $tickCounter = 0;
|
private int $tickCounter = 0;
|
||||||
private float $nextTick = 0;
|
private float $nextTick = 0;
|
||||||
/** @var float[] */
|
/** @var float[] */
|
||||||
private array $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
|
private array $tickAverage;
|
||||||
/** @var float[] */
|
/** @var float[] */
|
||||||
private array $useAverage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
private array $useAverage;
|
||||||
private float $currentTPS = 20;
|
private float $currentTPS = self::TARGET_TICKS_PER_SECOND;
|
||||||
private float $currentUse = 0;
|
private float $currentUse = 0;
|
||||||
private float $startTime;
|
private float $startTime;
|
||||||
|
|
||||||
@ -251,6 +275,8 @@ class Server{
|
|||||||
private string $dataPath;
|
private string $dataPath;
|
||||||
private string $pluginPath;
|
private string $pluginPath;
|
||||||
|
|
||||||
|
private PlayerDataProvider $playerDataProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[]
|
||||||
* @phpstan-var array<string, string>
|
* @phpstan-var array<string, string>
|
||||||
@ -484,49 +510,22 @@ class Server{
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPlayerDataPath(string $username) : string{
|
|
||||||
return Path::join($this->getDataPath(), 'players', strtolower($username) . '.dat');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the server has stored any saved data for this player.
|
* Returns whether the server has stored any saved data for this player.
|
||||||
*/
|
*/
|
||||||
public function hasOfflinePlayerData(string $name) : bool{
|
public function hasOfflinePlayerData(string $name) : bool{
|
||||||
return file_exists($this->getPlayerDataPath($name));
|
return $this->playerDataProvider->hasData($name);
|
||||||
}
|
|
||||||
|
|
||||||
private function handleCorruptedPlayerData(string $name) : void{
|
|
||||||
$path = $this->getPlayerDataPath($name);
|
|
||||||
rename($path, $path . '.bak');
|
|
||||||
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOfflinePlayerData(string $name) : ?CompoundTag{
|
public function getOfflinePlayerData(string $name) : ?CompoundTag{
|
||||||
return Timings::$syncPlayerDataLoad->time(function() use ($name) : ?CompoundTag{
|
return Timings::$syncPlayerDataLoad->time(function() use ($name) : ?CompoundTag{
|
||||||
$name = strtolower($name);
|
try{
|
||||||
$path = $this->getPlayerDataPath($name);
|
return $this->playerDataProvider->loadData($name);
|
||||||
|
}catch(PlayerDataLoadException $e){
|
||||||
if(file_exists($path)){
|
$this->logger->debug("Failed to load player data for $name: " . $e->getMessage());
|
||||||
$contents = @file_get_contents($path);
|
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
||||||
if($contents === false){
|
return null;
|
||||||
throw new \RuntimeException("Failed to read player data file \"$path\" (permission denied?)");
|
|
||||||
}
|
|
||||||
$decompressed = @zlib_decode($contents);
|
|
||||||
if($decompressed === false){
|
|
||||||
$this->logger->debug("Failed to decompress raw player data for \"$name\"");
|
|
||||||
$this->handleCorruptedPlayerData($name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
|
||||||
return (new BigEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag();
|
|
||||||
}catch(NbtDataException $e){ //corrupt data
|
|
||||||
$this->logger->debug("Failed to decode NBT data for \"$name\": " . $e->getMessage());
|
|
||||||
$this->handleCorruptedPlayerData($name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,11 +539,9 @@ class Server{
|
|||||||
|
|
||||||
if(!$ev->isCancelled()){
|
if(!$ev->isCancelled()){
|
||||||
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
||||||
$nbt = new BigEndianNbtSerializer();
|
|
||||||
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
|
|
||||||
try{
|
try{
|
||||||
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
|
$this->playerDataProvider->saveData($name, $ev->getSaveData());
|
||||||
}catch(\RuntimeException $e){
|
}catch(PlayerDataSaveException $e){
|
||||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||||
$this->logger->logException($e);
|
$this->logger->logException($e);
|
||||||
}
|
}
|
||||||
@ -560,51 +557,47 @@ class Server{
|
|||||||
$ev->call();
|
$ev->call();
|
||||||
$class = $ev->getPlayerClass();
|
$class = $ev->getPlayerClass();
|
||||||
|
|
||||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
|
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
|
||||||
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
||||||
$spawn = $playerPos->asVector3();
|
|
||||||
}else{
|
}else{
|
||||||
$world = $this->worldManager->getDefaultWorld();
|
$world = $this->worldManager->getDefaultWorld();
|
||||||
if($world === null){
|
if($world === null){
|
||||||
throw new AssumptionFailedError("Default world should always be loaded");
|
throw new AssumptionFailedError("Default world should always be loaded");
|
||||||
}
|
}
|
||||||
$playerPos = null;
|
$playerPos = null;
|
||||||
$spawn = $world->getSpawnLocation();
|
|
||||||
}
|
}
|
||||||
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
|
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
|
||||||
$playerPromiseResolver = new PromiseResolver();
|
$playerPromiseResolver = new PromiseResolver();
|
||||||
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
|
||||||
function() use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
|
|
||||||
if(!$session->isConnected()){
|
|
||||||
$playerPromiseResolver->reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stick with the original spawn at the time of generation request, even if it changed since then.
|
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
|
||||||
* This is because we know for sure that that chunk will be generated, but the one at the new location
|
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
|
||||||
* might not be, and it would be much more complex to go back and redo the whole thing.
|
if(!$player->hasPlayedBefore()){
|
||||||
*
|
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
|
||||||
* TODO: this relies on the assumption that getSafeSpawn() will only alter the Y coordinate of the
|
|
||||||
* provided position. If this assumption is broken, we'll start seeing crashes in here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Player::__construct()
|
|
||||||
* @var Player $player
|
|
||||||
*/
|
|
||||||
$player = new $class($this, $session, $playerInfo, $authenticated, $playerPos ?? Location::fromObject($world->getSafeSpawn($spawn), $world), $offlinePlayerData);
|
|
||||||
if(!$player->hasPlayedBefore()){
|
|
||||||
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
|
|
||||||
}
|
|
||||||
$playerPromiseResolver->resolve($player);
|
|
||||||
},
|
|
||||||
static function() use ($playerPromiseResolver, $session) : void{
|
|
||||||
if($session->isConnected()){
|
|
||||||
$session->disconnect("Spawn terrain generation failed");
|
|
||||||
}
|
|
||||||
$playerPromiseResolver->reject();
|
|
||||||
}
|
}
|
||||||
);
|
$playerPromiseResolver->resolve($player);
|
||||||
|
};
|
||||||
|
|
||||||
|
if($playerPos === null){ //new player or no valid position due to world not being loaded
|
||||||
|
$world->requestSafeSpawn()->onCompletion(
|
||||||
|
function(Position $spawn) use ($createPlayer, $playerPromiseResolver, $session, $world) : void{
|
||||||
|
if(!$session->isConnected()){
|
||||||
|
$playerPromiseResolver->reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$createPlayer(Location::fromObject($spawn, $world));
|
||||||
|
},
|
||||||
|
function() use ($playerPromiseResolver, $session) : void{
|
||||||
|
if($session->isConnected()){
|
||||||
|
//TODO: this needs to be localized - this might be reached if the spawn world was unloaded while the player was logging in
|
||||||
|
$session->disconnect("Failed to find a safe spawn location");
|
||||||
|
}
|
||||||
|
$playerPromiseResolver->reject();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}else{ //returning player with a valid position - safe spawn not required
|
||||||
|
$createPlayer($playerPos);
|
||||||
|
}
|
||||||
|
|
||||||
return $playerPromiseResolver->getPromise();
|
return $playerPromiseResolver->getPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,8 +773,11 @@ class Server{
|
|||||||
}
|
}
|
||||||
self::$instance = $this;
|
self::$instance = $this;
|
||||||
$this->startTime = microtime(true);
|
$this->startTime = microtime(true);
|
||||||
|
$this->tickAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, self::TARGET_TICKS_PER_SECOND);
|
||||||
|
$this->useAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, 0);
|
||||||
|
|
||||||
$this->tickSleeper = new SleeperHandler();
|
Timings::init();
|
||||||
|
$this->tickSleeper = new TimeTrackingSleeperHandler(Timings::$serverInterrupts);
|
||||||
|
|
||||||
$this->signalHandler = new SignalHandler(function() : void{
|
$this->signalHandler = new SignalHandler(function() : void{
|
||||||
$this->logger->info("Received signal interrupt, stopping the server");
|
$this->logger->info("Received signal interrupt, stopping the server");
|
||||||
@ -806,7 +802,7 @@ class Server{
|
|||||||
$this->logger->info("Loading server configuration");
|
$this->logger->info("Loading server configuration");
|
||||||
$pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml");
|
$pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml");
|
||||||
if(!file_exists($pocketmineYmlPath)){
|
if(!file_exists($pocketmineYmlPath)){
|
||||||
$content = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")), "Missing required resource file");
|
$content = Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml"));
|
||||||
if(VersionInfo::IS_DEVELOPMENT_BUILD){
|
if(VersionInfo::IS_DEVELOPMENT_BUILD){
|
||||||
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
|
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
|
||||||
}
|
}
|
||||||
@ -900,6 +896,9 @@ class Server{
|
|||||||
if($this->configGroup->getPropertyInt("network.batch-threshold", 256) >= 0){
|
if($this->configGroup->getPropertyInt("network.batch-threshold", 256) >= 0){
|
||||||
$netCompressionThreshold = $this->configGroup->getPropertyInt("network.batch-threshold", 256);
|
$netCompressionThreshold = $this->configGroup->getPropertyInt("network.batch-threshold", 256);
|
||||||
}
|
}
|
||||||
|
if($netCompressionThreshold < 0){
|
||||||
|
$netCompressionThreshold = null;
|
||||||
|
}
|
||||||
|
|
||||||
$netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6);
|
$netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6);
|
||||||
if($netCompressionLevel < 1 || $netCompressionLevel > 9){
|
if($netCompressionLevel < 1 || $netCompressionLevel > 9){
|
||||||
@ -961,15 +960,14 @@ class Server{
|
|||||||
)));
|
)));
|
||||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||||
|
|
||||||
Timings::init();
|
|
||||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false));
|
TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false));
|
||||||
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", 20);
|
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND);
|
||||||
|
|
||||||
DefaultPermissions::registerCorePermissions();
|
DefaultPermissions::registerCorePermissions();
|
||||||
|
|
||||||
$this->commandMap = new SimpleCommandMap($this);
|
$this->commandMap = new SimpleCommandMap($this);
|
||||||
|
|
||||||
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes.json"));
|
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\RESOURCE_PATH, "legacy_recipes.json"));
|
||||||
|
|
||||||
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
|
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
|
||||||
|
|
||||||
@ -979,7 +977,7 @@ class Server{
|
|||||||
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
|
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
|
||||||
}
|
}
|
||||||
try{
|
try{
|
||||||
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(file_get_contents($graylistFile)));
|
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile)));
|
||||||
}catch(\InvalidArgumentException $e){
|
}catch(\InvalidArgumentException $e){
|
||||||
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
|
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
|
||||||
$this->forceShutdownExit();
|
$this->forceShutdownExit();
|
||||||
@ -1001,12 +999,14 @@ class Server{
|
|||||||
|
|
||||||
$this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager);
|
$this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager);
|
||||||
$this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave()));
|
$this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave()));
|
||||||
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", 6000));
|
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval()));
|
||||||
|
|
||||||
$this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io"));
|
$this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io"));
|
||||||
|
|
||||||
$this->queryInfo = new QueryInfo($this);
|
$this->queryInfo = new QueryInfo($this);
|
||||||
|
|
||||||
|
$this->playerDataProvider = new DatFilePlayerDataProvider(Path::join($this->dataPath, "players"));
|
||||||
|
|
||||||
register_shutdown_function([$this, "crashDump"]);
|
register_shutdown_function([$this, "crashDump"]);
|
||||||
|
|
||||||
$loadErrorCount = 0;
|
$loadErrorCount = 0;
|
||||||
@ -1039,7 +1039,7 @@ class Server{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
|
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
|
||||||
$this->sendUsageTicker = 6000;
|
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
|
||||||
$this->sendUsage(SendUsageTask::TYPE_OPEN);
|
$this->sendUsage(SendUsageTask::TYPE_OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1174,10 +1174,18 @@ class Server{
|
|||||||
return !$anyWorldFailedToLoad;
|
return !$anyWorldFailedToLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
|
private function startupPrepareConnectableNetworkInterfaces(
|
||||||
|
string $ip,
|
||||||
|
int $port,
|
||||||
|
bool $ipV6,
|
||||||
|
bool $useQuery,
|
||||||
|
PacketBroadcaster $packetBroadcaster,
|
||||||
|
EntityEventBroadcaster $entityEventBroadcaster,
|
||||||
|
PacketSerializerContext $packetSerializerContext
|
||||||
|
) : bool{
|
||||||
$prettyIp = $ipV6 ? "[$ip]" : $ip;
|
$prettyIp = $ipV6 ? "[$ip]" : $ip;
|
||||||
try{
|
try{
|
||||||
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6));
|
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext));
|
||||||
}catch(NetworkInterfaceStartException $e){
|
}catch(NetworkInterfaceStartException $e){
|
||||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
|
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
|
||||||
$ip,
|
$ip,
|
||||||
@ -1203,11 +1211,15 @@ class Server{
|
|||||||
private function startupPrepareNetworkInterfaces() : bool{
|
private function startupPrepareNetworkInterfaces() : bool{
|
||||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||||
|
|
||||||
|
$packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
|
||||||
|
$packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext);
|
||||||
|
$entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster);
|
||||||
|
|
||||||
if(
|
if(
|
||||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery) ||
|
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext) ||
|
||||||
(
|
(
|
||||||
$this->configGroup->getConfigBool("enable-ipv6", true) &&
|
$this->configGroup->getConfigBool("enable-ipv6", true) &&
|
||||||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery)
|
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext)
|
||||||
)
|
)
|
||||||
){
|
){
|
||||||
return false;
|
return false;
|
||||||
@ -1339,46 +1351,10 @@ class Server{
|
|||||||
/**
|
/**
|
||||||
* @param Player[] $players
|
* @param Player[] $players
|
||||||
* @param ClientboundPacket[] $packets
|
* @param ClientboundPacket[] $packets
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public function broadcastPackets(array $players, array $packets) : bool{
|
public function broadcastPackets(array $players, array $packets) : bool{
|
||||||
if(count($packets) === 0){
|
return NetworkBroadcastUtils::broadcastPackets($players, $packets);
|
||||||
throw new \InvalidArgumentException("Cannot broadcast empty list of packets");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Timings::$broadcastPackets->time(function() use ($players, $packets) : bool{
|
|
||||||
/** @var NetworkSession[] $recipients */
|
|
||||||
$recipients = [];
|
|
||||||
foreach($players as $player){
|
|
||||||
if($player->isConnected()){
|
|
||||||
$recipients[] = $player->getNetworkSession();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(count($recipients) === 0){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ev = new DataPacketSendEvent($recipients, $packets);
|
|
||||||
$ev->call();
|
|
||||||
if($ev->isCancelled()){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$recipients = $ev->getTargets();
|
|
||||||
|
|
||||||
/** @var PacketBroadcaster[] $broadcasters */
|
|
||||||
$broadcasters = [];
|
|
||||||
/** @var NetworkSession[][] $broadcasterTargets */
|
|
||||||
$broadcasterTargets = [];
|
|
||||||
foreach($recipients as $recipient){
|
|
||||||
$broadcaster = $recipient->getBroadcaster();
|
|
||||||
$broadcasters[spl_object_id($broadcaster)] = $broadcaster;
|
|
||||||
$broadcasterTargets[spl_object_id($broadcaster)][] = $recipient;
|
|
||||||
}
|
|
||||||
foreach($broadcasters as $broadcaster){
|
|
||||||
$broadcaster->broadcastPackets($broadcasterTargets[spl_object_id($broadcaster)], $packets);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1386,14 +1362,16 @@ class Server{
|
|||||||
*
|
*
|
||||||
* @param bool|null $sync Compression on the main thread (true) or workers (false). Default is automatic (null).
|
* @param bool|null $sync Compression on the main thread (true) or workers (false). Default is automatic (null).
|
||||||
*/
|
*/
|
||||||
public function prepareBatch(PacketBatch $stream, Compressor $compressor, ?bool $sync = null) : CompressBatchPromise{
|
public function prepareBatch(PacketBatch $stream, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise{
|
||||||
|
$timings ??= Timings::$playerNetworkSendCompress;
|
||||||
try{
|
try{
|
||||||
Timings::$playerNetworkSendCompress->startTiming();
|
$timings->startTiming();
|
||||||
|
|
||||||
$buffer = $stream->getBuffer();
|
$buffer = $stream->getBuffer();
|
||||||
|
|
||||||
if($sync === null){
|
if($sync === null){
|
||||||
$sync = !($this->networkCompressionAsync && $compressor->willCompress($buffer));
|
$threshold = $compressor->getCompressionThreshold();
|
||||||
|
$sync = !$this->networkCompressionAsync || $threshold === null || strlen($stream->getBuffer()) < $threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
$promise = new CompressBatchPromise();
|
$promise = new CompressBatchPromise();
|
||||||
@ -1406,7 +1384,7 @@ class Server{
|
|||||||
|
|
||||||
return $promise;
|
return $promise;
|
||||||
}finally{
|
}finally{
|
||||||
Timings::$playerNetworkSendCompress->stopTiming();
|
$timings->stopTiming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1821,11 +1799,11 @@ class Server{
|
|||||||
$this->network->tick();
|
$this->network->tick();
|
||||||
Timings::$connection->stopTiming();
|
Timings::$connection->stopTiming();
|
||||||
|
|
||||||
if(($this->tickCounter % 20) === 0){
|
if(($this->tickCounter % self::TARGET_TICKS_PER_SECOND) === 0){
|
||||||
if($this->doTitleTick){
|
if($this->doTitleTick){
|
||||||
$this->titleTick();
|
$this->titleTick();
|
||||||
}
|
}
|
||||||
$this->currentTPS = 20;
|
$this->currentTPS = self::TARGET_TICKS_PER_SECOND;
|
||||||
$this->currentUse = 0;
|
$this->currentUse = 0;
|
||||||
|
|
||||||
$queryRegenerateEvent = new QueryRegenerateEvent(new QueryInfo($this));
|
$queryRegenerateEvent = new QueryRegenerateEvent(new QueryInfo($this));
|
||||||
@ -1837,18 +1815,18 @@ class Server{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){
|
if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){
|
||||||
$this->sendUsageTicker = 6000;
|
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
|
||||||
$this->sendUsage(SendUsageTask::TYPE_STATUS);
|
$this->sendUsage(SendUsageTask::TYPE_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(($this->tickCounter % 100) === 0){
|
if(($this->tickCounter % self::TICKS_PER_WORLD_CACHE_CLEAR) === 0){
|
||||||
foreach($this->worldManager->getWorlds() as $world){
|
foreach($this->worldManager->getWorlds() as $world){
|
||||||
$world->clearCache();
|
$world->clearCache();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($this->getTicksPerSecondAverage() < 12){
|
if(($this->tickCounter % self::TICKS_PER_TPS_OVERLOAD_WARNING) === 0 && $this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){
|
||||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
|
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getMemoryManager()->check();
|
$this->getMemoryManager()->check();
|
||||||
@ -1865,19 +1843,21 @@ class Server{
|
|||||||
Timings::$serverTick->stopTiming();
|
Timings::$serverTick->stopTiming();
|
||||||
|
|
||||||
$now = microtime(true);
|
$now = microtime(true);
|
||||||
$this->currentTPS = min(20, 1 / max(0.001, $now - $tickTime));
|
$totalTickTimeSeconds = $now - $tickTime + ($this->tickSleeper->getNotificationProcessingTime() / 1_000_000_000);
|
||||||
$this->currentUse = min(1, ($now - $tickTime) / 0.05);
|
$this->currentTPS = min(self::TARGET_TICKS_PER_SECOND, 1 / max(0.001, $totalTickTimeSeconds));
|
||||||
|
$this->currentUse = min(1, $totalTickTimeSeconds / self::TARGET_SECONDS_PER_TICK);
|
||||||
|
|
||||||
TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate);
|
TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate);
|
||||||
|
|
||||||
$idx = $this->tickCounter % 20;
|
$idx = $this->tickCounter % self::TARGET_TICKS_PER_SECOND;
|
||||||
$this->tickAverage[$idx] = $this->currentTPS;
|
$this->tickAverage[$idx] = $this->currentTPS;
|
||||||
$this->useAverage[$idx] = $this->currentUse;
|
$this->useAverage[$idx] = $this->currentUse;
|
||||||
|
$this->tickSleeper->resetNotificationProcessingTime();
|
||||||
|
|
||||||
if(($this->nextTick - $tickTime) < -1){
|
if(($this->nextTick - $tickTime) < -1){
|
||||||
$this->nextTick = $tickTime;
|
$this->nextTick = $tickTime;
|
||||||
}else{
|
}else{
|
||||||
$this->nextTick += 0.05;
|
$this->nextTick += self::TARGET_SECONDS_PER_TICK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
src/TimeTrackingSleeperHandler.php
Normal file
64
src/TimeTrackingSleeperHandler.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine;
|
||||||
|
|
||||||
|
use pocketmine\snooze\SleeperHandler;
|
||||||
|
use pocketmine\timings\TimingsHandler;
|
||||||
|
use function hrtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Snooze sleeper handler which captures notification processing time.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class TimeTrackingSleeperHandler extends SleeperHandler{
|
||||||
|
|
||||||
|
private int $notificationProcessingTimeNs = 0;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private TimingsHandler $timings
|
||||||
|
){
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time in nanoseconds spent processing notifications since the last reset.
|
||||||
|
*/
|
||||||
|
public function getNotificationProcessingTime() : int{ return $this->notificationProcessingTimeNs; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the notification processing time tracker to zero.
|
||||||
|
*/
|
||||||
|
public function resetNotificationProcessingTime() : void{ $this->notificationProcessingTimeNs = 0; }
|
||||||
|
|
||||||
|
public function processNotifications() : void{
|
||||||
|
$startTime = hrtime(true);
|
||||||
|
$this->timings->startTiming();
|
||||||
|
try{
|
||||||
|
parent::processNotifications();
|
||||||
|
}finally{
|
||||||
|
$this->notificationProcessingTimeNs += hrtime(true) - $startTime;
|
||||||
|
$this->timings->stopTiming();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,9 +31,9 @@ use function str_repeat;
|
|||||||
|
|
||||||
final class VersionInfo{
|
final class VersionInfo{
|
||||||
public const NAME = "PocketMine-MP";
|
public const NAME = "PocketMine-MP";
|
||||||
public const BASE_VERSION = "4.12.3";
|
public const BASE_VERSION = "4.18.0-ALPHA1";
|
||||||
public const IS_DEVELOPMENT_BUILD = false;
|
public const IS_DEVELOPMENT_BUILD = false;
|
||||||
public const BUILD_CHANNEL = "stable";
|
public const BUILD_CHANNEL = "alpha";
|
||||||
|
|
||||||
private function __construct(){
|
private function __construct(){
|
||||||
//NOOP
|
//NOOP
|
||||||
|
@ -112,7 +112,14 @@ abstract class BaseBanner extends Transparent{
|
|||||||
return SupportType::NONE();
|
return SupportType::NONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function canBeSupportedBy(Block $block) : bool{
|
||||||
|
return $block->isSolid();
|
||||||
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
|
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if($item instanceof ItemBanner){
|
if($item instanceof ItemBanner){
|
||||||
$this->color = $item->getColor();
|
$this->color = $item->getColor();
|
||||||
$this->setPatterns($item->getPatterns());
|
$this->setPatterns($item->getPatterns());
|
||||||
@ -124,7 +131,7 @@ abstract class BaseBanner extends Transparent{
|
|||||||
abstract protected function getSupportingFace() : int;
|
abstract protected function getSupportingFace() : int;
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
if($this->getSide($this->getSupportingFace())->getId() === BlockLegacyIds::AIR){
|
if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,14 +114,13 @@ final class Bell extends Transparent{
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||||
//TODO: this isn't the actual logic, but it's the closest approximation we can support for now
|
return !$block->getSupportType($face)->equals(SupportType::NONE());
|
||||||
return $block->isSolid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($face === Facing::UP){
|
if($face === Facing::UP){
|
||||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()))){
|
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()), Facing::UP)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if($player !== null){
|
if($player !== null){
|
||||||
@ -129,18 +128,18 @@ final class Bell extends Transparent{
|
|||||||
}
|
}
|
||||||
$this->setAttachmentType(BellAttachmentType::FLOOR());
|
$this->setAttachmentType(BellAttachmentType::FLOOR());
|
||||||
}elseif($face === Facing::DOWN){
|
}elseif($face === Facing::DOWN){
|
||||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()))){
|
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()), Facing::DOWN)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$this->setAttachmentType(BellAttachmentType::CEILING());
|
$this->setAttachmentType(BellAttachmentType::CEILING());
|
||||||
}else{
|
}else{
|
||||||
$this->setFacing($face);
|
$this->setFacing($face);
|
||||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))))){
|
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))), $face)){
|
||||||
$this->setAttachmentType(BellAttachmentType::ONE_WALL());
|
$this->setAttachmentType(BellAttachmentType::ONE_WALL());
|
||||||
}else{
|
}else{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)))){
|
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)), Facing::opposite($face))){
|
||||||
$this->setAttachmentType(BellAttachmentType::TWO_WALLS());
|
$this->setAttachmentType(BellAttachmentType::TWO_WALLS());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,10 +148,10 @@ final class Bell extends Transparent{
|
|||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
if(
|
if(
|
||||||
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP))) ||
|
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP), Facing::DOWN)) ||
|
||||||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN))) ||
|
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN), Facing::UP)) ||
|
||||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))) ||
|
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)) ||
|
||||||
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))))
|
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing), Facing::opposite($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)))
|
||||||
){
|
){
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
}
|
}
|
||||||
@ -161,21 +160,20 @@ final class Bell extends Transparent{
|
|||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($player !== null){
|
if($player !== null){
|
||||||
$faceHit = Facing::opposite($player->getHorizontalFacing());
|
$faceHit = Facing::opposite($player->getHorizontalFacing());
|
||||||
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
|
|
||||||
$this->ring($faceHit);
|
|
||||||
}
|
|
||||||
if($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)){
|
|
||||||
$this->ring($faceHit);
|
|
||||||
}
|
|
||||||
if(
|
if(
|
||||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
|
$this->attachmentType->equals(BellAttachmentType::CEILING()) ||
|
||||||
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
|
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)) ||
|
||||||
|
(
|
||||||
|
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
|
||||||
|
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
|
||||||
|
)
|
||||||
){
|
){
|
||||||
$this->ring($faceHit);
|
$this->ring($faceHit);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ring(int $faceHit) : void{
|
public function ring(int $faceHit) : void{
|
||||||
|
@ -253,6 +253,14 @@ class Block{
|
|||||||
* Generates a block transaction to set all blocks affected by placing this block. Usually this is just the block
|
* Generates a block transaction to set all blocks affected by placing this block. Usually this is just the block
|
||||||
* itself, but may be multiple blocks in some cases (such as doors).
|
* itself, but may be multiple blocks in some cases (such as doors).
|
||||||
*
|
*
|
||||||
|
* @param BlockTransaction $tx Blocks to be set should be added to this transaction (do not modify thr world directly)
|
||||||
|
* @param Item $item Item used to place the block
|
||||||
|
* @param Block $blockReplace Block expected to be replaced
|
||||||
|
* @param Block $blockClicked Block that was clicked using the item
|
||||||
|
* @param int $face Face of the clicked block which was clicked
|
||||||
|
* @param Vector3 $clickVector Exact position inside the clicked block where the click occurred, relative to the block's position
|
||||||
|
* @param Player|null $player Player who placed the block, or null if it was not a player
|
||||||
|
*
|
||||||
* @return bool whether the placement should go ahead
|
* @return bool whether the placement should go ahead
|
||||||
*/
|
*/
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
|
@ -61,7 +61,7 @@ abstract class Button extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($this->canBeSupportedBy($blockClicked, $face)){
|
if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||||
$this->facing = $face;
|
$this->facing = $face;
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ class ItemFrame extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($face === Facing::DOWN || $face === Facing::UP || !$blockClicked->isSolid()){
|
if($face === Facing::DOWN || $face === Facing::UP || !$blockReplace->getSide(Facing::opposite($face))->isSolid()){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
|||||||
use pocketmine\block\tile\Jukebox as JukeboxTile;
|
use pocketmine\block\tile\Jukebox as JukeboxTile;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\Record;
|
use pocketmine\item\Record;
|
||||||
|
use pocketmine\lang\KnownTranslationKeys;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\world\sound\RecordSound;
|
use pocketmine\world\sound\RecordSound;
|
||||||
@ -44,7 +45,7 @@ class Jukebox extends Opaque{
|
|||||||
if($this->record !== null){
|
if($this->record !== null){
|
||||||
$this->ejectRecord();
|
$this->ejectRecord();
|
||||||
}elseif($item instanceof Record){
|
}elseif($item instanceof Record){
|
||||||
$player->sendJukeboxPopup("record.nowPlaying", [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]);
|
$player->sendJukeboxPopup(KnownTranslationKeys::RECORD_NOWPLAYING, [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]);
|
||||||
$this->insertRecord($item->pop());
|
$this->insertRecord($item->pop());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class Ladder extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($this->canBeSupportedBy($blockClicked, $face) && Facing::axis($face) !== Axis::Y){
|
if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face) && Facing::axis($face) !== Axis::Y){
|
||||||
$this->facing = $face;
|
$this->facing = $face;
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ class Lever extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if(!$this->canBeSupportedBy($blockClicked, $face)){
|
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ abstract class PressurePlate extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($this->canBeSupportedBy($blockClicked)){
|
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -55,5 +55,11 @@ abstract class PressurePlate extends Transparent{
|
|||||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function onNearbyBlockChange() : void{
|
||||||
|
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||||
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class RedMushroomBlock extends Opaque{
|
|||||||
protected MushroomBlockType $mushroomBlockType;
|
protected MushroomBlockType $mushroomBlockType;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||||
$this->mushroomBlockType = MushroomBlockType::PORES();
|
$this->mushroomBlockType = MushroomBlockType::ALL_CAP();
|
||||||
parent::__construct($idInfo, $name, $breakInfo);
|
parent::__construct($idInfo, $name, $breakInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +42,11 @@ class RedMushroomBlock extends Opaque{
|
|||||||
return MushroomBlockTypeIdMap::getInstance()->toId($this->mushroomBlockType);
|
return MushroomBlockTypeIdMap::getInstance()->toId($this->mushroomBlockType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function writeStateToItemMeta() : int{
|
||||||
|
//these blocks always drop as all-cap, but may exist in other forms in the inventory (particularly creative)
|
||||||
|
return BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_CAP;
|
||||||
|
}
|
||||||
|
|
||||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||||
$type = MushroomBlockTypeIdMap::getInstance()->fromId($stateMeta);
|
$type = MushroomBlockTypeIdMap::getInstance()->fromId($stateMeta);
|
||||||
if($type === null){
|
if($type === null){
|
||||||
|
@ -41,7 +41,7 @@ class Thin extends Transparent{
|
|||||||
|
|
||||||
foreach(Facing::HORIZONTAL as $facing){
|
foreach(Facing::HORIZONTAL as $facing){
|
||||||
$side = $this->getSide($facing);
|
$side = $this->getSide($facing);
|
||||||
if($side instanceof Thin || $side->isFullCube()){
|
if($side instanceof Thin || $side instanceof Wall || $side->isFullCube()){
|
||||||
$this->connections[$facing] = true;
|
$this->connections[$facing] = true;
|
||||||
}else{
|
}else{
|
||||||
unset($this->connections[$facing]);
|
unset($this->connections[$facing]);
|
||||||
|
@ -65,7 +65,6 @@ class Torch extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
$below = $this->getSide(Facing::DOWN);
|
|
||||||
$face = Facing::opposite($this->facing);
|
$face = Facing::opposite($this->facing);
|
||||||
|
|
||||||
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
|
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
|
||||||
@ -74,10 +73,7 @@ class Torch extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){
|
if($face !== Facing::DOWN && $this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||||
$this->facing = Facing::UP;
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
|
|
||||||
$this->facing = $face;
|
$this->facing = $face;
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}else{
|
}else{
|
||||||
|
@ -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{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if(!$blockClicked->isFullCube() || Facing::axis($face) === Axis::Y){
|
if(!$blockReplace->getSide(Facing::opposite($face))->isFullCube() || Facing::axis($face) === Axis::Y){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class Wall extends Transparent{
|
|||||||
|
|
||||||
foreach(Facing::HORIZONTAL as $facing){
|
foreach(Facing::HORIZONTAL as $facing){
|
||||||
$block = $this->getSide($facing);
|
$block = $this->getSide($facing);
|
||||||
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
|
if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || ($block->isSolid() && !$block->isTransparent())){
|
||||||
$this->connections[$facing] = $facing;
|
$this->connections[$facing] = $facing;
|
||||||
}else{
|
}else{
|
||||||
unset($this->connections[$facing]);
|
unset($this->connections[$facing]);
|
||||||
|
@ -113,7 +113,7 @@ final class WallCoralFan extends BaseCoral{
|
|||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
$axis = Facing::axis($face);
|
$axis = Facing::axis($face);
|
||||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockClicked, $face)){
|
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$this->facing = $face;
|
$this->facing = $face;
|
||||||
|
@ -39,19 +39,23 @@ class WaterLily extends Flowable{
|
|||||||
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
|
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||||
if($blockClicked instanceof Water){
|
return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||||
$up = $blockClicked->getSide(Facing::UP);
|
}
|
||||||
if($up->canBeReplaced()){
|
|
||||||
return parent::place($tx, $item, $up, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
private function canBeSupportedBy(Block $block) : bool{
|
||||||
|
return $block instanceof Water;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
|
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
if(!($this->getSide(Facing::DOWN) instanceof Water)){
|
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ trait ContainerTrait{
|
|||||||
$newContents = [];
|
$newContents = [];
|
||||||
/** @var CompoundTag $itemNBT */
|
/** @var CompoundTag $itemNBT */
|
||||||
foreach($inventoryTag as $itemNBT){
|
foreach($inventoryTag as $itemNBT){
|
||||||
$newContents[$itemNBT->getByte("Slot")] = Item::nbtDeserialize($itemNBT);
|
$newContents[$itemNBT->getByte(Item::TAG_SLOT)] = Item::nbtDeserialize($itemNBT);
|
||||||
}
|
}
|
||||||
$inventory->setContents($newContents);
|
$inventory->setContents($newContents);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ use function array_slice;
|
|||||||
use function count;
|
use function count;
|
||||||
use function explode;
|
use function explode;
|
||||||
use function is_int;
|
use function is_int;
|
||||||
use function strpos;
|
use function str_contains;
|
||||||
|
|
||||||
class SignText{
|
class SignText{
|
||||||
public const LINE_COUNT = 4;
|
public const LINE_COUNT = 4;
|
||||||
@ -54,7 +54,7 @@ class SignText{
|
|||||||
foreach($lines as $k => $line){
|
foreach($lines as $k => $line){
|
||||||
$this->checkLineIndex($k);
|
$this->checkLineIndex($k);
|
||||||
Utils::checkUTF8($line);
|
Utils::checkUTF8($line);
|
||||||
if(strpos($line, "\n") !== false){
|
if(str_contains($line, "\n")){
|
||||||
throw new \InvalidArgumentException("Line must not contain newlines");
|
throw new \InvalidArgumentException("Line must not contain newlines");
|
||||||
}
|
}
|
||||||
//TODO: add length checks
|
//TODO: add length checks
|
||||||
|
@ -72,8 +72,8 @@ use pocketmine\utils\TextFormat;
|
|||||||
use function array_shift;
|
use function array_shift;
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
|
use function str_contains;
|
||||||
use function strcasecmp;
|
use function strcasecmp;
|
||||||
use function strpos;
|
|
||||||
use function strtolower;
|
use function strtolower;
|
||||||
use function trim;
|
use function trim;
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ class SimpleCommandMap implements CommandMap{
|
|||||||
$values = $this->server->getCommandAliases();
|
$values = $this->server->getCommandAliases();
|
||||||
|
|
||||||
foreach($values as $alias => $commandStrings){
|
foreach($values as $alias => $commandStrings){
|
||||||
if(strpos($alias, ":") !== false){
|
if(str_contains($alias, ":")){
|
||||||
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
|
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ use pocketmine\item\LegacyStringToItemParserException;
|
|||||||
use pocketmine\item\StringToItemParser;
|
use pocketmine\item\StringToItemParser;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
@ -59,23 +58,9 @@ class ClearCommand extends VanillaCommand{
|
|||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($args[0])){
|
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
|
||||||
$target = $sender->getServer()->getPlayerByPrefix($args[0]);
|
if($target === null){
|
||||||
if($target === null){
|
return true;
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if($target !== $sender && !$this->testPermission($sender, DefaultPermissionNames::COMMAND_CLEAR_OTHER)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}elseif($sender instanceof Player){
|
|
||||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_CLEAR_SELF)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$target = $sender;
|
|
||||||
}else{
|
|
||||||
throw new InvalidCommandSyntaxException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$targetItem = null;
|
$targetItem = null;
|
||||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\command\defaults;
|
namespace pocketmine\command\defaults;
|
||||||
|
|
||||||
use pocketmine\command\CommandSender;
|
use pocketmine\command\CommandSender;
|
||||||
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use Symfony\Component\Filesystem\Path;
|
use Symfony\Component\Filesystem\Path;
|
||||||
use function date;
|
use function date;
|
||||||
@ -33,7 +34,7 @@ class DumpMemoryCommand extends VanillaCommand{
|
|||||||
public function __construct(string $name){
|
public function __construct(string $name){
|
||||||
parent::__construct(
|
parent::__construct(
|
||||||
$name,
|
$name,
|
||||||
"Dumps the memory",
|
KnownTranslationFactory::pocketmine_command_dumpmemory_description(),
|
||||||
"/$name [path]"
|
"/$name [path]"
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_DUMPMEMORY);
|
$this->setPermission(DefaultPermissionNames::COMMAND_DUMPMEMORY);
|
||||||
|
@ -32,6 +32,7 @@ use pocketmine\permission\DefaultPermissionNames;
|
|||||||
use pocketmine\utils\Limits;
|
use pocketmine\utils\Limits;
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
use function count;
|
use function count;
|
||||||
|
use function implode;
|
||||||
use function strtolower;
|
use function strtolower;
|
||||||
|
|
||||||
class EffectCommand extends VanillaCommand{
|
class EffectCommand extends VanillaCommand{
|
||||||
@ -42,7 +43,10 @@ class EffectCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::pocketmine_command_effect_description(),
|
KnownTranslationFactory::pocketmine_command_effect_description(),
|
||||||
KnownTranslationFactory::commands_effect_usage()
|
KnownTranslationFactory::commands_effect_usage()
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_EFFECT);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_EFFECT_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_EFFECT_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||||
@ -54,10 +58,8 @@ class EffectCommand extends VanillaCommand{
|
|||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
|
||||||
|
|
||||||
if($player === null){
|
if($player === null){
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$effectManager = $player->getEffects();
|
$effectManager = $player->getEffects();
|
||||||
|
@ -29,8 +29,8 @@ use pocketmine\item\enchantment\EnchantmentInstance;
|
|||||||
use pocketmine\item\enchantment\StringToEnchantmentParser;
|
use pocketmine\item\enchantment\StringToEnchantmentParser;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use function count;
|
use function count;
|
||||||
|
use function implode;
|
||||||
|
|
||||||
class EnchantCommand extends VanillaCommand{
|
class EnchantCommand extends VanillaCommand{
|
||||||
|
|
||||||
@ -40,7 +40,10 @@ class EnchantCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::pocketmine_command_enchant_description(),
|
KnownTranslationFactory::pocketmine_command_enchant_description(),
|
||||||
KnownTranslationFactory::commands_enchant_usage()
|
KnownTranslationFactory::commands_enchant_usage()
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_ENCHANT);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_ENCHANT_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_ENCHANT_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||||
@ -52,10 +55,8 @@ class EnchantCommand extends VanillaCommand{
|
|||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
|
||||||
|
|
||||||
if($player === null){
|
if($player === null){
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +29,8 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
|||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\player\GameMode;
|
use pocketmine\player\GameMode;
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use function count;
|
use function count;
|
||||||
|
use function implode;
|
||||||
|
|
||||||
class GamemodeCommand extends VanillaCommand{
|
class GamemodeCommand extends VanillaCommand{
|
||||||
|
|
||||||
@ -41,7 +40,10 @@ class GamemodeCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::pocketmine_command_gamemode_description(),
|
KnownTranslationFactory::pocketmine_command_gamemode_description(),
|
||||||
KnownTranslationFactory::commands_gamemode_usage()
|
KnownTranslationFactory::commands_gamemode_usage()
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_GAMEMODE);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_GAMEMODE_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_GAMEMODE_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||||
@ -59,17 +61,9 @@ class GamemodeCommand extends VanillaCommand{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($args[1])){
|
$target = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
|
||||||
$target = $sender->getServer()->getPlayerByPrefix($args[1]);
|
if($target === null){
|
||||||
if($target === null){
|
return true;
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}elseif($sender instanceof Player){
|
|
||||||
$target = $sender;
|
|
||||||
}else{
|
|
||||||
throw new InvalidCommandSyntaxException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($target->getGamemode()->equals($gameMode)){
|
if($target->getGamemode()->equals($gameMode)){
|
||||||
|
@ -47,7 +47,10 @@ class GiveCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::pocketmine_command_give_description(),
|
KnownTranslationFactory::pocketmine_command_give_description(),
|
||||||
KnownTranslationFactory::pocketmine_command_give_usage()
|
KnownTranslationFactory::pocketmine_command_give_usage()
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_GIVE);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_GIVE_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_GIVE_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||||
@ -59,9 +62,8 @@ class GiveCommand extends VanillaCommand{
|
|||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
|
||||||
if($player === null){
|
if($player === null){
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,6 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
|||||||
use pocketmine\event\entity\EntityDamageEvent;
|
use pocketmine\event\entity\EntityDamageEvent;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
|
|
||||||
@ -55,32 +53,16 @@ class KillCommand extends VanillaCommand{
|
|||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count($args) === 1){
|
$player = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
|
||||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_KILL_OTHER)){
|
if($player === null){
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
|
||||||
|
|
||||||
if($player instanceof Player){
|
|
||||||
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
|
||||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
|
|
||||||
}else{
|
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($sender instanceof Player){
|
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_KILL_SELF)){
|
if($player === $sender){
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_kill_successful($sender->getName()));
|
$sender->sendMessage(KnownTranslationFactory::commands_kill_successful($sender->getName()));
|
||||||
}else{
|
}else{
|
||||||
throw new InvalidCommandSyntaxException();
|
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -70,7 +70,7 @@ use function explode;
|
|||||||
use function max;
|
use function max;
|
||||||
use function microtime;
|
use function microtime;
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
use function strpos;
|
use function str_starts_with;
|
||||||
use function strtolower;
|
use function strtolower;
|
||||||
|
|
||||||
class ParticleCommand extends VanillaCommand{
|
class ParticleCommand extends VanillaCommand{
|
||||||
@ -208,17 +208,17 @@ class ParticleCommand extends VanillaCommand{
|
|||||||
return new EntityFlameParticle();
|
return new EntityFlameParticle();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strpos($name, "iconcrack_") === 0){
|
if(str_starts_with($name, "iconcrack_")){
|
||||||
$d = explode("_", $name);
|
$d = explode("_", $name);
|
||||||
if(count($d) === 3){
|
if(count($d) === 3){
|
||||||
return new ItemBreakParticle(ItemFactory::getInstance()->get((int) $d[1], (int) $d[2]));
|
return new ItemBreakParticle(ItemFactory::getInstance()->get((int) $d[1], (int) $d[2]));
|
||||||
}
|
}
|
||||||
}elseif(strpos($name, "blockcrack_") === 0){
|
}elseif(str_starts_with($name, "blockcrack_")){
|
||||||
$d = explode("_", $name);
|
$d = explode("_", $name);
|
||||||
if(count($d) === 2){
|
if(count($d) === 2){
|
||||||
return new TerrainParticle(BlockFactory::getInstance()->get(((int) $d[1]) & 0xff, ((int) $d[1]) >> 12));
|
return new TerrainParticle(BlockFactory::getInstance()->get(((int) $d[1]) & 0xff, ((int) $d[1]) >> 12));
|
||||||
}
|
}
|
||||||
}elseif(strpos($name, "blockdust_") === 0){
|
}elseif(str_starts_with($name, "blockdust_")){
|
||||||
$d = explode("_", $name);
|
$d = explode("_", $name);
|
||||||
if(count($d) >= 4){
|
if(count($d) >= 4){
|
||||||
return new DustParticle(new Color(((int) $d[1]) & 0xff, ((int) $d[2]) & 0xff, ((int) $d[3]) & 0xff, isset($d[4]) ? ((int) $d[4]) & 0xff : 255));
|
return new DustParticle(new Color(((int) $d[1]) & 0xff, ((int) $d[2]) & 0xff, ((int) $d[3]) & 0xff, isset($d[4]) ? ((int) $d[4]) & 0xff : 255));
|
||||||
|
@ -29,10 +29,10 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
|||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use pocketmine\world\Position;
|
use pocketmine\world\Position;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use function count;
|
use function count;
|
||||||
|
use function implode;
|
||||||
use function round;
|
use function round;
|
||||||
|
|
||||||
class SpawnpointCommand extends VanillaCommand{
|
class SpawnpointCommand extends VanillaCommand{
|
||||||
@ -43,7 +43,10 @@ class SpawnpointCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::pocketmine_command_spawnpoint_description(),
|
KnownTranslationFactory::pocketmine_command_spawnpoint_description(),
|
||||||
KnownTranslationFactory::commands_spawnpoint_usage()
|
KnownTranslationFactory::commands_spawnpoint_usage()
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_SPAWNPOINT);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||||
@ -51,23 +54,9 @@ class SpawnpointCommand extends VanillaCommand{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$target = null;
|
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF, DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER);
|
||||||
|
if($target === null){
|
||||||
if(count($args) === 0){
|
return true;
|
||||||
if($sender instanceof Player){
|
|
||||||
$target = $sender;
|
|
||||||
}else{
|
|
||||||
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
$target = $sender->getServer()->getPlayerByPrefix($args[0]);
|
|
||||||
if($target === null){
|
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count($args) === 4){
|
if(count($args) === 4){
|
||||||
@ -81,19 +70,13 @@ class SpawnpointCommand extends VanillaCommand{
|
|||||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($x, 2), (string) round($y, 2), (string) round($z, 2)));
|
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($x, 2), (string) round($y, 2), (string) round($z, 2)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}elseif(count($args) <= 1){
|
}elseif(count($args) <= 1 && $sender instanceof Player){
|
||||||
if($sender instanceof Player){
|
$cpos = $sender->getPosition();
|
||||||
$cpos = $sender->getPosition();
|
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
|
||||||
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
|
$target->setSpawn($pos);
|
||||||
$target->setSpawn($pos);
|
|
||||||
|
|
||||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
|
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
|
||||||
return true;
|
return true;
|
||||||
}else{
|
|
||||||
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
|
@ -35,6 +35,7 @@ use pocketmine\utils\TextFormat;
|
|||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use function array_shift;
|
use function array_shift;
|
||||||
use function count;
|
use function count;
|
||||||
|
use function implode;
|
||||||
use function round;
|
use function round;
|
||||||
|
|
||||||
class TeleportCommand extends VanillaCommand{
|
class TeleportCommand extends VanillaCommand{
|
||||||
@ -46,7 +47,10 @@ class TeleportCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::commands_tp_usage(),
|
KnownTranslationFactory::commands_tp_usage(),
|
||||||
["teleport"]
|
["teleport"]
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_TELEPORT);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_TELEPORT_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_TELEPORT_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findPlayer(CommandSender $sender, string $playerName) : ?Player{
|
private function findPlayer(CommandSender $sender, string $playerName) : ?Player{
|
||||||
@ -67,31 +71,25 @@ class TeleportCommand extends VanillaCommand{
|
|||||||
case 1: // /tp targetPlayer
|
case 1: // /tp targetPlayer
|
||||||
case 3: // /tp x y z
|
case 3: // /tp x y z
|
||||||
case 5: // /tp x y z yaw pitch - TODO: 5 args could be target x y z yaw :(
|
case 5: // /tp x y z yaw pitch - TODO: 5 args could be target x y z yaw :(
|
||||||
if(!($sender instanceof Player)){
|
$subjectName = null; //self
|
||||||
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$subject = $sender;
|
|
||||||
$targetArgs = $args;
|
|
||||||
break;
|
break;
|
||||||
case 2: // /tp player1 player2
|
case 2: // /tp player1 player2
|
||||||
case 4: // /tp player1 x y z - TODO: 4 args could be x y z yaw :(
|
case 4: // /tp player1 x y z - TODO: 4 args could be x y z yaw :(
|
||||||
case 6: // /tp player1 x y z yaw pitch
|
case 6: // /tp player1 x y z yaw pitch
|
||||||
$subject = $this->findPlayer($sender, $args[0]);
|
$subjectName = array_shift($args);
|
||||||
if($subject === null){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$targetArgs = $args;
|
|
||||||
array_shift($targetArgs);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(count($targetArgs)){
|
$subject = $this->fetchPermittedPlayerTarget($sender, $subjectName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
|
||||||
|
if($subject === null){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(count($args)){
|
||||||
case 1:
|
case 1:
|
||||||
$targetPlayer = $this->findPlayer($sender, $targetArgs[0]);
|
$targetPlayer = $this->findPlayer($sender, $args[0]);
|
||||||
if($targetPlayer === null){
|
if($targetPlayer === null){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -103,17 +101,17 @@ class TeleportCommand extends VanillaCommand{
|
|||||||
case 3:
|
case 3:
|
||||||
case 5:
|
case 5:
|
||||||
$base = $subject->getLocation();
|
$base = $subject->getLocation();
|
||||||
if(count($targetArgs) === 5){
|
if(count($args) === 5){
|
||||||
$yaw = (float) $targetArgs[3];
|
$yaw = (float) $args[3];
|
||||||
$pitch = (float) $targetArgs[4];
|
$pitch = (float) $args[4];
|
||||||
}else{
|
}else{
|
||||||
$yaw = $base->yaw;
|
$yaw = $base->yaw;
|
||||||
$pitch = $base->pitch;
|
$pitch = $base->pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
$x = $this->getRelativeDouble($base->x, $sender, $targetArgs[0]);
|
$x = $this->getRelativeDouble($base->x, $sender, $args[0]);
|
||||||
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], World::Y_MIN, World::Y_MAX);
|
$y = $this->getRelativeDouble($base->y, $sender, $args[1], World::Y_MIN, World::Y_MAX);
|
||||||
$z = $this->getRelativeDouble($base->z, $sender, $targetArgs[2]);
|
$z = $this->getRelativeDouble($base->z, $sender, $args[2]);
|
||||||
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
|
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
|
||||||
|
|
||||||
$subject->teleport($targetLocation);
|
$subject->teleport($targetLocation);
|
||||||
|
@ -27,7 +27,6 @@ use pocketmine\command\CommandSender;
|
|||||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\utils\TextFormat;
|
|
||||||
use function array_slice;
|
use function array_slice;
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
@ -40,7 +39,10 @@ class TitleCommand extends VanillaCommand{
|
|||||||
KnownTranslationFactory::pocketmine_command_title_description(),
|
KnownTranslationFactory::pocketmine_command_title_description(),
|
||||||
KnownTranslationFactory::commands_title_usage()
|
KnownTranslationFactory::commands_title_usage()
|
||||||
);
|
);
|
||||||
$this->setPermission(DefaultPermissionNames::COMMAND_TITLE);
|
$this->setPermission(implode(";", [
|
||||||
|
DefaultPermissionNames::COMMAND_TITLE_SELF,
|
||||||
|
DefaultPermissionNames::COMMAND_TITLE_OTHER
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||||
@ -52,9 +54,8 @@ class TitleCommand extends VanillaCommand{
|
|||||||
throw new InvalidCommandSyntaxException();
|
throw new InvalidCommandSyntaxException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_TITLE_SELF, DefaultPermissionNames::COMMAND_TITLE_OTHER);
|
||||||
if($player === null){
|
if($player === null){
|
||||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ use pocketmine\command\Command;
|
|||||||
use pocketmine\command\CommandSender;
|
use pocketmine\command\CommandSender;
|
||||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
use function is_numeric;
|
use function is_numeric;
|
||||||
use function substr;
|
use function substr;
|
||||||
@ -35,6 +36,28 @@ abstract class VanillaCommand extends Command{
|
|||||||
public const MAX_COORD = 30000000;
|
public const MAX_COORD = 30000000;
|
||||||
public const MIN_COORD = -30000000;
|
public const MIN_COORD = -30000000;
|
||||||
|
|
||||||
|
protected function fetchPermittedPlayerTarget(CommandSender $sender, ?string $target, string $selfPermission, string $otherPermission) : ?Player{
|
||||||
|
if($target !== null){
|
||||||
|
$player = $sender->getServer()->getPlayerByPrefix($target);
|
||||||
|
}elseif($sender instanceof Player){
|
||||||
|
$player = $sender;
|
||||||
|
}else{
|
||||||
|
throw new InvalidCommandSyntaxException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($player === null){
|
||||||
|
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(
|
||||||
|
($player === $sender && $this->testPermission($sender, $selfPermission)) ||
|
||||||
|
($player !== $sender && $this->testPermission($sender, $otherPermission))
|
||||||
|
){
|
||||||
|
return $player;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getInteger(CommandSender $sender, string $value, int $min = self::MIN_COORD, int $max = self::MAX_COORD) : int{
|
protected function getInteger(CommandSender $sender, string $value, int $min = self::MIN_COORD, int $max = self::MAX_COORD) : int{
|
||||||
$i = (int) $value;
|
$i = (int) $value;
|
||||||
|
|
||||||
|
@ -34,9 +34,15 @@ use function usort;
|
|||||||
class CraftingManager{
|
class CraftingManager{
|
||||||
use DestructorCallbackTrait;
|
use DestructorCallbackTrait;
|
||||||
|
|
||||||
/** @var ShapedRecipe[][] */
|
/**
|
||||||
|
* @var ShapedRecipe[][]
|
||||||
|
* @phpstan-var array<string, list<ShapedRecipe>>
|
||||||
|
*/
|
||||||
protected $shapedRecipes = [];
|
protected $shapedRecipes = [];
|
||||||
/** @var ShapelessRecipe[][] */
|
/**
|
||||||
|
* @var ShapelessRecipe[][]
|
||||||
|
* @phpstan-var array<string, list<ShapelessRecipe>>
|
||||||
|
*/
|
||||||
protected $shapelessRecipes = [];
|
protected $shapelessRecipes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,6 +139,7 @@ class CraftingManager{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ShapelessRecipe[][]
|
* @return ShapelessRecipe[][]
|
||||||
|
* @phpstan-return array<string, list<ShapelessRecipe>>
|
||||||
*/
|
*/
|
||||||
public function getShapelessRecipes() : array{
|
public function getShapelessRecipes() : array{
|
||||||
return $this->shapelessRecipes;
|
return $this->shapelessRecipes;
|
||||||
@ -140,6 +147,7 @@ class CraftingManager{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ShapedRecipe[][]
|
* @return ShapedRecipe[][]
|
||||||
|
* @phpstan-return array<string, list<ShapedRecipe>>
|
||||||
*/
|
*/
|
||||||
public function getShapedRecipes() : array{
|
public function getShapedRecipes() : array{
|
||||||
return $this->shapedRecipes;
|
return $this->shapedRecipes;
|
||||||
|
@ -26,9 +26,8 @@ namespace pocketmine\crafting;
|
|||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\ItemFactory;
|
use pocketmine\item\ItemFactory;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Filesystem;
|
||||||
use function array_map;
|
use function array_map;
|
||||||
use function file_get_contents;
|
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function json_decode;
|
use function json_decode;
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ final class CraftingManagerFromDataHelper{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function make(string $filePath) : CraftingManager{
|
public static function make(string $filePath) : CraftingManager{
|
||||||
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true);
|
$recipes = json_decode(Filesystem::fileGetContents($filePath), true);
|
||||||
if(!is_array($recipes)){
|
if(!is_array($recipes)){
|
||||||
throw new AssumptionFailedError("recipes.json root should contain a map of recipe types");
|
throw new AssumptionFailedError("recipes.json root should contain a map of recipe types");
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ use pocketmine\utils\Utils;
|
|||||||
use function array_values;
|
use function array_values;
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
|
use function str_contains;
|
||||||
use function strlen;
|
use function strlen;
|
||||||
use function strpos;
|
|
||||||
|
|
||||||
class ShapedRecipe implements CraftingRecipe{
|
class ShapedRecipe implements CraftingRecipe{
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
@ -86,7 +86,7 @@ class ShapedRecipe implements CraftingRecipe{
|
|||||||
$this->shape = $shape;
|
$this->shape = $shape;
|
||||||
|
|
||||||
foreach($ingredients as $char => $i){
|
foreach($ingredients as $char => $i){
|
||||||
if(strpos(implode($this->shape), $char) === false){
|
if(!str_contains(implode($this->shape), $char)){
|
||||||
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
|
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ use function phpversion;
|
|||||||
use function preg_replace;
|
use function preg_replace;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
use function str_split;
|
use function str_split;
|
||||||
|
use function str_starts_with;
|
||||||
use function strpos;
|
use function strpos;
|
||||||
use function substr;
|
use function substr;
|
||||||
use function zend_version;
|
use function zend_version;
|
||||||
@ -237,7 +238,7 @@ class CrashDump{
|
|||||||
|
|
||||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||||
$frameCleanPath = Filesystem::cleanPath($filePath);
|
$frameCleanPath = Filesystem::cleanPath($filePath);
|
||||||
if(strpos($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX) !== 0){
|
if(!str_starts_with($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX)){
|
||||||
if($crashFrame){
|
if($crashFrame){
|
||||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
|
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
|
||||||
}else{
|
}else{
|
||||||
@ -250,7 +251,7 @@ class CrashDump{
|
|||||||
$file->setAccessible(true);
|
$file->setAccessible(true);
|
||||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||||
$filePath = Filesystem::cleanPath($file->getValue($plugin));
|
$filePath = Filesystem::cleanPath($file->getValue($plugin));
|
||||||
if(strpos($frameCleanPath, $filePath) === 0){
|
if(str_starts_with($frameCleanPath, $filePath)){
|
||||||
$this->data->plugin = $plugin->getName();
|
$this->data->plugin = $plugin->getName();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
50
src/data/bedrock/BedrockDataFiles.php
Normal file
50
src/data/bedrock/BedrockDataFiles.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\data\bedrock;
|
||||||
|
|
||||||
|
use const pocketmine\BEDROCK_DATA_PATH;
|
||||||
|
|
||||||
|
final class BedrockDataFiles{
|
||||||
|
private function __construct(){
|
||||||
|
//NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
public const BANNER_PATTERNS_JSON = BEDROCK_DATA_PATH . '/banner_patterns.json';
|
||||||
|
public const BIOME_DEFINITIONS_NBT = BEDROCK_DATA_PATH . '/biome_definitions.nbt';
|
||||||
|
public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt';
|
||||||
|
public const BIOME_ID_MAP_JSON = BEDROCK_DATA_PATH . '/biome_id_map.json';
|
||||||
|
public const BLOCK_ID_TO_ITEM_ID_MAP_JSON = BEDROCK_DATA_PATH . '/block_id_to_item_id_map.json';
|
||||||
|
public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json';
|
||||||
|
public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt';
|
||||||
|
public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json';
|
||||||
|
public const CREATIVEITEMS_JSON = BEDROCK_DATA_PATH . '/creativeitems.json';
|
||||||
|
public const ENTITY_ID_MAP_JSON = BEDROCK_DATA_PATH . '/entity_id_map.json';
|
||||||
|
public const ENTITY_IDENTIFIERS_NBT = BEDROCK_DATA_PATH . '/entity_identifiers.nbt';
|
||||||
|
public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json';
|
||||||
|
public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json';
|
||||||
|
public const PARTICLE_ID_MAP_JSON = BEDROCK_DATA_PATH . '/particle_id_map.json';
|
||||||
|
public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin';
|
||||||
|
public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json';
|
||||||
|
public const REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json';
|
||||||
|
}
|
@ -73,6 +73,8 @@ final class EnchantmentIdMap{
|
|||||||
$this->register(EnchantmentIds::MENDING, VanillaEnchantments::MENDING());
|
$this->register(EnchantmentIds::MENDING, VanillaEnchantments::MENDING());
|
||||||
|
|
||||||
$this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING());
|
$this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING());
|
||||||
|
|
||||||
|
$this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(int $mcpeId, Enchantment $enchantment) : void{
|
public function register(int $mcpeId, Enchantment $enchantment) : void{
|
||||||
|
@ -66,4 +66,5 @@ final class EnchantmentIds{
|
|||||||
public const PIERCING = 34;
|
public const PIERCING = 34;
|
||||||
public const QUICK_CHARGE = 35;
|
public const QUICK_CHARGE = 35;
|
||||||
public const SOUL_SPEED = 36;
|
public const SOUL_SPEED = 36;
|
||||||
|
public const SWIFT_SNEAK = 37;
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,11 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\data\bedrock;
|
namespace pocketmine\data\bedrock;
|
||||||
|
|
||||||
use pocketmine\utils\SingletonTrait;
|
use pocketmine\utils\SingletonTrait;
|
||||||
use Symfony\Component\Filesystem\Path;
|
|
||||||
|
|
||||||
final class LegacyBiomeIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
final class LegacyBiomeIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
||||||
use SingletonTrait;
|
use SingletonTrait;
|
||||||
|
|
||||||
public function __construct(){
|
public function __construct(){
|
||||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'biome_id_map.json'));
|
parent::__construct(BedrockDataFiles::BIOME_ID_MAP_JSON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,6 @@ final class LegacyBlockIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
|||||||
use SingletonTrait;
|
use SingletonTrait;
|
||||||
|
|
||||||
public function __construct(){
|
public function __construct(){
|
||||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'block_id_map.json'));
|
parent::__construct(Path::join(\pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH, 'block_legacy_id_map.json'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,11 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\data\bedrock;
|
namespace pocketmine\data\bedrock;
|
||||||
|
|
||||||
use pocketmine\utils\SingletonTrait;
|
use pocketmine\utils\SingletonTrait;
|
||||||
use Symfony\Component\Filesystem\Path;
|
|
||||||
|
|
||||||
final class LegacyEntityIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
final class LegacyEntityIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
||||||
use SingletonTrait;
|
use SingletonTrait;
|
||||||
|
|
||||||
public function __construct(){
|
public function __construct(){
|
||||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'entity_id_map.json'));
|
parent::__construct(BedrockDataFiles::ENTITY_ID_MAP_JSON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,6 @@ final class LegacyItemIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
|||||||
use SingletonTrait;
|
use SingletonTrait;
|
||||||
|
|
||||||
public function __construct(){
|
public function __construct(){
|
||||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'item_id_map.json'));
|
parent::__construct(Path::join(\pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, 'item_legacy_id_map.json'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,7 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\data\bedrock;
|
namespace pocketmine\data\bedrock;
|
||||||
|
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Filesystem;
|
||||||
use function file_get_contents;
|
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function is_int;
|
use function is_int;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
@ -45,7 +44,7 @@ abstract class LegacyToStringBidirectionalIdMap{
|
|||||||
private array $stringToLegacy = [];
|
private array $stringToLegacy = [];
|
||||||
|
|
||||||
public function __construct(string $file){
|
public function __construct(string $file){
|
||||||
$stringToLegacyId = json_decode(Utils::assumeNotFalse(file_get_contents($file), "Missing required resource file"), true);
|
$stringToLegacyId = json_decode(Filesystem::fileGetContents($file), true);
|
||||||
if(!is_array($stringToLegacyId)){
|
if(!is_array($stringToLegacyId)){
|
||||||
throw new AssumptionFailedError("Invalid format of ID map");
|
throw new AssumptionFailedError("Invalid format of ID map");
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ use pocketmine\nbt\tag\DoubleTag;
|
|||||||
use pocketmine\nbt\tag\FloatTag;
|
use pocketmine\nbt\tag\FloatTag;
|
||||||
use pocketmine\nbt\tag\ListTag;
|
use pocketmine\nbt\tag\ListTag;
|
||||||
use pocketmine\nbt\tag\StringTag;
|
use pocketmine\nbt\tag\StringTag;
|
||||||
|
use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||||
use pocketmine\network\mcpe\protocol\AddActorPacket;
|
use pocketmine\network\mcpe\protocol\AddActorPacket;
|
||||||
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
|
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
|
||||||
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
|
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
|
||||||
@ -81,6 +83,15 @@ abstract class Entity{
|
|||||||
public const MOTION_THRESHOLD = 0.00001;
|
public const MOTION_THRESHOLD = 0.00001;
|
||||||
protected const STEP_CLIP_MULTIPLIER = 0.4;
|
protected const STEP_CLIP_MULTIPLIER = 0.4;
|
||||||
|
|
||||||
|
private const TAG_FIRE = "Fire"; //TAG_Short
|
||||||
|
private const TAG_ON_GROUND = "OnGround"; //TAG_Byte
|
||||||
|
private const TAG_FALL_DISTANCE = "FallDistance"; //TAG_Float
|
||||||
|
private const TAG_CUSTOM_NAME = "CustomName"; //TAG_String
|
||||||
|
private const TAG_CUSTOM_NAME_VISIBLE = "CustomNameVisible"; //TAG_Byte
|
||||||
|
public const TAG_POS = "Pos"; //TAG_List<TAG_Double>|TAG_List<TAG_Float>
|
||||||
|
public const TAG_MOTION = "Motion"; //TAG_List<TAG_Double>|TAG_List<TAG_Float>
|
||||||
|
public const TAG_ROTATION = "Rotation"; //TAG_List<TAG_Float>
|
||||||
|
|
||||||
private static int $entityCount = 1;
|
private static int $entityCount = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,7 +244,7 @@ abstract class Entity{
|
|||||||
$this->recalculateBoundingBox();
|
$this->recalculateBoundingBox();
|
||||||
|
|
||||||
if($nbt !== null){
|
if($nbt !== null){
|
||||||
$this->motion = EntityDataHelper::parseVec3($nbt, "Motion", true);
|
$this->motion = EntityDataHelper::parseVec3($nbt, self::TAG_MOTION, true);
|
||||||
}else{
|
}else{
|
||||||
$this->motion = new Vector3(0, 0, 0);
|
$this->motion = new Vector3(0, 0, 0);
|
||||||
}
|
}
|
||||||
@ -466,17 +477,17 @@ abstract class Entity{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = CompoundTag::create()
|
$nbt = CompoundTag::create()
|
||||||
->setTag("Pos", new ListTag([
|
->setTag(self::TAG_POS, new ListTag([
|
||||||
new DoubleTag($this->location->x),
|
new DoubleTag($this->location->x),
|
||||||
new DoubleTag($this->location->y),
|
new DoubleTag($this->location->y),
|
||||||
new DoubleTag($this->location->z)
|
new DoubleTag($this->location->z)
|
||||||
]))
|
]))
|
||||||
->setTag("Motion", new ListTag([
|
->setTag(self::TAG_MOTION, new ListTag([
|
||||||
new DoubleTag($this->motion->x),
|
new DoubleTag($this->motion->x),
|
||||||
new DoubleTag($this->motion->y),
|
new DoubleTag($this->motion->y),
|
||||||
new DoubleTag($this->motion->z)
|
new DoubleTag($this->motion->z)
|
||||||
]))
|
]))
|
||||||
->setTag("Rotation", new ListTag([
|
->setTag(self::TAG_ROTATION, new ListTag([
|
||||||
new FloatTag($this->location->yaw),
|
new FloatTag($this->location->yaw),
|
||||||
new FloatTag($this->location->pitch)
|
new FloatTag($this->location->pitch)
|
||||||
]));
|
]));
|
||||||
@ -485,33 +496,33 @@ abstract class Entity{
|
|||||||
EntityFactory::getInstance()->injectSaveId(get_class($this), $nbt);
|
EntityFactory::getInstance()->injectSaveId(get_class($this), $nbt);
|
||||||
|
|
||||||
if($this->getNameTag() !== ""){
|
if($this->getNameTag() !== ""){
|
||||||
$nbt->setString("CustomName", $this->getNameTag());
|
$nbt->setString(self::TAG_CUSTOM_NAME, $this->getNameTag());
|
||||||
$nbt->setByte("CustomNameVisible", $this->isNameTagVisible() ? 1 : 0);
|
$nbt->setByte(self::TAG_CUSTOM_NAME_VISIBLE, $this->isNameTagVisible() ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nbt->setFloat("FallDistance", $this->fallDistance);
|
$nbt->setFloat(self::TAG_FALL_DISTANCE, $this->fallDistance);
|
||||||
$nbt->setShort("Fire", $this->fireTicks);
|
$nbt->setShort(self::TAG_FIRE, $this->fireTicks);
|
||||||
$nbt->setByte("OnGround", $this->onGround ? 1 : 0);
|
$nbt->setByte(self::TAG_ON_GROUND, $this->onGround ? 1 : 0);
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initEntity(CompoundTag $nbt) : void{
|
protected function initEntity(CompoundTag $nbt) : void{
|
||||||
$this->fireTicks = $nbt->getShort("Fire", 0);
|
$this->fireTicks = $nbt->getShort(self::TAG_FIRE, 0);
|
||||||
|
|
||||||
$this->onGround = $nbt->getByte("OnGround", 0) !== 0;
|
$this->onGround = $nbt->getByte(self::TAG_ON_GROUND, 0) !== 0;
|
||||||
|
|
||||||
$this->fallDistance = $nbt->getFloat("FallDistance", 0.0);
|
$this->fallDistance = $nbt->getFloat(self::TAG_FALL_DISTANCE, 0.0);
|
||||||
|
|
||||||
if(($customNameTag = $nbt->getTag("CustomName")) instanceof StringTag){
|
if(($customNameTag = $nbt->getTag(self::TAG_CUSTOM_NAME)) instanceof StringTag){
|
||||||
$this->setNameTag($customNameTag->getValue());
|
$this->setNameTag($customNameTag->getValue());
|
||||||
|
|
||||||
if(($customNameVisibleTag = $nbt->getTag("CustomNameVisible")) instanceof StringTag){
|
if(($customNameVisibleTag = $nbt->getTag(self::TAG_CUSTOM_NAME_VISIBLE)) instanceof StringTag){
|
||||||
//Older versions incorrectly saved this as a string (see 890f72dbf23a77f294169b79590770470041adc4)
|
//Older versions incorrectly saved this as a string (see 890f72dbf23a77f294169b79590770470041adc4)
|
||||||
$this->setNameTagVisible($customNameVisibleTag->getValue() !== "");
|
$this->setNameTagVisible($customNameVisibleTag->getValue() !== "");
|
||||||
}else{
|
}else{
|
||||||
$this->setNameTagVisible($nbt->getByte("CustomNameVisible", 1) !== 0);
|
$this->setNameTagVisible($nbt->getByte(self::TAG_CUSTOM_NAME_VISIBLE, 1) !== 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -776,7 +787,7 @@ abstract class Entity{
|
|||||||
$this->spawnTo($player);
|
$this->spawnTo($player);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$this->server->broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
|
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
|
||||||
$this->id,
|
$this->id,
|
||||||
$this->getOffsetPosition($this->location),
|
$this->getOffsetPosition($this->location),
|
||||||
$this->location->pitch,
|
$this->location->pitch,
|
||||||
@ -791,7 +802,16 @@ abstract class Entity{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function broadcastMotion() : void{
|
protected function broadcastMotion() : void{
|
||||||
$this->server->broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
|
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGravity() : float{
|
||||||
|
return $this->gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGravity(float $gravity) : void{
|
||||||
|
Utils::checkFloatNotInfOrNaN("gravity", $gravity);
|
||||||
|
$this->gravity = $gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasGravity() : bool{
|
public function hasGravity() : bool{
|
||||||
@ -1519,7 +1539,7 @@ abstract class Entity{
|
|||||||
$id = spl_object_id($player);
|
$id = spl_object_id($player);
|
||||||
if(isset($this->hasSpawned[$id])){
|
if(isset($this->hasSpawned[$id])){
|
||||||
if($send){
|
if($send){
|
||||||
$player->getNetworkSession()->onEntityRemoved($this);
|
$player->getNetworkSession()->getEntityEventBroadcaster()->onEntityRemoved([$player->getNetworkSession()], $this);
|
||||||
}
|
}
|
||||||
unset($this->hasSpawned[$id]);
|
unset($this->hasSpawned[$id]);
|
||||||
}
|
}
|
||||||
@ -1530,9 +1550,11 @@ abstract class Entity{
|
|||||||
* player moves, viewers will once again be able to see the entity.
|
* player moves, viewers will once again be able to see the entity.
|
||||||
*/
|
*/
|
||||||
public function despawnFromAll() : void{
|
public function despawnFromAll() : void{
|
||||||
foreach($this->hasSpawned as $player){
|
NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
$this->despawnFrom($player);
|
$this->hasSpawned,
|
||||||
}
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEntityRemoved($recipients, $this)
|
||||||
|
);
|
||||||
|
$this->hasSpawned = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1606,9 +1628,7 @@ abstract class Entity{
|
|||||||
$targets = $targets ?? $this->hasSpawned;
|
$targets = $targets ?? $this->hasSpawned;
|
||||||
$data = $data ?? $this->getAllNetworkData();
|
$data = $data ?? $this->getAllNetworkData();
|
||||||
|
|
||||||
foreach($targets as $p){
|
NetworkBroadcastUtils::broadcastEntityEvent($targets, fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->syncActorData($recipients, $this, $data));
|
||||||
$p->getNetworkSession()->syncActorData($this, $data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1662,7 +1682,7 @@ abstract class Entity{
|
|||||||
* @param Player[]|null $targets
|
* @param Player[]|null $targets
|
||||||
*/
|
*/
|
||||||
public function broadcastAnimation(Animation $animation, ?array $targets = null) : void{
|
public function broadcastAnimation(Animation $animation, ?array $targets = null) : void{
|
||||||
$this->server->broadcastPackets($targets ?? $this->getViewers(), $animation->encode());
|
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $animation->encode());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1671,7 +1691,7 @@ abstract class Entity{
|
|||||||
*/
|
*/
|
||||||
public function broadcastSound(Sound $sound, ?array $targets = null) : void{
|
public function broadcastSound(Sound $sound, ?array $targets = null) : void{
|
||||||
if(!$this->silent){
|
if(!$this->silent){
|
||||||
$this->server->broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
|
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,19 +57,19 @@ final class EntityDataHelper{
|
|||||||
* @throws SavedDataLoadingException
|
* @throws SavedDataLoadingException
|
||||||
*/
|
*/
|
||||||
public static function parseLocation(CompoundTag $nbt, World $world) : Location{
|
public static function parseLocation(CompoundTag $nbt, World $world) : Location{
|
||||||
$pos = self::parseVec3($nbt, "Pos", false);
|
$pos = self::parseVec3($nbt, Entity::TAG_POS, false);
|
||||||
|
|
||||||
$yawPitch = $nbt->getTag("Rotation");
|
$yawPitch = $nbt->getTag(Entity::TAG_ROTATION);
|
||||||
if(!($yawPitch instanceof ListTag) || $yawPitch->getTagType() !== NBT::TAG_Float){
|
if(!($yawPitch instanceof ListTag) || $yawPitch->getTagType() !== NBT::TAG_Float){
|
||||||
throw new SavedDataLoadingException("'Rotation' should be a List<Float>");
|
throw new SavedDataLoadingException("'" . Entity::TAG_ROTATION . "' should be a List<Float>");
|
||||||
}
|
}
|
||||||
/** @var FloatTag[] $values */
|
/** @var FloatTag[] $values */
|
||||||
$values = $yawPitch->getValue();
|
$values = $yawPitch->getValue();
|
||||||
if(count($values) !== 2){
|
if(count($values) !== 2){
|
||||||
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
|
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
|
||||||
}
|
}
|
||||||
self::validateFloat("Rotation", "yaw", $values[0]->getValue());
|
self::validateFloat(Entity::TAG_ROTATION, "yaw", $values[0]->getValue());
|
||||||
self::validateFloat("Rotation", "pitch", $values[1]->getValue());
|
self::validateFloat(Entity::TAG_ROTATION, "pitch", $values[1]->getValue());
|
||||||
|
|
||||||
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
|
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,9 @@ use function reset;
|
|||||||
final class EntityFactory{
|
final class EntityFactory{
|
||||||
use SingletonTrait;
|
use SingletonTrait;
|
||||||
|
|
||||||
|
public const TAG_IDENTIFIER = "identifier"; //TAG_String
|
||||||
|
public const TAG_LEGACY_ID = "id"; //TAG_Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Closure[] save ID => creator function
|
* @var \Closure[] save ID => creator function
|
||||||
* @phpstan-var array<int|string, \Closure(World, CompoundTag) : Entity>
|
* @phpstan-var array<int|string, \Closure(World, CompoundTag) : Entity>
|
||||||
@ -113,9 +116,9 @@ final class EntityFactory{
|
|||||||
}, ['FallingSand', 'minecraft:falling_block'], LegacyIds::FALLING_BLOCK);
|
}, ['FallingSand', 'minecraft:falling_block'], LegacyIds::FALLING_BLOCK);
|
||||||
|
|
||||||
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
|
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
|
||||||
$itemTag = $nbt->getCompoundTag("Item");
|
$itemTag = $nbt->getCompoundTag(ItemEntity::TAG_ITEM);
|
||||||
if($itemTag === null){
|
if($itemTag === null){
|
||||||
throw new SavedDataLoadingException("Expected \"Item\" NBT tag not found");
|
throw new SavedDataLoadingException("Expected \"" . ItemEntity::TAG_ITEM . "\" NBT tag not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
$item = Item::nbtDeserialize($itemTag);
|
$item = Item::nbtDeserialize($itemTag);
|
||||||
@ -126,14 +129,14 @@ final class EntityFactory{
|
|||||||
}, ['Item', 'minecraft:item'], LegacyIds::ITEM);
|
}, ['Item', 'minecraft:item'], LegacyIds::ITEM);
|
||||||
|
|
||||||
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
|
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
|
||||||
$motive = PaintingMotive::getMotiveByName($nbt->getString("Motive"));
|
$motive = PaintingMotive::getMotiveByName($nbt->getString(Painting::TAG_MOTIVE));
|
||||||
if($motive === null){
|
if($motive === null){
|
||||||
throw new SavedDataLoadingException("Unknown painting motive");
|
throw new SavedDataLoadingException("Unknown painting motive");
|
||||||
}
|
}
|
||||||
$blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ"));
|
$blockIn = new Vector3($nbt->getInt(Painting::TAG_TILE_X), $nbt->getInt(Painting::TAG_TILE_Y), $nbt->getInt(Painting::TAG_TILE_Z));
|
||||||
if(($directionTag = $nbt->getTag("Direction")) instanceof ByteTag){
|
if(($directionTag = $nbt->getTag(Painting::TAG_DIRECTION_BE)) instanceof ByteTag){
|
||||||
$facing = Painting::DATA_TO_FACING[$directionTag->getValue()] ?? Facing::NORTH;
|
$facing = Painting::DATA_TO_FACING[$directionTag->getValue()] ?? Facing::NORTH;
|
||||||
}elseif(($facingTag = $nbt->getTag("Facing")) instanceof ByteTag){
|
}elseif(($facingTag = $nbt->getTag(Painting::TAG_FACING_JE)) instanceof ByteTag){
|
||||||
$facing = Painting::DATA_TO_FACING[$facingTag->getValue()] ?? Facing::NORTH;
|
$facing = Painting::DATA_TO_FACING[$facingTag->getValue()] ?? Facing::NORTH;
|
||||||
}else{
|
}else{
|
||||||
throw new SavedDataLoadingException("Missing facing info");
|
throw new SavedDataLoadingException("Missing facing info");
|
||||||
@ -151,7 +154,7 @@ final class EntityFactory{
|
|||||||
}, ['Snowball', 'minecraft:snowball'], LegacyIds::SNOWBALL);
|
}, ['Snowball', 'minecraft:snowball'], LegacyIds::SNOWBALL);
|
||||||
|
|
||||||
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
|
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
|
||||||
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER));
|
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort(SplashPotion::TAG_POTION_ID, PotionTypeIds::WATER));
|
||||||
if($potionType === null){
|
if($potionType === null){
|
||||||
throw new SavedDataLoadingException("No such potion type");
|
throw new SavedDataLoadingException("No such potion type");
|
||||||
}
|
}
|
||||||
@ -217,7 +220,7 @@ final class EntityFactory{
|
|||||||
*/
|
*/
|
||||||
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
|
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
|
||||||
try{
|
try{
|
||||||
$saveId = $nbt->getTag("identifier") ?? $nbt->getTag("id");
|
$saveId = $nbt->getTag(self::TAG_IDENTIFIER) ?? $nbt->getTag(self::TAG_LEGACY_ID);
|
||||||
$func = null;
|
$func = null;
|
||||||
if($saveId instanceof StringTag){
|
if($saveId instanceof StringTag){
|
||||||
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
|
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
|
||||||
@ -238,7 +241,7 @@ final class EntityFactory{
|
|||||||
|
|
||||||
public function injectSaveId(string $class, CompoundTag $saveData) : void{
|
public function injectSaveId(string $class, CompoundTag $saveData) : void{
|
||||||
if(isset($this->saveNames[$class])){
|
if(isset($this->saveNames[$class])){
|
||||||
$saveData->setTag("identifier", new StringTag($this->saveNames[$class]));
|
$saveData->setTag(self::TAG_IDENTIFIER, new StringTag($this->saveNames[$class]));
|
||||||
}else{
|
}else{
|
||||||
throw new \InvalidArgumentException("Entity $class is not registered");
|
throw new \InvalidArgumentException("Entity $class is not registered");
|
||||||
}
|
}
|
||||||
|
@ -47,9 +47,13 @@ use pocketmine\nbt\tag\ListTag;
|
|||||||
use pocketmine\nbt\tag\StringTag;
|
use pocketmine\nbt\tag\StringTag;
|
||||||
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
|
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
|
||||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||||
|
use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||||
|
use pocketmine\network\mcpe\protocol\types\AbilitiesData;
|
||||||
|
use pocketmine\network\mcpe\protocol\types\AbilitiesLayer;
|
||||||
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
|
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
|
||||||
use pocketmine\network\mcpe\protocol\types\DeviceOS;
|
use pocketmine\network\mcpe\protocol\types\DeviceOS;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||||
@ -60,7 +64,6 @@ use pocketmine\network\mcpe\protocol\types\GameMode;
|
|||||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
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\UpdateAbilitiesPacket;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\Limits;
|
use pocketmine\utils\Limits;
|
||||||
@ -77,6 +80,26 @@ use function random_int;
|
|||||||
|
|
||||||
class Human extends Living implements ProjectileSource, InventoryHolder{
|
class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||||
|
|
||||||
|
private const TAG_INVENTORY = "Inventory"; //TAG_List<TAG_Compound>
|
||||||
|
private const TAG_OFF_HAND_ITEM = "OffHandItem"; //TAG_Compound
|
||||||
|
private const TAG_ENDER_CHEST_INVENTORY = "EnderChestInventory"; //TAG_List<TAG_Compound>
|
||||||
|
private const TAG_SELECTED_INVENTORY_SLOT = "SelectedInventorySlot"; //TAG_Int
|
||||||
|
private const TAG_FOOD_LEVEL = "foodLevel"; //TAG_Int
|
||||||
|
private const TAG_FOOD_EXHAUSTION_LEVEL = "foodExhaustionLevel"; //TAG_Float
|
||||||
|
private const TAG_FOOD_SATURATION_LEVEL = "foodSaturationLevel"; //TAG_Float
|
||||||
|
private const TAG_FOOD_TICK_TIMER = "foodTickTimer"; //TAG_Int
|
||||||
|
private const TAG_XP_LEVEL = "XpLevel"; //TAG_Int
|
||||||
|
private const TAG_XP_PROGRESS = "XpP"; //TAG_Float
|
||||||
|
private const TAG_LIFETIME_XP_TOTAL = "XpTotal"; //TAG_Int
|
||||||
|
private const TAG_XP_SEED = "XpSeed"; //TAG_Int
|
||||||
|
private const TAG_NAME_TAG = "NameTag"; //TAG_String
|
||||||
|
private const TAG_SKIN = "Skin"; //TAG_Compound
|
||||||
|
private const TAG_SKIN_NAME = "Name"; //TAG_String
|
||||||
|
private const TAG_SKIN_DATA = "Data"; //TAG_ByteArray
|
||||||
|
private const TAG_SKIN_CAPE_DATA = "CapeData"; //TAG_ByteArray
|
||||||
|
private const TAG_SKIN_GEOMETRY_NAME = "GeometryName"; //TAG_String
|
||||||
|
private const TAG_SKIN_GEOMETRY_DATA = "GeometryData"; //TAG_ByteArray
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::PLAYER; }
|
public static function getNetworkTypeId() : string{ return EntityIds::PLAYER; }
|
||||||
|
|
||||||
/** @var PlayerInventory */
|
/** @var PlayerInventory */
|
||||||
@ -114,16 +137,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
* @throws SavedDataLoadingException
|
* @throws SavedDataLoadingException
|
||||||
*/
|
*/
|
||||||
public static function parseSkinNBT(CompoundTag $nbt) : Skin{
|
public static function parseSkinNBT(CompoundTag $nbt) : Skin{
|
||||||
$skinTag = $nbt->getCompoundTag("Skin");
|
$skinTag = $nbt->getCompoundTag(self::TAG_SKIN);
|
||||||
if($skinTag === null){
|
if($skinTag === null){
|
||||||
throw new SavedDataLoadingException("Missing skin data");
|
throw new SavedDataLoadingException("Missing skin data");
|
||||||
}
|
}
|
||||||
return new Skin( //this throws if the skin is invalid
|
return new Skin( //this throws if the skin is invalid
|
||||||
$skinTag->getString("Name"),
|
$skinTag->getString(self::TAG_SKIN_NAME),
|
||||||
($skinDataTag = $skinTag->getTag("Data")) instanceof StringTag ? $skinDataTag->getValue() : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM)
|
($skinDataTag = $skinTag->getTag(self::TAG_SKIN_DATA)) instanceof StringTag ? $skinDataTag->getValue() : $skinTag->getByteArray(self::TAG_SKIN_DATA), //old data (this used to be saved as a StringTag in older versions of PM)
|
||||||
$skinTag->getByteArray("CapeData", ""),
|
$skinTag->getByteArray(self::TAG_SKIN_CAPE_DATA, ""),
|
||||||
$skinTag->getString("GeometryName", ""),
|
$skinTag->getString(self::TAG_SKIN_GEOMETRY_NAME, ""),
|
||||||
$skinTag->getByteArray("GeometryData", "")
|
$skinTag->getByteArray(self::TAG_SKIN_GEOMETRY_DATA, "")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +176,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
* @param Player[]|null $targets
|
* @param Player[]|null $targets
|
||||||
*/
|
*/
|
||||||
public function sendSkin(?array $targets = null) : void{
|
public function sendSkin(?array $targets = null) : void{
|
||||||
$this->server->broadcastPackets($targets ?? $this->hasSpawned, [
|
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->hasSpawned, [
|
||||||
PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin))
|
PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -168,9 +191,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function emote(string $emoteId) : void{
|
public function emote(string $emoteId) : void{
|
||||||
foreach($this->getViewers() as $player){
|
NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
$player->getNetworkSession()->onEmote($this, $emoteId);
|
$this->getViewers(),
|
||||||
}
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEmote($recipients, $this, $emoteId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHungerManager() : HungerManager{
|
public function getHungerManager() : HungerManager{
|
||||||
@ -221,7 +245,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
|
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
|
||||||
*/
|
*/
|
||||||
protected function initHumanData(CompoundTag $nbt) : void{
|
protected function initHumanData(CompoundTag $nbt) : void{
|
||||||
if(($nameTagTag = $nbt->getTag("NameTag")) instanceof StringTag){
|
if(($nameTagTag = $nbt->getTag(self::TAG_NAME_TAG)) instanceof StringTag){
|
||||||
$this->setNameTag($nameTagTag->getValue());
|
$this->setNameTag($nameTagTag->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,11 +273,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->xpManager = new ExperienceManager($this);
|
$this->xpManager = new ExperienceManager($this);
|
||||||
|
|
||||||
$this->inventory = new PlayerInventory($this);
|
$this->inventory = new PlayerInventory($this);
|
||||||
$syncHeldItem = function() : void{
|
$syncHeldItem = fn() => NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
foreach($this->getViewers() as $viewer){
|
$this->getViewers(),
|
||||||
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this)
|
||||||
}
|
);
|
||||||
};
|
|
||||||
$this->inventory->getListeners()->add(new CallbackInventoryListener(
|
$this->inventory->getListeners()->add(new CallbackInventoryListener(
|
||||||
function(Inventory $unused, int $slot, Item $unused2) use ($syncHeldItem) : void{
|
function(Inventory $unused, int $slot, Item $unused2) use ($syncHeldItem) : void{
|
||||||
if($slot === $this->inventory->getHeldItemIndex()){
|
if($slot === $this->inventory->getHeldItemIndex()){
|
||||||
@ -270,14 +293,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->enderInventory = new PlayerEnderInventory($this);
|
$this->enderInventory = new PlayerEnderInventory($this);
|
||||||
$this->initHumanData($nbt);
|
$this->initHumanData($nbt);
|
||||||
|
|
||||||
$inventoryTag = $nbt->getListTag("Inventory");
|
$inventoryTag = $nbt->getListTag(self::TAG_INVENTORY);
|
||||||
if($inventoryTag !== null){
|
if($inventoryTag !== null){
|
||||||
$inventoryItems = [];
|
$inventoryItems = [];
|
||||||
$armorInventoryItems = [];
|
$armorInventoryItems = [];
|
||||||
|
|
||||||
/** @var CompoundTag $item */
|
/** @var CompoundTag $item */
|
||||||
foreach($inventoryTag as $i => $item){
|
foreach($inventoryTag as $i => $item){
|
||||||
$slot = $item->getByte("Slot");
|
$slot = $item->getByte(Item::TAG_SLOT);
|
||||||
if($slot >= 0 && $slot < 9){ //Hotbar
|
if($slot >= 0 && $slot < 9){ //Hotbar
|
||||||
//Old hotbar saving stuff, ignore it
|
//Old hotbar saving stuff, ignore it
|
||||||
}elseif($slot >= 100 && $slot < 104){ //Armor
|
}elseif($slot >= 100 && $slot < 104){ //Armor
|
||||||
@ -290,45 +313,43 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
self::populateInventoryFromListTag($this->inventory, $inventoryItems);
|
self::populateInventoryFromListTag($this->inventory, $inventoryItems);
|
||||||
self::populateInventoryFromListTag($this->armorInventory, $armorInventoryItems);
|
self::populateInventoryFromListTag($this->armorInventory, $armorInventoryItems);
|
||||||
}
|
}
|
||||||
$offHand = $nbt->getCompoundTag("OffHandItem");
|
$offHand = $nbt->getCompoundTag(self::TAG_OFF_HAND_ITEM);
|
||||||
if($offHand !== null){
|
if($offHand !== null){
|
||||||
$this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand));
|
$this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand));
|
||||||
}
|
}
|
||||||
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(function() : void{
|
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
foreach($this->getViewers() as $viewer){
|
$this->getViewers(),
|
||||||
$viewer->getNetworkSession()->onMobOffHandItemChange($this);
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobOffHandItemChange($recipients, $this)
|
||||||
}
|
)));
|
||||||
}));
|
|
||||||
|
|
||||||
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
|
$enderChestInventoryTag = $nbt->getListTag(self::TAG_ENDER_CHEST_INVENTORY);
|
||||||
if($enderChestInventoryTag !== null){
|
if($enderChestInventoryTag !== null){
|
||||||
$enderChestInventoryItems = [];
|
$enderChestInventoryItems = [];
|
||||||
|
|
||||||
/** @var CompoundTag $item */
|
/** @var CompoundTag $item */
|
||||||
foreach($enderChestInventoryTag as $i => $item){
|
foreach($enderChestInventoryTag as $i => $item){
|
||||||
$enderChestInventoryItems[$item->getByte("Slot")] = Item::nbtDeserialize($item);
|
$enderChestInventoryItems[$item->getByte(Item::TAG_SLOT)] = Item::nbtDeserialize($item);
|
||||||
}
|
}
|
||||||
self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems);
|
self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
|
$this->inventory->setHeldItemIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0));
|
||||||
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
|
$this->inventory->getHeldItemIndexChangeListeners()->add(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
foreach($this->getViewers() as $viewer){
|
$this->getViewers(),
|
||||||
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this)
|
||||||
}
|
));
|
||||||
});
|
|
||||||
|
|
||||||
$this->hungerManager->setFood((float) $nbt->getInt("foodLevel", (int) $this->hungerManager->getFood()));
|
$this->hungerManager->setFood((float) $nbt->getInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood()));
|
||||||
$this->hungerManager->setExhaustion($nbt->getFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion()));
|
$this->hungerManager->setExhaustion($nbt->getFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion()));
|
||||||
$this->hungerManager->setSaturation($nbt->getFloat("foodSaturationLevel", $this->hungerManager->getSaturation()));
|
$this->hungerManager->setSaturation($nbt->getFloat(self::TAG_FOOD_SATURATION_LEVEL, $this->hungerManager->getSaturation()));
|
||||||
$this->hungerManager->setFoodTickTimer($nbt->getInt("foodTickTimer", $this->hungerManager->getFoodTickTimer()));
|
$this->hungerManager->setFoodTickTimer($nbt->getInt(self::TAG_FOOD_TICK_TIMER, $this->hungerManager->getFoodTickTimer()));
|
||||||
|
|
||||||
$this->xpManager->setXpAndProgressNoEvent(
|
$this->xpManager->setXpAndProgressNoEvent(
|
||||||
$nbt->getInt("XpLevel", 0),
|
$nbt->getInt(self::TAG_XP_LEVEL, 0),
|
||||||
$nbt->getFloat("XpP", 0.0));
|
$nbt->getFloat(self::TAG_XP_PROGRESS, 0.0));
|
||||||
$this->xpManager->setLifetimeTotalXp($nbt->getInt("XpTotal", 0));
|
$this->xpManager->setLifetimeTotalXp($nbt->getInt(self::TAG_LIFETIME_XP_TOTAL, 0));
|
||||||
|
|
||||||
if(($xpSeedTag = $nbt->getTag("XpSeed")) instanceof IntTag){
|
if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){
|
||||||
$this->xpSeed = $xpSeedTag->getValue();
|
$this->xpSeed = $xpSeedTag->getValue();
|
||||||
}else{
|
}else{
|
||||||
$this->xpSeed = random_int(Limits::INT32_MIN, Limits::INT32_MAX);
|
$this->xpSeed = random_int(Limits::INT32_MIN, Limits::INT32_MAX);
|
||||||
@ -391,24 +412,24 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
|
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
|
||||||
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : [],
|
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : [],
|
||||||
$this->offHandInventory !== null ? array_values($this->offHandInventory->getContents()) : [],
|
$this->offHandInventory !== null ? array_values($this->offHandInventory->getContents()) : [],
|
||||||
), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()); });
|
), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()) && !$item->keepOnDeath(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
|
|
||||||
$nbt->setInt("foodLevel", (int) $this->hungerManager->getFood());
|
$nbt->setInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood());
|
||||||
$nbt->setFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion());
|
$nbt->setFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion());
|
||||||
$nbt->setFloat("foodSaturationLevel", $this->hungerManager->getSaturation());
|
$nbt->setFloat(self::TAG_FOOD_SATURATION_LEVEL, $this->hungerManager->getSaturation());
|
||||||
$nbt->setInt("foodTickTimer", $this->hungerManager->getFoodTickTimer());
|
$nbt->setInt(self::TAG_FOOD_TICK_TIMER, $this->hungerManager->getFoodTickTimer());
|
||||||
|
|
||||||
$nbt->setInt("XpLevel", $this->xpManager->getXpLevel());
|
$nbt->setInt(self::TAG_XP_LEVEL, $this->xpManager->getXpLevel());
|
||||||
$nbt->setFloat("XpP", $this->xpManager->getXpProgress());
|
$nbt->setFloat(self::TAG_XP_PROGRESS, $this->xpManager->getXpProgress());
|
||||||
$nbt->setInt("XpTotal", $this->xpManager->getLifetimeTotalXp());
|
$nbt->setInt(self::TAG_LIFETIME_XP_TOTAL, $this->xpManager->getLifetimeTotalXp());
|
||||||
$nbt->setInt("XpSeed", $this->xpSeed);
|
$nbt->setInt(self::TAG_XP_SEED, $this->xpSeed);
|
||||||
|
|
||||||
$inventoryTag = new ListTag([], NBT::TAG_Compound);
|
$inventoryTag = new ListTag([], NBT::TAG_Compound);
|
||||||
$nbt->setTag("Inventory", $inventoryTag);
|
$nbt->setTag(self::TAG_INVENTORY, $inventoryTag);
|
||||||
if($this->inventory !== null){
|
if($this->inventory !== null){
|
||||||
//Normal inventory
|
//Normal inventory
|
||||||
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
|
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
|
||||||
@ -427,11 +448,11 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nbt->setInt("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
|
$nbt->setInt(self::TAG_SELECTED_INVENTORY_SLOT, $this->inventory->getHeldItemIndex());
|
||||||
}
|
}
|
||||||
$offHandItem = $this->offHandInventory->getItem(0);
|
$offHandItem = $this->offHandInventory->getItem(0);
|
||||||
if(!$offHandItem->isNull()){
|
if(!$offHandItem->isNull()){
|
||||||
$nbt->setTag("OffHandItem", $offHandItem->nbtSerialize());
|
$nbt->setTag(self::TAG_OFF_HAND_ITEM, $offHandItem->nbtSerialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->enderInventory !== null){
|
if($this->enderInventory !== null){
|
||||||
@ -446,16 +467,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nbt->setTag("EnderChestInventory", new ListTag($items, NBT::TAG_Compound));
|
$nbt->setTag(self::TAG_ENDER_CHEST_INVENTORY, new ListTag($items, NBT::TAG_Compound));
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->skin !== null){
|
if($this->skin !== null){
|
||||||
$nbt->setTag("Skin", CompoundTag::create()
|
$nbt->setTag(self::TAG_SKIN, CompoundTag::create()
|
||||||
->setString("Name", $this->skin->getSkinId())
|
->setString(self::TAG_SKIN_NAME, $this->skin->getSkinId())
|
||||||
->setByteArray("Data", $this->skin->getSkinData())
|
->setByteArray(self::TAG_SKIN_DATA, $this->skin->getSkinData())
|
||||||
->setByteArray("CapeData", $this->skin->getCapeData())
|
->setByteArray(self::TAG_SKIN_CAPE_DATA, $this->skin->getCapeData())
|
||||||
->setString("GeometryName", $this->skin->getGeometryName())
|
->setString(self::TAG_SKIN_GEOMETRY_NAME, $this->skin->getGeometryName())
|
||||||
->setByteArray("GeometryData", $this->skin->getGeometryData())
|
->setByteArray(self::TAG_SKIN_GEOMETRY_DATA, $this->skin->getGeometryData())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,11 +490,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function sendSpawnPacket(Player $player) : void{
|
protected function sendSpawnPacket(Player $player) : void{
|
||||||
|
$networkSession = $player->getNetworkSession();
|
||||||
if(!($this instanceof Player)){
|
if(!($this instanceof Player)){
|
||||||
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
|
$networkSession->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
|
$networkSession->sendDataPacket(AddPlayerPacket::create(
|
||||||
$this->getUniqueId(),
|
$this->getUniqueId(),
|
||||||
$this->getName(),
|
$this->getName(),
|
||||||
$this->getId(),
|
$this->getId(),
|
||||||
@ -487,14 +509,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
GameMode::SURVIVAL,
|
GameMode::SURVIVAL,
|
||||||
$this->getAllNetworkData(),
|
$this->getAllNetworkData(),
|
||||||
new PropertySyncData([], []),
|
new PropertySyncData([], []),
|
||||||
UpdateAbilitiesPacket::create(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
|
UpdateAbilitiesPacket::create(new AbilitiesData(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
|
||||||
new UpdateAbilitiesPacketLayer(
|
new AbilitiesLayer(
|
||||||
UpdateAbilitiesPacketLayer::LAYER_BASE,
|
AbilitiesLayer::LAYER_BASE,
|
||||||
array_fill(0, UpdateAbilitiesPacketLayer::NUMBER_OF_ABILITIES, false),
|
array_fill(0, AbilitiesLayer::NUMBER_OF_ABILITIES, false),
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
)
|
)
|
||||||
]),
|
])),
|
||||||
[], //TODO: entity links
|
[], //TODO: entity links
|
||||||
"", //device ID (we intentionally don't send this - secvuln)
|
"", //device ID (we intentionally don't send this - secvuln)
|
||||||
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)
|
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)
|
||||||
@ -503,11 +525,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately
|
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately
|
||||||
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
|
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
|
||||||
|
|
||||||
$player->getNetworkSession()->onMobArmorChange($this);
|
$entityEventBroadcaster = $networkSession->getEntityEventBroadcaster();
|
||||||
$player->getNetworkSession()->onMobOffHandItemChange($this);
|
$entityEventBroadcaster->onMobArmorChange([$networkSession], $this);
|
||||||
|
$entityEventBroadcaster->onMobOffHandItemChange([$networkSession], $this);
|
||||||
|
|
||||||
if(!($this instanceof Player)){
|
if(!($this instanceof Player)){
|
||||||
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
|
$networkSession->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ use pocketmine\nbt\tag\CompoundTag;
|
|||||||
use pocketmine\nbt\tag\FloatTag;
|
use pocketmine\nbt\tag\FloatTag;
|
||||||
use pocketmine\nbt\tag\ListTag;
|
use pocketmine\nbt\tag\ListTag;
|
||||||
use pocketmine\nbt\tag\ShortTag;
|
use pocketmine\nbt\tag\ShortTag;
|
||||||
|
use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||||
@ -77,6 +79,16 @@ use const M_PI;
|
|||||||
abstract class Living extends Entity{
|
abstract class Living extends Entity{
|
||||||
protected const DEFAULT_BREATH_TICKS = 300;
|
protected const DEFAULT_BREATH_TICKS = 300;
|
||||||
|
|
||||||
|
private const TAG_LEGACY_HEALTH = "HealF"; //TAG_Float
|
||||||
|
private const TAG_HEALTH = "Health"; //TAG_Float
|
||||||
|
private const TAG_BREATH_TICKS = "Air"; //TAG_Short
|
||||||
|
private const TAG_ACTIVE_EFFECTS = "ActiveEffects"; //TAG_List<TAG_Compound>
|
||||||
|
private const TAG_EFFECT_ID = "Id"; //TAG_Byte
|
||||||
|
private const TAG_EFFECT_DURATION = "Duration"; //TAG_Int
|
||||||
|
private const TAG_EFFECT_AMPLIFIER = "Amplifier"; //TAG_Byte
|
||||||
|
private const TAG_EFFECT_SHOW_PARTICLES = "ShowParticles"; //TAG_Byte
|
||||||
|
private const TAG_EFFECT_AMBIENT = "Ambient"; //TAG_Byte
|
||||||
|
|
||||||
protected $gravity = 0.08;
|
protected $gravity = 0.08;
|
||||||
protected $drag = 0.02;
|
protected $drag = 0.02;
|
||||||
|
|
||||||
@ -133,19 +145,16 @@ abstract class Living extends Entity{
|
|||||||
|
|
||||||
$this->armorInventory = new ArmorInventory($this);
|
$this->armorInventory = new ArmorInventory($this);
|
||||||
//TODO: load/save armor inventory contents
|
//TODO: load/save armor inventory contents
|
||||||
$this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
|
$this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
function(Inventory $unused) : void{
|
$this->getViewers(),
|
||||||
foreach($this->getViewers() as $viewer){
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this)
|
||||||
$viewer->getNetworkSession()->onMobArmorChange($this);
|
)));
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
$health = $this->getMaxHealth();
|
$health = $this->getMaxHealth();
|
||||||
|
|
||||||
if(($healFTag = $nbt->getTag("HealF")) instanceof FloatTag){
|
if(($healFTag = $nbt->getTag(self::TAG_LEGACY_HEALTH)) instanceof FloatTag){
|
||||||
$health = $healFTag->getValue();
|
$health = $healFTag->getValue();
|
||||||
}elseif(($healthTag = $nbt->getTag("Health")) instanceof ShortTag){
|
}elseif(($healthTag = $nbt->getTag(self::TAG_HEALTH)) instanceof ShortTag){
|
||||||
$health = $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
|
$health = $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
|
||||||
}elseif($healthTag instanceof FloatTag){
|
}elseif($healthTag instanceof FloatTag){
|
||||||
$health = $healthTag->getValue();
|
$health = $healthTag->getValue();
|
||||||
@ -153,23 +162,23 @@ abstract class Living extends Entity{
|
|||||||
|
|
||||||
$this->setHealth($health);
|
$this->setHealth($health);
|
||||||
|
|
||||||
$this->setAirSupplyTicks($nbt->getShort("Air", self::DEFAULT_BREATH_TICKS));
|
$this->setAirSupplyTicks($nbt->getShort(self::TAG_BREATH_TICKS, self::DEFAULT_BREATH_TICKS));
|
||||||
|
|
||||||
/** @var CompoundTag[]|ListTag|null $activeEffectsTag */
|
/** @var CompoundTag[]|ListTag|null $activeEffectsTag */
|
||||||
$activeEffectsTag = $nbt->getListTag("ActiveEffects");
|
$activeEffectsTag = $nbt->getListTag(self::TAG_ACTIVE_EFFECTS);
|
||||||
if($activeEffectsTag !== null){
|
if($activeEffectsTag !== null){
|
||||||
foreach($activeEffectsTag as $e){
|
foreach($activeEffectsTag as $e){
|
||||||
$effect = EffectIdMap::getInstance()->fromId($e->getByte("Id"));
|
$effect = EffectIdMap::getInstance()->fromId($e->getByte(self::TAG_EFFECT_ID));
|
||||||
if($effect === null){
|
if($effect === null){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->effectManager->add(new EffectInstance(
|
$this->effectManager->add(new EffectInstance(
|
||||||
$effect,
|
$effect,
|
||||||
$e->getInt("Duration"),
|
$e->getInt(self::TAG_EFFECT_DURATION),
|
||||||
Binary::unsignByte($e->getByte("Amplifier")),
|
Binary::unsignByte($e->getByte(self::TAG_EFFECT_AMPLIFIER)),
|
||||||
$e->getByte("ShowParticles", 1) !== 0,
|
$e->getByte(self::TAG_EFFECT_SHOW_PARTICLES, 1) !== 0,
|
||||||
$e->getByte("Ambient", 0) !== 0
|
$e->getByte(self::TAG_EFFECT_AMBIENT, 0) !== 0
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +193,13 @@ abstract class Living extends Entity{
|
|||||||
$this->attributeMap->add($this->absorptionAttr = AttributeFactory::getInstance()->mustGet(Attribute::ABSORPTION));
|
$this->attributeMap->add($this->absorptionAttr = AttributeFactory::getInstance()->mustGet(Attribute::ABSORPTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name used to describe this entity in chat and command outputs.
|
||||||
|
*/
|
||||||
|
public function getDisplayName() : string{
|
||||||
|
return $this->nameTag !== "" ? $this->nameTag : $this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
public function setHealth(float $amount) : void{
|
public function setHealth(float $amount) : void{
|
||||||
$wasAlive = $this->isAlive();
|
$wasAlive = $this->isAlive();
|
||||||
parent::setHealth($amount);
|
parent::setHealth($amount);
|
||||||
@ -272,22 +288,22 @@ abstract class Living extends Entity{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setFloat("Health", $this->getHealth());
|
$nbt->setFloat(self::TAG_HEALTH, $this->getHealth());
|
||||||
|
|
||||||
$nbt->setShort("Air", $this->getAirSupplyTicks());
|
$nbt->setShort(self::TAG_BREATH_TICKS, $this->getAirSupplyTicks());
|
||||||
|
|
||||||
if(count($this->effectManager->all()) > 0){
|
if(count($this->effectManager->all()) > 0){
|
||||||
$effects = [];
|
$effects = [];
|
||||||
foreach($this->effectManager->all() as $effect){
|
foreach($this->effectManager->all() as $effect){
|
||||||
$effects[] = CompoundTag::create()
|
$effects[] = CompoundTag::create()
|
||||||
->setByte("Id", EffectIdMap::getInstance()->toId($effect->getType()))
|
->setByte(self::TAG_EFFECT_ID, EffectIdMap::getInstance()->toId($effect->getType()))
|
||||||
->setByte("Amplifier", Binary::signByte($effect->getAmplifier()))
|
->setByte(self::TAG_EFFECT_AMPLIFIER, Binary::signByte($effect->getAmplifier()))
|
||||||
->setInt("Duration", $effect->getDuration())
|
->setInt(self::TAG_EFFECT_DURATION, $effect->getDuration())
|
||||||
->setByte("Ambient", $effect->isAmbient() ? 1 : 0)
|
->setByte(self::TAG_EFFECT_AMBIENT, $effect->isAmbient() ? 1 : 0)
|
||||||
->setByte("ShowParticles", $effect->isVisible() ? 1 : 0);
|
->setByte(self::TAG_EFFECT_SHOW_PARTICLES, $effect->isVisible() ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$nbt->setTag("ActiveEffects", new ListTag($effects));
|
$nbt->setTag(self::TAG_ACTIVE_EFFECTS, new ListTag($effects));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
@ -448,7 +464,9 @@ abstract class Living extends Entity{
|
|||||||
*/
|
*/
|
||||||
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
|
protected function applyPostDamageEffects(EntityDamageEvent $source) : void{
|
||||||
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
|
$this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION)));
|
||||||
$this->damageArmor($source->getBaseDamage());
|
if($source->canBeReducedByArmor()){
|
||||||
|
$this->damageArmor($source->getBaseDamage());
|
||||||
|
}
|
||||||
|
|
||||||
if($source instanceof EntityDamageByEntityEvent && ($attacker = $source->getDamager()) !== null){
|
if($source instanceof EntityDamageByEntityEvent && ($attacker = $source->getDamager()) !== null){
|
||||||
$damage = 0;
|
$damage = 0;
|
||||||
@ -831,7 +849,8 @@ abstract class Living extends Entity{
|
|||||||
protected function sendSpawnPacket(Player $player) : void{
|
protected function sendSpawnPacket(Player $player) : void{
|
||||||
parent::sendSpawnPacket($player);
|
parent::sendSpawnPacket($player);
|
||||||
|
|
||||||
$player->getNetworkSession()->onMobArmorChange($this);
|
$networkSession = $player->getNetworkSession();
|
||||||
|
$networkSession->getEntityEventBroadcaster()->onMobArmorChange([$networkSession], $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||||
|
@ -36,6 +36,8 @@ class Villager extends Living implements Ageable{
|
|||||||
public const PROFESSION_BLACKSMITH = 3;
|
public const PROFESSION_BLACKSMITH = 3;
|
||||||
public const PROFESSION_BUTCHER = 4;
|
public const PROFESSION_BUTCHER = 4;
|
||||||
|
|
||||||
|
private const TAG_PROFESSION = "Profession"; //TAG_Int
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::VILLAGER; }
|
public static function getNetworkTypeId() : string{ return EntityIds::VILLAGER; }
|
||||||
|
|
||||||
private bool $baby = false;
|
private bool $baby = false;
|
||||||
@ -53,7 +55,7 @@ class Villager extends Living implements Ageable{
|
|||||||
parent::initEntity($nbt);
|
parent::initEntity($nbt);
|
||||||
|
|
||||||
/** @var int $profession */
|
/** @var int $profession */
|
||||||
$profession = $nbt->getInt("Profession", self::PROFESSION_FARMER);
|
$profession = $nbt->getInt(self::TAG_PROFESSION, self::PROFESSION_FARMER);
|
||||||
|
|
||||||
if($profession > 4 || $profession < 0){
|
if($profession > 4 || $profession < 0){
|
||||||
$profession = self::PROFESSION_FARMER;
|
$profession = self::PROFESSION_FARMER;
|
||||||
@ -64,7 +66,7 @@ class Villager extends Living implements Ageable{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setInt("Profession", $this->getProfession());
|
$nbt->setInt(self::TAG_PROFESSION, $this->getProfession());
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class ExperienceOrb extends Entity{
|
|||||||
|
|
||||||
public const TAG_VALUE_PC = "Value"; //short
|
public const TAG_VALUE_PC = "Value"; //short
|
||||||
public const TAG_VALUE_PE = "experience value"; //int (WTF?)
|
public const TAG_VALUE_PE = "experience value"; //int (WTF?)
|
||||||
|
private const TAG_AGE = "Age"; //TAG_Short
|
||||||
|
|
||||||
/** Max distance an orb will follow a player across. */
|
/** Max distance an orb will follow a player across. */
|
||||||
public const MAX_TARGET_DISTANCE = 8.0;
|
public const MAX_TARGET_DISTANCE = 8.0;
|
||||||
@ -109,13 +110,13 @@ class ExperienceOrb extends Entity{
|
|||||||
protected function initEntity(CompoundTag $nbt) : void{
|
protected function initEntity(CompoundTag $nbt) : void{
|
||||||
parent::initEntity($nbt);
|
parent::initEntity($nbt);
|
||||||
|
|
||||||
$this->age = $nbt->getShort("Age", 0);
|
$this->age = $nbt->getShort(self::TAG_AGE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
|
|
||||||
$nbt->setShort("Age", $this->age);
|
$nbt->setShort(self::TAG_AGE, $this->age);
|
||||||
|
|
||||||
$nbt->setShort(self::TAG_VALUE_PC, $this->getXpValue());
|
$nbt->setShort(self::TAG_VALUE_PC, $this->getXpValue());
|
||||||
$nbt->setInt(self::TAG_VALUE_PE, $this->getXpValue());
|
$nbt->setInt(self::TAG_VALUE_PE, $this->getXpValue());
|
||||||
|
@ -44,6 +44,10 @@ use function abs;
|
|||||||
|
|
||||||
class FallingBlock extends Entity{
|
class FallingBlock extends Entity{
|
||||||
|
|
||||||
|
private const TAG_TILE_ID = "TileID"; //TAG_Int
|
||||||
|
private const TAG_TILE = "Tile"; //TAG_Byte
|
||||||
|
private const TAG_DATA = "Data"; //TAG_Byte
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::FALLING_BLOCK; }
|
public static function getNetworkTypeId() : string{ return EntityIds::FALLING_BLOCK; }
|
||||||
|
|
||||||
protected $gravity = 0.04;
|
protected $gravity = 0.04;
|
||||||
@ -65,9 +69,9 @@ class FallingBlock extends Entity{
|
|||||||
$blockId = 0;
|
$blockId = 0;
|
||||||
|
|
||||||
//TODO: 1.8+ save format
|
//TODO: 1.8+ save format
|
||||||
if(($tileIdTag = $nbt->getTag("TileID")) instanceof IntTag){
|
if(($tileIdTag = $nbt->getTag(self::TAG_TILE_ID)) instanceof IntTag){
|
||||||
$blockId = $tileIdTag->getValue();
|
$blockId = $tileIdTag->getValue();
|
||||||
}elseif(($tileTag = $nbt->getTag("Tile")) instanceof ByteTag){
|
}elseif(($tileTag = $nbt->getTag(self::TAG_TILE)) instanceof ByteTag){
|
||||||
$blockId = $tileTag->getValue();
|
$blockId = $tileTag->getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +79,7 @@ class FallingBlock extends Entity{
|
|||||||
throw new SavedDataLoadingException("Missing block info from NBT");
|
throw new SavedDataLoadingException("Missing block info from NBT");
|
||||||
}
|
}
|
||||||
|
|
||||||
$damage = $nbt->getByte("Data", 0);
|
$damage = $nbt->getByte(self::TAG_DATA, 0);
|
||||||
|
|
||||||
return $factory->get($blockId, $damage);
|
return $factory->get($blockId, $damage);
|
||||||
}
|
}
|
||||||
@ -138,8 +142,8 @@ class FallingBlock extends Entity{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setInt("TileID", $this->block->getId());
|
$nbt->setInt(self::TAG_TILE_ID, $this->block->getId());
|
||||||
$nbt->setByte("Data", $this->block->getMeta());
|
$nbt->setByte(self::TAG_DATA, $this->block->getMeta());
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ use pocketmine\item\Item;
|
|||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||||
|
use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||||
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||||
@ -43,6 +45,13 @@ use function max;
|
|||||||
|
|
||||||
class ItemEntity extends Entity{
|
class ItemEntity extends Entity{
|
||||||
|
|
||||||
|
private const TAG_HEALTH = "Health"; //TAG_Short
|
||||||
|
private const TAG_AGE = "Age"; //TAG_Short
|
||||||
|
private const TAG_PICKUP_DELAY = "PickupDelay"; //TAG_Short
|
||||||
|
private const TAG_OWNER = "Owner"; //TAG_String
|
||||||
|
private const TAG_THROWER = "Thrower"; //TAG_String
|
||||||
|
public const TAG_ITEM = "Item"; //TAG_Compound
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::ITEM; }
|
public static function getNetworkTypeId() : string{ return EntityIds::ITEM; }
|
||||||
|
|
||||||
public const MERGE_CHECK_PERIOD = 2; //0.1 seconds
|
public const MERGE_CHECK_PERIOD = 2; //0.1 seconds
|
||||||
@ -81,17 +90,17 @@ class ItemEntity extends Entity{
|
|||||||
parent::initEntity($nbt);
|
parent::initEntity($nbt);
|
||||||
|
|
||||||
$this->setMaxHealth(5);
|
$this->setMaxHealth(5);
|
||||||
$this->setHealth($nbt->getShort("Health", (int) $this->getHealth()));
|
$this->setHealth($nbt->getShort(self::TAG_HEALTH, (int) $this->getHealth()));
|
||||||
|
|
||||||
$age = $nbt->getShort("Age", 0);
|
$age = $nbt->getShort(self::TAG_AGE, 0);
|
||||||
if($age === -32768){
|
if($age === -32768){
|
||||||
$this->despawnDelay = self::NEVER_DESPAWN;
|
$this->despawnDelay = self::NEVER_DESPAWN;
|
||||||
}else{
|
}else{
|
||||||
$this->despawnDelay = max(0, self::DEFAULT_DESPAWN_DELAY - $age);
|
$this->despawnDelay = max(0, self::DEFAULT_DESPAWN_DELAY - $age);
|
||||||
}
|
}
|
||||||
$this->pickupDelay = $nbt->getShort("PickupDelay", $this->pickupDelay);
|
$this->pickupDelay = $nbt->getShort(self::TAG_PICKUP_DELAY, $this->pickupDelay);
|
||||||
$this->owner = $nbt->getString("Owner", $this->owner);
|
$this->owner = $nbt->getString(self::TAG_OWNER, $this->owner);
|
||||||
$this->thrower = $nbt->getString("Thrower", $this->thrower);
|
$this->thrower = $nbt->getString(self::TAG_THROWER, $this->thrower);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function onFirstUpdate(int $currentTick) : void{
|
protected function onFirstUpdate(int $currentTick) : void{
|
||||||
@ -106,33 +115,42 @@ class ItemEntity extends Entity{
|
|||||||
|
|
||||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||||
|
|
||||||
if(!$this->isFlaggedForDespawn() && $this->pickupDelay !== self::NEVER_DESPAWN){ //Infinite delay
|
if($this->isFlaggedForDespawn()){
|
||||||
|
return $hasUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->pickupDelay !== self::NEVER_DESPAWN && $this->pickupDelay > 0){ //Infinite delay
|
||||||
|
$hasUpdate = true;
|
||||||
$this->pickupDelay -= $tickDiff;
|
$this->pickupDelay -= $tickDiff;
|
||||||
if($this->pickupDelay < 0){
|
if($this->pickupDelay < 0){
|
||||||
$this->pickupDelay = 0;
|
$this->pickupDelay = 0;
|
||||||
}
|
}
|
||||||
if($this->hasMovementUpdate() && $this->despawnDelay % self::MERGE_CHECK_PERIOD === 0){
|
}
|
||||||
$mergeable = [$this]; //in case the merge target ends up not being this
|
|
||||||
$mergeTarget = $this;
|
|
||||||
foreach($this->getWorld()->getNearbyEntities($this->boundingBox->expandedCopy(0.5, 0.5, 0.5), $this) as $entity){
|
|
||||||
if(!$entity instanceof ItemEntity || $entity->isFlaggedForDespawn()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($entity->isMergeable($this)){
|
if($this->hasMovementUpdate() && $this->despawnDelay % self::MERGE_CHECK_PERIOD === 0){
|
||||||
$mergeable[] = $entity;
|
$mergeable = [$this]; //in case the merge target ends up not being this
|
||||||
if($entity->item->getCount() > $mergeTarget->item->getCount()){
|
$mergeTarget = $this;
|
||||||
$mergeTarget = $entity;
|
foreach($this->getWorld()->getNearbyEntities($this->boundingBox->expandedCopy(0.5, 0.5, 0.5), $this) as $entity){
|
||||||
}
|
if(!$entity instanceof ItemEntity || $entity->isFlaggedForDespawn()){
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
foreach($mergeable as $itemEntity){
|
|
||||||
if($itemEntity !== $mergeTarget){
|
if($entity->isMergeable($this)){
|
||||||
$itemEntity->tryMergeInto($mergeTarget);
|
$mergeable[] = $entity;
|
||||||
|
if($entity->item->getCount() > $mergeTarget->item->getCount()){
|
||||||
|
$mergeTarget = $entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach($mergeable as $itemEntity){
|
||||||
|
if($itemEntity !== $mergeTarget){
|
||||||
|
$itemEntity->tryMergeInto($mergeTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$this->isFlaggedForDespawn() && $this->despawnDelay !== self::NEVER_DESPAWN){
|
||||||
|
$hasUpdate = true;
|
||||||
$this->despawnDelay -= $tickDiff;
|
$this->despawnDelay -= $tickDiff;
|
||||||
if($this->despawnDelay <= 0){
|
if($this->despawnDelay <= 0){
|
||||||
$ev = new ItemDespawnEvent($this);
|
$ev = new ItemDespawnEvent($this);
|
||||||
@ -141,7 +159,6 @@ class ItemEntity extends Entity{
|
|||||||
$this->despawnDelay = self::DEFAULT_DESPAWN_DELAY;
|
$this->despawnDelay = self::DEFAULT_DESPAWN_DELAY;
|
||||||
}else{
|
}else{
|
||||||
$this->flagForDespawn();
|
$this->flagForDespawn();
|
||||||
$hasUpdate = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,20 +212,20 @@ class ItemEntity extends Entity{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setTag("Item", $this->item->nbtSerialize());
|
$nbt->setTag(self::TAG_ITEM, $this->item->nbtSerialize());
|
||||||
$nbt->setShort("Health", (int) $this->getHealth());
|
$nbt->setShort(self::TAG_HEALTH, (int) $this->getHealth());
|
||||||
if($this->despawnDelay === self::NEVER_DESPAWN){
|
if($this->despawnDelay === self::NEVER_DESPAWN){
|
||||||
$age = -32768;
|
$age = -32768;
|
||||||
}else{
|
}else{
|
||||||
$age = self::DEFAULT_DESPAWN_DELAY - $this->despawnDelay;
|
$age = self::DEFAULT_DESPAWN_DELAY - $this->despawnDelay;
|
||||||
}
|
}
|
||||||
$nbt->setShort("Age", $age);
|
$nbt->setShort(self::TAG_AGE, $age);
|
||||||
$nbt->setShort("PickupDelay", $this->pickupDelay);
|
$nbt->setShort(self::TAG_PICKUP_DELAY, $this->pickupDelay);
|
||||||
if($this->owner !== null){
|
if($this->owner !== null){
|
||||||
$nbt->setString("Owner", $this->owner);
|
$nbt->setString(self::TAG_OWNER, $this->owner);
|
||||||
}
|
}
|
||||||
if($this->thrower !== null){
|
if($this->thrower !== null){
|
||||||
$nbt->setString("Thrower", $this->thrower);
|
$nbt->setString(self::TAG_THROWER, $this->thrower);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
@ -313,9 +330,10 @@ class ItemEntity extends Entity{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->getViewers() as $viewer){
|
NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
|
$this->getViewers(),
|
||||||
}
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
|
||||||
|
);
|
||||||
|
|
||||||
$inventory = $ev->getInventory();
|
$inventory = $ev->getInventory();
|
||||||
if($inventory !== null){
|
if($inventory !== null){
|
||||||
|
@ -41,6 +41,13 @@ use pocketmine\world\World;
|
|||||||
use function ceil;
|
use function ceil;
|
||||||
|
|
||||||
class Painting extends Entity{
|
class Painting extends Entity{
|
||||||
|
public const TAG_TILE_X = "TileX"; //TAG_Int
|
||||||
|
public const TAG_TILE_Y = "TileY"; //TAG_Int
|
||||||
|
public const TAG_TILE_Z = "TileZ"; //TAG_Int
|
||||||
|
public const TAG_FACING_JE = "Facing"; //TAG_Byte
|
||||||
|
public const TAG_DIRECTION_BE = "Direction"; //TAG_Byte
|
||||||
|
public const TAG_MOTIVE = "Motive"; //TAG_String
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::PAINTING; }
|
public static function getNetworkTypeId() : string{ return EntityIds::PAINTING; }
|
||||||
|
|
||||||
public const DATA_TO_FACING = [
|
public const DATA_TO_FACING = [
|
||||||
@ -88,14 +95,14 @@ class Painting extends Entity{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setInt("TileX", (int) $this->blockIn->x);
|
$nbt->setInt(self::TAG_TILE_X, (int) $this->blockIn->x);
|
||||||
$nbt->setInt("TileY", (int) $this->blockIn->y);
|
$nbt->setInt(self::TAG_TILE_Y, (int) $this->blockIn->y);
|
||||||
$nbt->setInt("TileZ", (int) $this->blockIn->z);
|
$nbt->setInt(self::TAG_TILE_Z, (int) $this->blockIn->z);
|
||||||
|
|
||||||
$nbt->setByte("Facing", self::FACING_TO_DATA[$this->facing]);
|
$nbt->setByte(self::TAG_FACING_JE, self::FACING_TO_DATA[$this->facing]);
|
||||||
$nbt->setByte("Direction", self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
|
$nbt->setByte(self::TAG_DIRECTION_BE, self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
|
||||||
|
|
||||||
$nbt->setString("Motive", $this->motive->getName());
|
$nbt->setString(self::TAG_MOTIVE, $this->motive->getName());
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ use pocketmine\world\Position;
|
|||||||
|
|
||||||
class PrimedTNT extends Entity implements Explosive{
|
class PrimedTNT extends Entity implements Explosive{
|
||||||
|
|
||||||
|
private const TAG_FUSE = "Fuse"; //TAG_Short
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::TNT; }
|
public static function getNetworkTypeId() : string{ return EntityIds::TNT; }
|
||||||
|
|
||||||
protected $gravity = 0.04;
|
protected $gravity = 0.04;
|
||||||
@ -81,7 +83,7 @@ class PrimedTNT extends Entity implements Explosive{
|
|||||||
protected function initEntity(CompoundTag $nbt) : void{
|
protected function initEntity(CompoundTag $nbt) : void{
|
||||||
parent::initEntity($nbt);
|
parent::initEntity($nbt);
|
||||||
|
|
||||||
$this->fuse = $nbt->getShort("Fuse", 80);
|
$this->fuse = $nbt->getShort(self::TAG_FUSE, 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canCollideWith(Entity $entity) : bool{
|
public function canCollideWith(Entity $entity) : bool{
|
||||||
@ -90,7 +92,7 @@ class PrimedTNT extends Entity implements Explosive{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setShort("Fuse", $this->fuse);
|
$nbt->setShort(self::TAG_FUSE, $this->fuse);
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@ use pocketmine\event\entity\ProjectileHitEvent;
|
|||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
use pocketmine\math\RayTraceResult;
|
use pocketmine\math\RayTraceResult;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
|
use pocketmine\network\mcpe\EntityEventBroadcaster;
|
||||||
|
use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
||||||
@ -52,6 +54,7 @@ class Arrow extends Projectile{
|
|||||||
|
|
||||||
private const TAG_PICKUP = "pickup"; //TAG_Byte
|
private const TAG_PICKUP = "pickup"; //TAG_Byte
|
||||||
public const TAG_CRIT = "crit"; //TAG_Byte
|
public const TAG_CRIT = "crit"; //TAG_Byte
|
||||||
|
private const TAG_LIFE = "life"; //TAG_Short
|
||||||
|
|
||||||
protected $gravity = 0.05;
|
protected $gravity = 0.05;
|
||||||
protected $drag = 0.01;
|
protected $drag = 0.01;
|
||||||
@ -83,14 +86,14 @@ class Arrow extends Projectile{
|
|||||||
|
|
||||||
$this->pickupMode = $nbt->getByte(self::TAG_PICKUP, self::PICKUP_ANY);
|
$this->pickupMode = $nbt->getByte(self::TAG_PICKUP, self::PICKUP_ANY);
|
||||||
$this->critical = $nbt->getByte(self::TAG_CRIT, 0) === 1;
|
$this->critical = $nbt->getByte(self::TAG_CRIT, 0) === 1;
|
||||||
$this->collideTicks = $nbt->getShort("life", $this->collideTicks);
|
$this->collideTicks = $nbt->getShort(self::TAG_LIFE, $this->collideTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setByte(self::TAG_PICKUP, $this->pickupMode);
|
$nbt->setByte(self::TAG_PICKUP, $this->pickupMode);
|
||||||
$nbt->setByte(self::TAG_CRIT, $this->critical ? 1 : 0);
|
$nbt->setByte(self::TAG_CRIT, $this->critical ? 1 : 0);
|
||||||
$nbt->setShort("life", $this->collideTicks);
|
$nbt->setShort(self::TAG_LIFE, $this->collideTicks);
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,9 +198,10 @@ class Arrow extends Projectile{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->getViewers() as $viewer){
|
NetworkBroadcastUtils::broadcastEntityEvent(
|
||||||
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
|
$this->getViewers(),
|
||||||
}
|
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
|
||||||
|
);
|
||||||
|
|
||||||
$ev->getInventory()?->addItem($ev->getItem());
|
$ev->getInventory()?->addItem($ev->getItem());
|
||||||
$this->flagForDespawn();
|
$this->flagForDespawn();
|
||||||
|
@ -50,6 +50,12 @@ use const M_PI;
|
|||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
|
|
||||||
abstract class Projectile extends Entity{
|
abstract class Projectile extends Entity{
|
||||||
|
private const TAG_DAMAGE = "damage"; //TAG_Double
|
||||||
|
private const TAG_TILE_X = "tileX"; //TAG_Int
|
||||||
|
private const TAG_TILE_Y = "tileY"; //TAG_Int
|
||||||
|
private const TAG_TILE_Z = "tileZ"; //TAG_Int
|
||||||
|
private const TAG_BLOCK_ID = "blockId"; //TAG_Int
|
||||||
|
private const TAG_BLOCK_DATA = "blockData"; //TAG_Byte
|
||||||
|
|
||||||
/** @var float */
|
/** @var float */
|
||||||
protected $damage = 0.0;
|
protected $damage = 0.0;
|
||||||
@ -75,22 +81,22 @@ abstract class Projectile extends Entity{
|
|||||||
|
|
||||||
$this->setMaxHealth(1);
|
$this->setMaxHealth(1);
|
||||||
$this->setHealth(1);
|
$this->setHealth(1);
|
||||||
$this->damage = $nbt->getDouble("damage", $this->damage);
|
$this->damage = $nbt->getDouble(self::TAG_DAMAGE, $this->damage);
|
||||||
|
|
||||||
(function() use ($nbt) : void{
|
(function() use ($nbt) : void{
|
||||||
if(($tileXTag = $nbt->getTag("tileX")) instanceof IntTag && ($tileYTag = $nbt->getTag("tileY")) instanceof IntTag && ($tileZTag = $nbt->getTag("tileZ")) instanceof IntTag){
|
if(($tileXTag = $nbt->getTag(self::TAG_TILE_X)) instanceof IntTag && ($tileYTag = $nbt->getTag(self::TAG_TILE_Y)) instanceof IntTag && ($tileZTag = $nbt->getTag(self::TAG_TILE_Z)) instanceof IntTag){
|
||||||
$blockPos = new Vector3($tileXTag->getValue(), $tileYTag->getValue(), $tileZTag->getValue());
|
$blockPos = new Vector3($tileXTag->getValue(), $tileYTag->getValue(), $tileZTag->getValue());
|
||||||
}else{
|
}else{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(($blockIdTag = $nbt->getTag("blockId")) instanceof IntTag){
|
if(($blockIdTag = $nbt->getTag(self::TAG_BLOCK_ID)) instanceof IntTag){
|
||||||
$blockId = $blockIdTag->getValue();
|
$blockId = $blockIdTag->getValue();
|
||||||
}else{
|
}else{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(($blockDataTag = $nbt->getTag("blockData")) instanceof ByteTag){
|
if(($blockDataTag = $nbt->getTag(self::TAG_BLOCK_DATA)) instanceof ByteTag){
|
||||||
$blockData = $blockDataTag->getValue();
|
$blockData = $blockDataTag->getValue();
|
||||||
}else{
|
}else{
|
||||||
return;
|
return;
|
||||||
@ -134,17 +140,17 @@ abstract class Projectile extends Entity{
|
|||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
|
|
||||||
$nbt->setDouble("damage", $this->damage);
|
$nbt->setDouble(self::TAG_DAMAGE, $this->damage);
|
||||||
|
|
||||||
if($this->blockHit !== null){
|
if($this->blockHit !== null){
|
||||||
$pos = $this->blockHit->getPosition();
|
$pos = $this->blockHit->getPosition();
|
||||||
$nbt->setInt("tileX", $pos->x);
|
$nbt->setInt(self::TAG_TILE_X, $pos->x);
|
||||||
$nbt->setInt("tileY", $pos->y);
|
$nbt->setInt(self::TAG_TILE_Y, $pos->y);
|
||||||
$nbt->setInt("tileZ", $pos->z);
|
$nbt->setInt(self::TAG_TILE_Z, $pos->z);
|
||||||
|
|
||||||
//we intentionally use different ones to PC because we don't have stringy IDs
|
//we intentionally use different ones to PC because we don't have stringy IDs
|
||||||
$nbt->setInt("blockId", $this->blockHit->getId());
|
$nbt->setInt(self::TAG_BLOCK_ID, $this->blockHit->getId());
|
||||||
$nbt->setByte("blockData", $this->blockHit->getMeta());
|
$nbt->setByte(self::TAG_BLOCK_DATA, $this->blockHit->getMeta());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
|
@ -50,6 +50,8 @@ use function sqrt;
|
|||||||
|
|
||||||
class SplashPotion extends Throwable{
|
class SplashPotion extends Throwable{
|
||||||
|
|
||||||
|
public const TAG_POTION_ID = "PotionId"; //TAG_Short
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::SPLASH_POTION; }
|
public static function getNetworkTypeId() : string{ return EntityIds::SPLASH_POTION; }
|
||||||
|
|
||||||
protected $gravity = 0.05;
|
protected $gravity = 0.05;
|
||||||
@ -66,7 +68,7 @@ class SplashPotion extends Throwable{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setShort("PotionId", PotionTypeIdMap::getInstance()->toId($this->getPotionType()));
|
$nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->getPotionType()));
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
@ -98,17 +98,15 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
|||||||
if($e instanceof Player){
|
if($e instanceof Player){
|
||||||
return KnownTranslationFactory::death_attack_player($name, $e->getDisplayName());
|
return KnownTranslationFactory::death_attack_player($name, $e->getDisplayName());
|
||||||
}elseif($e instanceof Living){
|
}elseif($e instanceof Living){
|
||||||
return KnownTranslationFactory::death_attack_mob($name, $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName());
|
return KnownTranslationFactory::death_attack_mob($name, $e->getDisplayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EntityDamageEvent::CAUSE_PROJECTILE:
|
case EntityDamageEvent::CAUSE_PROJECTILE:
|
||||||
if($deathCause instanceof EntityDamageByEntityEvent){
|
if($deathCause instanceof EntityDamageByEntityEvent){
|
||||||
$e = $deathCause->getDamager();
|
$e = $deathCause->getDamager();
|
||||||
if($e instanceof Player){
|
if($e instanceof Living){
|
||||||
return KnownTranslationFactory::death_attack_arrow($name, $e->getDisplayName());
|
return KnownTranslationFactory::death_attack_arrow($name, $e->getDisplayName());
|
||||||
}elseif($e instanceof Living){
|
|
||||||
return KnownTranslationFactory::death_attack_arrow($name, $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -149,10 +147,8 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
|||||||
case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION:
|
case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION:
|
||||||
if($deathCause instanceof EntityDamageByEntityEvent){
|
if($deathCause instanceof EntityDamageByEntityEvent){
|
||||||
$e = $deathCause->getDamager();
|
$e = $deathCause->getDamager();
|
||||||
if($e instanceof Player){
|
if($e instanceof Living){
|
||||||
return KnownTranslationFactory::death_attack_explosion_player($name, $e->getDisplayName());
|
return KnownTranslationFactory::death_attack_explosion_player($name, $e->getDisplayName());
|
||||||
}elseif($e instanceof Living){
|
|
||||||
return KnownTranslationFactory::death_attack_explosion_player($name, $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return KnownTranslationFactory::death_attack_explosion($name);
|
return KnownTranslationFactory::death_attack_explosion($name);
|
||||||
|
@ -69,8 +69,8 @@ class PlayerPreLoginEvent extends Event implements Cancellable{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object containing self-proclaimed information about the connecting player.
|
* Returns an object containing self-proclaimed information about the connecting player.
|
||||||
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, it's unknown if the player is real or a
|
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, this could be a hacker posing as another
|
||||||
* hacker.
|
* player.
|
||||||
*/
|
*/
|
||||||
public function getPlayerInfo() : PlayerInfo{
|
public function getPlayerInfo() : PlayerInfo{
|
||||||
return $this->playerInfo;
|
return $this->playerInfo;
|
||||||
@ -109,7 +109,7 @@ class PlayerPreLoginEvent extends Event implements Cancellable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a reason to disallow the player to continue continue authenticating, with a message.
|
* Sets a reason to disallow the player to continue authenticating, with a message.
|
||||||
* This can also be used to change kick messages for already-set flags.
|
* This can also be used to change kick messages for already-set flags.
|
||||||
*/
|
*/
|
||||||
public function setKickReason(int $flag, string $message) : void{
|
public function setKickReason(int $flag, string $message) : void{
|
||||||
|
54
src/event/server/DataPacketDecodeEvent.php
Normal file
54
src/event/server/DataPacketDecodeEvent.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\event\server;
|
||||||
|
|
||||||
|
use pocketmine\event\Cancellable;
|
||||||
|
use pocketmine\event\CancellableTrait;
|
||||||
|
use pocketmine\network\mcpe\NetworkSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before a packet is decoded and handled by the network session.
|
||||||
|
* Cancelling this event will drop the packet without decoding it, minimizing wasted CPU time.
|
||||||
|
*/
|
||||||
|
class DataPacketDecodeEvent extends ServerEvent implements Cancellable{
|
||||||
|
use CancellableTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private NetworkSession $origin,
|
||||||
|
private int $packetId,
|
||||||
|
private string $packetBuffer
|
||||||
|
){}
|
||||||
|
|
||||||
|
public function getOrigin() : NetworkSession{
|
||||||
|
return $this->origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPacketId() : int{
|
||||||
|
return $this->packetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPacketBuffer() : string{
|
||||||
|
return $this->packetBuffer;
|
||||||
|
}
|
||||||
|
}
|
73
src/event/world/WorldParticleEvent.php
Normal file
73
src/event/world/WorldParticleEvent.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\event\world;
|
||||||
|
|
||||||
|
use pocketmine\event\Cancellable;
|
||||||
|
use pocketmine\event\CancellableTrait;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\world\particle\Particle;
|
||||||
|
use pocketmine\world\World;
|
||||||
|
|
||||||
|
class WorldParticleEvent extends WorldEvent implements Cancellable{
|
||||||
|
use CancellableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Player[] $recipients
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
World $world,
|
||||||
|
private Particle $particle,
|
||||||
|
private Vector3 $position,
|
||||||
|
private array $recipients
|
||||||
|
){
|
||||||
|
parent::__construct($world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParticle() : Particle{
|
||||||
|
return $this->particle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setParticle(Particle $particle) : void{
|
||||||
|
$this->particle = $particle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosition() : Vector3{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Player[]
|
||||||
|
*/
|
||||||
|
public function getRecipients() : array{
|
||||||
|
return $this->recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Player[] $recipients
|
||||||
|
*/
|
||||||
|
public function setRecipients(array $recipients) : void{
|
||||||
|
$this->recipients = $recipients;
|
||||||
|
}
|
||||||
|
}
|
77
src/event/world/WorldSoundEvent.php
Normal file
77
src/event/world/WorldSoundEvent.php
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\event\world;
|
||||||
|
|
||||||
|
use pocketmine\event\Cancellable;
|
||||||
|
use pocketmine\event\CancellableTrait;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\world\sound\Sound;
|
||||||
|
use pocketmine\world\World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a sound is played in a world
|
||||||
|
* @see World::addSound()
|
||||||
|
*/
|
||||||
|
class WorldSoundEvent extends WorldEvent implements Cancellable{
|
||||||
|
use CancellableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Player[] $recipients
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
World $world,
|
||||||
|
private Sound $sound,
|
||||||
|
private Vector3 $position,
|
||||||
|
private array $recipients
|
||||||
|
){
|
||||||
|
parent::__construct($world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSound() : Sound{
|
||||||
|
return $this->sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSound(Sound $sound) : void{
|
||||||
|
$this->sound = $sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosition() : Vector3{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Player[]
|
||||||
|
*/
|
||||||
|
public function getRecipients() : array{
|
||||||
|
return $this->recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Player[] $recipients
|
||||||
|
*/
|
||||||
|
public function setRecipients(array $recipients) : void{
|
||||||
|
$this->recipients = $recipients;
|
||||||
|
}
|
||||||
|
}
|
@ -25,9 +25,9 @@ namespace pocketmine\inventory;
|
|||||||
|
|
||||||
use pocketmine\item\Durable;
|
use pocketmine\item\Durable;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\utils\Filesystem;
|
||||||
use pocketmine\utils\SingletonTrait;
|
use pocketmine\utils\SingletonTrait;
|
||||||
use Symfony\Component\Filesystem\Path;
|
use Symfony\Component\Filesystem\Path;
|
||||||
use function file_get_contents;
|
|
||||||
use function json_decode;
|
use function json_decode;
|
||||||
|
|
||||||
final class CreativeInventory{
|
final class CreativeInventory{
|
||||||
@ -37,7 +37,7 @@ final class CreativeInventory{
|
|||||||
private array $creative = [];
|
private array $creative = [];
|
||||||
|
|
||||||
private function __construct(){
|
private function __construct(){
|
||||||
$creativeItems = json_decode(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "creativeitems.json")), true);
|
$creativeItems = json_decode(Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, "legacy_creativeitems.json")), true);
|
||||||
|
|
||||||
foreach($creativeItems as $data){
|
foreach($creativeItems as $data){
|
||||||
$item = Item::jsonDeserialize($data);
|
$item = Item::jsonDeserialize($data);
|
||||||
|
@ -59,12 +59,27 @@ class Item implements \JsonSerializable{
|
|||||||
use ItemEnchantmentHandlingTrait;
|
use ItemEnchantmentHandlingTrait;
|
||||||
|
|
||||||
public const TAG_ENCH = "ench";
|
public const TAG_ENCH = "ench";
|
||||||
|
private const TAG_ENCH_ID = "id"; //TAG_Short
|
||||||
|
private const TAG_ENCH_LVL = "lvl"; //TAG_Short
|
||||||
|
|
||||||
public const TAG_DISPLAY = "display";
|
public const TAG_DISPLAY = "display";
|
||||||
public const TAG_BLOCK_ENTITY_TAG = "BlockEntityTag";
|
public const TAG_BLOCK_ENTITY_TAG = "BlockEntityTag";
|
||||||
|
|
||||||
public const TAG_DISPLAY_NAME = "Name";
|
public const TAG_DISPLAY_NAME = "Name";
|
||||||
public const TAG_DISPLAY_LORE = "Lore";
|
public const TAG_DISPLAY_LORE = "Lore";
|
||||||
|
|
||||||
|
public const TAG_KEEP_ON_DEATH = "minecraft:keep_on_death";
|
||||||
|
|
||||||
|
private const TAG_ID = "id"; //TAG_Short
|
||||||
|
private const TAG_COUNT = "Count"; //TAG_Byte
|
||||||
|
private const TAG_DAMAGE = "Damage"; //TAG_Short
|
||||||
|
private const TAG_TAG = "tag"; //TAG_Compound
|
||||||
|
|
||||||
|
public const TAG_SLOT = "Slot"; //TAG_Byte
|
||||||
|
|
||||||
|
private const TAG_CAN_PLACE_ON = "CanPlaceOn"; //TAG_List<TAG_String>
|
||||||
|
private const TAG_CAN_DESTROY = "CanDestroy"; //TAG_List<TAG_String>
|
||||||
|
|
||||||
private ItemIdentifier $identifier;
|
private ItemIdentifier $identifier;
|
||||||
private CompoundTag $nbt;
|
private CompoundTag $nbt;
|
||||||
|
|
||||||
@ -96,6 +111,8 @@ class Item implements \JsonSerializable{
|
|||||||
*/
|
*/
|
||||||
protected $canDestroy;
|
protected $canDestroy;
|
||||||
|
|
||||||
|
protected bool $keepOnDeath = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register
|
* Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register
|
||||||
* into the index.
|
* into the index.
|
||||||
@ -222,6 +239,17 @@ class Item implements \JsonSerializable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether players will retain this item on death. If a non-player dies it will be excluded from the drops.
|
||||||
|
*/
|
||||||
|
public function keepOnDeath() : bool{
|
||||||
|
return $this->keepOnDeath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setKeepOnDeath(bool $keepOnDeath) : void{
|
||||||
|
$this->keepOnDeath = $keepOnDeath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this Item has a non-empty NBT.
|
* Returns whether this Item has a non-empty NBT.
|
||||||
*/
|
*/
|
||||||
@ -290,8 +318,8 @@ class Item implements \JsonSerializable{
|
|||||||
if($enchantments !== null && $enchantments->getTagType() === NBT::TAG_Compound){
|
if($enchantments !== null && $enchantments->getTagType() === NBT::TAG_Compound){
|
||||||
/** @var CompoundTag $enchantment */
|
/** @var CompoundTag $enchantment */
|
||||||
foreach($enchantments as $enchantment){
|
foreach($enchantments as $enchantment){
|
||||||
$magicNumber = $enchantment->getShort("id", -1);
|
$magicNumber = $enchantment->getShort(self::TAG_ENCH_ID, -1);
|
||||||
$level = $enchantment->getShort("lvl", 0);
|
$level = $enchantment->getShort(self::TAG_ENCH_LVL, 0);
|
||||||
if($level <= 0){
|
if($level <= 0){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -305,7 +333,7 @@ class Item implements \JsonSerializable{
|
|||||||
$this->blockEntityTag = $tag->getCompoundTag(self::TAG_BLOCK_ENTITY_TAG);
|
$this->blockEntityTag = $tag->getCompoundTag(self::TAG_BLOCK_ENTITY_TAG);
|
||||||
|
|
||||||
$this->canPlaceOn = [];
|
$this->canPlaceOn = [];
|
||||||
$canPlaceOn = $tag->getListTag("CanPlaceOn");
|
$canPlaceOn = $tag->getListTag(self::TAG_CAN_PLACE_ON);
|
||||||
if($canPlaceOn !== null && $canPlaceOn->getTagType() === NBT::TAG_String){
|
if($canPlaceOn !== null && $canPlaceOn->getTagType() === NBT::TAG_String){
|
||||||
/** @var StringTag $entry */
|
/** @var StringTag $entry */
|
||||||
foreach($canPlaceOn as $entry){
|
foreach($canPlaceOn as $entry){
|
||||||
@ -313,13 +341,15 @@ class Item implements \JsonSerializable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->canDestroy = [];
|
$this->canDestroy = [];
|
||||||
$canDestroy = $tag->getListTag("CanDestroy");
|
$canDestroy = $tag->getListTag(self::TAG_CAN_DESTROY);
|
||||||
if($canDestroy !== null && $canDestroy->getTagType() === NBT::TAG_String){
|
if($canDestroy !== null && $canDestroy->getTagType() === NBT::TAG_String){
|
||||||
/** @var StringTag $entry */
|
/** @var StringTag $entry */
|
||||||
foreach($canDestroy as $entry){
|
foreach($canDestroy as $entry){
|
||||||
$this->canDestroy[$entry->getValue()] = $entry->getValue();
|
$this->canDestroy[$entry->getValue()] = $entry->getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->keepOnDeath = $tag->getByte(self::TAG_KEEP_ON_DEATH, 0) !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function serializeCompoundTag(CompoundTag $tag) : void{
|
protected function serializeCompoundTag(CompoundTag $tag) : void{
|
||||||
@ -346,8 +376,8 @@ class Item implements \JsonSerializable{
|
|||||||
$ench = new ListTag();
|
$ench = new ListTag();
|
||||||
foreach($this->getEnchantments() as $enchantmentInstance){
|
foreach($this->getEnchantments() as $enchantmentInstance){
|
||||||
$ench->push(CompoundTag::create()
|
$ench->push(CompoundTag::create()
|
||||||
->setShort("id", EnchantmentIdMap::getInstance()->toId($enchantmentInstance->getType()))
|
->setShort(self::TAG_ENCH_ID, EnchantmentIdMap::getInstance()->toId($enchantmentInstance->getType()))
|
||||||
->setShort("lvl", $enchantmentInstance->getLevel())
|
->setShort(self::TAG_ENCH_LVL, $enchantmentInstance->getLevel())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$tag->setTag(self::TAG_ENCH, $ench);
|
$tag->setTag(self::TAG_ENCH, $ench);
|
||||||
@ -364,18 +394,24 @@ class Item implements \JsonSerializable{
|
|||||||
foreach($this->canPlaceOn as $item){
|
foreach($this->canPlaceOn as $item){
|
||||||
$canPlaceOn->push(new StringTag($item));
|
$canPlaceOn->push(new StringTag($item));
|
||||||
}
|
}
|
||||||
$tag->setTag("CanPlaceOn", $canPlaceOn);
|
$tag->setTag(self::TAG_CAN_PLACE_ON, $canPlaceOn);
|
||||||
}else{
|
}else{
|
||||||
$tag->removeTag("CanPlaceOn");
|
$tag->removeTag(self::TAG_CAN_PLACE_ON);
|
||||||
}
|
}
|
||||||
if(count($this->canDestroy) > 0){
|
if(count($this->canDestroy) > 0){
|
||||||
$canDestroy = new ListTag();
|
$canDestroy = new ListTag();
|
||||||
foreach($this->canDestroy as $item){
|
foreach($this->canDestroy as $item){
|
||||||
$canDestroy->push(new StringTag($item));
|
$canDestroy->push(new StringTag($item));
|
||||||
}
|
}
|
||||||
$tag->setTag("CanDestroy", $canDestroy);
|
$tag->setTag(self::TAG_CAN_DESTROY, $canDestroy);
|
||||||
}else{
|
}else{
|
||||||
$tag->removeTag("CanDestroy");
|
$tag->removeTag(self::TAG_CAN_DESTROY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->keepOnDeath){
|
||||||
|
$tag->setByte(self::TAG_KEEP_ON_DEATH, 1);
|
||||||
|
}else{
|
||||||
|
$tag->removeTag(self::TAG_KEEP_ON_DEATH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,6 +590,16 @@ class Item implements \JsonSerializable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a player uses the item to interact with entity, for example by using a name tag.
|
||||||
|
*
|
||||||
|
* @param Vector3 $clickVector The exact position of the click (absolute coordinates)
|
||||||
|
* @return bool whether some action took place
|
||||||
|
*/
|
||||||
|
public function onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of ticks a player must wait before activating this item again.
|
* Returns the number of ticks a player must wait before activating this item again.
|
||||||
*/
|
*/
|
||||||
@ -655,17 +701,17 @@ class Item implements \JsonSerializable{
|
|||||||
*/
|
*/
|
||||||
public function nbtSerialize(int $slot = -1) : CompoundTag{
|
public function nbtSerialize(int $slot = -1) : CompoundTag{
|
||||||
$result = CompoundTag::create()
|
$result = CompoundTag::create()
|
||||||
->setShort("id", $this->getId())
|
->setShort(self::TAG_ID, $this->getId())
|
||||||
->setByte("Count", Binary::signByte($this->count))
|
->setByte(self::TAG_COUNT, Binary::signByte($this->count))
|
||||||
->setShort("Damage", $this->getMeta());
|
->setShort(self::TAG_DAMAGE, $this->getMeta());
|
||||||
|
|
||||||
$tag = $this->getNamedTag();
|
$tag = $this->getNamedTag();
|
||||||
if($tag->count() > 0){
|
if($tag->count() > 0){
|
||||||
$result->setTag("tag", $tag);
|
$result->setTag(self::TAG_TAG, $tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($slot !== -1){
|
if($slot !== -1){
|
||||||
$result->setByte("Slot", $slot);
|
$result->setByte(self::TAG_SLOT, $slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -677,14 +723,14 @@ class Item implements \JsonSerializable{
|
|||||||
* @throws SavedDataLoadingException
|
* @throws SavedDataLoadingException
|
||||||
*/
|
*/
|
||||||
public static function nbtDeserialize(CompoundTag $tag) : Item{
|
public static function nbtDeserialize(CompoundTag $tag) : Item{
|
||||||
if($tag->getTag("id") === null || $tag->getTag("Count") === null){
|
if($tag->getTag(self::TAG_ID) === null || $tag->getTag(self::TAG_COUNT) === null){
|
||||||
return VanillaItems::AIR();
|
return VanillaItems::AIR();
|
||||||
}
|
}
|
||||||
|
|
||||||
$count = Binary::unsignByte($tag->getByte("Count"));
|
$count = Binary::unsignByte($tag->getByte(self::TAG_COUNT));
|
||||||
$meta = $tag->getShort("Damage", 0);
|
$meta = $tag->getShort(self::TAG_DAMAGE, 0);
|
||||||
|
|
||||||
$idTag = $tag->getTag("id");
|
$idTag = $tag->getTag(self::TAG_ID);
|
||||||
if($idTag instanceof ShortTag){
|
if($idTag instanceof ShortTag){
|
||||||
$item = ItemFactory::getInstance()->get($idTag->getValue(), $meta, $count);
|
$item = ItemFactory::getInstance()->get($idTag->getValue(), $meta, $count);
|
||||||
}elseif($idTag instanceof StringTag){ //PC item save format
|
}elseif($idTag instanceof StringTag){ //PC item save format
|
||||||
@ -699,7 +745,7 @@ class Item implements \JsonSerializable{
|
|||||||
throw new SavedDataLoadingException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($idTag) . " given");
|
throw new SavedDataLoadingException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($idTag) . " given");
|
||||||
}
|
}
|
||||||
|
|
||||||
$itemNBT = $tag->getCompoundTag("tag");
|
$itemNBT = $tag->getCompoundTag(self::TAG_TAG);
|
||||||
if($itemNBT !== null){
|
if($itemNBT !== null){
|
||||||
$item->setNamedTag(clone $itemNBT);
|
$item->setNamedTag(clone $itemNBT);
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,10 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\item;
|
namespace pocketmine\item;
|
||||||
|
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
|
use pocketmine\utils\Filesystem;
|
||||||
use pocketmine\utils\SingletonTrait;
|
use pocketmine\utils\SingletonTrait;
|
||||||
use pocketmine\utils\Utils;
|
|
||||||
use Symfony\Component\Filesystem\Path;
|
use Symfony\Component\Filesystem\Path;
|
||||||
use function explode;
|
use function explode;
|
||||||
use function file_get_contents;
|
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function is_int;
|
use function is_int;
|
||||||
use function is_numeric;
|
use function is_numeric;
|
||||||
@ -53,7 +52,7 @@ final class LegacyStringToItemParser{
|
|||||||
private static function make() : self{
|
private static function make() : self{
|
||||||
$result = new self(ItemFactory::getInstance());
|
$result = new self(ItemFactory::getInstance());
|
||||||
|
|
||||||
$mappingsRaw = Utils::assumeNotFalse(@file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, 'item_from_string_bc_map.json')), "Missing required resource file");
|
$mappingsRaw = Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, 'item_from_string_bc_map.json'));
|
||||||
|
|
||||||
$mappings = json_decode($mappingsRaw, true);
|
$mappings = json_decode($mappingsRaw, true);
|
||||||
if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array");
|
if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array");
|
||||||
|
@ -53,6 +53,7 @@ final class StringToEnchantmentParser extends StringToTParser{
|
|||||||
$result->register("respiration", fn() => VanillaEnchantments::RESPIRATION());
|
$result->register("respiration", fn() => VanillaEnchantments::RESPIRATION());
|
||||||
$result->register("sharpness", fn() => VanillaEnchantments::SHARPNESS());
|
$result->register("sharpness", fn() => VanillaEnchantments::SHARPNESS());
|
||||||
$result->register("silk_touch", fn() => VanillaEnchantments::SILK_TOUCH());
|
$result->register("silk_touch", fn() => VanillaEnchantments::SILK_TOUCH());
|
||||||
|
$result->register("swift_sneak", fn() => VanillaEnchantments::SWIFT_SNEAK());
|
||||||
$result->register("thorns", fn() => VanillaEnchantments::THORNS());
|
$result->register("thorns", fn() => VanillaEnchantments::THORNS());
|
||||||
$result->register("unbreaking", fn() => VanillaEnchantments::UNBREAKING());
|
$result->register("unbreaking", fn() => VanillaEnchantments::UNBREAKING());
|
||||||
$result->register("vanishing", fn() => VanillaEnchantments::VANISHING());
|
$result->register("vanishing", fn() => VanillaEnchantments::VANISHING());
|
||||||
|
@ -49,6 +49,7 @@ use pocketmine\utils\RegistryTrait;
|
|||||||
* @method static Enchantment RESPIRATION()
|
* @method static Enchantment RESPIRATION()
|
||||||
* @method static SharpnessEnchantment SHARPNESS()
|
* @method static SharpnessEnchantment SHARPNESS()
|
||||||
* @method static Enchantment SILK_TOUCH()
|
* @method static Enchantment SILK_TOUCH()
|
||||||
|
* @method static Enchantment SWIFT_SNEAK()
|
||||||
* @method static Enchantment THORNS()
|
* @method static Enchantment THORNS()
|
||||||
* @method static Enchantment UNBREAKING()
|
* @method static Enchantment UNBREAKING()
|
||||||
* @method static Enchantment VANISHING()
|
* @method static Enchantment VANISHING()
|
||||||
@ -95,6 +96,8 @@ final class VanillaEnchantments{
|
|||||||
self::register("MENDING", new Enchantment(KnownTranslationFactory::enchantment_mending(), Rarity::RARE, ItemFlags::NONE, ItemFlags::ALL, 1));
|
self::register("MENDING", new Enchantment(KnownTranslationFactory::enchantment_mending(), Rarity::RARE, ItemFlags::NONE, ItemFlags::ALL, 1));
|
||||||
|
|
||||||
self::register("VANISHING", new Enchantment(KnownTranslationFactory::enchantment_curse_vanishing(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::ALL, 1));
|
self::register("VANISHING", new Enchantment(KnownTranslationFactory::enchantment_curse_vanishing(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::ALL, 1));
|
||||||
|
|
||||||
|
self::register("SWIFT_SNEAK", new Enchantment(KnownTranslationFactory::enchantment_swift_sneak(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::LEGS, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function register(string $name, Enchantment $member) : void{
|
protected static function register(string $name, Enchantment $member) : void{
|
||||||
|
@ -661,6 +661,12 @@ final class KnownTranslationFactory{
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function death_attack_fireworks(Translatable|string $param0) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_FIREWORKS, [
|
||||||
|
0 => $param0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public static function death_attack_generic(Translatable|string $param0) : Translatable{
|
public static function death_attack_generic(Translatable|string $param0) : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [
|
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [
|
||||||
0 => $param0,
|
0 => $param0,
|
||||||
@ -760,6 +766,10 @@ final class KnownTranslationFactory{
|
|||||||
return new Translatable(KnownTranslationKeys::DISCONNECTIONSCREEN_INVALIDSKIN, []);
|
return new Translatable(KnownTranslationKeys::DISCONNECTIONSCREEN_INVALIDSKIN, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function disconnectionScreen_loggedinOtherLocation() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::DISCONNECTIONSCREEN_LOGGEDINOTHERLOCATION, []);
|
||||||
|
}
|
||||||
|
|
||||||
public static function disconnectionScreen_noReason() : Translatable{
|
public static function disconnectionScreen_noReason() : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::DISCONNECTIONSCREEN_NOREASON, []);
|
return new Translatable(KnownTranslationKeys::DISCONNECTIONSCREEN_NOREASON, []);
|
||||||
}
|
}
|
||||||
@ -1166,6 +1176,10 @@ final class KnownTranslationFactory{
|
|||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION, []);
|
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_command_dumpmemory_description() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_DUMPMEMORY_DESCRIPTION, []);
|
||||||
|
}
|
||||||
|
|
||||||
public static function pocketmine_command_effect_description() : Translatable{
|
public static function pocketmine_command_effect_description() : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_EFFECT_DESCRIPTION, []);
|
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_EFFECT_DESCRIPTION, []);
|
||||||
}
|
}
|
||||||
@ -1574,6 +1588,51 @@ final class KnownTranslationFactory{
|
|||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_DEBUG_ENABLE, []);
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DEBUG_ENABLE, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_ban(Translatable|string $reason) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_BAN, [
|
||||||
|
"reason" => $reason,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_ban_hardcore() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_BAN_HARDCORE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_ban_ip() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_BAN_IP, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_ban_noReason() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_BAN_NOREASON, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_error(Translatable|string $error, Translatable|string $errorId) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_ERROR, [
|
||||||
|
"error" => $error,
|
||||||
|
"errorId" => $errorId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_error_authentication() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_ERROR_AUTHENTICATION, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_error_badPacket() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_ERROR_BADPACKET, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_error_internal() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_ERROR_INTERNAL, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_error_loginTimeout() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_ERROR_LOGINTIMEOUT, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_error_respawn() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_ERROR_RESPAWN, []);
|
||||||
|
}
|
||||||
|
|
||||||
public static function pocketmine_disconnect_incompatibleProtocol(Translatable|string $param0) : Translatable{
|
public static function pocketmine_disconnect_incompatibleProtocol(Translatable|string $param0) : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_INCOMPATIBLEPROTOCOL, [
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_INCOMPATIBLEPROTOCOL, [
|
||||||
0 => $param0,
|
0 => $param0,
|
||||||
@ -1602,6 +1661,28 @@ final class KnownTranslationFactory{
|
|||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_INVALIDSESSION_TOOLATE, []);
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_INVALIDSESSION_TOOLATE, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_kick(Translatable|string $reason) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_KICK, [
|
||||||
|
"reason" => $reason,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_kick_noReason() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_KICK_NOREASON, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_transfer() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_TRANSFER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_whitelisted() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_WHITELISTED, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_disconnect_xblImpersonation() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_DISCONNECT_XBLIMPERSONATION, []);
|
||||||
|
}
|
||||||
|
|
||||||
public static function pocketmine_level_ambiguousFormat(Translatable|string $param0) : Translatable{
|
public static function pocketmine_level_ambiguousFormat(Translatable|string $param0) : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_AMBIGUOUSFORMAT, [
|
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_AMBIGUOUSFORMAT, [
|
||||||
0 => $param0,
|
0 => $param0,
|
||||||
@ -1707,6 +1788,274 @@ final class KnownTranslationFactory{
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_network_session_close(Translatable|string $reason) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_NETWORK_SESSION_CLOSE, [
|
||||||
|
"reason" => $reason,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_network_session_open() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_NETWORK_SESSION_OPEN, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_network_session_playerName(Translatable|string $playerName) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_NETWORK_SESSION_PLAYERNAME, [
|
||||||
|
"playerName" => $playerName,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_broadcast_admin() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_BROADCAST_ADMIN, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_broadcast_user() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_BROADCAST_USER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_ban_ip() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_BAN_IP, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_ban_list() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_BAN_LIST, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_ban_player() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_BAN_PLAYER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_clear_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_CLEAR_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_clear_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_CLEAR_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_defaultgamemode() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_DEFAULTGAMEMODE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_difficulty() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_DIFFICULTY, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_dumpmemory() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_DUMPMEMORY, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_effect_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_EFFECT_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_effect_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_EFFECT_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_enchant_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_ENCHANT_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_enchant_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_ENCHANT_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_gamemode_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_GAMEMODE_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_gamemode_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_GAMEMODE_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_gc() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_GC, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_give_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_GIVE_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_give_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_GIVE_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_help() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_HELP, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_kick() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_KICK, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_kill_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_KILL_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_kill_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_KILL_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_list() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_LIST, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_me() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_ME, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_op_give() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_OP_GIVE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_op_take() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_OP_TAKE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_particle() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_PARTICLE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_plugins() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_PLUGINS, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_save_disable() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SAVE_DISABLE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_save_enable() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SAVE_ENABLE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_save_perform() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SAVE_PERFORM, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_say() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SAY, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_seed() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SEED, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_setworldspawn() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SETWORLDSPAWN, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_spawnpoint_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SPAWNPOINT_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_spawnpoint_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_SPAWNPOINT_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_status() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_STATUS, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_stop() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_STOP, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_teleport_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TELEPORT_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_teleport_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TELEPORT_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_tell() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TELL, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_time_add() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TIME_ADD, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_time_query() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TIME_QUERY, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_time_set() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TIME_SET, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_time_start() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TIME_START, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_time_stop() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TIME_STOP, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_timings() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TIMINGS, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_title_other() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TITLE_OTHER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_title_self() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TITLE_SELF, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_transferserver() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_TRANSFERSERVER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_unban_ip() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_UNBAN_IP, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_unban_player() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_UNBAN_PLAYER, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_version() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_VERSION, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_whitelist_add() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_ADD, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_whitelist_disable() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_DISABLE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_whitelist_enable() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_ENABLE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_whitelist_list() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_LIST, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_whitelist_reload() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_RELOAD, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_command_whitelist_remove() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_group_console() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_CONSOLE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_group_operator() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_OPERATOR, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_permission_group_user() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_USER, []);
|
||||||
|
}
|
||||||
|
|
||||||
public static function pocketmine_player_invalidEntity(Translatable|string $param0) : Translatable{
|
public static function pocketmine_player_invalidEntity(Translatable|string $param0) : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLAYER_INVALIDENTITY, [
|
return new Translatable(KnownTranslationKeys::POCKETMINE_PLAYER_INVALIDENTITY, [
|
||||||
0 => $param0,
|
0 => $param0,
|
||||||
@ -2241,6 +2590,12 @@ final class KnownTranslationFactory{
|
|||||||
return new Translatable(KnownTranslationKeys::QUERY_WARNING2, []);
|
return new Translatable(KnownTranslationKeys::QUERY_WARNING2, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function record_nowPlaying(Translatable|string $param0) : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::RECORD_NOWPLAYING, [
|
||||||
|
0 => $param0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public static function server_port() : Translatable{
|
public static function server_port() : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::SERVER_PORT, []);
|
return new Translatable(KnownTranslationKeys::SERVER_PORT, []);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user