mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
Compare commits
294 Commits
5.13.0
...
gameplay-p
Author | SHA1 | Date | |
---|---|---|---|
a6042ec7e8 | |||
f1a3b42620 | |||
f3763ae691 | |||
12214792b3 | |||
e494460cfd | |||
2c299e2b49 | |||
ac1e70cd96 | |||
0bbd4af496 | |||
b0bfc30b07 | |||
a91cef37f6 | |||
26afa97cdc | |||
57082c8148 | |||
74ee38ab99 | |||
df069b0418 | |||
ea43fd1917 | |||
93a9007f3c | |||
a593180ef9 | |||
61560ec375 | |||
89f88a9333 | |||
9cab72ed59 | |||
2bd9f4108b | |||
ca5d9c3731 | |||
cbcc4d24e0 | |||
223fd74255 | |||
0fef4c6683 | |||
cd2a1fdc1d | |||
a396233e57 | |||
d666f17ec6 | |||
a84326cb2d | |||
0543bf301e | |||
603ab081bc | |||
daacc8eddb | |||
269effcecf | |||
ae750b60d0 | |||
905a10e980 | |||
52fe2cb97f | |||
8cdc7d7ee1 | |||
12ae8dc03b | |||
a9787f0d99 | |||
5325ecee37 | |||
30ee0aa63d | |||
aef4fa7174 | |||
53aa380ca3 | |||
a5f607138c | |||
8338ebaffd | |||
082119cfd2 | |||
7460e12b6a | |||
a53b0116a0 | |||
5b72f202bf | |||
8b6adf86d6 | |||
0070426e97 | |||
a523ed6e40 | |||
7c2ed7d884 | |||
1e3a858de6 | |||
844ba0ff9f | |||
49bdaee930 | |||
9195c88670 | |||
ae19d05fd5 | |||
bf7a53b00f | |||
e710b3750f | |||
8ccd1edb17 | |||
faf1e26bac | |||
b4259ef988 | |||
b2aa6396c3 | |||
1555fa17e7 | |||
e77f2c5198 | |||
e8e441f739 | |||
48a908ee8c | |||
dc87a2b10e | |||
d3add78d3e | |||
ff695a5f97 | |||
4b630cb726 | |||
4331f69b9c | |||
fb1213e964 | |||
8a693f2a4c | |||
8474eaf5f1 | |||
a75d4687ce | |||
0b0c425805 | |||
b5469dede2 | |||
e0d270a870 | |||
2d9cee3d62 | |||
ed64231c57 | |||
33a7b46329 | |||
9b58d35516 | |||
3629ee7e7b | |||
fbeb017670 | |||
09bf203267 | |||
f3cc4a28e1 | |||
fe70150db2 | |||
054538e6b7 | |||
3586bc42a9 | |||
1f86949836 | |||
053a71c59d | |||
07d5046b83 | |||
4a702b97fd | |||
d2c3b8dacb | |||
fa3529966f | |||
2ff6470792 | |||
05a9e9c76e | |||
231eec911f | |||
8c04d47b1b | |||
b3b8aaddff | |||
2173aab967 | |||
e598364f06 | |||
734ca1cc6b | |||
96b12bddc1 | |||
84464cde4f | |||
72fc138631 | |||
c63d0ef1b6 | |||
82c416624d | |||
9e19391f20 | |||
c0b74b0341 | |||
3c96e72f7d | |||
0376e37966 | |||
94dff74494 | |||
0065fe649f | |||
8ef5e737de | |||
e7d8d99ca6 | |||
414e8acf8c | |||
d372af351a | |||
4814db4fe7 | |||
d01203d7c4 | |||
847f931660 | |||
22718c4971 | |||
acbfb0a3e9 | |||
7f9e79c83e | |||
7e343617b9 | |||
d945cbf517 | |||
c4e72c880e | |||
3ef7001d8e | |||
f1b1a7022d | |||
59d14de1d8 | |||
c8f567b093 | |||
5cc1068cd4 | |||
f6e6f15c63 | |||
4e6b34f573 | |||
a4a07a8e5a | |||
0a7cbdd56d | |||
a5babb2c9f | |||
49c2f13cf0 | |||
60cac18104 | |||
f6e2a1ecce | |||
00e39821f0 | |||
72d941fc1b | |||
8cb2e577a1 | |||
2a7b183ab8 | |||
e9b597af6c | |||
9381fc4172 | |||
281afb6838 | |||
ede363eb0f | |||
bdbcfd10cc | |||
e6f9cdd990 | |||
5241118f0b | |||
93a270d251 | |||
a7638cf914 | |||
c44e6c59fc | |||
92f380bb9c | |||
c32744ebc7 | |||
e3baf3cddb | |||
9176b2494a | |||
0f365886e0 | |||
8c3cf7a687 | |||
994fa5f792 | |||
3ed9615180 | |||
f5ab2979a0 | |||
929cd63135 | |||
585dc835e7 | |||
13f5cc9f87 | |||
1ce774ae73 | |||
d077bda30c | |||
e017780cc3 | |||
25c66e4c8b | |||
ee17ac5246 | |||
33dc995cc7 | |||
26761c2b87 | |||
5a926a79cb | |||
c4a2b6494d | |||
2aa64dc15e | |||
d0d7a995fb | |||
be2437ac6e | |||
bdb5845cec | |||
54e7749c0b | |||
237677c028 | |||
787afb6b00 | |||
df4ada81e5 | |||
a96f1a5083 | |||
1f510caf88 | |||
32be474840 | |||
f750b01d8b | |||
31484ee5c1 | |||
786d84a9e1 | |||
d6c48fd3a2 | |||
85606925a1 | |||
ef9791eaed | |||
accaa0acce | |||
616f96a703 | |||
824e270041 | |||
37bf4bc0b0 | |||
5d60ba36b7 | |||
68d2e2915e | |||
2ffc38c835 | |||
20f5741ed7 | |||
f6c0b228ec | |||
23b2b75acf | |||
25ea9b2218 | |||
77db7a8941 | |||
af4294295b | |||
b342c497d1 | |||
428e38f913 | |||
5ae13209c0 | |||
585aee9386 | |||
433bd6a8aa | |||
22a1549998 | |||
0ec8465fcf | |||
f121654452 | |||
08c6e63aac | |||
4c418b4318 | |||
fb9a74e879 | |||
554775841e | |||
373dd9938c | |||
f772bb7384 | |||
5ef247620a | |||
1b082f99e9 | |||
371eccd007 | |||
9b6a0c9945 | |||
ab3be50b49 | |||
27dc43f131 | |||
d67f5a5c6f | |||
ed158f8a1b | |||
d70a7d34a7 | |||
be6754494f | |||
d273ccf87c | |||
737f5066a0 | |||
10238d7934 | |||
6077748caa | |||
50e2c469a5 | |||
fa87602661 | |||
d3c9c137ad | |||
37322e0d50 | |||
55cf24aa02 | |||
3590d84d03 | |||
68f8fa8caf | |||
1ad190024a | |||
769a149057 | |||
ea339355bb | |||
b9288c238b | |||
16f29c775e | |||
e30e27dd57 | |||
cd6634d34b | |||
f013079ff6 | |||
c4abac4606 | |||
11fbc8db6f | |||
022362a01a | |||
98380e46bf | |||
dad9a7e6cd | |||
de6a91dabc | |||
0615afa766 | |||
d5919dc094 | |||
09904dc519 | |||
f799cfaba6 | |||
11f119551d | |||
2584314202 | |||
337e462c8f | |||
b680a1693c | |||
0e5395c59b | |||
94e0bf954b | |||
556b00d11f | |||
981f49ff56 | |||
f527a4c8fe | |||
7148c7a222 | |||
e31fd122d9 | |||
a835069564 | |||
b77193b987 | |||
11ca208d93 | |||
8d7f1a8557 | |||
7ff0ae19d6 | |||
1de66cb0de | |||
9f3533d870 | |||
2d24fac067 | |||
a6202d0442 | |||
8ec304e66e | |||
ac8dbf8640 | |||
dbc7105e5b | |||
3b97d067a3 | |||
781e3643dd | |||
5ad63f27bb | |||
f13eaaab05 | |||
b9a1ef1357 | |||
4abc36275c | |||
4b5ac53276 | |||
90409b50d1 | |||
bc2abf4b15 | |||
e1ae9a7d69 | |||
10a962daa2 |
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @pmmp/server-developers
|
10
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
10
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
@ -7,13 +7,13 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- tell us what you want -->
|
||||
## Description
|
||||
<!--- Describe the problem you want to solve -->
|
||||
## Problem description
|
||||
|
||||
|
||||
<!--- explain why you want this and why it's a good idea -->
|
||||
## Justification
|
||||
<!--- Describe what changes you want to make to solve this problem -->
|
||||
## Proposed solution
|
||||
|
||||
|
||||
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
|
||||
## Alternative methods
|
||||
## Alternative solutions that don't require API changes
|
||||
|
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,43 +1,32 @@
|
||||
## Introduction
|
||||
<!-- Summarize your PR here. Keep it short and simple. -->
|
||||
<!-- Explain existing problems or why this pull request is necessary -->
|
||||
|
||||
### Relevant issues
|
||||
<!-- List relevant issues here -->
|
||||
### Related issues & PRs
|
||||
<!--
|
||||
|
||||
* Fixes #1
|
||||
* Fixes #2
|
||||
|
||||
* Related to #2
|
||||
-->
|
||||
|
||||
## Changes
|
||||
### API changes
|
||||
<!-- Any additions to the API that should be documented in release notes? -->
|
||||
<!-- If not, you can delete this section -->
|
||||
|
||||
### Behavioural changes
|
||||
<!-- Any change in how the server behaves, or its performance? -->
|
||||
<!-- If not, you can delete this section -->
|
||||
|
||||
## Backwards compatibility
|
||||
<!-- Any possible backwards incompatible changes? How are they solved, or how can they be solved? -->
|
||||
<!-- If not, you can delete this section -->
|
||||
|
||||
## Follow-up
|
||||
<!-- Suggest any actions to be done before/after merging this pull request -->
|
||||
<!--
|
||||
|
||||
Requires translations:
|
||||
|
||||
| Name | Value in eng.ini |
|
||||
| :--: | :---: |
|
||||
| `foo.bar` | `Foo bar` |
|
||||
|
||||
-->
|
||||
<!-- For example, future changes that this PR lays the groundwork for -->
|
||||
|
||||
## Tests
|
||||
<!--
|
||||
PRs which have not been tested MUST be marked as draft.
|
||||
If this PR affects gameplay or user experience in some way, it must be manually tested.
|
||||
Include any screenshots or videos of manual testing here.
|
||||
Any test plugin code should also be pasted here if it can't be adapted to a PHPUnit test.
|
||||
-->
|
||||
I tested this PR by doing the following (tick all that apply):
|
||||
- [ ] Writing PHPUnit tests (commit these in the `tests/phpunit` folder)
|
||||
- [ ] Playtesting using a Minecraft client (provide screenshots or a video)
|
||||
- [ ] Writing a test plugin (provide the code and sample output)
|
||||
- [ ] Other (provide details)
|
||||
|
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@ -12,6 +12,22 @@ updates:
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
groups:
|
||||
production-patch-updates:
|
||||
dependency-type: production
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "patch"
|
||||
development-patch-updates:
|
||||
dependency-type: development
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "patch"
|
||||
phpstan:
|
||||
patterns:
|
||||
- "phpstan/*"
|
||||
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
|
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
||||
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v5.2.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
2
.github/workflows/discord-release-notify.yml
vendored
2
.github/workflows/discord-release-notify.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.30.0
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
with:
|
||||
php-version: 8.2
|
||||
|
||||
|
65
.github/workflows/draft-release-from-pr.yml
vendored
Normal file
65
.github/workflows/draft-release-from-pr.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: Draft release from PR
|
||||
|
||||
on:
|
||||
#presume that pull_request_target is safe at this point, since the PR was approved and merged
|
||||
#we need write access to prepare the release & create comments
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
branches:
|
||||
- stable
|
||||
- minor-next
|
||||
- major-next
|
||||
- "legacy/*"
|
||||
paths:
|
||||
- "src/VersionInfo.php"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check release
|
||||
uses: ./.github/workflows/draft-release-pr-check.yml
|
||||
|
||||
draft:
|
||||
name: Create GitHub draft release
|
||||
needs: [check]
|
||||
if: needs.check.outputs.valid == 'true'
|
||||
|
||||
uses: ./.github/workflows/draft-release.yml
|
||||
|
||||
post-draft-url-comment:
|
||||
name: Post draft release URL as comment
|
||||
needs: [draft]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Post draft release URL on PR
|
||||
uses: thollander/actions-comment-pull-request@v3
|
||||
with:
|
||||
message: "[Draft release ${{ needs.draft.outputs.version }}](${{ needs.draft.outputs.draft-url }}) has been created for commit ${{ github.sha }}. Please review and publish it."
|
||||
|
||||
trigger-post-release-workflow:
|
||||
name: Trigger post-release RestrictedActions workflow
|
||||
# Not sure if needs is actually needed here
|
||||
needs: [check]
|
||||
if: needs.check.outputs.valid == 'true'
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
|
||||
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: RestrictedActions
|
||||
|
||||
- name: Dispatch post-release restricted action
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
repository: ${{ github.repository_owner }}/RestrictedActions
|
||||
event-type: pocketmine_mp_post_release
|
||||
client-payload: '{"branch": "${{ github.ref }}"}'
|
13
.github/workflows/draft-release-from-tag.yml
vendored
Normal file
13
.github/workflows/draft-release-from-tag.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
#Allows creating a release by pushing a tag
|
||||
#This might be useful for retroactive releases
|
||||
name: Draft release from git tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: "*"
|
||||
|
||||
jobs:
|
||||
draft:
|
||||
name: Create GitHub draft release
|
||||
if: "startsWith(github.event.head_commit.message, 'Release ')"
|
||||
uses: ./.github/workflows/draft-release.yml
|
111
.github/workflows/draft-release-pr-check.yml
vendored
Normal file
111
.github/workflows/draft-release-pr-check.yml
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
name: Release PR checks
|
||||
|
||||
on:
|
||||
#do checks on every PR update
|
||||
pull_request:
|
||||
branches:
|
||||
- stable
|
||||
- minor-next
|
||||
- major-next
|
||||
- "legacy/*"
|
||||
paths:
|
||||
- "src/VersionInfo.php"
|
||||
|
||||
#allow this workflow to be invoked on PR merge, prior to creating the release
|
||||
workflow_call:
|
||||
outputs:
|
||||
valid:
|
||||
description: Whether this commit is valid for release
|
||||
value: ${{ jobs.check-intent.outputs.valid && jobs.check-validity.result == 'success' }}
|
||||
|
||||
permissions:
|
||||
contents: read #for user access check
|
||||
|
||||
jobs:
|
||||
check-intent:
|
||||
name: Check release trigger
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
outputs:
|
||||
valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check IS_DEVELOPMENT_BUILD flag
|
||||
id: validate
|
||||
run: |
|
||||
echo DEV_BUILD=$(sed -n "s/^\s*public const IS_DEVELOPMENT_BUILD = \(true\|false\);$/\1/p" src/VersionInfo.php) >> $GITHUB_OUTPUT
|
||||
|
||||
check-validity:
|
||||
name: Validate release info
|
||||
needs: [check-intent]
|
||||
#don't do these checks if this isn't a release - we don't want to generate unnecessary failed statuses
|
||||
if: needs.check-intent.outputs.valid == 'true'
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
with:
|
||||
php-version: 8.2
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
|
||||
|
||||
- name: Check author permissions
|
||||
id: check-permission
|
||||
uses: actions-cool/check-user-permission@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
require: write
|
||||
username: ${{ github.event.pull_request.user.login }}
|
||||
#technically this would be fine for dependabot but generally bots don't count as team members
|
||||
check-bot: true
|
||||
|
||||
- name: Abort if user permissions are insufficient
|
||||
#user doesn't have permission or is a bot
|
||||
if: steps.check-permission.outputs.require-result != 'true' || steps.check-permission.outputs.check-result != 'false'
|
||||
run: |
|
||||
echo "::error::This user is not authorized to trigger releases"
|
||||
exit 1
|
||||
|
||||
- name: Check changelog file is present
|
||||
id: file-presence
|
||||
run: |
|
||||
CHANGELOG_FILE="changelogs/$(php build/dump-version-info.php changelog_file_name)"
|
||||
if [ ! -f "${{ github.workspace }}/$CHANGELOG_FILE" ]; then
|
||||
echo "::error::$CHANGELOG_FILE does not exist"
|
||||
exit 1
|
||||
fi
|
||||
echo FILE="$CHANGELOG_FILE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check header is present in changelog file
|
||||
run: |
|
||||
FILE="${{ steps.file-presence.outputs.FILE }}"
|
||||
VERSION="$(php build/dump-version-info.php base_version)"
|
||||
if ! grep -Fqx "# $VERSION" "${{ github.workspace }}/$FILE"; then
|
||||
echo "::error::Header for $VERSION not found in $FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check version is valid for the selected channel
|
||||
run: |
|
||||
CHANNEL="$(php build/dump-version-info.php channel)"
|
||||
if [ "$(php build/dump-version-info.php suffix_valid)" != "true" ]; then
|
||||
echo "::error::Version $(php build/dump-version-info.php base_version) is not allowed on the $CHANNEL channel"
|
||||
exit 1
|
||||
fi
|
42
.github/workflows/draft-release.yml
vendored
42
.github/workflows/draft-release.yml
vendored
@ -1,26 +1,36 @@
|
||||
name: Draft release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: "*"
|
||||
workflow_call:
|
||||
outputs:
|
||||
draft-url:
|
||||
description: 'The URL of the draft release'
|
||||
value: ${{ jobs.draft.outputs.draft-url }}
|
||||
version:
|
||||
description: 'PocketMine-MP version'
|
||||
value: ${{ jobs.draft.outputs.version }}
|
||||
|
||||
jobs:
|
||||
draft:
|
||||
name: Create GitHub draft release
|
||||
if: "startsWith(github.event.head_commit.message, 'Release ')"
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version: [8.2]
|
||||
|
||||
outputs:
|
||||
draft-url: ${{ steps.create-draft.outputs.html_url }}
|
||||
version: ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.30.0
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
|
||||
@ -53,17 +63,17 @@ jobs:
|
||||
- name: Get PocketMine-MP release version
|
||||
id: get-pm-version
|
||||
run: |
|
||||
echo PM_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;') >> $GITHUB_OUTPUT
|
||||
echo MCPE_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;') >> $GITHUB_OUTPUT
|
||||
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 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
|
||||
echo PM_VERSION=$(php build/dump-version-info.php base_version) >> $GITHUB_OUTPUT
|
||||
echo PM_MAJOR=$(php build/dump-version-info.php major_version) >> $GITHUB_OUTPUT
|
||||
echo MCPE_VERSION=$(php build/dump-version-info.php mcpe_version) >> $GITHUB_OUTPUT
|
||||
echo CHANGELOG_FILE_NAME=$(php build/dump-version-info.php changelog_file_name) >> $GITHUB_OUTPUT
|
||||
echo CHANGELOG_MD_HEADER=$(php build/dump-version-info.php changelog_md_header) >> $GITHUB_OUTPUT
|
||||
echo PRERELEASE=$(php build/dump-version-info.php prerelease) >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate PHP binary download URL
|
||||
id: php-binary-url
|
||||
run: |
|
||||
echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/php-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT
|
||||
echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-php-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate build info
|
||||
run: |
|
||||
@ -76,6 +86,9 @@ jobs:
|
||||
${{ steps.php-binary-url.outputs.PHP_BINARY_URL }} \
|
||||
> build_info.json
|
||||
|
||||
- name: Generate core permission doc for doc.pmmp.io
|
||||
run: php tools/generate-permission-doc.php rst
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -84,20 +97,23 @@ jobs:
|
||||
${{ github.workspace }}/PocketMine-MP.phar
|
||||
${{ github.workspace }}/start.*
|
||||
${{ github.workspace }}/build_info.json
|
||||
${{ github.workspace }}/core-permissions.rst
|
||||
|
||||
- name: Create draft release
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
id: create-draft
|
||||
with:
|
||||
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,${{ github.workspace }}/core-permissions.rst
|
||||
commit: ${{ github.sha }}
|
||||
draft: true
|
||||
prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }}
|
||||
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
skipIfReleaseExists: true #for release PRs, tags will be created on release publish and trigger the tag release workflow - don't create a second draft
|
||||
body: |
|
||||
**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 }}${{ steps.get-pm-version.outputs.CHANGELOG_SUFFIX }}.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.CHANGELOG_FILE_NAME }}#${{ steps.get-pm-version.outputs.CHANGELOG_MD_HEADER }}) for details.
|
||||
|
||||
:information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}).
|
||||
|
21
.github/workflows/main-php-matrix.yml
vendored
21
.github/workflows/main-php-matrix.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
@ -62,7 +62,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
@ -96,7 +96,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
@ -128,7 +128,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
@ -147,17 +147,8 @@ jobs:
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Regenerate registry annotations
|
||||
run: php build/generate-registry-annotations.php src
|
||||
|
||||
- name: Regenerate KnownTranslation APIs
|
||||
run: php build/generate-known-translation-apis.php
|
||||
|
||||
- name: Regenerate BedrockData available files constants
|
||||
run: php build/generate-bedrockdata-path-consts.php
|
||||
|
||||
- name: Regenerate YmlServerProperties constants
|
||||
run: php build/generate-pocketmine-yml-property-consts.php
|
||||
- name: Update generated code
|
||||
run: composer update-codegen
|
||||
|
||||
- name: Verify code is unchanged
|
||||
run: |
|
||||
|
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.30.0
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
with:
|
||||
php-version: 8.2
|
||||
tools: php-cs-fixer:3.49
|
||||
@ -37,3 +37,15 @@ jobs:
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff --ansi
|
||||
|
||||
shellcheck:
|
||||
name: ShellCheck
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
|
33
.github/workflows/pr-remove-waiting-label.yml
vendored
Normal file
33
.github/workflows/pr-remove-waiting-label.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: Remove waiting label from PRs
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: synchronize
|
||||
|
||||
jobs:
|
||||
delabel:
|
||||
name: Remove label
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Remove label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
script: |
|
||||
const [owner, repo] = context.payload.repository.full_name.split('/');
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: context.payload.number,
|
||||
name: "Status: Waiting on Author",
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
//probably label wasn't set on the issue
|
||||
console.log('Failed to remove label (probably label isn\'t on the PR): ' + error.message);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
38
.github/workflows/team-pr-auto-approve.yml
vendored
Normal file
38
.github/workflows/team-pr-auto-approve.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
#Due to GitHub awkwardness, it's not easy to reduce the review requirement for collaborators.
|
||||
#Our policy is that 2 collaborators should be aware of every change.
|
||||
#For outside PRs, this means 2 collaborator reviews.
|
||||
#For PRs made by collaborators, this means 1 reviewer + the author.
|
||||
#We trust that collaborators don't need as much oversight.
|
||||
name: Auto approve collaborator PRs
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- ready_for_review
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
name: Request approval
|
||||
runs-on: ubuntu-latest
|
||||
if: '! github.event.pull_request.draft'
|
||||
|
||||
steps:
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
|
||||
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: RestrictedActions
|
||||
|
||||
- name: Dispatch restricted action
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
repository: ${{ github.repository_owner }}/RestrictedActions
|
||||
event-type: auto_approve_collaborator_pr
|
||||
client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}", "reviewer_id": "0" }'
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -53,3 +53,6 @@ Documentation/*
|
||||
# php-cs-fixer
|
||||
/.php_cs.cache
|
||||
/.php-cs-fixer.cache
|
||||
|
||||
# install-local-protocol.sh
|
||||
/composer-local-protocol.*
|
||||
|
87
build/dump-version-info.php
Normal file
87
build/dump-version-info.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?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);
|
||||
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\VersionInfo;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
/*
|
||||
* Dumps version info in a machine-readable format for use in GitHub Actions workflows
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var string[]|\Closure[] $options
|
||||
* @phpstan-var array<string, string|\Closure() : string> $options
|
||||
*/
|
||||
$options = [
|
||||
"base_version" => VersionInfo::BASE_VERSION,
|
||||
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0],
|
||||
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
||||
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"changelog_file_name" => function() : string{
|
||||
$version = VersionInfo::VERSION();
|
||||
$result = $version->getMajor() . "." . $version->getMinor();
|
||||
$suffix = $version->getSuffix();
|
||||
if($suffix !== ""){
|
||||
if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
|
||||
fwrite(STDERR, "error: invalid current version suffix \"$suffix\"; aborting" . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
$baseSuffix = $matches[1];
|
||||
$result .= "-" . strtolower($baseSuffix);
|
||||
}
|
||||
return $result . ".md";
|
||||
},
|
||||
"changelog_md_header" => fn() : string => str_replace(".", "", VersionInfo::BASE_VERSION),
|
||||
"prerelease" => fn() : bool => VersionInfo::VERSION()->getSuffix() !== "",
|
||||
"channel" => VersionInfo::BUILD_CHANNEL,
|
||||
"suffix_valid" => function() : bool{
|
||||
//TODO: maybe this should be put into its own script?
|
||||
$suffix = VersionInfo::VERSION()->getSuffix();
|
||||
if(VersionInfo::BUILD_CHANNEL === "stable"){
|
||||
//stable builds may not have suffixes
|
||||
return $suffix === "";
|
||||
}
|
||||
if(VersionInfo::BUILD_CHANNEL === "alpha" || VersionInfo::BUILD_CHANNEL === "beta"){
|
||||
$upperChannel = strtoupper(VersionInfo::BUILD_CHANNEL);
|
||||
$upperSuffix = strtoupper($suffix);
|
||||
return str_starts_with($upperSuffix, $upperChannel) && is_numeric(substr($upperSuffix, strlen($upperChannel)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
];
|
||||
if(count($argv) !== 2 || !isset($options[$argv[1]])){
|
||||
fwrite(STDERR, "Please provide an option (one of: " . implode(", ", array_keys($options)) . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$result = $options[$argv[1]];
|
||||
if($result instanceof Closure){
|
||||
$result = $result();
|
||||
}
|
||||
if(is_bool($result)){
|
||||
echo $result ? "true" : "false";
|
||||
}else{
|
||||
echo $result;
|
||||
}
|
@ -122,7 +122,7 @@ if(!is_array($ids)){
|
||||
throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object");
|
||||
}
|
||||
$cleanedIds = [];
|
||||
foreach($ids as $name => $id){
|
||||
foreach(Utils::promoteKeys($ids) as $name => $id){
|
||||
if(!is_string($name) || !is_int($id)){
|
||||
throw new \RuntimeException("Invalid biome ID map, expected string => int map");
|
||||
}
|
||||
|
@ -102,6 +102,25 @@ function generateClassHeader(string $className) : string{
|
||||
return <<<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 $namespace;
|
||||
|
@ -61,6 +61,25 @@ function generateItemIds(ItemTypeDictionary $dictionary, BlockItemIdMap $blockIt
|
||||
fwrite($file, <<<'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\item;
|
||||
|
@ -42,7 +42,7 @@ $constants = [];
|
||||
* @phpstan-param-out array<string, string> $constants
|
||||
*/
|
||||
function collectProperties(string $prefix, array $properties, array &$constants) : void{
|
||||
foreach($properties as $propertyName => $property){
|
||||
foreach(Utils::promoteKeys($properties) as $propertyName => $property){
|
||||
$fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName;
|
||||
|
||||
$constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName));
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\update_registry_annotations;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function basename;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
@ -48,6 +49,7 @@ if(count($argv) !== 2){
|
||||
|
||||
/**
|
||||
* @param object[] $members
|
||||
* @phpstan-param array<string, object> $members
|
||||
*/
|
||||
function generateMethodAnnotations(string $namespaceName, array $members) : string{
|
||||
$selfName = basename(__FILE__);
|
||||
@ -60,7 +62,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
|
||||
|
||||
static $lineTmpl = " * @method static %2\$s %s()";
|
||||
$memberLines = [];
|
||||
foreach($members as $name => $member){
|
||||
foreach(Utils::stringifyKeys($members) as $name => $member){
|
||||
$reflect = new \ReflectionClass($member);
|
||||
while($reflect !== false && $reflect->isAnonymous()){
|
||||
$reflect = $reflect->getParentClass();
|
||||
|
@ -86,7 +86,8 @@ function systemWrapper(string $command, string $errorMessage) : void{
|
||||
|
||||
function main() : void{
|
||||
$filteredOpts = [];
|
||||
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help"])) as $optName => $optValue){
|
||||
$postCommitOnly = false;
|
||||
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help", "post"])) as $optName => $optValue){
|
||||
if($optName === "help"){
|
||||
fwrite(STDOUT, "Options:\n");
|
||||
|
||||
@ -96,6 +97,10 @@ function main() : void{
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
if($optName === "post"){
|
||||
$postCommitOnly = true;
|
||||
continue;
|
||||
}
|
||||
if(!is_string($optValue)){
|
||||
fwrite(STDERR, "--$optName expects exactly 1 value\n");
|
||||
exit(1);
|
||||
@ -141,20 +146,25 @@ function main() : void{
|
||||
$channel ??= "stable";
|
||||
}
|
||||
|
||||
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
|
||||
echo "$currentVer will be published on release channel \"$channel\".\n";
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes");
|
||||
system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result);
|
||||
if($result === 0){
|
||||
echo "error: no changelog changes detected; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
|
||||
systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit");
|
||||
systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag");
|
||||
|
||||
if($postCommitOnly){
|
||||
echo "Skipping release commit & tag. Bumping to next version $nextVer directly.\n";
|
||||
}else{
|
||||
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
|
||||
echo "$currentVer will be published on release channel \"$channel\".\n";
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes");
|
||||
system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result);
|
||||
if($result === 0){
|
||||
echo "error: no changelog changes detected; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
|
||||
systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit");
|
||||
systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag");
|
||||
}
|
||||
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
|
||||
systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
|
||||
|
Submodule build/php updated: 6f619bf7a0...5016e0a3d5
168
build/server-phar-stub.php
Normal file
168
build/server-phar-stub.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?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\server_phar_stub;
|
||||
|
||||
use function clearstatcache;
|
||||
use function copy;
|
||||
use function fclose;
|
||||
use function fflush;
|
||||
use function flock;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function getmypid;
|
||||
use function hrtime;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function mkdir;
|
||||
use function number_format;
|
||||
use function str_replace;
|
||||
use function stream_get_contents;
|
||||
use function sys_get_temp_dir;
|
||||
use function tempnam;
|
||||
use function unlink;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const LOCK_EX;
|
||||
use const LOCK_NB;
|
||||
use const LOCK_UN;
|
||||
|
||||
/**
|
||||
* Finds the appropriate tmp directory to store the decompressed phar cache, accounting for potential file name
|
||||
* collisions.
|
||||
*/
|
||||
function preparePharCacheDirectory() : string{
|
||||
clearstatcache();
|
||||
|
||||
$i = 0;
|
||||
do{
|
||||
$tmpPath = sys_get_temp_dir() . '/PocketMine-MP-phar-cache.' . $i;
|
||||
$i++;
|
||||
}while(is_file($tmpPath));
|
||||
if(!@mkdir($tmpPath) && !is_dir($tmpPath)){
|
||||
throw new \RuntimeException("Failed to create temporary directory $tmpPath. Please ensure the disk has enough space and that the current user has permission to write to this location.");
|
||||
}
|
||||
|
||||
return $tmpPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes caches left behind by previous server instances.
|
||||
* This ensures that the tmp directory doesn't get flooded by servers crashing in restart loops.
|
||||
*/
|
||||
function cleanupPharCache(string $tmpPath) : void{
|
||||
clearstatcache();
|
||||
|
||||
/** @var string[] $matches */
|
||||
foreach(new \RegexIterator(
|
||||
new \FilesystemIterator(
|
||||
$tmpPath,
|
||||
\FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS
|
||||
),
|
||||
'/(.+)\.lock$/',
|
||||
\RegexIterator::GET_MATCH
|
||||
) as $matches){
|
||||
$lockFilePath = $matches[0];
|
||||
$baseTmpPath = $matches[1];
|
||||
|
||||
$file = @fopen($lockFilePath, "rb");
|
||||
if($file === false){
|
||||
//another process probably deleted the lock file already
|
||||
continue;
|
||||
}
|
||||
|
||||
if(flock($file, LOCK_EX | LOCK_NB)){
|
||||
//this tmpfile is no longer in use
|
||||
flock($file, LOCK_UN);
|
||||
fclose($file);
|
||||
|
||||
unlink($lockFilePath);
|
||||
unlink($baseTmpPath . ".tar");
|
||||
unlink($baseTmpPath);
|
||||
echo "Deleted stale phar cache at $baseTmpPath\n";
|
||||
}else{
|
||||
$pid = stream_get_contents($file);
|
||||
fclose($file);
|
||||
|
||||
echo "Phar cache at $baseTmpPath is still in use by PID $pid\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertPharToTar(string $tmpName, string $pharPath) : string{
|
||||
$tmpPharPath = $tmpName . ".phar";
|
||||
copy($pharPath, $tmpPharPath);
|
||||
|
||||
$phar = new \Phar($tmpPharPath);
|
||||
//phar requires phar.readonly=0, and zip doesn't support disabling compression - tar is the only viable option
|
||||
//we don't need phar anyway since we don't need to directly execute the file, only require files from inside it
|
||||
$phar->convertToData(\Phar::TAR, \Phar::NONE);
|
||||
unset($phar);
|
||||
\Phar::unlinkArchive($tmpPharPath);
|
||||
|
||||
return $tmpName . ".tar";
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a phar tmp cache to prevent it from being deleted by other server instances.
|
||||
* This code looks similar to Filesystem::createLockFile(), but we can't use that because it's inside the compressed
|
||||
* phar.
|
||||
*/
|
||||
function lockPharCache(string $lockFilePath) : void{
|
||||
//this static variable will keep the file(s) locked until the process ends
|
||||
static $lockFiles = [];
|
||||
|
||||
$lockFile = fopen($lockFilePath, "wb");
|
||||
if($lockFile === false){
|
||||
throw new \RuntimeException("Failed to open temporary file");
|
||||
}
|
||||
flock($lockFile, LOCK_EX); //this tells other server instances not to delete this cache file
|
||||
fwrite($lockFile, (string) getmypid()); //maybe useful for debugging
|
||||
fflush($lockFile);
|
||||
$lockFiles[$lockFilePath] = $lockFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a decompressed .tar of PocketMine-MP.phar in the system temp directory for loading code from.
|
||||
*
|
||||
* @return string path to the temporary decompressed phar (actually a .tar)
|
||||
*/
|
||||
function preparePharCache(string $tmpPath, string $pharPath) : string{
|
||||
clearstatcache();
|
||||
|
||||
$tmpName = tempnam($tmpPath, "PMMP");
|
||||
if($tmpName === false){
|
||||
throw new \RuntimeException("Failed to create temporary file");
|
||||
}
|
||||
|
||||
lockPharCache($tmpName . ".lock");
|
||||
return convertPharToTar($tmpName, $pharPath);
|
||||
}
|
||||
|
||||
$tmpDir = preparePharCacheDirectory();
|
||||
cleanupPharCache($tmpDir);
|
||||
echo "Preparing PocketMine-MP.phar decompressed cache...\n";
|
||||
$start = hrtime(true);
|
||||
$cacheName = preparePharCache($tmpDir, __FILE__);
|
||||
echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n";
|
||||
|
||||
require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php';
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\server_phar;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Git;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function dirname;
|
||||
@ -169,21 +171,7 @@ function main() : void{
|
||||
'git' => $gitHash,
|
||||
'build' => $build
|
||||
],
|
||||
<<<'STUB'
|
||||
<?php
|
||||
|
||||
$tmpDir = sys_get_temp_dir();
|
||||
if(!is_readable($tmpDir) or !is_writable($tmpDir)){
|
||||
echo "ERROR: tmpdir $tmpDir is not accessible." . PHP_EOL;
|
||||
echo "Check that the directory exists, and that the current user has read/write permissions for it." . PHP_EOL;
|
||||
echo "Alternatively, set 'sys_temp_dir' to a different directory in your php.ini file." . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require("phar://" . __FILE__ . "/src/PocketMine.php");
|
||||
__HALT_COMPILER();
|
||||
STUB
|
||||
,
|
||||
Filesystem::fileGetContents(Path::join(__DIR__, 'server-phar-stub.php')) . "\n__HALT_COMPILER();",
|
||||
\Phar::SHA1,
|
||||
\Phar::GZ
|
||||
) as $line){
|
||||
|
94
changelogs/5.14.md
Normal file
94
changelogs/5.14.md
Normal file
@ -0,0 +1,94 @@
|
||||
# 5.14.0
|
||||
Released 5th April 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.70**
|
||||
|
||||
This is a minor feature release, including performance improvements, minor gameplay features, new API features, and various internal improvements.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for a `--no-log-file` command-line option, which disables the creation of a `server.log` file.
|
||||
- **Use this with caution.** If you don't have another mechanism for collecting logs (e.g. Docker), this may make debugging harder.
|
||||
- Added support for automatic `server.log` rotation. When the `server.log` exceeds 32 MB, it will be renamed and moved to the `log_archive` folder in the server's data directory.
|
||||
- Files in the `log_archive` folder can be safely modified or deleted without stopping the server.
|
||||
- We suggest a cron job or similar to manage old log files (e.g. deleting or compressing them).
|
||||
- Added a new cache mechanism for `PocketMine-MP.phar`. This has several advantages:
|
||||
- Caches are now reused by all threads - this significantly reduces `/tmp` usage (previously every thread generated its own cache, wasting lots of space)
|
||||
- Dead cache files are automatically cleaned up by new servers - this means that a server crash loop won't flood `/tmp` anymore
|
||||
- `/status` now reports a more accurate number of threads on Windows.
|
||||
- Large resource packs are now able to be properly downloaded from the server.
|
||||
- Larger player skin sizes are now accepted by the server.
|
||||
- Improved logging from world providers to reduce spam when chunks contain invalid data.
|
||||
- Added more error logging for Anvil, PMAnvil and MCRegion worlds.
|
||||
- PHP deprecation warnings no longer cause the server to crash. This should make it easier for server owners to update to newer PHP versions.
|
||||
|
||||
## Performance
|
||||
- Improved world loading performance. This was achieved through a combination of changes:
|
||||
- Improvements to `BlockStateUpgrader` to avoid unnecessary work
|
||||
- Improvements to `BlockStateUpgradeSchema` to clean up stupid code
|
||||
- Improvements to `BlockStateReader` unused state handling
|
||||
- Optimizations to `RegistryTrait` (see below)
|
||||
- Improved performance of `RegistryTrait::__callStatic()` accessor by introducing a fast-path optimization. Ensure that you access registries with the correct function name case to benefit from this.
|
||||
- This improves the performance of `VanillaBlocks::WHATEVER()`, `VanillaItems`, etc.
|
||||
|
||||
## Tools
|
||||
- `tools/generate-blockstate-upgrade-schema.php` now supports generating schemas using `flattenedValueRemaps` (described in [BlockStateUpgradeSchema](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/4.0.0)).
|
||||
|
||||
## Gameplay
|
||||
- Added sounds for armour equipping and unequipping.
|
||||
- Added sound for picking berries from a sweet berry bush.
|
||||
|
||||
## API
|
||||
### `pocketmine\block\utils`
|
||||
- The following enum cases have been added:
|
||||
- `BannerPatternType::GLOBE`
|
||||
- `BannerPatternType::PIGLIN`
|
||||
|
||||
### `pocketmine\event\player`
|
||||
- The following classes have been added:
|
||||
- `PlayerResourcePackOfferEvent` - called before the server tells a connecting client which resource packs are available to download - allows customizing the pack list and other options
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following API methods have been added:
|
||||
- `public ArmorMaterial->getEquipSound() : ?\pocketmine\world\Sound` - returns the sound to play when this armour is equipped or unequipped
|
||||
- The following API methods have signature changes:
|
||||
- `ArmorMaterial->__construct()` now accepts an optional `?Sound $equipSound` parameter
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following API methods have signature changes:
|
||||
- `MainLogger->__construct()` now accepts `null` for the `$logFile` parameter - this disables the creation of a logger thread and log file
|
||||
- `MainLogger->__construct()` now accepts an optional `?string $logArchiveDir` parameter. If set, this enables log archiving in the specified directory when the current log file exceeds 32 MB.
|
||||
|
||||
## Dependencies
|
||||
- Now uses [`pocketmine/bedrock-block-upgrade-schema` version 4.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/4.0.0).
|
||||
- Now uses [`pmmp/ext-pmmpthread` version 6.1.0](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.1.0).
|
||||
- Now uses [`pocketmine/errorhandler` version 0.7.0](https://github.com/pmmp/ErrorHandler/releases/tag/0.7.0).
|
||||
- Now uses [`pocketmine/raklib` version 1.1.0](https://github.com/pmmp/RakLib/releases/tag/1.1.0).
|
||||
- Now uses [`pocketmine/raklib-ipc` version 1.0.0](https://github.com/pmmp/RakLibIpc/releases/tag/1.0.0).
|
||||
|
||||
## Internals
|
||||
- (Re)Added support for RakLib packet ACK receipts. This was used to throttle resource pack sending and prevent network overloading.
|
||||
- Added `NetworkSession->sendDataPacketWithReceipt()` to make use of this feature.
|
||||
- `PacketSender` now requires an additional `?int $receiptId` parameter.
|
||||
- `ResourcePackPacketHandler` now uses `sendDataPacketWithReceipt()` to send resource packs, and delays sending the next chunk until the current one is acknowledged.
|
||||
- `ResourcePackPacketHandler` now accepts resource pack info directly in the constructor, instead of `ResourcePackManager`. This eases the implementation of `PlayerResourcePackOfferEvent`.
|
||||
- Increased `ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE` to 8 MB (previously 2 MB). While this weakens server security, it appears to be necessary to deal with extremely bloated Persona skins.
|
||||
- Increased max split packet parts accepted by `RakLib` to 512 (previously 128). Again, this is necessary to deal with extremely bloated Persona skins.
|
||||
- Added a new cache mechanism for `PocketMine-MP.phar`.
|
||||
- `ext-phar`'s default mechanism is extremely wasteful (generating a separate cache file per thread), and doesn't clean up after itself.
|
||||
- The new cache mechanism is shared between all threads, and automatically cleans up stale caches.
|
||||
- The phar stub (`build/server-phar-stub.php`) now converts the phar contents into a `.tar`, and decompresses all the files into `$TMPDIR/PocketMine-MP-phar-cache.<random>/`.
|
||||
- `phar://` URIs still work with this system, but `new Phar(__FILE__)` must be replaced by `new PharData(__FILE__)` within PocketMine-MP core code.
|
||||
- Backtraces from a `phar`'d server will now point to a location in the extracted phar cache, rather than the phar itself.
|
||||
- `block_factory_consistency_check` test (actually for `RuntimeBlockStateRegistry`) now stores less data, and is no longer affected by changes to internal state ID construction.
|
||||
|
||||
# 5.14.1
|
||||
Released 5th April 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed incorrect `pmmpthread` version check in server bootstrap.
|
16
changelogs/5.15.md
Normal file
16
changelogs/5.15.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 5.15.0
|
||||
Released 25th April 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.80**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.80.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.20.80.
|
||||
- Removed support for earlier versions.
|
26
changelogs/5.16.md
Normal file
26
changelogs/5.16.md
Normal file
@ -0,0 +1,26 @@
|
||||
# 5.16.0
|
||||
Released 13th June 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.21.0**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.0.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.21.0.
|
||||
- Removed support for earlier versions.
|
||||
- Generated permission docs are now included with every release.
|
||||
- Crash throttle message (which appears when the server crashed after being up for less than 120 seconds) now shows the server uptime as well as the wait time. This should make it clearer how the wait time is decided.
|
||||
|
||||
## Tools
|
||||
- Added `install-local-protocol.sh` script. This allows installing local copies of protocol dependencies without needing to create releases. Useful for integration testing when doing protocol updates.
|
||||
|
||||
## Fixes
|
||||
- Attacking an entity with a higher damage weapon while it's on attack cooldown from a lower damage weapon (switching) no longer causes additional knockback to the victim.
|
||||
- Wooden stairs can now be used as fuel in furnaces.
|
||||
- Fixed incorrect description of the permission `pocketmine.command.save.perform`.
|
38
changelogs/5.17.md
Normal file
38
changelogs/5.17.md
Normal file
@ -0,0 +1,38 @@
|
||||
# 5.17.0
|
||||
Released 10th July 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.21.2**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.2.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.21.2.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## API
|
||||
### `pocketmine\player`
|
||||
- The following methods have been added:
|
||||
- `public function closeAllForms() : void` - closes the current viewing form and forms in queue.
|
||||
|
||||
## Fixes
|
||||
- Bowl can now be used as fuel.
|
||||
- Bells always drops themselves even when using an incompatible tool.
|
||||
|
||||
# 5.17.1
|
||||
Released 13th August 2024.
|
||||
|
||||
## Documentation
|
||||
- Added a note about `BlockStateData::CURRENT_VERSION`.
|
||||
|
||||
## Fixes
|
||||
- Fixed anvil placement rotation to match vanilla.
|
||||
- Fixed outdated `BedrockWorldData` version, this was preventing use newer worlds.
|
||||
|
||||
## Internals
|
||||
- Dependabot: PHPStan and patch updates are now grouped into a single PR.
|
30
changelogs/5.18.md
Normal file
30
changelogs/5.18.md
Normal file
@ -0,0 +1,30 @@
|
||||
# 5.18.0
|
||||
Released 16th August 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.21.20**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.20.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.21.20.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Fixes
|
||||
- Use `VISIBLE_MOB_EFFECTS` actor metadata property to send effect bubbles, this fixes effect bubbles not showing
|
||||
|
||||
# 5.18.1
|
||||
Released 3rd September 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed shift-crafting.
|
||||
- Blue Ice block no longer emits light & it's now dropped when mined with a tool with silk touch enchantment.
|
||||
|
||||
## Internals
|
||||
- Pull Requests from team members now get an approval automatically. This means that if a team member makes a PR, only one other approval should be needed.
|
||||
- Added [ShellCheck](https://github.com/koalaman/shellcheck) to the CI tests.
|
16
changelogs/5.19.md
Normal file
16
changelogs/5.19.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 5.19.0
|
||||
Released 21st September 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.21.30**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.30.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.21.30.
|
||||
- Removed support for earlier versions.
|
25
changelogs/5.20.md
Normal file
25
changelogs/5.20.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 5.20.0
|
||||
Released 26th October 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.21.40**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.40.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.21.40.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed a bug in `tools/generate-blockstate-upgrade-schema.php` that caused it to fail on 1.21.40 with the new mushroom block changes.
|
||||
|
||||
# 5.20.1
|
||||
Released 31st October 2024.
|
||||
|
||||
## Fixes
|
||||
- Workaround old mob heads in world saves not being upgraded correctly and causing crashes.
|
128
changelogs/5.21.md
Normal file
128
changelogs/5.21.md
Normal file
@ -0,0 +1,128 @@
|
||||
# 5.21.0
|
||||
Released 3rd November 2024.
|
||||
|
||||
This is a minor feature release, including gameplay features and minor internals improvements.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**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.
|
||||
|
||||
## Gameplay
|
||||
- Added the following new blocks:
|
||||
- Campfire
|
||||
- Chiseled Copper
|
||||
- Chiseled Tuff
|
||||
- Chiseled Tuff Bricks
|
||||
- Copper Bulb
|
||||
- Copper Door
|
||||
- Copper Grate
|
||||
- Copper Trapdoor
|
||||
- Polished Tuff, Slabs, Stairs and Walls
|
||||
- Soul Campfire
|
||||
- Tuff Bricks, Slabs, Stairs and Walls
|
||||
- Tuff Slab, Stairs and Walls
|
||||
- Added the following new types of painting:
|
||||
- backyard
|
||||
- baroque
|
||||
- bouquet
|
||||
- cavebird
|
||||
- changing
|
||||
- cotan
|
||||
- endboss
|
||||
- fern
|
||||
- finding
|
||||
- humble
|
||||
- lowmist
|
||||
- meditative
|
||||
- orb
|
||||
- owlemons
|
||||
- passage
|
||||
- pond
|
||||
- prairie_ride
|
||||
- sunflowers
|
||||
- tides
|
||||
- unpacked
|
||||
- Armor slots are now properly restricted (on the server side) to only contain the appropriate type of armor or headwear.
|
||||
- Implemented Aqua Affinity enchantment. Since the server doesn't currently enforce any movement restrictions in water, this enchantment works based on client-side behaviour only.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- The following new API methods have been added:
|
||||
- `public ChiseledBookshelf->getLastInteractedSlot() : ?ChiseledBookshelfSlot`
|
||||
- `public ChiseledBookshelf->setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : $this`
|
||||
- The following new classes have been added:
|
||||
- `utils\CopperMaterial` - interface implemented by all copper-like blocks with oxidation and waxed properties
|
||||
- `CopperBulb`
|
||||
- `CopperDoor`
|
||||
- `CopperGrate`
|
||||
- `CopperTrapdoor`
|
||||
- `SoulCampfire`
|
||||
- `Campfire`
|
||||
- The following enums have new cases:
|
||||
- `utils\BannerPatternType` has new cases `FLOW` and `GUSTER`
|
||||
|
||||
### `pocketmine\crafting`
|
||||
- The following enums have new cases:
|
||||
- `FurnaceType` has new cases `CAMPFIRE` and `SOUL_CAMPFIRE`
|
||||
|
||||
### `pocketmine\event`
|
||||
- The following new classes have been added:
|
||||
- `block\CampfireCookEvent` - called when a campfire finishes cooking an item
|
||||
|
||||
### `pocketmine\inventory`
|
||||
- Added support for slot validators, which permit restricting the types of items a player can put into an inventory slot.
|
||||
- The following new classes have been added:
|
||||
- `transaction\action\SlotValidator` - interface
|
||||
- `transaction\action\CallbackSlotValidator` - class allowing a closure to be used for slot content validation
|
||||
- `SlotValidatedInventory` - implemented by inventories which support the use of slot validators
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new API methods have been added:
|
||||
- `public Item->getCooldownTag() : ?string` - returns the cooldown group this item belongs to, used for ensuring that, for example, different types of goat horns all respect a general horn cooldown
|
||||
- The following new classes have been added:
|
||||
- `ItemCooldownTags` - list of cooldown group tags used by PocketMine-MP
|
||||
|
||||
### `pocketmine\world\sound`
|
||||
- The following new classes have been added
|
||||
- `CampfireSound` - sound made by campfires while lit
|
||||
|
||||
## Tools
|
||||
- `tools/blockstate-upgrade-schema-utils.php` (formerly `generate-blockstate-upgrade-schema.php`) has several improvements:
|
||||
- Support for generating `flattenedProperties` rules as per [BedrockBlockUpgradeSchema 5.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/5.0.0)
|
||||
- Improved criteria for flattened property selection to minimize the amount of rules required
|
||||
- Several subcommands are now available:
|
||||
- `generate` - generates a schema from provided data
|
||||
- `update` - regenerates an existing schema in a newer format
|
||||
- `update-all` - regenerates a folder of existing schemas in a newer format (useful for updating `BedrockBlockUpgradeSchema` en masse)
|
||||
- `test` - verifies that a schema produces the results expected by provided data
|
||||
|
||||
## Internals
|
||||
- Fixed incorrect visibility of `createEntity` in spawn eggs.
|
||||
- Added support for newer `BedrockBlockUpgradeSchema` in `BlockStateUpgrader`.
|
||||
|
||||
# 5.21.1
|
||||
Released 12th November 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when applying a cooldown to an item with 1 count.
|
||||
- Fixed garbage collector cycle count increase on player disconnect.
|
||||
- Fixed weakness effect being applied to all attack types, causing damage splash potions to become weaker.
|
||||
- Fixed Enchanted Golden Apple regeneration effect amplifier to match vanilla.
|
||||
|
||||
# 5.21.2
|
||||
Released 29th November 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed blocks destroyable by water being able to be placed inside water.
|
||||
- Fixed deprecation warnings about nullable typehints on PHP 8.4.
|
||||
- Fixed `Utils::getNiceClosureName()` not working correctly on PHP 8.4.
|
||||
- Fixed incorrect break animations when breaking the block behind an instantly-breakable block like a torch.
|
||||
- Fixed candle extinguish logic.
|
||||
- Fixed various documentation issues around array key types.
|
||||
- Introduced a new PHPStan rule along with `Utils::promoteKeys()` to improve PHPStan error reporting around unspecified array key types. Previously, some errors were missed due to PHPStan's BenevolentUnionType.
|
||||
|
||||
## DevOps
|
||||
- `pmmp/server-developers` team is now automatically requested for review on any new PR.
|
||||
- `Status: Waiting on Author` label is now automatically removed from PRs when they are updated.
|
@ -22,7 +22,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pmmpthread": "^6.0.7",
|
||||
"ext-pmmpthread": "^6.1.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
@ -33,29 +33,29 @@
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.6.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-data": "~2.9.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.8.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-protocol": "~29.0.0+bedrock-1.20.70",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.19.0",
|
||||
"pocketmine/errorhandler": "^0.7.0",
|
||||
"pocketmine/locale-data": "~2.22.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/math": "~1.0.0",
|
||||
"pocketmine/nbt": "~1.0.0",
|
||||
"pocketmine/raklib": "^0.15.0",
|
||||
"pocketmine/raklib-ipc": "^0.2.0",
|
||||
"pocketmine/raklib": "~1.1.0",
|
||||
"pocketmine/raklib-ipc": "~1.0.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"symfony/filesystem": "~6.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.60",
|
||||
"phpstan/phpstan": "1.11.11",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
|
||||
"phpunit/phpunit": "^10.5.24"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -83,11 +83,14 @@
|
||||
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
|
||||
"@php -dphar.readonly=0 build/server-phar.php"
|
||||
],
|
||||
"update-registry-annotations": [
|
||||
"update-codegen": [
|
||||
"@php build/generate-bedrockdata-path-consts.php",
|
||||
"@php build/generate-biome-ids.php",
|
||||
"@php build/generate-block-serializer-consts.php vendor/pocketmine/bedrock-data/canonical_block_states.nbt",
|
||||
"@php build/generate-item-type-names.php vendor/pocketmine/bedrock-data/required_item_list.json",
|
||||
"@php build/generate-known-translation-apis.php",
|
||||
"@php build/generate-pocketmine-yml-property-consts.php",
|
||||
"@php build/generate-registry-annotations.php src"
|
||||
],
|
||||
"update-translation-apis": [
|
||||
"@php build/generate-known-translation-apis.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
381
composer.lock
generated
381
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e861861cb1f632e1db21efc6875a0aba",
|
||||
"content-hash": "c57e8f52250edfd03906219fe14fc240",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -67,25 +67,25 @@
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brick/math.git",
|
||||
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
|
||||
"reference": "f510c0a40911935b77b86859eb5223d58d660df1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
|
||||
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
|
||||
"reference": "f510c0a40911935b77b86859eb5223d58d660df1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"vimeo/psalm": "5.0.0"
|
||||
"phpunit/phpunit": "^10.1",
|
||||
"vimeo/psalm": "5.16.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -105,12 +105,17 @@
|
||||
"arithmetic",
|
||||
"bigdecimal",
|
||||
"bignum",
|
||||
"bignumber",
|
||||
"brick",
|
||||
"math"
|
||||
"decimal",
|
||||
"integer",
|
||||
"math",
|
||||
"mathematics",
|
||||
"rational"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brick/math/issues",
|
||||
"source": "https://github.com/brick/math/tree/0.11.0"
|
||||
"source": "https://github.com/brick/math/tree/0.12.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -118,20 +123,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-15T23:15:59+00:00"
|
||||
"time": "2023-11-29T23:19:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "3.6.0",
|
||||
"version": "5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d"
|
||||
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1496e275db5148cb96bdaa998115e5e31a5c1e4d",
|
||||
"reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
|
||||
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -142,22 +147,22 @@
|
||||
"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/3.6.0"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0"
|
||||
},
|
||||
"time": "2024-02-28T19:25:25+00:00"
|
||||
"time": "2024-11-03T14:13:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "2.9.0+bedrock-1.20.70",
|
||||
"version": "2.14.1+bedrock-1.21.40",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "10b6696b662fd80a282eff7dca6c99d321c5b9e3"
|
||||
"reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/10b6696b662fd80a282eff7dca6c99d321c5b9e3",
|
||||
"reference": "10b6696b662fd80a282eff7dca6c99d321c5b9e3",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/4a41864ed09613ecec6791e2ae076a8ec7089cc4",
|
||||
"reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -168,22 +173,22 @@
|
||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.70"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/2.14.1+bedrock-1.21.40"
|
||||
},
|
||||
"time": "2024-03-13T13:55:05+00:00"
|
||||
"time": "2024-11-12T21:36:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||
"version": "1.8.0",
|
||||
"version": "1.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||
"reference": "4c4dc3bbceb944c5de429b6e752ab7a15652078c"
|
||||
"reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/4c4dc3bbceb944c5de429b6e752ab7a15652078c",
|
||||
"reference": "4c4dc3bbceb944c5de429b6e752ab7a15652078c",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1cf81305f2ffcf7dde9577c4f16a55c765192b03",
|
||||
"reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -194,22 +199,22 @@
|
||||
"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.8.0"
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.1"
|
||||
},
|
||||
"time": "2024-02-28T19:25:53+00:00"
|
||||
"time": "2024-11-12T21:33:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "29.0.0+bedrock-1.20.70",
|
||||
"version": "35.0.0+bedrock-1.21.40",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "8d63f39bb2cded3d3e578fd3cf7bc769b9674857"
|
||||
"reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/8d63f39bb2cded3d3e578fd3cf7bc769b9674857",
|
||||
"reference": "8d63f39bb2cded3d3e578fd3cf7bc769b9674857",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
|
||||
"reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -222,7 +227,7 @@
|
||||
"ramsey/uuid": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.59",
|
||||
"phpstan/phpstan": "1.11.9",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5 || ^10.0"
|
||||
@ -240,9 +245,9 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/29.0.0+bedrock-1.20.70"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.40"
|
||||
},
|
||||
"time": "2024-03-13T14:35:54+00:00"
|
||||
"time": "2024-10-24T15:45:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -376,25 +381,25 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/errorhandler",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/ErrorHandler.git",
|
||||
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878"
|
||||
"reference": "cae94884368a74ece5294b9ff7fef18732dcd921"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/dae214a04348b911e8219ebf125ff1c5589cc878",
|
||||
"reference": "dae214a04348b911e8219ebf125ff1c5589cc878",
|
||||
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/cae94884368a74ece5294b9ff7fef18732dcd921",
|
||||
"reference": "cae94884368a74ece5294b9ff7fef18732dcd921",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"phpstan/phpstan": "~1.10.3",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -409,22 +414,22 @@
|
||||
"description": "Utilities to handle nasty PHP E_* errors in a usable way",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/ErrorHandler/issues",
|
||||
"source": "https://github.com/pmmp/ErrorHandler/tree/0.6.0"
|
||||
"source": "https://github.com/pmmp/ErrorHandler/tree/0.7.0"
|
||||
},
|
||||
"time": "2022-01-08T21:05:46+00:00"
|
||||
"time": "2024-04-02T18:29:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "2.19.6",
|
||||
"version": "2.22.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e"
|
||||
"reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e",
|
||||
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
|
||||
"reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -432,9 +437,9 @@
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/2.19.6"
|
||||
"source": "https://github.com/pmmp/Language/tree/2.22.0"
|
||||
},
|
||||
"time": "2023-08-08T16:53:23+00:00"
|
||||
"time": "2024-11-16T13:28:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
@ -616,28 +621,28 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.15.1",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca"
|
||||
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/79b7b4d1d7516dc6e322514453645ad9452b20ca",
|
||||
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97",
|
||||
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-sockets": "*",
|
||||
"php": "^8.0",
|
||||
"php": "^8.1",
|
||||
"php-64bit": "*",
|
||||
"php-ipv6": "*",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/log": "^0.3.0 || ^0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.9.17",
|
||||
"phpstan/phpstan": "1.10.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
@ -653,32 +658,32 @@
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.15.1"
|
||||
"source": "https://github.com/pmmp/RakLib/tree/1.1.1"
|
||||
},
|
||||
"time": "2023-03-07T15:10:34+00:00"
|
||||
"time": "2024-03-04T14:02:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib-ipc",
|
||||
"version": "0.2.0",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLibIpc.git",
|
||||
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c"
|
||||
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
|
||||
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/ce632ef2c6743e71eddb5dc329c49af6555f90bc",
|
||||
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php-64bit": "*",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/raklib": "^0.15.0"
|
||||
"pocketmine/raklib": "^0.15.0 || ^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.9.17",
|
||||
"phpstan/phpstan": "1.10.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
@ -694,9 +699,9 @@
|
||||
"description": "Channel-based protocols for inter-thread/inter-process communication with RakLib",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLibIpc/issues",
|
||||
"source": "https://github.com/pmmp/RakLibIpc/tree/0.2.0"
|
||||
"source": "https://github.com/pmmp/RakLibIpc/tree/1.0.1"
|
||||
},
|
||||
"time": "2023-02-13T13:40:40+00:00"
|
||||
"time": "2024-03-01T15:55:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
@ -829,20 +834,20 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.7.5",
|
||||
"version": "4.7.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e"
|
||||
"reference": "91039bc1faa45ba123c4328958e620d382ec7088"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
|
||||
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
|
||||
"reference": "91039bc1faa45ba123c4328958e620d382ec7088",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
|
||||
"ext-json": "*",
|
||||
"php": "^8.0",
|
||||
"ramsey/collection": "^1.2 || ^2.0"
|
||||
@ -905,7 +910,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.5"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -917,20 +922,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-11-08T05:53:05+00:00"
|
||||
"time": "2024-04-27T21:32:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.4.3",
|
||||
"version": "v6.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb"
|
||||
"reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
|
||||
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3",
|
||||
"reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -938,6 +943,9 @@
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-mbstring": "~1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/process": "^5.4|^6.4|^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -964,7 +972,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.3"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.13"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -980,24 +988,24 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-23T14:51:35+00:00"
|
||||
"time": "2024-10-25T15:07:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
|
||||
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
@ -1043,7 +1051,7 @@
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1059,24 +1067,24 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.29.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
|
||||
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
@ -1123,7 +1131,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1139,22 +1147,22 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-29T20:11:03+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.11.1",
|
||||
"version": "1.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1162,11 +1170,12 @@
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.6.8",
|
||||
"doctrine/common": "<2.13.3 || >=3,<3.2.2"
|
||||
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.6.8",
|
||||
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||
"phpspec/prophecy": "^1.10",
|
||||
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
@ -1192,7 +1201,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1200,20 +1209,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-03-08T13:26:56+00:00"
|
||||
"time": "2024-11-08T17:47:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.0.2",
|
||||
"version": "v5.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
|
||||
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
|
||||
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
|
||||
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1224,7 +1233,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"ircmaxell/php-yacc": "^0.0.7",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
"phpunit/phpunit": "^9.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
@ -1256,9 +1265,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
|
||||
},
|
||||
"time": "2024-03-05T20:51:40+00:00"
|
||||
"time": "2024-10-08T18:51:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1380,16 +1389,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.60",
|
||||
"version": "1.11.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe"
|
||||
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
|
||||
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3",
|
||||
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1432,31 +1441,27 @@
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-07T13:30:19+00:00"
|
||||
"time": "2024-08-19T14:37:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
"version": "1.3.16",
|
||||
"version": "1.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95"
|
||||
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d5242a59d035e46774f2e634b374bc39ff62cb95",
|
||||
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11",
|
||||
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"phpstan/phpstan": "^1.10"
|
||||
"phpstan/phpstan": "^1.11"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<7.0"
|
||||
@ -1488,27 +1493,27 @@
|
||||
"description": "PHPUnit extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.16"
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0"
|
||||
},
|
||||
"time": "2024-02-23T09:51:20+00:00"
|
||||
"time": "2024-04-20T06:39:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542"
|
||||
"reference": "363f921dd8441777d4fc137deb99beb486c77df1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542",
|
||||
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1",
|
||||
"reference": "363f921dd8441777d4fc137deb99beb486c77df1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"phpstan/phpstan": "^1.10.34"
|
||||
"phpstan/phpstan": "^1.11"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
@ -1537,38 +1542,38 @@
|
||||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.0"
|
||||
},
|
||||
"time": "2023-10-30T14:35:06+00:00"
|
||||
"time": "2024-04-20T06:37:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "10.1.14",
|
||||
"version": "10.1.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
|
||||
"reference": "7e308268858ed6baedc8704a304727d20bc07c77"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
|
||||
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
|
||||
"reference": "7e308268858ed6baedc8704a304727d20bc07c77",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"nikic/php-parser": "^4.19.1 || ^5.1.0",
|
||||
"php": ">=8.1",
|
||||
"phpunit/php-file-iterator": "^4.0",
|
||||
"phpunit/php-text-template": "^3.0",
|
||||
"sebastian/code-unit-reverse-lookup": "^3.0",
|
||||
"sebastian/complexity": "^3.0",
|
||||
"sebastian/environment": "^6.0",
|
||||
"sebastian/lines-of-code": "^2.0",
|
||||
"sebastian/version": "^4.0",
|
||||
"theseer/tokenizer": "^1.2.0"
|
||||
"phpunit/php-file-iterator": "^4.1.0",
|
||||
"phpunit/php-text-template": "^3.0.1",
|
||||
"sebastian/code-unit-reverse-lookup": "^3.0.0",
|
||||
"sebastian/complexity": "^3.2.0",
|
||||
"sebastian/environment": "^6.1.0",
|
||||
"sebastian/lines-of-code": "^2.0.2",
|
||||
"sebastian/version": "^4.0.1",
|
||||
"theseer/tokenizer": "^1.2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.1"
|
||||
@ -1580,7 +1585,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "10.1-dev"
|
||||
"dev-main": "10.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1609,7 +1614,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1617,7 +1622,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-12T15:33:41+00:00"
|
||||
"time": "2024-08-22T04:31:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -1864,16 +1869,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "10.3.5",
|
||||
"version": "10.5.38",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503"
|
||||
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503",
|
||||
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132",
|
||||
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1883,26 +1888,26 @@
|
||||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"myclabs/deep-copy": "^1.10.1",
|
||||
"phar-io/manifest": "^2.0.3",
|
||||
"phar-io/version": "^3.0.2",
|
||||
"myclabs/deep-copy": "^1.12.0",
|
||||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.1",
|
||||
"phpunit/php-code-coverage": "^10.1.5",
|
||||
"phpunit/php-file-iterator": "^4.0",
|
||||
"phpunit/php-invoker": "^4.0",
|
||||
"phpunit/php-text-template": "^3.0",
|
||||
"phpunit/php-timer": "^6.0",
|
||||
"sebastian/cli-parser": "^2.0",
|
||||
"sebastian/code-unit": "^2.0",
|
||||
"sebastian/comparator": "^5.0",
|
||||
"sebastian/diff": "^5.0",
|
||||
"sebastian/environment": "^6.0",
|
||||
"sebastian/exporter": "^5.1",
|
||||
"sebastian/global-state": "^6.0.1",
|
||||
"sebastian/object-enumerator": "^5.0",
|
||||
"sebastian/recursion-context": "^5.0",
|
||||
"sebastian/type": "^4.0",
|
||||
"sebastian/version": "^4.0"
|
||||
"phpunit/php-code-coverage": "^10.1.16",
|
||||
"phpunit/php-file-iterator": "^4.1.0",
|
||||
"phpunit/php-invoker": "^4.0.0",
|
||||
"phpunit/php-text-template": "^3.0.1",
|
||||
"phpunit/php-timer": "^6.0.0",
|
||||
"sebastian/cli-parser": "^2.0.1",
|
||||
"sebastian/code-unit": "^2.0.0",
|
||||
"sebastian/comparator": "^5.0.3",
|
||||
"sebastian/diff": "^5.1.1",
|
||||
"sebastian/environment": "^6.1.0",
|
||||
"sebastian/exporter": "^5.1.2",
|
||||
"sebastian/global-state": "^6.0.2",
|
||||
"sebastian/object-enumerator": "^5.0.0",
|
||||
"sebastian/recursion-context": "^5.0.0",
|
||||
"sebastian/type": "^4.0.0",
|
||||
"sebastian/version": "^4.0.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "To be able to generate mocks based on WSDL files"
|
||||
@ -1913,7 +1918,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "10.3-dev"
|
||||
"dev-main": "10.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1945,7 +1950,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1961,7 +1966,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-09-19T05:42:37+00:00"
|
||||
"time": "2024-10-28T13:06:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -2133,16 +2138,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
"version": "5.0.1",
|
||||
"version": "5.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||
"reference": "2db5010a484d53ebf536087a70b4a5423c102372"
|
||||
"reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372",
|
||||
"reference": "2db5010a484d53ebf536087a70b4a5423c102372",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
|
||||
"reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2153,7 +2158,7 @@
|
||||
"sebastian/exporter": "^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.3"
|
||||
"phpunit/phpunit": "^10.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -2198,7 +2203,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
||||
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2206,7 +2211,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-14T13:18:12+00:00"
|
||||
"time": "2024-10-18T14:56:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/complexity",
|
||||
@ -2335,16 +2340,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "6.0.1",
|
||||
"version": "6.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951"
|
||||
"reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951",
|
||||
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
|
||||
"reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2359,7 +2364,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "6.0-dev"
|
||||
"dev-main": "6.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2387,7 +2392,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||
"security": "https://github.com/sebastianbergmann/environment/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/6.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2395,7 +2400,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-11T05:39:26+00:00"
|
||||
"time": "2024-03-23T08:47:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
@ -2953,7 +2958,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pmmpthread": "^6.0.7",
|
||||
"ext-pmmpthread": "^6.1.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
|
21
install-local-protocol.sh
Normal file
21
install-local-protocol.sh
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "--- Installing BedrockProtocol, BedrockData, BedrockBlockUpgradeSchema, BedrockItemUpgradeSchema dependencies from local repositories."
|
||||
echo "--- This allows you to perform integration tests using PocketMine-MP, without immediately publishing new versions of these libraries."
|
||||
|
||||
cp composer.json composer-local-protocol.json
|
||||
cp composer.lock composer-local-protocol.lock
|
||||
|
||||
export COMPOSER=composer-local-protocol.json
|
||||
composer config repositories.bedrock-protocol path ../deps/BedrockProtocol
|
||||
composer config repositories.bedrock-data path ../deps/BedrockData
|
||||
composer config repositories.bedrock-block-upgrade-schema path ../deps/BedrockBlockUpgradeSchema
|
||||
composer config repositories.bedrock-item-upgrade-schema path ../deps/BedrockItemUpgradeSchema
|
||||
|
||||
composer require pocketmine/bedrock-protocol:*@dev pocketmine/bedrock-data:*@dev pocketmine/bedrock-block-upgrade-schema:*@dev pocketmine/bedrock-item-upgrade-schema:*@dev
|
||||
|
||||
composer install
|
||||
|
||||
echo "--- Local dependencies have been successfully installed."
|
||||
echo "--- This script does not modify composer.json. To go back to the original dependency versions, simply run 'composer install'."
|
||||
|
@ -40,6 +40,7 @@ parameters:
|
||||
- build/php
|
||||
dynamicConstantNames:
|
||||
- pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD
|
||||
- pocketmine\VersionInfo::BUILD_CHANNEL
|
||||
- pocketmine\DEBUG
|
||||
- pocketmine\IS_DEVELOPMENT_BUILD
|
||||
stubFiles:
|
||||
@ -47,8 +48,6 @@ parameters:
|
||||
- tests/phpstan/stubs/leveldb.stub
|
||||
- tests/phpstan/stubs/pmmpthread.stub
|
||||
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
|
||||
staticReflectionClassNamePatterns:
|
||||
- "#^COM$#"
|
||||
typeAliases:
|
||||
#variadics don't work for this - mixed probably shouldn't work either, but for now it does
|
||||
#what we actually need is something that accepts an infinite number of parameters, but in the absence of that,
|
||||
|
@ -47,4 +47,6 @@ final class BootstrapOptions{
|
||||
public const DATA = "data";
|
||||
/** Shows basic server version information and exits */
|
||||
public const VERSION = "version";
|
||||
/** Disables writing logs to server.log */
|
||||
public const NO_LOG_FILE = "no-log-file";
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ class MemoryManager{
|
||||
continue;
|
||||
}
|
||||
$methodStatics = [];
|
||||
foreach($method->getStaticVariables() as $name => $variable){
|
||||
foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
|
||||
$methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($methodStatics) > 0){
|
||||
@ -360,7 +360,7 @@ class MemoryManager{
|
||||
'_SESSION' => true
|
||||
];
|
||||
|
||||
foreach($GLOBALS as $varName => $value){
|
||||
foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
@ -376,7 +376,7 @@ class MemoryManager{
|
||||
$reflect = new \ReflectionFunction($function);
|
||||
|
||||
$vars = [];
|
||||
foreach($reflect->getStaticVariables() as $varName => $variable){
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
|
||||
$vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($vars) > 0){
|
||||
@ -416,7 +416,7 @@ class MemoryManager{
|
||||
$info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
foreach($reflect->getStaticVariables() as $name => $variable){
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
|
||||
$info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}else{
|
||||
@ -499,7 +499,7 @@ class MemoryManager{
|
||||
}
|
||||
$data = [];
|
||||
$numeric = 0;
|
||||
foreach($from as $key => $value){
|
||||
foreach(Utils::promoteKeys($from) as $key => $value){
|
||||
$data[$numeric] = [
|
||||
"k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
|
||||
"v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
|
||||
|
@ -124,8 +124,8 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
|
||||
if(version_compare($pmmpthread_version, "6.0.7") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
|
||||
$messages[] = "pmmpthread ^6.0.7 is required, while you have $pmmpthread_version.";
|
||||
if(version_compare($pmmpthread_version, "6.1.0") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
|
||||
$messages[] = "pmmpthread ^6.1.0 is required, while you have $pmmpthread_version.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,7 +317,7 @@ JIT_WARNING
|
||||
//Logger has a dependency on timezone
|
||||
Timezone::init();
|
||||
|
||||
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI]);
|
||||
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI, BootstrapOptions::NO_LOG_FILE]);
|
||||
if(isset($opts[BootstrapOptions::ENABLE_ANSI])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){
|
||||
@ -325,8 +325,13 @@ JIT_WARNING
|
||||
}else{
|
||||
Terminal::init();
|
||||
}
|
||||
$logFile = isset($opts[BootstrapOptions::NO_LOG_FILE]) ? null : Path::join($dataPath, "server.log");
|
||||
|
||||
$logger = new MainLogger($logFile, Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()), false, Path::join($dataPath, "log_archive"));
|
||||
if($logFile === null){
|
||||
$logger->notice("Logging to file disabled. Ensure logs are collected by other means (e.g. Docker logs).");
|
||||
}
|
||||
|
||||
$logger = new MainLogger(Path::join($dataPath, "server.log"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()));
|
||||
\GlobalLogger::set($logger);
|
||||
|
||||
emit_performance_warnings($logger);
|
||||
|
@ -89,6 +89,8 @@ use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\scheduler\TimingsCollectionTask;
|
||||
use pocketmine\scheduler\TimingsControlTask;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\stats\SendUsageTask;
|
||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||
@ -736,12 +738,15 @@ class Server{
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
* @phpstan-return array<string, list<string>>
|
||||
*/
|
||||
public function getCommandAliases() : array{
|
||||
$section = $this->configGroup->getProperty(Yml::ALIASES);
|
||||
$result = [];
|
||||
if(is_array($section)){
|
||||
foreach($section as $key => $value){
|
||||
foreach(Utils::promoteKeys($section) as $key => $value){
|
||||
//TODO: more validation needed here
|
||||
//key might not be a string, value might not be list<string>
|
||||
$commands = [];
|
||||
if(is_array($value)){
|
||||
$commands = $value;
|
||||
@ -749,7 +754,7 @@ class Server{
|
||||
$commands[] = (string) $value;
|
||||
}
|
||||
|
||||
$result[$key] = $commands;
|
||||
$result[(string) $key] = $commands;
|
||||
}
|
||||
}
|
||||
|
||||
@ -891,7 +896,36 @@ class Server{
|
||||
$poolSize = max(1, (int) $poolSize);
|
||||
}
|
||||
|
||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
||||
|
||||
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper);
|
||||
$this->asyncPool->addWorkerStartHook(function(int $i) : void{
|
||||
if(TimingsHandler::isEnabled()){
|
||||
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled(true), $i);
|
||||
}
|
||||
});
|
||||
TimingsHandler::getToggleCallbacks()->add(function(bool $enable) : void{
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled($enable), $workerId);
|
||||
}
|
||||
});
|
||||
TimingsHandler::getReloadCallbacks()->add(function() : void{
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
$this->asyncPool->submitTaskToWorker(TimingsControlTask::reload(), $workerId);
|
||||
}
|
||||
});
|
||||
TimingsHandler::getCollectCallbacks()->add(function() : array{
|
||||
$promises = [];
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
$resolver = new PromiseResolver();
|
||||
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
|
||||
|
||||
$promises[] = $resolver->getPromise();
|
||||
}
|
||||
|
||||
return $promises;
|
||||
});
|
||||
|
||||
$netCompressionThreshold = -1;
|
||||
if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){
|
||||
@ -965,9 +999,6 @@ class Server{
|
||||
)));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||
|
||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
||||
|
||||
DefaultPermissions::registerCorePermissions();
|
||||
|
||||
$this->commandMap = new SimpleCommandMap($this);
|
||||
@ -1094,7 +1125,11 @@ class Server{
|
||||
|
||||
$anyWorldFailedToLoad = false;
|
||||
|
||||
foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){
|
||||
foreach(Utils::promoteKeys((array) $this->configGroup->getProperty(Yml::WORLDS, [])) as $name => $options){
|
||||
if(!is_string($name)){
|
||||
//TODO: this probably should be an error
|
||||
continue;
|
||||
}
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
@ -1666,9 +1701,11 @@ class Server{
|
||||
$this->isRunning = false;
|
||||
|
||||
//Force minimum uptime to be >= 120 seconds, to reduce the impact of spammy crash loops
|
||||
$spacing = ((int) $this->startTime) - time() + 120;
|
||||
$uptime = time() - ((int) $this->startTime);
|
||||
$minUptime = 120;
|
||||
$spacing = $minUptime - $uptime;
|
||||
if($spacing > 0){
|
||||
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
echo "--- Uptime {$uptime}s - waiting {$spacing}s to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
sleep($spacing);
|
||||
}
|
||||
@Process::kill(Process::pid());
|
||||
|
@ -31,8 +31,8 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.13.0";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BASE_VERSION = "5.21.3";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
/**
|
||||
@ -63,7 +63,8 @@ final class VersionInfo{
|
||||
if(\Phar::running(true) === ""){
|
||||
$gitHash = Git::getRepositoryStatePretty(\pocketmine\PATH);
|
||||
}else{
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$pharPath = \Phar::running(false);
|
||||
$phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath);
|
||||
$meta = $phar->getMetadata();
|
||||
if(isset($meta["git"])){
|
||||
$gitHash = $meta["git"];
|
||||
@ -82,7 +83,8 @@ final class VersionInfo{
|
||||
if(self::$buildNumber === null){
|
||||
self::$buildNumber = 0;
|
||||
if(\Phar::running(true) !== ""){
|
||||
$phar = new \Phar(\Phar::running(false));
|
||||
$pharPath = \Phar::running(false);
|
||||
$phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath);
|
||||
$meta = $phar->getMetadata();
|
||||
if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){
|
||||
self::$buildNumber = $meta["build"];
|
||||
|
@ -35,10 +35,10 @@ use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\AnvilFallSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function lcg_value;
|
||||
use function round;
|
||||
|
||||
class Anvil extends Transparent implements Fallable{
|
||||
@ -91,13 +91,13 @@ class Anvil extends Transparent implements Fallable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
$this->facing = Facing::rotateY($player->getHorizontalFacing(), true);
|
||||
$this->facing = Facing::rotateY($player->getHorizontalFacing(), false);
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onHitGround(FallingBlock $blockEntity) : bool{
|
||||
if(lcg_value() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){
|
||||
if(Utils::getRandomFloat() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){
|
||||
if($this->damage !== self::VERY_DAMAGED){
|
||||
$this->damage = $this->damage + 1;
|
||||
}else{
|
||||
|
@ -150,6 +150,10 @@ final class Bell extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
return [$this->asItem()];
|
||||
}
|
||||
|
||||
private function isValidFaceToRing(int $faceHit) : bool{
|
||||
return match($this->attachmentType){
|
||||
BellAttachmentType::CEILING => true,
|
||||
|
@ -362,6 +362,8 @@ class Block{
|
||||
*
|
||||
* A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g.
|
||||
* data from a block entity).
|
||||
*
|
||||
* @phpstan-impure
|
||||
*/
|
||||
public function readStateFromWorld() : Block{
|
||||
return $this;
|
||||
|
@ -745,8 +745,28 @@ final class BlockTypeIds{
|
||||
public const PITCHER_PLANT = 10715;
|
||||
public const PITCHER_CROP = 10716;
|
||||
public const DOUBLE_PITCHER_CROP = 10717;
|
||||
public const CAMPFIRE = 10718;
|
||||
public const SOUL_CAMPFIRE = 10719;
|
||||
public const TUFF_SLAB = 10720;
|
||||
public const TUFF_STAIRS = 10721;
|
||||
public const TUFF_WALL = 10722;
|
||||
public const CHISELED_TUFF = 10723;
|
||||
public const TUFF_BRICKS = 10724;
|
||||
public const TUFF_BRICK_SLAB = 10725;
|
||||
public const TUFF_BRICK_STAIRS = 10726;
|
||||
public const TUFF_BRICK_WALL = 10727;
|
||||
public const CHISELED_TUFF_BRICKS = 10728;
|
||||
public const POLISHED_TUFF = 10729;
|
||||
public const POLISHED_TUFF_SLAB = 10730;
|
||||
public const POLISHED_TUFF_STAIRS = 10731;
|
||||
public const POLISHED_TUFF_WALL = 10732;
|
||||
public const COPPER_BULB = 10733;
|
||||
public const COPPER_DOOR = 10734;
|
||||
public const COPPER_TRAPDOOR = 10735;
|
||||
public const CHISELED_COPPER = 10736;
|
||||
public const COPPER_GRATE = 10737;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10718;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10738;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
@ -27,10 +27,6 @@ use pocketmine\item\Item;
|
||||
|
||||
class BlueIce extends Opaque{
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getFrictionFactor() : float{
|
||||
return 0.99;
|
||||
}
|
||||
@ -38,4 +34,8 @@ class BlueIce extends Opaque{
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,9 @@ class CakeWithCandle extends BaseCake{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($this->lit && $face !== Facing::UP){
|
||||
return true;
|
||||
}
|
||||
if($this->onInteractCandle($item, $face, $clickVector, $player, $returnedItems)){
|
||||
return true;
|
||||
}
|
||||
|
285
src/block/Campfire.php
Normal file
285
src/block/Campfire.php
Normal file
@ -0,0 +1,285 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\inventory\CampfireInventory;
|
||||
use pocketmine\block\tile\Campfire as TileCampfire;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\crafting\FurnaceRecipe;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\entity\projectile\SplashPotion;
|
||||
use pocketmine\event\block\CampfireCookEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\Shovel;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\BlazeShootSound;
|
||||
use pocketmine\world\sound\FireExtinguishSound;
|
||||
use pocketmine\world\sound\FlintSteelSound;
|
||||
use pocketmine\world\sound\ItemFrameAddItemSound;
|
||||
use function count;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
class Campfire extends Transparent{
|
||||
use HorizontalFacingTrait{
|
||||
HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState;
|
||||
}
|
||||
use LightableTrait{
|
||||
LightableTrait::describeBlockOnlyState as encodeLitState;
|
||||
}
|
||||
|
||||
private const UPDATE_INTERVAL_TICKS = 10;
|
||||
|
||||
/**
|
||||
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
|
||||
* has never been set in the world.
|
||||
*/
|
||||
protected CampfireInventory $inventory;
|
||||
|
||||
/**
|
||||
* @var int[] slot => ticks
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
protected array $cookingTimes = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$this->encodeFacingState($w);
|
||||
$this->encodeLitState($w);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileCampfire){
|
||||
$this->inventory = $tile->getInventory();
|
||||
$this->cookingTimes = $tile->getCookingTimes();
|
||||
}else{
|
||||
$this->inventory = new CampfireInventory($this->position);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileCampfire){
|
||||
$tile->setCookingTimes($this->cookingTimes);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 15 : 0;
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::CHARCOAL()->setCount(2)
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
|
||||
* has never been set in the world.
|
||||
*/
|
||||
public function getInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
protected function getFurnaceType() : FurnaceType{
|
||||
return FurnaceType::CAMPFIRE;
|
||||
}
|
||||
|
||||
protected function getEntityCollisionDamage() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ticks during the item in the given slot has been cooked.
|
||||
*/
|
||||
public function setCookingTime(int $slot, int $time) : void{
|
||||
if($slot < 0 || $slot > 3){
|
||||
throw new \InvalidArgumentException("Slot must be in range 0-3");
|
||||
}
|
||||
if($time < 0 || $time > $this->getFurnaceType()->getCookDurationTicks()){
|
||||
throw new \InvalidArgumentException("CookingTime must be in range 0-" . $this->getFurnaceType()->getCookDurationTicks());
|
||||
}
|
||||
$this->cookingTimes[$slot] = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ticks during the item in the given slot has been cooked.
|
||||
*/
|
||||
public function getCookingTime(int $slot) : int{
|
||||
return $this->cookingTimes[$slot] ?? 0;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->getSide(Facing::DOWN) instanceof Campfire){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$this->facing = $player->getHorizontalFacing();
|
||||
}
|
||||
$this->lit = true;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if(!$this->lit){
|
||||
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){
|
||||
$item->pop();
|
||||
$this->ignite();
|
||||
$this->position->getWorld()->addSound($this->position, new BlazeShootSound());
|
||||
return true;
|
||||
}elseif($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
|
||||
if($item instanceof Durable){
|
||||
$item->applyDamage(1);
|
||||
}
|
||||
$this->ignite();
|
||||
return true;
|
||||
}
|
||||
}elseif($item instanceof Shovel){
|
||||
$item->applyDamage(1);
|
||||
$this->extinguish();
|
||||
return true;
|
||||
}
|
||||
|
||||
if($this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($this->getFurnaceType())->match($item) !== null){
|
||||
$ingredient = clone $item;
|
||||
$ingredient->setCount(1);
|
||||
if(count($this->inventory->addItem($ingredient)) === 0){
|
||||
$item->pop();
|
||||
$this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->lit && $this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
|
||||
$this->extinguish();
|
||||
//TODO: Waterlogging
|
||||
}
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if(!$this->lit){
|
||||
if($entity->isOnFire()){
|
||||
$this->ignite();
|
||||
return false;
|
||||
}
|
||||
}elseif($entity instanceof Living){
|
||||
$entity->attack(new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, $this->getEntityCollisionDamage()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||
if($this->lit && $projectile instanceof SplashPotion && $projectile->getPotionType() === PotionType::WATER){
|
||||
$this->extinguish();
|
||||
}
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
if($this->lit){
|
||||
$items = $this->inventory->getContents();
|
||||
$furnaceType = $this->getFurnaceType();
|
||||
$maxCookDuration = $furnaceType->getCookDurationTicks();
|
||||
foreach($items as $slot => $item){
|
||||
$this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + self::UPDATE_INTERVAL_TICKS));
|
||||
if($this->getCookingTime($slot) >= $maxCookDuration){
|
||||
$result =
|
||||
($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ?
|
||||
$recipe->getResult() :
|
||||
VanillaItems::AIR();
|
||||
|
||||
$ev = new CampfireCookEvent($this, $slot, $item, $result);
|
||||
$ev->call();
|
||||
|
||||
if ($ev->isCancelled()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->inventory->setItem($slot, VanillaItems::AIR());
|
||||
$this->setCookingTime($slot, 0);
|
||||
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult());
|
||||
}
|
||||
}
|
||||
if(count($items) > 0){
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
}
|
||||
if(mt_rand(1, 6) === 1){
|
||||
$this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
|
||||
}
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
|
||||
}
|
||||
}
|
||||
|
||||
private function extinguish() : void{
|
||||
$this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLit(false));
|
||||
}
|
||||
|
||||
private function ignite() : void{
|
||||
$this->position->getWorld()->addSound($this->position, new FlintSteelSound());
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLit(true));
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
|
||||
}
|
||||
}
|
@ -48,11 +48,32 @@ class ChiseledBookshelf extends Opaque{
|
||||
*/
|
||||
private array $slots = [];
|
||||
|
||||
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileChiseledBookshelf){
|
||||
$this->lastInteractedSlot = $tile->getLastInteractedSlot();
|
||||
}else{
|
||||
$this->lastInteractedSlot = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileChiseledBookshelf){
|
||||
$tile->setLastInteractedSlot($this->lastInteractedSlot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given slot is displayed as occupied.
|
||||
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
|
||||
@ -92,6 +113,23 @@ class ChiseledBookshelf extends Opaque{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last slot interacted by a player or null if no slot has been interacted with yet.
|
||||
*/
|
||||
public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{
|
||||
return $this->lastInteractedSlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last slot interacted by a player.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : self{
|
||||
$this->lastInteractedSlot = $lastInteractedSlot;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($face !== $this->facing){
|
||||
return false;
|
||||
@ -112,10 +150,12 @@ class ChiseledBookshelf extends Opaque{
|
||||
$returnedItems[] = $inventory->getItem($slot->value);
|
||||
$inventory->clear($slot->value);
|
||||
$this->setSlot($slot, false);
|
||||
$this->lastInteractedSlot = $slot;
|
||||
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
|
||||
//TODO: type tags like blocks would be better for this
|
||||
$inventory->setItem($slot->value, $item->pop());
|
||||
$this->setSlot($slot, true);
|
||||
$this->lastInteractedSlot = $slot;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class Copper extends Opaque{
|
||||
class Copper extends Opaque implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
}
|
||||
|
69
src/block/CopperBulb.php
Normal file
69
src/block/CopperBulb.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
class CopperBulb extends Opaque implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
use LightableTrait{
|
||||
describeBlockOnlyState as encodeLitState;
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$this->encodeLitState($w);
|
||||
$w->bool($this->powered);
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function togglePowered(bool $powered) : self{
|
||||
if($powered === $this->powered){
|
||||
return $this;
|
||||
}
|
||||
if ($powered) {
|
||||
$this->setLit(!$this->lit);
|
||||
}
|
||||
$this->setPowered($powered);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
if ($this->lit) {
|
||||
return match($this->oxidation){
|
||||
CopperOxidation::NONE => 15,
|
||||
CopperOxidation::EXPOSED => 12,
|
||||
CopperOxidation::WEATHERED => 8,
|
||||
CopperOxidation::OXIDIZED => 4,
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
53
src/block/CopperDoor.php
Normal file
53
src/block/CopperDoor.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class CopperDoor extends Door implements CopperMaterial{
|
||||
use CopperTrait{
|
||||
onInteract as onInteractCopper;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) {
|
||||
//copy copper properties to other half
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
$world = $this->position->getWorld();
|
||||
if ($other instanceof CopperDoor) {
|
||||
$other->setOxidation($this->oxidation);
|
||||
$other->setWaxed($this->waxed);
|
||||
$world->setBlock($other->position, $other);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
|
||||
}
|
||||
}
|
33
src/block/CopperGrate.php
Normal file
33
src/block/CopperGrate.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperGrate extends Transparent implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
|
||||
//TODO: waterlogging!
|
||||
}
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperSlab extends Slab{
|
||||
class CopperSlab extends Slab implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
}
|
||||
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperStairs extends Stair{
|
||||
class CopperStairs extends Stair implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
}
|
||||
|
44
src/block/CopperTrapdoor.php
Normal file
44
src/block/CopperTrapdoor.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class CopperTrapdoor extends Trapdoor implements CopperMaterial{
|
||||
use CopperTrait{
|
||||
onInteract as onInteractCopper;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\BlockTeleportEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\GameMode;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\particle\DragonEggTeleportParticle;
|
||||
use pocketmine\world\World;
|
||||
@ -50,7 +50,7 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
|
||||
if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
|
||||
if($player !== null && !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE)){
|
||||
$this->teleport();
|
||||
return true;
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ use pocketmine\event\entity\EntityTrampleFarmlandEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\utils\Utils;
|
||||
use function intdiv;
|
||||
use function lcg_value;
|
||||
|
||||
class Farmland extends Transparent{
|
||||
public const MAX_WETNESS = 7;
|
||||
@ -148,7 +148,7 @@ class Farmland extends Transparent{
|
||||
}
|
||||
|
||||
public function onEntityLand(Entity $entity) : ?float{
|
||||
if($entity instanceof Living && lcg_value() < $entity->getFallDistance() - 0.5){
|
||||
if($entity instanceof Living && Utils::getRandomFloat() < $entity->getFallDistance() - 0.5){
|
||||
$ev = new EntityTrampleFarmlandEvent($entity, $this);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
/**
|
||||
* "Flowable" blocks are destroyed if water flows into the same space as the block. These blocks usually don't have any
|
||||
@ -40,6 +41,11 @@ abstract class Flowable extends Transparent{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
return (!$this->canBeFlowedInto() || !$blockReplace instanceof Liquid) &&
|
||||
parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
|
@ -24,60 +24,20 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\MultiAnySupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\World;
|
||||
use function array_key_first;
|
||||
use function count;
|
||||
use function shuffle;
|
||||
|
||||
class GlowLichen extends Transparent{
|
||||
|
||||
/** @var int[] */
|
||||
protected array $faces = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->facingFlags($this->faces);
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getFaces() : array{ return $this->faces; }
|
||||
|
||||
public function hasFace(int $face) : bool{
|
||||
return isset($this->faces[$face]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
* @return $this
|
||||
*/
|
||||
public function setFaces(array $faces) : self{
|
||||
$uniqueFaces = [];
|
||||
foreach($faces as $face){
|
||||
Facing::validate($face);
|
||||
$uniqueFaces[$face] = $face;
|
||||
}
|
||||
$this->faces = $uniqueFaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setFace(int $face, bool $value) : self{
|
||||
Facing::validate($face);
|
||||
if($value){
|
||||
$this->faces[$face] = $face;
|
||||
}else{
|
||||
unset($this->faces[$face]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
use MultiAnySupportTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return 7;
|
||||
@ -102,39 +62,11 @@ class GlowLichen extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->faces = $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
|
||||
$availableFaces = $this->getAvailableFaces();
|
||||
|
||||
if(count($availableFaces) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$opposite = Facing::opposite($face);
|
||||
$placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces);
|
||||
$this->faces[$placedFace] = $placedFace;
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if($this->getAdjacentSupportType($face) !== SupportType::FULL){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$world = $this->position->getWorld();
|
||||
if(count($this->faces) === 0){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
protected function getInitialPlaceFaces(Block $blockReplace) : array{
|
||||
return $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
|
||||
}
|
||||
|
||||
private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{
|
||||
@ -261,17 +193,4 @@ class GlowLichen extends Transparent{
|
||||
public function getFlammability() : int{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int> $faces
|
||||
*/
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
return $faces;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class Ice extends Transparent{
|
||||
@ -39,7 +40,8 @@ class Ice extends Transparent{
|
||||
}
|
||||
|
||||
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if(($player === null || $player->isSurvival()) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
//TODO: we should probably pass instaBreak in here, since events can override it
|
||||
if(($player === null || !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE)) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::WATER());
|
||||
return true;
|
||||
}
|
||||
|
@ -31,13 +31,13 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\ItemFrameAddItemSound;
|
||||
use pocketmine\world\sound\ItemFrameRemoveItemSound;
|
||||
use pocketmine\world\sound\ItemFrameRotateItemSound;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
use function lcg_value;
|
||||
|
||||
class ItemFrame extends Flowable{
|
||||
use AnyFacingTrait;
|
||||
@ -154,7 +154,7 @@ class ItemFrame extends Flowable{
|
||||
return false;
|
||||
}
|
||||
$world = $this->position->getWorld();
|
||||
if(lcg_value() <= $this->itemDropChance){
|
||||
if(Utils::getRandomFloat() <= $this->itemDropChance){
|
||||
$world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem);
|
||||
$world->addSound($this->position, new ItemFrameRemoveItemSound());
|
||||
}
|
||||
@ -185,7 +185,7 @@ class ItemFrame extends Flowable{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
$drops = parent::getDropsForCompatibleTool($item);
|
||||
if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){
|
||||
if($this->framedItem !== null && Utils::getRandomFloat() <= $this->itemDropChance){
|
||||
$drops[] = clone $this->framedItem;
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,9 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\sound\FizzSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function lcg_value;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
public const MAX_DECAY = 7;
|
||||
@ -368,7 +368,7 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
protected function liquidCollide(Block $cause, Block $result) : bool{
|
||||
if(BlockEventHelper::form($this, $result, $cause)){
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.8));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class Magma extends Opaque{
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if($entity instanceof Living && !$entity->isSneaking()){
|
||||
if($entity instanceof Living && !$entity->isSneaking() && $entity->getFrostWalkerLevel() === 0){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ final class NetherRoots extends Flowable{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$supportBlock->getTypeId() === BlockTypeIds::SOUL_SOIL;
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ class RuntimeBlockStateRegistry{
|
||||
|
||||
/**
|
||||
* @return Block[]
|
||||
* @phpstan-return array<int, Block>
|
||||
*/
|
||||
public function getAllKnownStates() : array{
|
||||
return $this->fullList;
|
||||
|
@ -21,20 +21,28 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade\model;
|
||||
namespace pocketmine\block;
|
||||
|
||||
final class BlockStateUpgradeSchemaModelFlattenedName{
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
/** @required */
|
||||
public string $prefix;
|
||||
/** @required */
|
||||
public string $flattenedProperty;
|
||||
/** @required */
|
||||
public string $suffix;
|
||||
class SoulCampfire extends Campfire{
|
||||
|
||||
public function __construct(string $prefix, string $flattenedProperty, string $suffix){
|
||||
$this->prefix = $prefix;
|
||||
$this->flattenedProperty = $flattenedProperty;
|
||||
$this->suffix = $suffix;
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 10 : 0;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaBlocks::SOUL_SOIL()->asItem()
|
||||
];
|
||||
}
|
||||
|
||||
protected function getEntityCollisionDamage() : int{
|
||||
return 2;
|
||||
}
|
||||
|
||||
protected function getFurnaceType() : FurnaceType{
|
||||
return FurnaceType::SOUL_CAMPFIRE;
|
||||
}
|
||||
}
|
@ -36,7 +36,9 @@ use pocketmine\world\Position;
|
||||
|
||||
class Sugarcane extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
use StaticSupportTrait {
|
||||
onNearbyBlockChange as onSupportBlockChange;
|
||||
}
|
||||
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
@ -97,7 +99,13 @@ class Sugarcane extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)){
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasSameTypeId($this)){
|
||||
if(!$this->hasNearbyWater($down)){
|
||||
$this->position->getWorld()->useBreakOn($this->position, createParticles: true);
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->age === self::MAX_AGE){
|
||||
$this->grow($this->position);
|
||||
}else{
|
||||
@ -123,4 +131,23 @@ class Sugarcane extends Flowable{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasNearbyWater(Block $down) : bool{
|
||||
foreach($down->getHorizontalSides() as $sideBlock){
|
||||
$blockId = $sideBlock->getTypeId();
|
||||
if($blockId === BlockTypeIds::WATER || $blockId === BlockTypeIds::FROSTED_ICE){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasSameTypeId($this) && !$this->hasNearbyWater($down)){
|
||||
$this->position->getWorld()->useBreakOn($this->position, createParticles: true);
|
||||
}else{
|
||||
$this->onSupportBlockChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\SweetBerriesPickSound;
|
||||
use function mt_rand;
|
||||
|
||||
class SweetBerryBush extends Flowable{
|
||||
@ -81,6 +82,7 @@ class SweetBerryBush extends Flowable{
|
||||
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
|
||||
$world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
|
||||
$world->dropItem($this->position, $this->asItem()->setCount($dropAmount));
|
||||
$world->addSound($this->position, new SweetBerriesPickSound());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,263 +0,0 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\BlockIdentifier as BID;
|
||||
use pocketmine\block\BlockTypeIds as Ids;
|
||||
use pocketmine\block\tile\Sign as TileSign;
|
||||
use pocketmine\block\utils\LeavesType;
|
||||
use pocketmine\block\utils\SaplingType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\item\VanillaItems;
|
||||
|
||||
/**
|
||||
* All wood-like blocks have different IDs for different wood types.
|
||||
*
|
||||
* We can't make these dynamic, because some types of wood have different type properties (e.g. crimson and warped planks
|
||||
* are not flammable, but all other planks are).
|
||||
*
|
||||
* In the future, it's entirely within the realm of reason that the other types of wood may differ in other ways, such
|
||||
* as flammability, hardness, required tool tier, etc.
|
||||
* Therefore, to stay on the safe side of Mojang, wood-like blocks have static types. This does unfortunately generate
|
||||
* a lot of ugly code.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class WoodLikeBlockIdHelper{
|
||||
|
||||
public static function getPlanksIdentifier(WoodType $type) : BID{
|
||||
return new BID(match($type){
|
||||
WoodType::OAK => Ids::OAK_PLANKS,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_PLANKS,
|
||||
WoodType::BIRCH => Ids::BIRCH_PLANKS,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_PLANKS,
|
||||
WoodType::ACACIA => Ids::ACACIA_PLANKS,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_PLANKS,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_PLANKS,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_PLANKS,
|
||||
WoodType::WARPED => Ids::WARPED_PLANKS,
|
||||
WoodType::CHERRY => Ids::CHERRY_PLANKS,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getFenceIdentifier(WoodType $type) : BID{
|
||||
return new BID(match($type){
|
||||
WoodType::OAK => Ids::OAK_FENCE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_FENCE,
|
||||
WoodType::BIRCH => Ids::BIRCH_FENCE,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_FENCE,
|
||||
WoodType::ACACIA => Ids::ACACIA_FENCE,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_FENCE,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_FENCE,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_FENCE,
|
||||
WoodType::WARPED => Ids::WARPED_FENCE,
|
||||
WoodType::CHERRY => Ids::CHERRY_FENCE,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getSlabIdentifier(WoodType $type) : BID{
|
||||
return new BID(match($type){
|
||||
WoodType::OAK => Ids::OAK_SLAB,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_SLAB,
|
||||
WoodType::BIRCH => Ids::BIRCH_SLAB,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_SLAB,
|
||||
WoodType::ACACIA => Ids::ACACIA_SLAB,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_SLAB,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_SLAB,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_SLAB,
|
||||
WoodType::WARPED => Ids::WARPED_SLAB,
|
||||
WoodType::CHERRY => Ids::CHERRY_SLAB,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getLogIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_LOG,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_LOG,
|
||||
WoodType::BIRCH => Ids::BIRCH_LOG,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_LOG,
|
||||
WoodType::ACACIA => Ids::ACACIA_LOG,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_LOG,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_LOG,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_STEM,
|
||||
WoodType::WARPED => Ids::WARPED_STEM,
|
||||
WoodType::CHERRY => Ids::CHERRY_LOG,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getAllSidedLogIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_WOOD,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_WOOD,
|
||||
WoodType::BIRCH => Ids::BIRCH_WOOD,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_WOOD,
|
||||
WoodType::ACACIA => Ids::ACACIA_WOOD,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_WOOD,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_WOOD,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_HYPHAE,
|
||||
WoodType::WARPED => Ids::WARPED_HYPHAE,
|
||||
WoodType::CHERRY => Ids::CHERRY_WOOD,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getLeavesIdentifier(LeavesType $leavesType) : BID{
|
||||
return new BID(match($leavesType){
|
||||
LeavesType::OAK => Ids::OAK_LEAVES,
|
||||
LeavesType::SPRUCE => Ids::SPRUCE_LEAVES,
|
||||
LeavesType::BIRCH => Ids::BIRCH_LEAVES,
|
||||
LeavesType::JUNGLE => Ids::JUNGLE_LEAVES,
|
||||
LeavesType::ACACIA => Ids::ACACIA_LEAVES,
|
||||
LeavesType::DARK_OAK => Ids::DARK_OAK_LEAVES,
|
||||
LeavesType::MANGROVE => Ids::MANGROVE_LEAVES,
|
||||
LeavesType::AZALEA => Ids::AZALEA_LEAVES,
|
||||
LeavesType::FLOWERING_AZALEA => Ids::FLOWERING_AZALEA_LEAVES,
|
||||
LeavesType::CHERRY => Ids::CHERRY_LEAVES,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getSaplingIdentifier(SaplingType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
SaplingType::OAK => Ids::OAK_SAPLING,
|
||||
SaplingType::SPRUCE => Ids::SPRUCE_SAPLING,
|
||||
SaplingType::BIRCH => Ids::BIRCH_SAPLING,
|
||||
SaplingType::JUNGLE => Ids::JUNGLE_SAPLING,
|
||||
SaplingType::ACACIA => Ids::ACACIA_SAPLING,
|
||||
SaplingType::DARK_OAK => Ids::DARK_OAK_SAPLING,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BID[]|\Closure[]
|
||||
* @phpstan-return array{BID, BID, \Closure() : \pocketmine\item\Item}
|
||||
*/
|
||||
public static function getSignInfo(WoodType $treeType) : array{
|
||||
$make = fn(int $floorId, int $wallId, \Closure $getItem) => [
|
||||
new BID($floorId, TileSign::class),
|
||||
new BID($wallId, TileSign::class),
|
||||
$getItem
|
||||
];
|
||||
return match($treeType){
|
||||
WoodType::OAK => $make(Ids::OAK_SIGN, Ids::OAK_WALL_SIGN, fn() => VanillaItems::OAK_SIGN()),
|
||||
WoodType::SPRUCE => $make(Ids::SPRUCE_SIGN, Ids::SPRUCE_WALL_SIGN, fn() => VanillaItems::SPRUCE_SIGN()),
|
||||
WoodType::BIRCH => $make(Ids::BIRCH_SIGN, Ids::BIRCH_WALL_SIGN, fn() => VanillaItems::BIRCH_SIGN()),
|
||||
WoodType::JUNGLE => $make(Ids::JUNGLE_SIGN, Ids::JUNGLE_WALL_SIGN, fn() => VanillaItems::JUNGLE_SIGN()),
|
||||
WoodType::ACACIA => $make(Ids::ACACIA_SIGN, Ids::ACACIA_WALL_SIGN, fn() => VanillaItems::ACACIA_SIGN()),
|
||||
WoodType::DARK_OAK => $make(Ids::DARK_OAK_SIGN, Ids::DARK_OAK_WALL_SIGN, fn() => VanillaItems::DARK_OAK_SIGN()),
|
||||
WoodType::MANGROVE => $make(Ids::MANGROVE_SIGN, Ids::MANGROVE_WALL_SIGN, fn() => VanillaItems::MANGROVE_SIGN()),
|
||||
WoodType::CRIMSON => $make(Ids::CRIMSON_SIGN, Ids::CRIMSON_WALL_SIGN, fn() => VanillaItems::CRIMSON_SIGN()),
|
||||
WoodType::WARPED => $make(Ids::WARPED_SIGN, Ids::WARPED_WALL_SIGN, fn() => VanillaItems::WARPED_SIGN()),
|
||||
WoodType::CHERRY => $make(Ids::CHERRY_SIGN, Ids::CHERRY_WALL_SIGN, fn() => VanillaItems::CHERRY_SIGN()),
|
||||
};
|
||||
}
|
||||
|
||||
public static function getTrapdoorIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_TRAPDOOR,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR,
|
||||
WoodType::BIRCH => Ids::BIRCH_TRAPDOOR,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_TRAPDOOR,
|
||||
WoodType::ACACIA => Ids::ACACIA_TRAPDOOR,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_TRAPDOOR,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_TRAPDOOR,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_TRAPDOOR,
|
||||
WoodType::WARPED => Ids::WARPED_TRAPDOOR,
|
||||
WoodType::CHERRY => Ids::CHERRY_TRAPDOOR,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getButtonIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_BUTTON,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_BUTTON,
|
||||
WoodType::BIRCH => Ids::BIRCH_BUTTON,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_BUTTON,
|
||||
WoodType::ACACIA => Ids::ACACIA_BUTTON,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_BUTTON,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_BUTTON,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_BUTTON,
|
||||
WoodType::WARPED => Ids::WARPED_BUTTON,
|
||||
WoodType::CHERRY => Ids::CHERRY_BUTTON,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getPressurePlateIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_PRESSURE_PLATE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE,
|
||||
WoodType::BIRCH => Ids::BIRCH_PRESSURE_PLATE,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_PRESSURE_PLATE,
|
||||
WoodType::ACACIA => Ids::ACACIA_PRESSURE_PLATE,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_PRESSURE_PLATE,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_PRESSURE_PLATE,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_PRESSURE_PLATE,
|
||||
WoodType::WARPED => Ids::WARPED_PRESSURE_PLATE,
|
||||
WoodType::CHERRY => Ids::CHERRY_PRESSURE_PLATE,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getDoorIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_DOOR,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_DOOR,
|
||||
WoodType::BIRCH => Ids::BIRCH_DOOR,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_DOOR,
|
||||
WoodType::ACACIA => Ids::ACACIA_DOOR,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_DOOR,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_DOOR,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_DOOR,
|
||||
WoodType::WARPED => Ids::WARPED_DOOR,
|
||||
WoodType::CHERRY => Ids::CHERRY_DOOR,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getFenceGateIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_FENCE_GATE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE,
|
||||
WoodType::BIRCH => Ids::BIRCH_FENCE_GATE,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_FENCE_GATE,
|
||||
WoodType::ACACIA => Ids::ACACIA_FENCE_GATE,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_FENCE_GATE,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_FENCE_GATE,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_FENCE_GATE,
|
||||
WoodType::WARPED => Ids::WARPED_FENCE_GATE,
|
||||
WoodType::CHERRY => Ids::CHERRY_FENCE_GATE,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getStairsIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_STAIRS,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_STAIRS,
|
||||
WoodType::BIRCH => Ids::BIRCH_STAIRS,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_STAIRS,
|
||||
WoodType::ACACIA => Ids::ACACIA_STAIRS,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_STAIRS,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_STAIRS,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_STAIRS,
|
||||
WoodType::WARPED => Ids::WARPED_STAIRS,
|
||||
WoodType::CHERRY => Ids::CHERRY_STAIRS,
|
||||
});
|
||||
}
|
||||
}
|
@ -28,6 +28,10 @@ use pocketmine\block\utils\WoodTypeTrait;
|
||||
class WoodenStairs extends Stair{
|
||||
use WoodTypeTrait;
|
||||
|
||||
public function getFuelTime() : int{
|
||||
return $this->woodType->isFlammable() ? 300 : 0;
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
return 5;
|
||||
}
|
||||
|
@ -21,19 +21,20 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
namespace pocketmine\block\inventory;
|
||||
|
||||
final class BlockStateUpgradeSchemaFlattenedName{
|
||||
use pocketmine\inventory\SimpleInventory;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
public function __construct(
|
||||
public string $prefix,
|
||||
public string $flattenedProperty,
|
||||
public string $suffix
|
||||
){}
|
||||
class CampfireInventory extends SimpleInventory implements BlockInventory{
|
||||
use BlockInventoryTrait;
|
||||
|
||||
public function equals(self $that) : bool{
|
||||
return $this->prefix === $that->prefix &&
|
||||
$this->flattenedProperty === $that->flattenedProperty &&
|
||||
$this->suffix === $that->suffix;
|
||||
public function __construct(Position $holder){
|
||||
$this->holder = $holder;
|
||||
parent::__construct(4);
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -39,7 +39,10 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
|
||||
public const SLOT_INPUT = 0;
|
||||
public const SLOT_LAPIS = 1;
|
||||
|
||||
/** @var EnchantingOption[] $options */
|
||||
/**
|
||||
* @var EnchantingOption[] $options
|
||||
* @phpstan-var list<EnchantingOption>
|
||||
*/
|
||||
private array $options = [];
|
||||
|
||||
public function __construct(Position $holder){
|
||||
|
143
src/block/tile/Campfire.php
Normal file
143
src/block/tile/Campfire.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?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\block\tile;
|
||||
|
||||
use pocketmine\block\Campfire as BlockCampfire;
|
||||
use pocketmine\block\inventory\CampfireInventory;
|
||||
use pocketmine\inventory\CallbackInventoryListener;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\world\World;
|
||||
|
||||
class Campfire extends Spawnable implements Container{
|
||||
use ContainerTrait;
|
||||
|
||||
private const TAG_FIRST_INPUT_ITEM = "Item1"; //TAG_Compound
|
||||
private const TAG_SECOND_INPUT_ITEM = "Item2"; //TAG_Compound
|
||||
private const TAG_THIRD_INPUT_ITEM = "Item3"; //TAG_Compound
|
||||
private const TAG_FOURTH_INPUT_ITEM = "Item4"; //TAG_Compound
|
||||
|
||||
private const TAG_FIRST_COOKING_TIME = "ItemTime1"; //TAG_Int
|
||||
private const TAG_SECOND_COOKING_TIME = "ItemTime2"; //TAG_Int
|
||||
private const TAG_THIRD_COOKING_TIME = "ItemTime3"; //TAG_Int
|
||||
private const TAG_FOURTH_COOKING_TIME = "ItemTime4"; //TAG_Int
|
||||
|
||||
protected CampfireInventory $inventory;
|
||||
/** @var array<int, int> */
|
||||
private array $cookingTimes = [];
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
$this->inventory = new CampfireInventory($this->position);
|
||||
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
|
||||
static function(Inventory $unused) use ($world, $pos) : void{
|
||||
$block = $world->getBlock($pos);
|
||||
if($block instanceof BlockCampfire){
|
||||
$world->setBlock($pos, $block);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public function getInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function getRealInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*/
|
||||
public function getCookingTimes() : array{
|
||||
return $this->cookingTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $cookingTimes
|
||||
* @phpstan-param array<int, int> $cookingTimes
|
||||
*/
|
||||
public function setCookingTimes(array $cookingTimes) : void{
|
||||
$this->cookingTimes = $cookingTimes;
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$items = [];
|
||||
$listeners = $this->inventory->getListeners()->toArray();
|
||||
$this->inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
|
||||
|
||||
foreach([
|
||||
[0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME],
|
||||
[1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME],
|
||||
[2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME],
|
||||
[3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME],
|
||||
] as [$slot, $itemTag, $cookingTimeTag]){
|
||||
if(($tag = $nbt->getTag($itemTag)) instanceof CompoundTag){
|
||||
$items[$slot] = Item::nbtDeserialize($tag);
|
||||
}
|
||||
if(($tag = $nbt->getTag($cookingTimeTag)) instanceof IntTag){
|
||||
$this->cookingTimes[$slot] = $tag->getValue();
|
||||
}
|
||||
}
|
||||
$this->inventory->setContents($items);
|
||||
$this->inventory->getListeners()->add(...$listeners);
|
||||
}
|
||||
|
||||
protected function writeSaveData(CompoundTag $nbt) : void{
|
||||
foreach([
|
||||
[0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME],
|
||||
[1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME],
|
||||
[2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME],
|
||||
[3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME],
|
||||
] as [$slot, $itemTag, $cookingTimeTag]){
|
||||
$item = $this->inventory->getItem($slot);
|
||||
if(!$item->isNull()){
|
||||
$nbt->setTag($itemTag, $item->nbtSerialize());
|
||||
if(isset($this->cookingTimes[$slot])){
|
||||
$nbt->setInt($cookingTimeTag, $this->cookingTimes[$slot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
foreach([
|
||||
0 => self::TAG_FIRST_INPUT_ITEM,
|
||||
1 => self::TAG_SECOND_INPUT_ITEM,
|
||||
2 => self::TAG_THIRD_INPUT_ITEM,
|
||||
3 => self::TAG_FOURTH_INPUT_ITEM
|
||||
] as $slot => $tag){
|
||||
$item = $this->inventory->getItem($slot);
|
||||
if(!$item->isNull()){
|
||||
$nbt->setTag($tag, TypeConverter::getInstance()->getItemTranslator()->toNetworkNbt($item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -40,8 +40,12 @@ use function count;
|
||||
class ChiseledBookshelf extends Tile implements Container{
|
||||
use ContainerTrait;
|
||||
|
||||
private const TAG_LAST_INTERACTED_SLOT = "LastInteractedSlot"; //TAG_Int
|
||||
|
||||
private SimpleInventory $inventory;
|
||||
|
||||
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
|
||||
@ -55,12 +59,30 @@ class ChiseledBookshelf extends Tile implements Container{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$this->loadItems($nbt);
|
||||
public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{
|
||||
return $this->lastInteractedSlot;
|
||||
}
|
||||
|
||||
public function writeSaveData(CompoundTag $nbt) : void{
|
||||
public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : void{
|
||||
$this->lastInteractedSlot = $lastInteractedSlot;
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$this->loadItems($nbt);
|
||||
|
||||
$lastInteractedSlot = $nbt->getInt(self::TAG_LAST_INTERACTED_SLOT, 0);
|
||||
if($lastInteractedSlot !== 0){
|
||||
$this->lastInteractedSlot = ChiseledBookshelfSlot::tryFrom($lastInteractedSlot - 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function writeSaveData(CompoundTag $nbt) : void{
|
||||
$this->saveItems($nbt);
|
||||
|
||||
$nbt->setInt(self::TAG_LAST_INTERACTED_SLOT, $this->lastInteractedSlot !== null ?
|
||||
$this->lastInteractedSlot->value + 1 :
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadItems(CompoundTag $tag) : void{
|
||||
|
@ -57,6 +57,7 @@ final class TileFactory{
|
||||
$this->register(Bell::class, ["Bell", "minecraft:bell"]);
|
||||
$this->register(BlastFurnace::class, ["BlastFurnace", "minecraft:blast_furnace"]);
|
||||
$this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
|
||||
$this->register(Campfire::class, ["Campfire", "minecraft:campfire"]);
|
||||
$this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]);
|
||||
$this->register(Chest::class, ["Chest", "minecraft:chest"]);
|
||||
$this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]);
|
||||
@ -79,7 +80,6 @@ final class TileFactory{
|
||||
$this->register(MobHead::class, ["Skull", "minecraft:skull"]);
|
||||
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
|
||||
|
||||
//TODO: Campfire
|
||||
//TODO: ChalkboardBlock
|
||||
//TODO: ChemistryTable
|
||||
//TODO: CommandBlock
|
||||
|
@ -81,14 +81,18 @@ enum BannerPatternType{
|
||||
case DIAGONAL_RIGHT;
|
||||
case DIAGONAL_UP_LEFT;
|
||||
case DIAGONAL_UP_RIGHT;
|
||||
case FLOW;
|
||||
case FLOWER;
|
||||
case GLOBE;
|
||||
case GRADIENT;
|
||||
case GRADIENT_UP;
|
||||
case GUSTER;
|
||||
case HALF_HORIZONTAL;
|
||||
case HALF_HORIZONTAL_BOTTOM;
|
||||
case HALF_VERTICAL;
|
||||
case HALF_VERTICAL_RIGHT;
|
||||
case MOJANG;
|
||||
case PIGLIN;
|
||||
case RHOMBUS;
|
||||
case SKULL;
|
||||
case SMALL_STRIPES;
|
||||
|
38
src/block/utils/CopperMaterial.php
Normal file
38
src/block/utils/CopperMaterial.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\block\utils;
|
||||
|
||||
/**
|
||||
* Represents copper blocks that have oxidized and waxed variations.
|
||||
*/
|
||||
interface CopperMaterial{
|
||||
|
||||
public function getOxidation() : CopperOxidation;
|
||||
|
||||
public function setOxidation(CopperOxidation $oxidation) : CopperMaterial;
|
||||
|
||||
public function isWaxed() : bool;
|
||||
|
||||
public function setWaxed(bool $waxed) : CopperMaterial;
|
||||
}
|
72
src/block/utils/MultiAnyFacingTrait.php
Normal file
72
src/block/utils/MultiAnyFacingTrait.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?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\block\utils;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
/**
|
||||
* Used by blocks that can have multiple target faces in the area of one solid block, such as covering three sides of a corner.
|
||||
*/
|
||||
trait MultiAnyFacingTrait{
|
||||
|
||||
/** @var int[] */
|
||||
protected array $faces = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->facingFlags($this->faces);
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getFaces() : array{ return $this->faces; }
|
||||
|
||||
public function hasFace(int $face) : bool{
|
||||
return isset($this->faces[$face]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
* @return $this
|
||||
*/
|
||||
public function setFaces(array $faces) : self{
|
||||
$uniqueFaces = [];
|
||||
foreach($faces as $face){
|
||||
Facing::validate($face);
|
||||
$uniqueFaces[$face] = $face;
|
||||
}
|
||||
$this->faces = $uniqueFaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setFace(int $face, bool $value) : self{
|
||||
Facing::validate($face);
|
||||
if($value){
|
||||
$this->faces[$face] = $face;
|
||||
}else{
|
||||
unset($this->faces[$face]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
96
src/block/utils/MultiAnySupportTrait.php
Normal file
96
src/block/utils/MultiAnySupportTrait.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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\block\utils;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function array_key_first;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Used by blocks that have multiple support requirements in the area of one solid block, such as covering three sides of a corner.
|
||||
* Prevents placement if support isn't available and automatically destroys a block side if it's support is removed.
|
||||
*/
|
||||
trait MultiAnySupportTrait{
|
||||
use MultiAnyFacingTrait;
|
||||
|
||||
/**
|
||||
* Returns a list of faces that block should already have when placed.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
abstract protected function getInitialPlaceFaces(Block $blockReplace) : array;
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->faces = $this->getInitialPlaceFaces($blockReplace);
|
||||
$availableFaces = $this->getAvailableFaces();
|
||||
|
||||
if(count($availableFaces) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$opposite = Facing::opposite($face);
|
||||
$placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces);
|
||||
$this->faces[$placedFace] = $placedFace;
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if($this->getAdjacentSupportType($face) !== SupportType::FULL){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$world = $this->position->getWorld();
|
||||
if(count($this->faces) === 0){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int> $faces
|
||||
*/
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
return $faces;
|
||||
}
|
||||
}
|
@ -36,13 +36,17 @@ use function str_contains;
|
||||
class SignText{
|
||||
public const LINE_COUNT = 4;
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array{0: string, 1: string, 2: string, 3: string}
|
||||
*/
|
||||
private array $lines;
|
||||
private Color $baseColor;
|
||||
private bool $glowing;
|
||||
|
||||
/**
|
||||
* @param string[]|null $lines index-sensitive; keys 0-3 will be used, regardless of array order
|
||||
* @phpstan-param array{0?: string, 1?: string, 2?: string, 3?: string}|null $lines
|
||||
*
|
||||
* @throws \InvalidArgumentException if the array size is greater than 4
|
||||
* @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array
|
||||
@ -82,6 +86,7 @@ class SignText{
|
||||
* Returns an array of lines currently on the sign.
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return array{0: string, 1: string, 2: string, 3: string}
|
||||
*/
|
||||
public function getLines() : array{
|
||||
return $this->lines;
|
||||
|
60
src/command/ClosureCommand.php
Normal file
60
src/command/ClosureCommand.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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\command;
|
||||
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
/**
|
||||
* @phpstan-type Execute \Closure(CommandSender $sender, Command $command, string $commandLabel, list<string> $args) : mixed
|
||||
*/
|
||||
final class ClosureCommand extends Command{
|
||||
/** @phpstan-var Execute */
|
||||
private \Closure $execute;
|
||||
|
||||
/**
|
||||
* @param string[] $permissions
|
||||
* @phpstan-param Execute $execute
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
\Closure $execute,
|
||||
array $permissions,
|
||||
Translatable|string $description = "",
|
||||
Translatable|string|null $usageMessage = null,
|
||||
array $aliases = []
|
||||
){
|
||||
Utils::validateCallableSignature(
|
||||
fn(CommandSender $sender, Command $command, string $commandLabel, array $args) : mixed => 1,
|
||||
$execute,
|
||||
);
|
||||
$this->execute = $execute;
|
||||
parent::__construct($name, $description, $usageMessage, $aliases);
|
||||
$this->setPermissions($permissions);
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
return ($this->execute)($sender, $this, $commandLabel, $args);
|
||||
}
|
||||
}
|
@ -44,10 +44,16 @@ abstract class Command{
|
||||
private string $nextLabel;
|
||||
private string $label;
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $aliases = [];
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $activeAliases = [];
|
||||
|
||||
private ?CommandMap $commandMap = null;
|
||||
@ -62,6 +68,7 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function __construct(string $name, Translatable|string $description = "", Translatable|string|null $usageMessage = null, array $aliases = []){
|
||||
$this->name = $name;
|
||||
@ -182,6 +189,7 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getAliases() : array{
|
||||
return $this->activeAliases;
|
||||
@ -201,6 +209,7 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function setAliases(array $aliases) : void{
|
||||
$this->aliases = $aliases;
|
||||
|
@ -64,12 +64,14 @@ use pocketmine\command\defaults\TransferServerCommand;
|
||||
use pocketmine\command\defaults\VanillaCommand;
|
||||
use pocketmine\command\defaults\VersionCommand;
|
||||
use pocketmine\command\defaults\WhitelistCommand;
|
||||
use pocketmine\command\defaults\XpCommand;
|
||||
use pocketmine\command\utils\CommandStringHelper;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
@ -80,7 +82,10 @@ use function trim;
|
||||
|
||||
class SimpleCommandMap implements CommandMap{
|
||||
|
||||
/** @var Command[] */
|
||||
/**
|
||||
* @var Command[]
|
||||
* @phpstan-var array<string, Command>
|
||||
*/
|
||||
protected array $knownCommands = [];
|
||||
|
||||
public function __construct(private Server $server){
|
||||
@ -128,7 +133,8 @@ class SimpleCommandMap implements CommandMap{
|
||||
new TitleCommand(),
|
||||
new TransferServerCommand(),
|
||||
new VersionCommand(),
|
||||
new WhitelistCommand()
|
||||
new WhitelistCommand(),
|
||||
new XpCommand(),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -169,7 +175,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
|
||||
public function unregister(Command $command) : bool{
|
||||
foreach($this->knownCommands as $lbl => $cmd){
|
||||
foreach(Utils::promoteKeys($this->knownCommands) as $lbl => $cmd){
|
||||
if($cmd === $command){
|
||||
unset($this->knownCommands[$lbl]);
|
||||
}
|
||||
@ -237,6 +243,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
/**
|
||||
* @return Command[]
|
||||
* @phpstan-return array<string, Command>
|
||||
*/
|
||||
public function getCommands() : array{
|
||||
return $this->knownCommands;
|
||||
@ -245,7 +252,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
public function registerServerAliases() : void{
|
||||
$values = $this->server->getCommandAliases();
|
||||
|
||||
foreach($values as $alias => $commandStrings){
|
||||
foreach(Utils::stringifyKeys($values) as $alias => $commandStrings){
|
||||
if(str_contains($alias, ":")){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
|
||||
continue;
|
||||
|
@ -26,28 +26,28 @@ namespace pocketmine\command\defaults;
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\scheduler\BulkCurlTask;
|
||||
use pocketmine\scheduler\BulkCurlTaskOperation;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\InternetException;
|
||||
use pocketmine\utils\InternetRequestResult;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\YmlServerProperties;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function count;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
use function fseek;
|
||||
use function fwrite;
|
||||
use function http_build_query;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
use function mkdir;
|
||||
use function stream_get_contents;
|
||||
use function strtolower;
|
||||
use const CURLOPT_AUTOREFERER;
|
||||
use const CURLOPT_FOLLOWLOCATION;
|
||||
@ -101,82 +101,91 @@ class TimingsCommand extends VanillaCommand{
|
||||
TimingsHandler::reload();
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset());
|
||||
}elseif($mode === "merged" || $mode === "report" || $paste){
|
||||
$timings = "";
|
||||
if($paste){
|
||||
$fileTimings = Utils::assumeNotFalse(fopen("php://temp", "r+b"), "Opening php://temp should never fail");
|
||||
}else{
|
||||
$index = 0;
|
||||
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
|
||||
|
||||
if(!file_exists($timingFolder)){
|
||||
mkdir($timingFolder, 0777);
|
||||
}
|
||||
$timings = Path::join($timingFolder, "timings.txt");
|
||||
while(file_exists($timings)){
|
||||
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
|
||||
}
|
||||
|
||||
$fileTimings = fopen($timings, "a+b");
|
||||
}
|
||||
$lines = TimingsHandler::printTimings();
|
||||
foreach($lines as $line){
|
||||
fwrite($fileTimings, $line . PHP_EOL);
|
||||
}
|
||||
|
||||
if($paste){
|
||||
fseek($fileTimings, 0);
|
||||
$data = [
|
||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||
"data" => $content = stream_get_contents($fileTimings)
|
||||
];
|
||||
fclose($fileTimings);
|
||||
|
||||
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask(
|
||||
[new BulkCurlTaskOperation(
|
||||
"https://$host?upload=true",
|
||||
10,
|
||||
[],
|
||||
[
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"User-Agent: $agent",
|
||||
"Content-Type: application/x-www-form-urlencoded"
|
||||
],
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]
|
||||
)],
|
||||
function(array $results) use ($sender, $host) : void{
|
||||
/** @phpstan-var array<InternetRequestResult|InternetException> $results */
|
||||
if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
|
||||
return;
|
||||
}
|
||||
$result = $results[0];
|
||||
if($result instanceof InternetException){
|
||||
$sender->getServer()->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
$response = json_decode($result->getBody(), true);
|
||||
if(is_array($response) && isset($response["id"])){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
|
||||
"https://" . $host . "/?id=" . $response["id"]));
|
||||
}else{
|
||||
$sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
|
||||
}
|
||||
}
|
||||
));
|
||||
}else{
|
||||
fclose($fileTimings);
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
|
||||
}
|
||||
$timingsPromise = TimingsHandler::requestPrintTimings();
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
|
||||
$timingsPromise->onCompletion(
|
||||
fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender),
|
||||
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
|
||||
);
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $lines
|
||||
* @phpstan-param list<string> $lines
|
||||
*/
|
||||
private function createReportFile(array $lines, CommandSender $sender) : void{
|
||||
$index = 0;
|
||||
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
|
||||
|
||||
if(!file_exists($timingFolder)){
|
||||
mkdir($timingFolder, 0777);
|
||||
}
|
||||
$timings = Path::join($timingFolder, "timings.txt");
|
||||
while(file_exists($timings)){
|
||||
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
|
||||
}
|
||||
|
||||
$fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b"));
|
||||
foreach($lines as $line){
|
||||
fwrite($fileTimings, $line . PHP_EOL);
|
||||
}
|
||||
fclose($fileTimings);
|
||||
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $lines
|
||||
* @phpstan-param list<string> $lines
|
||||
*/
|
||||
private function uploadReport(array $lines, CommandSender $sender) : void{
|
||||
$data = [
|
||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||
"data" => implode("\n", $lines)
|
||||
];
|
||||
|
||||
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask(
|
||||
[new BulkCurlTaskOperation(
|
||||
"https://$host?upload=true",
|
||||
10,
|
||||
[],
|
||||
[
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"User-Agent: $agent",
|
||||
"Content-Type: application/x-www-form-urlencoded"
|
||||
],
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]
|
||||
)],
|
||||
function(array $results) use ($sender, $host) : void{
|
||||
/** @phpstan-var array<InternetRequestResult|InternetException> $results */
|
||||
if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
|
||||
return;
|
||||
}
|
||||
$result = $results[0];
|
||||
if($result instanceof InternetException){
|
||||
$sender->getServer()->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
$response = json_decode($result->getBody(), true);
|
||||
if(is_array($response) && isset($response["id"])){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
|
||||
"https://" . $host . "/?id=" . $response["id"]));
|
||||
}else{
|
||||
$sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
89
src/command/defaults/XpCommand.php
Normal file
89
src/command/defaults/XpCommand.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?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\command\defaults;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function abs;
|
||||
use function count;
|
||||
use function str_ends_with;
|
||||
use function substr;
|
||||
|
||||
class XpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"xp",
|
||||
KnownTranslationFactory::pocketmine_command_xp_description(),
|
||||
KnownTranslationFactory::pocketmine_command_xp_usage()
|
||||
);
|
||||
$this->setPermissions([
|
||||
DefaultPermissionNames::COMMAND_XP_SELF,
|
||||
DefaultPermissionNames::COMMAND_XP_OTHER
|
||||
]);
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
if(count($args) < 1){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$xpManager = $player->getXpManager();
|
||||
if(str_ends_with($args[0], "L")){
|
||||
$xpLevelAttr = $player->getAttributeMap()->get(Attribute::EXPERIENCE_LEVEL) ?? throw new AssumptionFailedError();
|
||||
$maxXpLevel = (int) $xpLevelAttr->getMaxValue();
|
||||
$currentXpLevel = $xpManager->getXpLevel();
|
||||
$xpLevels = $this->getInteger($sender, substr($args[0], 0, -1), -$currentXpLevel, $maxXpLevel - $currentXpLevel);
|
||||
if($xpLevels >= 0){
|
||||
$xpManager->addXpLevels($xpLevels, false);
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_success_levels((string) $xpLevels, $player->getName()));
|
||||
}else{
|
||||
$xpLevels = abs($xpLevels);
|
||||
$xpManager->subtractXpLevels($xpLevels);
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_success_negative_levels((string) $xpLevels, $player->getName()));
|
||||
}
|
||||
}else{
|
||||
$xp = $this->getInteger($sender, $args[0], max: Limits::INT32_MAX);
|
||||
if($xp < 0){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_failure_widthdrawXp()->prefix(TextFormat::RED));
|
||||
}else{
|
||||
$xpManager->addXp($xp, false);
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_success((string) $xp, $player->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -29,23 +29,21 @@ use pocketmine\utils\Process;
|
||||
use function cli_set_process_title;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function feof;
|
||||
use function fwrite;
|
||||
use function stream_socket_client;
|
||||
use function is_numeric;
|
||||
use const PHP_EOL;
|
||||
use const STDOUT;
|
||||
|
||||
if(count($argv) !== 2 || !is_numeric($argv[1])){
|
||||
echo "Usage: " . $argv[0] . " <command token seed>" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$commandTokenSeed = (int) $argv[1];
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 2){
|
||||
die("Please provide a server to connect to");
|
||||
}
|
||||
|
||||
@cli_set_process_title('PocketMine-MP Console Reader');
|
||||
$errCode = null;
|
||||
$errMessage = null;
|
||||
$socket = stream_socket_client($argv[1], $errCode, $errMessage, 15.0);
|
||||
if($socket === false){
|
||||
throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage");
|
||||
}
|
||||
|
||||
/** @phpstan-var ThreadSafeArray<int, string> $channel */
|
||||
$channel = new ThreadSafeArray();
|
||||
@ -75,15 +73,15 @@ $thread = new class($channel) extends NativeThread{
|
||||
};
|
||||
|
||||
$thread->start(NativeThread::INHERIT_NONE);
|
||||
while(!feof($socket)){
|
||||
while(true){
|
||||
$line = $channel->synchronized(function() use ($channel) : ?string{
|
||||
if(count($channel) === 0){
|
||||
$channel->wait(1_000_000);
|
||||
}
|
||||
$line = $channel->shift();
|
||||
return $line;
|
||||
return $channel->shift();
|
||||
});
|
||||
if(@fwrite($socket, ($line ?? "") . "\n") === false){
|
||||
$message = $line !== null ? ConsoleReaderChildProcessUtils::createMessage($line, $commandTokenSeed) : "";
|
||||
if(@fwrite(STDOUT, $message . "\n") === false){
|
||||
//Always send even if there's no line, to check if the parent is alive
|
||||
//If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return
|
||||
//false even though the connection is actually broken. However, fwrite() will fail.
|
||||
|
@ -29,19 +29,16 @@ use Symfony\Component\Filesystem\Path;
|
||||
use function base64_encode;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function mt_rand;
|
||||
use function preg_replace;
|
||||
use function proc_close;
|
||||
use function proc_open;
|
||||
use function proc_terminate;
|
||||
use function rtrim;
|
||||
use function sprintf;
|
||||
use function stream_select;
|
||||
use function stream_socket_accept;
|
||||
use function stream_socket_get_name;
|
||||
use function stream_socket_server;
|
||||
use function stream_socket_shutdown;
|
||||
use function trim;
|
||||
use const PHP_BINARY;
|
||||
use const STREAM_SHUT_RDWR;
|
||||
|
||||
/**
|
||||
* This pile of shit exists because PHP on Windows is broken, and can't handle stream_select() on stdin or pipes
|
||||
@ -58,44 +55,44 @@ use const STREAM_SHUT_RDWR;
|
||||
* communication.
|
||||
*/
|
||||
final class ConsoleReaderChildProcessDaemon{
|
||||
public const TOKEN_DELIMITER = ":";
|
||||
public const TOKEN_HASH_ALGO = "xxh3";
|
||||
|
||||
private \PrefixedLogger $logger;
|
||||
/** @var resource */
|
||||
private $subprocess;
|
||||
/** @var resource */
|
||||
private $socket;
|
||||
private int $commandTokenSeed;
|
||||
|
||||
public function __construct(
|
||||
\Logger $logger
|
||||
){
|
||||
$this->logger = new \PrefixedLogger($logger, "Console Reader Daemon");
|
||||
$this->commandTokenSeed = mt_rand();
|
||||
$this->prepareSubprocess();
|
||||
}
|
||||
|
||||
private function prepareSubprocess() : void{
|
||||
$server = stream_socket_server("tcp://127.0.0.1:0");
|
||||
if($server === false){
|
||||
throw new \RuntimeException("Failed to open console reader socket server");
|
||||
}
|
||||
$address = Utils::assumeNotFalse(stream_socket_get_name($server, false), "stream_socket_get_name() shouldn't return false here");
|
||||
|
||||
//Windows sucks, and likes to corrupt UTF-8 file paths when they travel to the subprocess, so we base64 encode
|
||||
//the path to avoid the problem. This is an abysmally shitty hack, but here we are :(
|
||||
$sub = Utils::assumeNotFalse(proc_open(
|
||||
[PHP_BINARY, '-dopcache.enable_cli=0', '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address],
|
||||
[
|
||||
PHP_BINARY,
|
||||
'-dopcache.enable_cli=0',
|
||||
'-r',
|
||||
sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))),
|
||||
(string) $this->commandTokenSeed
|
||||
],
|
||||
[
|
||||
1 => ['socket'],
|
||||
2 => fopen("php://stderr", "w"),
|
||||
],
|
||||
$pipes
|
||||
), "Something has gone horribly wrong");
|
||||
|
||||
$client = stream_socket_accept($server, 15);
|
||||
if($client === false){
|
||||
throw new AssumptionFailedError("stream_socket_accept() returned false");
|
||||
}
|
||||
stream_socket_shutdown($server, STREAM_SHUT_RDWR);
|
||||
|
||||
$this->subprocess = $sub;
|
||||
$this->socket = $client;
|
||||
$this->socket = $pipes[1];
|
||||
}
|
||||
|
||||
private function shutdownSubprocess() : void{
|
||||
@ -104,7 +101,6 @@ final class ConsoleReaderChildProcessDaemon{
|
||||
//the first place).
|
||||
proc_terminate($this->subprocess);
|
||||
proc_close($this->subprocess);
|
||||
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
|
||||
}
|
||||
|
||||
public function readLine() : ?string{
|
||||
@ -112,13 +108,27 @@ final class ConsoleReaderChildProcessDaemon{
|
||||
$w = null;
|
||||
$e = null;
|
||||
if(stream_select($r, $w, $e, 0, 0) === 1){
|
||||
$command = fgets($this->socket);
|
||||
if($command === false){
|
||||
$line = fgets($this->socket);
|
||||
if($line === false){
|
||||
$this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)");
|
||||
$this->shutdownSubprocess();
|
||||
$this->prepareSubprocess();
|
||||
return null;
|
||||
}
|
||||
$line = rtrim($line, "\n");
|
||||
|
||||
if($line === ""){
|
||||
//keepalive
|
||||
return null;
|
||||
}
|
||||
|
||||
$command = ConsoleReaderChildProcessUtils::parseMessage($line, $this->commandTokenSeed);
|
||||
if($command === null){
|
||||
//this is not a command - it may be some kind of error output from the subprocess
|
||||
//write it directly to the console
|
||||
$this->logger->warning("Unexpected output from child process: $line");
|
||||
return null;
|
||||
}
|
||||
|
||||
$command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
|
||||
$command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
|
||||
|
71
src/console/ConsoleReaderChildProcessUtils.php
Normal file
71
src/console/ConsoleReaderChildProcessUtils.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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\console;
|
||||
|
||||
use function hash;
|
||||
use function strlen;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
|
||||
final class ConsoleReaderChildProcessUtils{
|
||||
public const TOKEN_DELIMITER = ":";
|
||||
public const TOKEN_HASH_ALGO = "xxh3";
|
||||
|
||||
private function __construct(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IPC message to transmit a user's input command to the parent process.
|
||||
*
|
||||
* Unfortunately we can't currently provide IPC pipes other than stdout/stderr to subprocesses on Windows, so this
|
||||
* adds a hash of the user input (with a counter as salt) to prevent unintended process output (like error messages)
|
||||
* from being treated as user input.
|
||||
*/
|
||||
public static function createMessage(string $line, int &$counter) : string{
|
||||
$token = hash(self::TOKEN_HASH_ALGO, $line, options: ['seed' => $counter]);
|
||||
$counter++;
|
||||
return $line . self::TOKEN_DELIMITER . $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a command from an IPC message from the console reader subprocess.
|
||||
* Returns the user's input command, or null if this isn't a user input.
|
||||
*/
|
||||
public static function parseMessage(string $message, int &$counter) : ?string{
|
||||
$delimiterPos = strrpos($message, self::TOKEN_DELIMITER);
|
||||
if($delimiterPos !== false){
|
||||
$left = substr($message, 0, $delimiterPos);
|
||||
$right = substr($message, $delimiterPos + strlen(self::TOKEN_DELIMITER));
|
||||
$expectedToken = hash(self::TOKEN_HASH_ALGO, $left, options: ['seed' => $counter]);
|
||||
|
||||
if($expectedToken === $right){
|
||||
$counter++;
|
||||
return $left;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -110,14 +110,15 @@ class CraftingManager{
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
* @phpstan-param list<Item> $items
|
||||
*
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
private static function pack(array $items) : array{
|
||||
/** @var Item[] $result */
|
||||
$result = [];
|
||||
|
||||
foreach($items as $i => $item){
|
||||
foreach($items as $item){
|
||||
foreach($result as $otherItem){
|
||||
if($item->canStackWith($otherItem)){
|
||||
$otherItem->setCount($otherItem->getCount() + $item->getCount());
|
||||
@ -134,6 +135,7 @@ class CraftingManager{
|
||||
|
||||
/**
|
||||
* @param Item[] $outputs
|
||||
* @phpstan-param list<Item> $outputs
|
||||
*/
|
||||
private static function hashOutputs(array $outputs) : string{
|
||||
$outputs = self::pack($outputs);
|
||||
|
@ -193,7 +193,7 @@ final class CraftingManagerFromDataHelper{
|
||||
*/
|
||||
private static function loadJsonObjectListIntoModel(\JsonMapper $mapper, string $modelClass, array $data) : array{
|
||||
$result = [];
|
||||
foreach($data as $i => $item){
|
||||
foreach(Utils::promoteKeys($data) as $i => $item){
|
||||
if(!is_object($item)){
|
||||
throw new SavedDataLoadingException("Invalid entry at index $i: expected object, got " . get_debug_type($item));
|
||||
}
|
||||
@ -236,6 +236,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$outputs[] = $output;
|
||||
}
|
||||
//TODO: check unlocking requirements - our current system doesn't support this
|
||||
$result->registerShapelessRecipe(new ShapelessRecipe(
|
||||
$inputs,
|
||||
$outputs,
|
||||
@ -262,6 +263,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$outputs[] = $output;
|
||||
}
|
||||
//TODO: check unlocking requirements - our current system doesn't support this
|
||||
$result->registerShapedRecipe(new ShapedRecipe(
|
||||
$recipe->shape,
|
||||
$inputs,
|
||||
@ -273,7 +275,8 @@ final class CraftingManagerFromDataHelper{
|
||||
"furnace" => FurnaceType::FURNACE,
|
||||
"blast_furnace" => FurnaceType::BLAST_FURNACE,
|
||||
"smoker" => FurnaceType::SMOKER,
|
||||
//TODO: campfire
|
||||
"campfire" => FurnaceType::CAMPFIRE,
|
||||
"soul_campfire" => FurnaceType::SOUL_CAMPFIRE,
|
||||
default => null
|
||||
};
|
||||
if($furnaceType === null){
|
||||
|
@ -30,6 +30,7 @@ interface CraftingRecipe{
|
||||
* Returns a list of items needed to craft this recipe. This MUST NOT include Air items or items with a zero count.
|
||||
*
|
||||
* @return RecipeIngredient[]
|
||||
* @phpstan-return list<RecipeIngredient>
|
||||
*/
|
||||
public function getIngredientList() : array;
|
||||
|
||||
@ -37,6 +38,7 @@ interface CraftingRecipe{
|
||||
* Returns a list of results this recipe will produce when the inputs in the given crafting grid are consumed.
|
||||
*
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResultsFor(CraftingGrid $grid) : array;
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\utils\LegacyEnumShimTrait;
|
||||
use pocketmine\world\sound\BlastFurnaceSound;
|
||||
use pocketmine\world\sound\CampfireSound;
|
||||
use pocketmine\world\sound\FurnaceSound;
|
||||
use pocketmine\world\sound\SmokerSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
@ -35,8 +36,10 @@ use function spl_object_id;
|
||||
* These are retained for backwards compatibility only.
|
||||
*
|
||||
* @method static FurnaceType BLAST_FURNACE()
|
||||
* @method static FurnaceType CAMPFIRE()
|
||||
* @method static FurnaceType FURNACE()
|
||||
* @method static FurnaceType SMOKER()
|
||||
* @method static FurnaceType SOUL_CAMPFIRE()
|
||||
*
|
||||
* @phpstan-type TMetadata array{0: int, 1: Sound}
|
||||
*/
|
||||
@ -46,6 +49,8 @@ enum FurnaceType{
|
||||
case FURNACE;
|
||||
case BLAST_FURNACE;
|
||||
case SMOKER;
|
||||
case CAMPFIRE;
|
||||
case SOUL_CAMPFIRE;
|
||||
|
||||
/**
|
||||
* @phpstan-return TMetadata
|
||||
@ -58,6 +63,7 @@ enum FurnaceType{
|
||||
self::FURNACE => [200, new FurnaceSound()],
|
||||
self::BLAST_FURNACE => [100, new BlastFurnaceSound()],
|
||||
self::SMOKER => [100, new SmokerSound()],
|
||||
self::CAMPFIRE, self::SOUL_CAMPFIRE => [600, new CampfireSound()]
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -32,11 +32,20 @@ use function str_contains;
|
||||
use function strlen;
|
||||
|
||||
class ShapedRecipe implements CraftingRecipe{
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $shape = [];
|
||||
/** @var RecipeIngredient[] char => RecipeIngredient map */
|
||||
/**
|
||||
* @var RecipeIngredient[] char => RecipeIngredient map
|
||||
* @phpstan-var array<string, RecipeIngredient>
|
||||
*/
|
||||
private array $ingredientList = [];
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var list<Item>
|
||||
*/
|
||||
private array $results = [];
|
||||
|
||||
private int $height;
|
||||
@ -56,6 +65,10 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
* @param Item[] $results List of items that this recipe produces when crafted.
|
||||
*
|
||||
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
|
||||
*
|
||||
* @phpstan-param list<string> $shape
|
||||
* @phpstan-param array<string, RecipeIngredient> $ingredients
|
||||
* @phpstan-param list<Item> $results
|
||||
*/
|
||||
public function __construct(array $shape, array $ingredients, array $results){
|
||||
$this->height = count($shape);
|
||||
@ -84,7 +97,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
$this->shape = $shape;
|
||||
|
||||
foreach($ingredients as $char => $i){
|
||||
foreach(Utils::stringifyKeys($ingredients) as $char => $i){
|
||||
if(!str_contains(implode($this->shape), $char)){
|
||||
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
|
||||
}
|
||||
@ -105,6 +118,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResults() : array{
|
||||
return Utils::cloneObjectArray($this->results);
|
||||
@ -112,6 +126,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResultsFor(CraftingGrid $grid) : array{
|
||||
return $this->getResults();
|
||||
@ -119,6 +134,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return (RecipeIngredient|null)[][]
|
||||
* @phpstan-return list<list<RecipeIngredient|null>>
|
||||
*/
|
||||
public function getIngredientMap() : array{
|
||||
$ingredients = [];
|
||||
@ -132,9 +148,6 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
return $ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
$ingredients = [];
|
||||
|
||||
@ -157,6 +170,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
/**
|
||||
* Returns an array of strings containing characters representing the recipe's shape.
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getShape() : array{
|
||||
return $this->shape;
|
||||
|
@ -28,15 +28,24 @@ use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
|
||||
class ShapelessRecipe implements CraftingRecipe{
|
||||
/** @var RecipeIngredient[] */
|
||||
/**
|
||||
* @var RecipeIngredient[]
|
||||
* @phpstan-var list<RecipeIngredient>
|
||||
*/
|
||||
private array $ingredients = [];
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var list<Item>
|
||||
*/
|
||||
private array $results;
|
||||
private ShapelessRecipeType $type;
|
||||
|
||||
/**
|
||||
* @param RecipeIngredient[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
|
||||
* @param Item[] $results List of result items created by this recipe.
|
||||
*
|
||||
* @phpstan-param list<RecipeIngredient> $ingredients
|
||||
* @phpstan-param list<Item> $results
|
||||
*/
|
||||
public function __construct(array $ingredients, array $results, ShapelessRecipeType $type){
|
||||
$this->type = $type;
|
||||
@ -50,6 +59,7 @@ class ShapelessRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResults() : array{
|
||||
return Utils::cloneObjectArray($this->results);
|
||||
@ -63,9 +73,6 @@ class ShapelessRecipe implements CraftingRecipe{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
return $this->ingredients;
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\crafting\json;
|
||||
|
||||
final class ShapedRecipeData{
|
||||
use function count;
|
||||
|
||||
final class ShapedRecipeData implements \JsonSerializable{
|
||||
/**
|
||||
* @required
|
||||
* @var string[]
|
||||
@ -51,22 +53,39 @@ final class ShapedRecipeData{
|
||||
/** @required */
|
||||
public int $priority;
|
||||
|
||||
/** @var RecipeIngredientData[] */
|
||||
public array $unlockingIngredients = [];
|
||||
|
||||
/**
|
||||
* TODO: convert this to use promoted properties - avoiding them for now since it would break JsonMapper
|
||||
*
|
||||
* @param string[] $shape
|
||||
* @param RecipeIngredientData[] $input
|
||||
* @param ItemStackData[] $output
|
||||
* @param RecipeIngredientData[] $unlockingIngredients
|
||||
*
|
||||
* @phpstan-param list<string> $shape
|
||||
* @phpstan-param array<string, RecipeIngredientData> $input
|
||||
* @phpstan-param list<ItemStackData> $output
|
||||
* @phpstan-param list<RecipeIngredientData> $unlockingIngredients
|
||||
*/
|
||||
public function __construct(array $shape, array $input, array $output, string $block, int $priority){
|
||||
public function __construct(array $shape, array $input, array $output, string $block, int $priority, array $unlockingIngredients = []){
|
||||
$this->block = $block;
|
||||
$this->priority = $priority;
|
||||
$this->shape = $shape;
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->unlockingIngredients = $unlockingIngredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function jsonSerialize() : array{
|
||||
$result = (array) $this;
|
||||
if(count($this->unlockingIngredients) === 0){
|
||||
unset($result["unlockingIngredients"]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\crafting\json;
|
||||
|
||||
final class ShapelessRecipeData{
|
||||
use function count;
|
||||
|
||||
final class ShapelessRecipeData implements \JsonSerializable{
|
||||
|
||||
/**
|
||||
* @required
|
||||
@ -45,17 +47,34 @@ final class ShapelessRecipeData{
|
||||
/** @required */
|
||||
public int $priority;
|
||||
|
||||
/** @var RecipeIngredientData[] */
|
||||
public array $unlockingIngredients = [];
|
||||
|
||||
/**
|
||||
* @param RecipeIngredientData[] $input
|
||||
* @param ItemStackData[] $output
|
||||
* @param RecipeIngredientData[] $unlockingIngredients
|
||||
*
|
||||
* @phpstan-param list<RecipeIngredientData> $input
|
||||
* @phpstan-param list<ItemStackData> $output
|
||||
* @phpstan-param list<RecipeIngredientData> $unlockingIngredients
|
||||
*/
|
||||
public function __construct(array $input, array $output, string $block, int $priority){
|
||||
public function __construct(array $input, array $output, string $block, int $priority, array $unlockingIngredients = []){
|
||||
$this->block = $block;
|
||||
$this->priority = $priority;
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->unlockingIngredients = $unlockingIngredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function jsonSerialize() : array{
|
||||
$result = (array) $this;
|
||||
if(count($this->unlockingIngredients) === 0){
|
||||
unset($result["unlockingIngredients"]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,12 @@ use function strpos;
|
||||
use function substr;
|
||||
use function zend_version;
|
||||
use function zlib_encode;
|
||||
use const E_COMPILE_ERROR;
|
||||
use const E_CORE_ERROR;
|
||||
use const E_ERROR;
|
||||
use const E_PARSE;
|
||||
use const E_RECOVERABLE_ERROR;
|
||||
use const E_USER_ERROR;
|
||||
use const FILE_IGNORE_NEW_LINES;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
@ -85,6 +91,9 @@ class CrashDump{
|
||||
public const PLUGIN_INVOLVEMENT_DIRECT = "direct";
|
||||
public const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
|
||||
|
||||
public const FATAL_ERROR_MASK =
|
||||
E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
|
||||
|
||||
private CrashDumpData $data;
|
||||
private string $encodedData;
|
||||
|
||||
@ -186,7 +195,7 @@ class CrashDump{
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = error_get_last();
|
||||
if($error === null){
|
||||
if($error === null || ($error["type"] & self::FATAL_ERROR_MASK) === 0){
|
||||
throw new \RuntimeException("Crash error information missing - did something use exit()?");
|
||||
}
|
||||
$error["trace"] = Utils::printableTrace(Utils::currentTrace(3)); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user