mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 10:53:05 +00:00
Compare commits
370 Commits
gameplay-p
...
5.33.0
Author | SHA1 | Date | |
---|---|---|---|
831c5a0464 | |||
5c363965f0 | |||
95679b5a29 | |||
f1b1e1977e | |||
23d612f1af | |||
8f7e16a9ad | |||
beaedc3627 | |||
48ba334218 | |||
0be15a7403 | |||
2404d63b1f | |||
dd9030f1f5 | |||
de234d1f38 | |||
db54c481aa | |||
ac2c07c3fe | |||
ec56d65bcc | |||
c548923116 | |||
4a2c7dc684 | |||
f04c458e54 | |||
5c0a109f18 | |||
31f6f5d252 | |||
0e498720bd | |||
00d6171463 | |||
be90c6c399 | |||
17ecf11a1b | |||
93e33dad8e | |||
4cdf064344 | |||
5bf0cbec87 | |||
ef53676a59 | |||
8f9478e82f | |||
7c521b456e | |||
47140cb8d7 | |||
e824266457 | |||
2bb78f2a94 | |||
547544b5b4 | |||
eea4f40138 | |||
237ac0f802 | |||
431790a319 | |||
c0fad353a2 | |||
e89523ce66 | |||
1e8612cfc8 | |||
cb7a562c8b | |||
edb8dcbe90 | |||
f633416f05 | |||
442049d564 | |||
cce55e8939 | |||
e375437439 | |||
c417ecd30d | |||
1f87c67e37 | |||
11612ed0e2 | |||
959fd7e5e6 | |||
0b9e680753 | |||
275fdc4280 | |||
173b685b02 | |||
89d18f929f | |||
5139800e13 | |||
cc17e68072 | |||
5bfa02716d | |||
a83c62a4a2 | |||
4a3a78f11a | |||
40ea6dd30d | |||
866df55edf | |||
d41f1b2889 | |||
051671df50 | |||
4047cbaafe | |||
0977f0db7d | |||
a1d74b5710 | |||
50e15db9ac | |||
55777d20c3 | |||
f1f6e796a4 | |||
6e861afd9e | |||
7ea0f2ff43 | |||
6dbd4282cb | |||
3176e7549e | |||
92c3ce7f02 | |||
3a5432b316 | |||
45e350ddf7 | |||
40a3ee68dd | |||
7082522509 | |||
e415518435 | |||
cb508f4382 | |||
177fa76434 | |||
2a97b4294d | |||
258923cc78 | |||
04494e845c | |||
0e511ff783 | |||
6826420876 | |||
f0161c84b9 | |||
3643d3aeb8 | |||
a662510cca | |||
670d3fb997 | |||
8843b1b568 | |||
9c71f4fc1c | |||
bd39caccb0 | |||
95b4db5169 | |||
48b80ecf78 | |||
4c3a2ef46e | |||
d053e9e168 | |||
5ebbcd5d33 | |||
a4ac28592c | |||
e99665fb12 | |||
b4b6bbe29f | |||
0910a219d4 | |||
56da492e48 | |||
035d2dec23 | |||
b40b99fe72 | |||
baafaed362 | |||
bf33a625c9 | |||
059f4ee7bf | |||
dd17adeaaf | |||
98f28f8b6d | |||
a554d2acf5 | |||
5527a0c6bf | |||
eee2e62d81 | |||
4d5c27a734 | |||
e1af2a4af1 | |||
f656d7d3d7 | |||
3636173d75 | |||
9606c0e0bb | |||
55123b36e1 | |||
94fb5d95b9 | |||
b5f236c019 | |||
7169f8e553 | |||
657e6c8130 | |||
647c2587a8 | |||
81d3017ad5 | |||
a37353c060 | |||
280911ec59 | |||
abb004fbc5 | |||
e0864e7ee8 | |||
dca37d5842 | |||
67f3bb9c52 | |||
acf4341d71 | |||
84bb9d2ab4 | |||
50f3fe2578 | |||
04de72e85e | |||
4bcef443f7 | |||
d90fc3415c | |||
e2e16a4ec5 | |||
134c7309c5 | |||
5e830c7320 | |||
c1cee1fc24 | |||
d789c75c00 | |||
6196b9c995 | |||
f2e7473629 | |||
6bf9a305de | |||
f8abcd5102 | |||
6f3506360e | |||
efaf9311b3 | |||
fe70b31881 | |||
cfafb584a8 | |||
ad6f7dfedb | |||
1ea5c060fd | |||
4a5c1e7540 | |||
2548422973 | |||
028815490e | |||
a74168953c | |||
f661443ec7 | |||
1073f372f8 | |||
835c383d4e | |||
d3f6c22996 | |||
6f3851be80 | |||
071c15d7de | |||
673b39e2a1 | |||
17faa19743 | |||
e88b81a4cb | |||
687112f4cd | |||
644c04bee4 | |||
c9e85603b0 | |||
d954772a20 | |||
c80a4d5b55 | |||
f416cb8902 | |||
f123df5e0d | |||
de26ebd124 | |||
6b04bb504f | |||
1c6a4bde86 | |||
c2f8e9365b | |||
4ef21fabab | |||
4407e585e4 | |||
463be36b72 | |||
8b57e9007a | |||
e03c586c86 | |||
802e373bf3 | |||
09acbfab4c | |||
7cfaf04b87 | |||
d9e0e51e14 | |||
069ecf007f | |||
341c7a03a9 | |||
7ae90dda5d | |||
73a4b076d6 | |||
00df508727 | |||
a6553097f4 | |||
afc4a3c7f1 | |||
ac7b5b3b13 | |||
7af5eb3da2 | |||
95284bc9de | |||
2291546610 | |||
aad2bce9e4 | |||
09f0ce458c | |||
50a1e59aa4 | |||
e8824a36b9 | |||
b1e63e544f | |||
9e9f8a4870 | |||
d0d84d4c51 | |||
9382e6e5b3 | |||
e3e0c14275 | |||
afac178cf4 | |||
e2f5e3e73c | |||
3a2d0d77d1 | |||
32b98dcbde | |||
092ea07d51 | |||
706f391068 | |||
7c654271a8 | |||
19425e35ea | |||
3df2bdb879 | |||
1fed9f6cb5 | |||
3050af0bc0 | |||
a08b06d322 | |||
c876253f76 | |||
67272f8f2b | |||
77be5f8e25 | |||
9744bd7073 | |||
51cf6817b1 | |||
fd04894a7b | |||
d2d6a59c72 | |||
788ee9a008 | |||
34f801ee3c | |||
d96ef21c4d | |||
246c1776df | |||
03e4b53ac4 | |||
91ac64783f | |||
9402a20ee3 | |||
2670e81668 | |||
e29aa2f337 | |||
5ef89200c6 | |||
9b3b45258a | |||
ca4debbf08 | |||
39e69276a1 | |||
94797e3afa | |||
03f98ee0a9 | |||
70368ea653 | |||
9d6a0cc738 | |||
21ccd90147 | |||
0a9a45a126 | |||
88937f1785 | |||
07987890a0 | |||
e5a783cb9e | |||
70fb9bbdfd | |||
f60120a75e | |||
fc86d3a44e | |||
393d7f72a9 | |||
b625fee94b | |||
6b606dca95 | |||
406ddf3e53 | |||
ec6077776a | |||
f7b5cd7ff3 | |||
3453ff03fd | |||
04e63172c3 | |||
51cb1875bb | |||
0b60a47cde | |||
d1066d0199 | |||
f349ce75e4 | |||
b3f15435cc | |||
847ae26cad | |||
d42ec06647 | |||
5e0f03dff0 | |||
4a83920db9 | |||
0a16daa619 | |||
e34f34f9f4 | |||
e8c4b743b5 | |||
689a7996b9 | |||
794641c0f8 | |||
9633b7d8a7 | |||
d25ec58a6f | |||
d69a887b0d | |||
38441a6ba3 | |||
47cb04f6a6 | |||
b1c7fc017a | |||
cd59e272bc | |||
7b1b35ab1f | |||
28d31c97f8 | |||
a17512de93 | |||
601be3fb33 | |||
2e32c50670 | |||
d1fa6edc50 | |||
a1ba8bc3da | |||
73edb8799d | |||
9592f066f3 | |||
db9ba83001 | |||
1b2d2a3fe1 | |||
84ec8b7abe | |||
357dfb5c7e | |||
0358b7dce4 | |||
97c5902ae2 | |||
9a130bce32 | |||
20849d6268 | |||
b6bd3ef30c | |||
c5a1c15389 | |||
e30ae487dc | |||
59f6c85105 | |||
90f0b85d2e | |||
8ee70b209e | |||
5c905d9a95 | |||
8b23231537 | |||
8e039f2711 | |||
4a4572131f | |||
3da0b82b86 | |||
da62eb9f33 | |||
09c434983b | |||
fbaa125d0c | |||
6ad9dde43d | |||
d634a5fa3d | |||
8cea4c13c4 | |||
dc2e82df7f | |||
debf8d18fc | |||
81e3730b99 | |||
8a5eb71432 | |||
306623e890 | |||
ada3acdba4 | |||
6a1d80e021 | |||
1ff0988e75 | |||
506cfe0362 | |||
fea17fa4a9 | |||
aee358d329 | |||
80899ea72c | |||
42f90e94ff | |||
8f536e6f21 | |||
45482e868d | |||
742aa46b88 | |||
cf1b360a62 | |||
0aa6cde259 | |||
8f8fe948c1 | |||
b10caf7437 | |||
de66d84d29 | |||
636f562bcf | |||
42094e6768 | |||
b341078765 | |||
f7687af337 | |||
ba93665fe7 | |||
6817215683 | |||
67b9d6222d | |||
6f197bc1bb | |||
bba525da02 | |||
ad6d34f1a6 | |||
a8eaa43bc8 | |||
fe7c282052 | |||
45917d495c | |||
1481977f35 | |||
8efdf501ad | |||
2b0daebc2a | |||
6b2da15b80 | |||
2ef02a2c5e | |||
15e8895e54 | |||
ea8f971287 | |||
62e1d87f5e | |||
ea068d4907 | |||
fa7bc78e7c | |||
0aaf4238a8 | |||
35a90d24ec | |||
9a6e258b6c | |||
a1448bfb88 | |||
ba6828c6bd | |||
5fc96c393d | |||
2d0321ff02 | |||
c56d4d3e3c | |||
06028aac97 | |||
950f7ad7a4 | |||
49da50659f | |||
ae9b4dbb05 | |||
fcef015f32 | |||
44771c892d |
19
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
19
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
@ -1,19 +0,0 @@
|
||||
---
|
||||
name: API change request
|
||||
about: Suggest a change, addition or removal to the plugin API
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- Describe the problem you want to solve -->
|
||||
## Problem description
|
||||
|
||||
|
||||
<!--- 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 solutions that don't require API changes
|
87
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
87
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
name: Bug report
|
||||
description: Report a feature of PocketMine-MP not working as expected
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Plugin information
|
||||
|
||||
> [!IMPORTANT]
|
||||
> It's strongly recommended to test for bugs without plugins before reporting an issue.
|
||||
> This helps avoid wasting maintainers' time on bugs that are not actually caused by PocketMine-MP.
|
||||
>
|
||||
> If you're not sure whether a plugin might be causing your issue, please seek help on our [Discord](https://discord.gg/bmSAZBG) before writing an issue.
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Plugin information
|
||||
options:
|
||||
- "I haven't tested without plugins"
|
||||
- Bug happens without plugins
|
||||
- Bug only happens with certain plugins (describe below)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Bug description
|
||||
|
||||
> [!TIP]
|
||||
> Helpful information to include:
|
||||
> - Steps to reproduce the issue
|
||||
> - Error backtraces
|
||||
> - Crashdumps
|
||||
> - Plugin code that triggers the issue
|
||||
> - List of installed plugins (use /plugins)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Steps to reproduce are critical to finding the cause of the problem!**
|
||||
> Without reproducing steps, the issue will probably not be solvable and may be closed.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem description
|
||||
description: Describe the problem, and how you encountered it
|
||||
placeholder: e.g. Steps to reproduce the issue
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: What did you expect to happen?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Version, OS and game info
|
||||
> [!WARNING]
|
||||
> "Latest" is not a valid version.
|
||||
> Failure to fill these fields with valid information may result in your issue being closed.
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: PocketMine-MP version
|
||||
placeholder: Use the /version command in PocketMine-MP
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: PHP version
|
||||
placeholder: Use the /version command in PocketMine-MP
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server OS
|
||||
placeholder: Use the /version command in PocketMine-MP
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Game version (if applicable)
|
||||
placeholder: e.g. Android, iOS, Windows, Xbox, PS4, Switch
|
||||
validations:
|
||||
required: false
|
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,37 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Unexpected non-crash behaviour (except missing gameplay features)
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Issue description
|
||||
|
||||
- Expected result: What were you expecting to happen?
|
||||
- Actual result: What actually happened?
|
||||
|
||||
### Steps to reproduce the issue
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
### OS and versions
|
||||
<!-- try the `version` command | LATEST IS NOT A VALID VERSION -->
|
||||
* PocketMine-MP:
|
||||
* PHP:
|
||||
* Using JIT: yes/no (delete as appropriate) <!-- look for the giant yellow warning in the log that says you're using JIT -->
|
||||
* Server OS:
|
||||
* Game version: Android/iOS/Win10/Xbox/PS4/Switch (delete as appropriate)
|
||||
|
||||
### Plugins
|
||||
<!--- use the `plugins` command and paste the output below -->
|
||||
|
||||
- If you remove all plugins, does the issue still occur?
|
||||
- If the issue is **not** reproducible without plugins:
|
||||
- Have you asked for help on our forums before creating an issue?
|
||||
- Can you provide sample, *minimal* reproducing code for the issue? If so, paste it in the bottom section
|
||||
|
||||
### Crashdump, backtrace or other files
|
||||
<!--- Submit crashdumps at https://crash.pmmp.io and paste a link -->
|
||||
<!--- Use gist or anything else to add other files and add links here -->
|
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -3,9 +3,6 @@ contact_links:
|
||||
- name: Help & support on Discord
|
||||
url: https://discord.gg/bmSAZBG
|
||||
about: We don't accept support requests on the issue tracker. Please try asking on Discord instead.
|
||||
- name: Help & support on forums
|
||||
url: https://forums.pmmp.io
|
||||
about: We don't accept support requests on the issue tracker. Please try asking on forums instead.
|
||||
- name: Documentation
|
||||
url: https://pmmp.rtfd.io
|
||||
about: PocketMine-MP documentation
|
||||
|
16
.github/ISSUE_TEMPLATE/crash.md
vendored
16
.github/ISSUE_TEMPLATE/crash.md
vendored
@ -1,16 +0,0 @@
|
||||
---
|
||||
name: Crash
|
||||
about: Report a crash in PocketMine-MP (not plugins)
|
||||
title: Server crashed
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- submit crashdump files to https://crash.pmmp.io -->
|
||||
<!--- or, copy the data between ===BEGIN CRASH DUMP=== and ===END CRASH DUMP and paste it on a site like https://pastebin.com -->
|
||||
<!--- DON'T JUST PASTE the crashdump into an issue -->
|
||||
Link to crashdump:
|
||||
|
||||
<!--- write additional information about the crash to help us find the problem -->
|
||||
### Additional comments (optional)
|
25
.github/ISSUE_TEMPLATE/crash.yml
vendored
Normal file
25
.github/ISSUE_TEMPLATE/crash.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Crash
|
||||
description: Report a crash in PocketMine-MP (not plugins)
|
||||
title: Server crashed
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!TIP]
|
||||
> Submit crashdump `.log` files to the [Crash Archive](https://crash.pmmp.io/submit).
|
||||
> If you can't submit the crashdump to the Crash Archive, paste it on a site like [GitHub Gist](https://gist.github.com) or [Pastebin](https://pastebin.com).
|
||||
|
||||
> [!CAUTION]
|
||||
> DON'T paste the crashdump data directly into an issue.
|
||||
|
||||
- type: input
|
||||
id: crashdump-url
|
||||
attributes:
|
||||
label: Link to crashdump
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional comments (optional)
|
||||
description: Any other information that might help us solve the problem
|
19
.github/ISSUE_TEMPLATE/feature-proposal.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature-proposal.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Feature addition, change, or removal
|
||||
description: Propose adding new features, or changing/removing existing ones
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem description
|
||||
description: Explain why a change is needed
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Proposed solution
|
||||
description: Describe what changes you think should be made
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Alternative solutions or workarounds"
|
||||
description: "Describe other ways you've explored to achieve your goal"
|
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@ -12,6 +12,10 @@ updates:
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
|
||||
#since we lock this to exact versions, it causes conflicts with minor-next & major-next in composer.lock
|
||||
#better to just test updates to this locally anyway since almost every version breaks something
|
||||
- dependency-name: phpstan/phpstan
|
||||
groups:
|
||||
production-patch-updates:
|
||||
dependency-type: production
|
||||
@ -37,4 +41,7 @@ updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
interval: monthly
|
||||
groups:
|
||||
github-actions:
|
||||
patterns: ["*"]
|
||||
|
32
.github/workflows/branch-sync-cron-trigger.yml
vendored
Normal file
32
.github/workflows/branch-sync-cron-trigger.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
#Since GitHub automatically disables cron actions after 60 days of repo inactivity, we need the active repo (PM)
|
||||
#to trigger the branch merge workflow explicitly. This avoids the need for TOS-violating actions which we previously
|
||||
#used to keep the restricted action active, as the workflow depends on the activity of this repo anyway.
|
||||
|
||||
name: Trigger branch sync
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" #once per day so we don't spam merge commits on busy days
|
||||
workflow_dispatch: #for testing
|
||||
|
||||
jobs:
|
||||
trigger:
|
||||
name: Trigger branch sync RestrictedActions workflow
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
|
||||
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: RestrictedActions
|
||||
|
||||
- name: Dispatch branch sync restricted action
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
repository: ${{ github.repository_owner }}/RestrictedActions
|
||||
event-type: pocketmine_mp_branch_sync
|
43
.github/workflows/build-docker-image.yml
vendored
43
.github/workflows/build-docker-image.yml
vendored
@ -4,11 +4,16 @@ on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Tag name to build'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Update Docker Hub images
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
@ -28,16 +33,28 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Clone pmmp/PocketMine-Docker repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: pmmp/PocketMine-Docker
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Get tag names
|
||||
|
||||
- name: Get tag name
|
||||
id: tag-name
|
||||
run: |
|
||||
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
||||
echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Unsupported event type: ${{ github.event_name }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Parse version
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${{ steps.tag-name.outputs.TAG_NAME }}"
|
||||
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
|
||||
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
|
||||
|
||||
@ -53,7 +70,7 @@ jobs:
|
||||
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -66,33 +83,33 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
tags: |
|
||||
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
|
||||
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
|
||||
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }}
|
||||
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }}
|
||||
build-args: |
|
||||
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
|
||||
PMMP_REPO=${{ github.repository }}
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
tags: |
|
||||
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
|
||||
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
|
||||
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }}
|
||||
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }}
|
||||
build-args: |
|
||||
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
|
||||
PMMP_REPO=${{ github.repository }}
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
47
.github/workflows/copilot-setup-steps.yml
vendored
Normal file
47
.github/workflows/copilot-setup-steps.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: "Copilot Agent environment setup"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/copilot-setup-steps.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/copilot-setup-steps.yml
|
||||
|
||||
jobs:
|
||||
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
|
||||
copilot-setup-steps:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
with:
|
||||
php-version: 8.3
|
||||
install-path: "./bin"
|
||||
pm-version-major: 5
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-8.3-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Clone extension stubs
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: pmmp/phpstorm-stubs
|
||||
path: extension-stubs
|
21
.github/workflows/discord-release-notify.yml
vendored
21
.github/workflows/discord-release-notify.yml
vendored
@ -4,16 +4,21 @@ on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Release to make notification for'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.35.3
|
||||
with:
|
||||
php-version: 8.2
|
||||
|
||||
@ -30,9 +35,17 @@ jobs:
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
|
||||
|
||||
- name: Get actual tag name
|
||||
- name: Get tag name
|
||||
id: tag-name
|
||||
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Unsupported event type: ${{ github.event_name }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run webhook post script
|
||||
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }}
|
||||
|
65
.github/workflows/draft-release-from-pr.yml
vendored
65
.github/workflows/draft-release-from-pr.yml
vendored
@ -1,65 +0,0 @@
|
||||
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
13
.github/workflows/draft-release-from-tag.yml
vendored
@ -1,13 +0,0 @@
|
||||
#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
|
10
.github/workflows/draft-release-pr-check.yml
vendored
10
.github/workflows/draft-release-pr-check.yml
vendored
@ -24,13 +24,13 @@ permissions:
|
||||
jobs:
|
||||
check-intent:
|
||||
name: Check release trigger
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
outputs:
|
||||
valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Check IS_DEVELOPMENT_BUILD flag
|
||||
id: validate
|
||||
@ -43,13 +43,13 @@ jobs:
|
||||
#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
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.35.3
|
||||
with:
|
||||
php-version: 8.2
|
||||
|
||||
|
128
.github/workflows/draft-release.yml
vendored
128
.github/workflows/draft-release.yml
vendored
@ -1,38 +1,95 @@
|
||||
name: Draft release
|
||||
|
||||
on:
|
||||
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 }}
|
||||
#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"
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
PHP_VERSION: "8.2"
|
||||
|
||||
jobs:
|
||||
draft:
|
||||
name: Create GitHub draft release
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version: [8.2]
|
||||
skip:
|
||||
name: Check whether to ignore this tag
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
outputs:
|
||||
draft-url: ${{ steps.create-draft.outputs.html_url }}
|
||||
version: ${{ steps.get-pm-version.outputs.PM_VERSION }}
|
||||
skip: ${{ steps.exists.outputs.exists == 'true' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check if release already exists
|
||||
id: exists
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
exists=false
|
||||
if [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||
tag="$(echo "${{ github.ref }}" | cut -d/ -f3-)"
|
||||
if gh release view "$tag" --repo "${{ github.repository }}"; then
|
||||
exists=true
|
||||
fi
|
||||
fi
|
||||
echo exists=$exists >> $GITHUB_OUTPUT
|
||||
|
||||
check:
|
||||
needs: [skip]
|
||||
if: needs.skip.outputs.skip != 'true'
|
||||
name: Check release
|
||||
uses: ./.github/workflows/draft-release-pr-check.yml
|
||||
|
||||
trigger-post-release-workflow:
|
||||
name: Trigger post-release RestrictedActions workflow
|
||||
needs: [check]
|
||||
if: needs.check.outputs.valid == 'true' && github.ref_type != 'tag' #can't do post-commit for a tag
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
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 }}"}'
|
||||
|
||||
draft:
|
||||
name: Create GitHub draft release
|
||||
needs: [check]
|
||||
if: needs.check.outputs.valid == 'true' || github.ref_type == 'tag' #ignore validity check for tags
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.35.3
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-version: ${{ env.PHP_VERSION }}
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
@ -50,7 +107,7 @@ jobs:
|
||||
- name: Calculate build number
|
||||
id: build-number
|
||||
run: |
|
||||
BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
|
||||
BUILD_NUMBER=$((2300+$GITHUB_RUN_NUMBER)) #to stay above jenkins
|
||||
echo "Build number: $BUILD_NUMBER"
|
||||
echo BUILD_NUMBER=$BUILD_NUMBER >> $GITHUB_OUTPUT
|
||||
|
||||
@ -63,23 +120,31 @@ jobs:
|
||||
- name: Get PocketMine-MP release version
|
||||
id: get-pm-version
|
||||
run: |
|
||||
echo PM_VERSION=$(php build/dump-version-info.php base_version) >> $GITHUB_OUTPUT
|
||||
PM_VERSION=$(php build/dump-version-info.php base_version)
|
||||
echo PM_VERSION=$PM_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
|
||||
|
||||
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
||||
tag="$(echo "${{ github.ref }}" | cut -d/ -f3-)"
|
||||
else
|
||||
tag="$PM_VERSION"
|
||||
fi
|
||||
echo TAG_NAME=$tag >> $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/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-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-${{ env.PHP_VERSION }}-latest" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate build info
|
||||
run: |
|
||||
php build/generate-build-info-json.php \
|
||||
${{ github.sha }} \
|
||||
${{ steps.get-pm-version.outputs.PM_VERSION }} \
|
||||
${{ steps.get-pm-version.outputs.TAG_NAME }} \
|
||||
${{ github.repository }} \
|
||||
${{ steps.build-number.outputs.BUILD_NUMBER }} \
|
||||
${{ github.run_id }} \
|
||||
@ -100,7 +165,7 @@ jobs:
|
||||
${{ github.workspace }}/core-permissions.rst
|
||||
|
||||
- name: Create draft release
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
uses: ncipollo/release-action@v1.18.0
|
||||
id: create-draft
|
||||
with:
|
||||
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst
|
||||
@ -108,12 +173,19 @@ jobs:
|
||||
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 }}
|
||||
tag: ${{ steps.get-pm-version.outputs.TAG_NAME }}
|
||||
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.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 }}).
|
||||
|
||||
:warning: Found a bug? Report it on our [issue tracker](${{ github.server_url }}/${{ github.repository }}/issues). **We can't fix bugs if you don't report them.**
|
||||
|
||||
- name: Post draft release URL on PR
|
||||
if: github.event_name == 'pull_request_target'
|
||||
uses: thollander/actions-comment-pull-request@v3
|
||||
with:
|
||||
message: "${{ vars.DRAFT_RELEASE_NOTIFICATION_MENTION }} [Draft release ${{ steps.get-pm-version.outputs.PM_VERSION }}](${{ steps.create-draft.outputs.html_url }}) has been created for commit ${{ github.sha }}. Please review and publish it."
|
||||
|
10
.github/workflows/main-php-matrix.yml
vendored
10
.github/workflows/main-php-matrix.yml
vendored
@ -15,7 +15,7 @@ on:
|
||||
type: number
|
||||
image:
|
||||
description: 'Runner image to use'
|
||||
default: 'ubuntu-20.04'
|
||||
default: 'ubuntu-22.04'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
@ -27,7 +27,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
@ -91,7 +91,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@ -125,7 +125,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.2.0
|
||||
|
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@ -20,18 +20,18 @@ jobs:
|
||||
|
||||
codestyle:
|
||||
name: Code Style checks
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.35.3
|
||||
with:
|
||||
php-version: 8.2
|
||||
tools: php-cs-fixer:3.49
|
||||
php-version: 8.3
|
||||
tools: php-cs-fixer:3.75
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -40,12 +40,12 @@ jobs:
|
||||
|
||||
shellcheck:
|
||||
name: ShellCheck
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
|
32
.github/workflows/pr-remove-waiting-label.yml
vendored
32
.github/workflows/pr-remove-waiting-label.yml
vendored
@ -15,19 +15,23 @@ jobs:
|
||||
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;
|
||||
async function removeLabel(owner, repo, issue_number, name) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: issue_number,
|
||||
name: name,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
//probably label wasn't set on the issue
|
||||
console.log('Failed to remove label ' + name + ' (probably label isn\'t on the PR): ' + error.message);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
const [owner, repo] = context.payload.repository.full_name.split('/');
|
||||
removeLabel(owner, repo, context.payload.number, "Status: Waiting on Author");
|
||||
removeLabel(owner, repo, context.payload.number, "Stale");
|
||||
|
29
.github/workflows/pr-stale.yml
vendored
Normal file
29
.github/workflows/pr-stale.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: 'Clean up stale PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
stale-pr-message: |
|
||||
This PR has been marked as "Waiting on Author", but we haven't seen any activity in 7 days.
|
||||
|
||||
If there is no further activity, it will be closed in 28 days.
|
||||
|
||||
Note for maintainers: Adding an assignee to the PR will prevent it from being marked as stale.
|
||||
|
||||
close-pr-message: |
|
||||
As this PR hasn't been updated for a while, unfortunately we'll have to close it.
|
||||
|
||||
days-before-pr-stale: 7
|
||||
days-before-pr-close: 28
|
||||
only-labels: "Status: Waiting on Author"
|
||||
close-pr-label: "Resolution: Abandoned"
|
||||
exempt-all-assignees: true
|
||||
|
5
.github/workflows/support.yml
vendored
5
.github/workflows/support.yml
vendored
@ -20,10 +20,7 @@ jobs:
|
||||
|
||||
- Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there
|
||||
|
||||
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io)
|
||||
|
||||
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
|
||||
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG)
|
||||
|
||||
close-issue: true
|
||||
lock-issue: false
|
||||
|
2
.github/workflows/team-pr-auto-approve.yml
vendored
2
.github/workflows/team-pr-auto-approve.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
steps:
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
|
||||
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
|
||||
|
19
.github/workflows/update-updater-api.yml
vendored
19
.github/workflows/update-updater-api.yml
vendored
@ -4,6 +4,11 @@ on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Release to publish info for'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -14,14 +19,22 @@ jobs:
|
||||
- name: Install jq
|
||||
run: sudo apt update && sudo apt install jq -y
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/update.pmmp.io
|
||||
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
|
||||
|
||||
- name: Get actual tag name
|
||||
- name: Get tag name
|
||||
id: tag-name
|
||||
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Unsupported event type: ${{ github.event_name }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download new release information
|
||||
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json
|
||||
|
@ -6,6 +6,12 @@ $finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/tests')
|
||||
->in(__DIR__ . '/tools')
|
||||
->notPath('plugins/DevTools')
|
||||
//JsonMapper will break if the FQNs in the doc comments for these are shortened :(
|
||||
->notPath('crafting/json')
|
||||
->notPath('inventory/json')
|
||||
->notPath('data/bedrock/block/upgrade/model')
|
||||
->notPath('data/bedrock/item/upgrade/model')
|
||||
|
||||
->notName('PocketMine.php');
|
||||
|
||||
return (new PhpCsFixer\Config)
|
||||
|
@ -123,7 +123,7 @@ The following are required as a minimum for pull requests. PRs that don't meet t
|
||||
- Remember, PRs with small diffs are much easier to review. Small PRs are generally reviewed and merged much faster than large ones.
|
||||
- **Start small.** Try fixing minor bugs or doing something isolated (e.g. adding a new block or item) before attempting larger changes.
|
||||
- This helps you get familiar with the codebase, the contribution process, and the expectations of maintainers.
|
||||
- Check out the [issues page]() for something that you could tackle without too much effort.
|
||||
- Check out ["Easy task" issues](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue+is%3Aopen+label%3A%22Easy+task%22) on the issues page for something that you could tackle without too much effort.
|
||||
- **Do not copy-paste other people's code**. Many PRs involve discussion about the changes, and changes are often requested by reviewers. If you don't understand the code you're copy-pasting, your PR is likely to fail.
|
||||
- **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE.
|
||||
- **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode.
|
||||
|
@ -12,7 +12,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/actions/workflows/main.yml"><img src="https://github.com/pmmp/PocketMine-MP/workflows/CI/badge.svg" alt="CI" /></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/actions/workflows/main.yml"><img src="https://github.com/pmmp/PocketMine-MP/actions/workflows/main.yml/badge.svg" alt="CI" /></a>
|
||||
<a href="https://github.com/pmmp/PocketMine-MP/releases/latest"><img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/pmmp/PocketMine-MP?label=release&sort=semver"></a>
|
||||
<a href="https://discord.gg/bmSAZBG"><img src="https://img.shields.io/discord/373199722573201408?label=discord&color=7289DA&logo=discord" alt="Discord" /></a>
|
||||
<br>
|
||||
@ -65,6 +65,8 @@ PocketMine-MP accepts community contributions! The following resources will be u
|
||||
* [Building and running PocketMine-MP from source](BUILDING.md)
|
||||
* [Contributing Guidelines](CONTRIBUTING.md)
|
||||
|
||||
New here? Check out [issues with the "Easy task" label](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Easy%20task%22) for things you could work to familiarise yourself with the codebase.
|
||||
|
||||
## Donate
|
||||
PocketMine-MP is free, but it requires a lot of time and effort from unpaid volunteers to develop. Donations enable us to keep delivering support for new versions and adding features your players love.
|
||||
|
||||
|
@ -31,12 +31,12 @@ require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var string[]|\Closure[] $options
|
||||
* @phpstan-var array<string, string|\Closure() : string> $options
|
||||
* @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],
|
||||
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION, limit: 2)[0],
|
||||
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
||||
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"changelog_file_name" => function() : string{
|
||||
|
@ -28,6 +28,7 @@ use function dirname;
|
||||
use function fclose;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function scandir;
|
||||
use function str_replace;
|
||||
@ -59,7 +60,7 @@ foreach($files as $file){
|
||||
continue;
|
||||
}
|
||||
$path = Path::join(BEDROCK_DATA_PATH, $file);
|
||||
if(!is_file($path)){
|
||||
if(!is_file($path) && !is_dir($path)){
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -67,6 +68,7 @@ foreach($files as $file){
|
||||
'README.md',
|
||||
'LICENSE',
|
||||
'composer.json',
|
||||
'.github'
|
||||
] as $ignored){
|
||||
if($file === $ignored){
|
||||
continue 2;
|
||||
|
@ -1,174 +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\build\make_release;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\VersionInfo;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function dirname;
|
||||
use function fgets;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function getopt;
|
||||
use function is_string;
|
||||
use function max;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function sprintf;
|
||||
use function str_pad;
|
||||
use function strlen;
|
||||
use function strtolower;
|
||||
use function system;
|
||||
use const STDERR;
|
||||
use const STDIN;
|
||||
use const STDOUT;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
|
||||
$versionInfo = Filesystem::fileGetContents($versionInfoPath);
|
||||
$versionInfo = preg_replace(
|
||||
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
||||
'$1const BASE_VERSION = "' . $newVersion . '";',
|
||||
$versionInfo
|
||||
);
|
||||
$versionInfo = preg_replace(
|
||||
'/^([\t ]*public )?const IS_DEVELOPMENT_BUILD = (?:true|false);$/m',
|
||||
'$1const IS_DEVELOPMENT_BUILD = ' . ($isDev ? 'true' : 'false') . ';',
|
||||
$versionInfo
|
||||
);
|
||||
$versionInfo = preg_replace(
|
||||
'/^([\t ]*public )?const BUILD_CHANNEL = ".*";$/m',
|
||||
'$1const BUILD_CHANNEL = "' . $channel . '";',
|
||||
$versionInfo
|
||||
);
|
||||
file_put_contents($versionInfoPath, $versionInfo);
|
||||
}
|
||||
|
||||
const ACCEPTED_OPTS = [
|
||||
"current" => "Version to insert and tag",
|
||||
"next" => "Version to put in the file after tagging",
|
||||
"channel" => "Release channel to post this build into"
|
||||
];
|
||||
|
||||
function systemWrapper(string $command, string $errorMessage) : void{
|
||||
system($command, $result);
|
||||
if($result !== 0){
|
||||
echo "error: $errorMessage; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function main() : void{
|
||||
$filteredOpts = [];
|
||||
$postCommitOnly = false;
|
||||
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help", "post"])) as $optName => $optValue){
|
||||
if($optName === "help"){
|
||||
fwrite(STDOUT, "Options:\n");
|
||||
|
||||
$maxLength = max(array_map(fn(string $str) => strlen($str), array_keys(ACCEPTED_OPTS)));
|
||||
foreach(ACCEPTED_OPTS as $acceptedName => $description){
|
||||
fwrite(STDOUT, str_pad("--$acceptedName", $maxLength + 4, " ", STR_PAD_LEFT) . ": $description\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
if($optName === "post"){
|
||||
$postCommitOnly = true;
|
||||
continue;
|
||||
}
|
||||
if(!is_string($optValue)){
|
||||
fwrite(STDERR, "--$optName expects exactly 1 value\n");
|
||||
exit(1);
|
||||
}
|
||||
$filteredOpts[$optName] = $optValue;
|
||||
}
|
||||
|
||||
$channel = $filteredOpts["channel"] ?? null;
|
||||
if(isset($filteredOpts["current"])){
|
||||
$currentVer = new VersionString($filteredOpts["current"]);
|
||||
}else{
|
||||
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
|
||||
}
|
||||
|
||||
$nextVer = isset($filteredOpts["next"]) ? new VersionString($filteredOpts["next"]) : null;
|
||||
|
||||
$suffix = $currentVer->getSuffix();
|
||||
if($suffix !== ""){
|
||||
if($channel === "stable"){
|
||||
fwrite(STDERR, "error: cannot release a suffixed build into the stable channel\n");
|
||||
exit(1);
|
||||
}
|
||||
if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
|
||||
echo "error: invalid current version suffix \"$suffix\"; aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
$nextVer ??= new VersionString(sprintf(
|
||||
"%u.%u.%u-%s%u",
|
||||
$currentVer->getMajor(),
|
||||
$currentVer->getMinor(),
|
||||
$currentVer->getPatch(),
|
||||
$matches[1],
|
||||
((int) $matches[2]) + 1
|
||||
));
|
||||
$channel ??= strtolower($matches[1]);
|
||||
}else{
|
||||
$nextVer ??= new VersionString(sprintf(
|
||||
"%u.%u.%u",
|
||||
$currentVer->getMajor(),
|
||||
$currentVer->getMinor(),
|
||||
$currentVer->getPatch() + 1
|
||||
));
|
||||
$channel ??= "stable";
|
||||
}
|
||||
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
|
||||
|
||||
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");
|
||||
systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
|
||||
}
|
||||
|
||||
main();
|
Submodule build/php updated: 5016e0a3d5...ce1b095a9c
@ -25,6 +25,7 @@ namespace pocketmine\server_phar_stub;
|
||||
|
||||
use function clearstatcache;
|
||||
use function copy;
|
||||
use function define;
|
||||
use function fclose;
|
||||
use function fflush;
|
||||
use function flock;
|
||||
@ -165,4 +166,5 @@ $start = hrtime(true);
|
||||
$cacheName = preparePharCache($tmpDir, __FILE__);
|
||||
echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n";
|
||||
|
||||
define('pocketmine\ORIGINAL_PHAR_PATH', __FILE__);
|
||||
require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php';
|
||||
|
@ -129,7 +129,7 @@ function buildPhar(string $pharPath, string $basePath, array $includedPaths, arr
|
||||
}
|
||||
|
||||
function main() : void{
|
||||
if(ini_get("phar.readonly") == 1){
|
||||
if(ini_get("phar.readonly") === "1"){
|
||||
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
16
changelogs/5.22.md
Normal file
16
changelogs/5.22.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 5.22.0
|
||||
Released 4th December 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.21.50**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.50.
|
||||
|
||||
**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.50.
|
||||
- Removed support for earlier versions.
|
139
changelogs/5.23.md
Normal file
139
changelogs/5.23.md
Normal file
@ -0,0 +1,139 @@
|
||||
# 5.23.0
|
||||
Released 5th December 2024.
|
||||
|
||||
This is a minor feature release, including new gameplay features, internals improvements, API additions and
|
||||
deprecations, and improvements to timings.
|
||||
|
||||
**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
|
||||
- `/timings` now supports collecting timings from async task workers. These new timings will be shown alongside `Full Server Tick` timings, but will not be counted in total load.
|
||||
- Added `/xp` command.
|
||||
- `start.sh` will now emit warnings when the server process exits with an unusual exit code. This helps to detect unexpected segmentation faults and other kinds of native errors.
|
||||
|
||||
## Gameplay
|
||||
- Added the following new items:
|
||||
- End Crystal
|
||||
- Goat Horn (all variants)
|
||||
- Ice Bomb (from Education Edition)
|
||||
- Recovery Compass
|
||||
- Added the following enchantments:
|
||||
- Frost Walker
|
||||
- Sugarcane now self-destructs when there is no water adjacent to the base block.
|
||||
- Added basic support for middle-clicking on entities to get their spawn eggs.
|
||||
- Added sounds when drinking potions.
|
||||
- Eating food is now allowed in creative mode and in peaceful difficulty.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- Extracted `MultiAnyFacingTrait` and `MultiAnySupportTrait` from `GlowLichen` to enable reuse in other blocks.
|
||||
- The following API methods have been deprecated:
|
||||
- `Campfire->getInventory()` - this was added by mistake and can't be well-supported given the way that blocks work
|
||||
|
||||
### `pocketmine\command`
|
||||
- The following classes have been added:
|
||||
- `ClosureCommand` - allows registering a closure to execute a command
|
||||
|
||||
### `pocketmine\event`
|
||||
- Added APIs to `PlayerInteractEvent` to allow toggling item and block interactions.
|
||||
- This allows various customisations, such as allowing interactions when sneaking, selectively disabling item or block reactions, etc.
|
||||
- If both item and block interactions are disabled, the event is **not** cancelled (blocks can still be placed).
|
||||
- The following API methods have been added:
|
||||
- `public PlayerInteractEvent->setUseBlock(bool $useBlock) : void`
|
||||
- `public PlayerInteractEvent->setUseItem(bool $useItem) : void`
|
||||
- `public PlayerInteractEvent->useBlock() : bool` - returns whether the block can respond to the interaction (toggling levers, opening/closing doors, etc).
|
||||
- `public PlayerInteractEvent->useItem() : bool` - returns whether the item can respond to the interaction (spawn eggs, flint & steel, etc).
|
||||
- The following new classes have been added:
|
||||
- `player\PlayerEntityPickEvent` - called when a player middle-clicks on an entity
|
||||
|
||||
### `pocketmine\inventory\transaction`
|
||||
- The following API methods have been deprecated:
|
||||
- `InventoryAction->onAddToTransaction()`
|
||||
|
||||
### `pocketmine\permission`
|
||||
- The following API methods have been deprecated:
|
||||
- `PermissionManager->getPermissionSubscriptions()`
|
||||
- `PermissionManager->subscribeToPermission()`
|
||||
- `PermissionManager->unsubscribeFromAllPermissions()`
|
||||
- `PermissionManager->unsubscribeFromPermission()`
|
||||
|
||||
### `pocketmine\plugin`
|
||||
- The following classes have been deprecated:
|
||||
- `DiskResourceProvider`
|
||||
- `ResourceProvider`
|
||||
|
||||
### `pocketmine\promise`
|
||||
- `Promise::all()` now accepts zero promises. This will return an already-resolved promise with an empty array.
|
||||
|
||||
### `pocketmine\scheduler`
|
||||
- Added PHPStan generic types to `TaskHandler` and related APIs in `TaskScheduler` and `Task`.
|
||||
- The following API methods have been deprecated
|
||||
- `AsyncTask->publishProgress()`
|
||||
- `AsyncTask->onProgressUpdate()`
|
||||
|
||||
### `pocketmine\timings`
|
||||
- Timings can now notify other code when timings are enabled/disabled, reloaded, or collected.
|
||||
- The intent of this is to facilitate timings usage on other threads, and have the results collected into a single timings report.
|
||||
- Timings cannot directly control timings on other threads, so these callbacks allow plugins to use custom mechanisms to toggle, reset and collect timings.
|
||||
- PocketMine-MP currently uses this to collect timings from async task workers. More internal threads may be supported in the future.
|
||||
- The following API methods have been added:
|
||||
- `public static TimingsHandler::getCollectCallbacks() : ObjectSet<\Closure() : list<Promise<list<string>>>` - callbacks for (asynchronously) collecting timings (typically from other threads). The returned promises should be resolved with the result of `TimingsHandler::printCurrentThreadRecords()`.
|
||||
- `public static TimingsHandler::getReloadCallbacks() : ObjectSet<\Closure() : void>` - callbacks called when timings are reset
|
||||
- `public static TimingsHandler::getToggleCallbacks() : ObjectSet<\Closure(bool $enable) : void>` - callbacks called when timings are enabled/disabled
|
||||
- `public static TimingsHandler::requestPrintTimings() : Promise<list<string>>` - asynchronously collects timing results from all threads and assembles them into a single report
|
||||
- The following API methods have been deprecated:
|
||||
- `TimingsHandler::printTimings()` - this function cannot support async timings collection. Use `TimingsHandler::requestPrintTimings()` instead.
|
||||
- `Timings::getAsyncTaskErrorTimings()` - internal method that is no longer needed
|
||||
- The following constants have been deprecated:
|
||||
- `Timings::GROUP_BREAKDOWN` - no longer used
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following API methods have been added:
|
||||
- `public static Utils::getRandomFloat() : float` - returns a random float between 0 and 1. Drop-in replacement for `lcg_value()` in PHP 8.4.
|
||||
|
||||
## Internals
|
||||
- Blocks are now always synced with the client during a right-click-block interaction. This clears mispredictions on the client in case the new `PlayerInteractEvent` flags were customized by plugins.
|
||||
- `VanillaBlocks` and `VanillaItems` now use reflection to lookup TypeId constants by registration name, instead of requiring TypeIds to be manually specified.
|
||||
- While this is obviously a hack, it prevents incorrect constants from being used when adding new blocks, and guarantees that the names of constants in `BlockTypeIds` and `ItemTypeIds` will match their corresponding entries in `VanillaBlocks` and `VanillaItems` respectively.
|
||||
- It also significantly improves readability of `VanillaBlocks` and `VanillaItems`, as well as eliminating ugly code like `WoodLikeBlockIdHelper`.
|
||||
- In PM6, the team is exploring options to redesign `VanillaBlocks` and `VanillaItems` to eliminate the need for defining separate TypeIds entirely.
|
||||
- `ConsoleReader` now uses socket support in `proc_open()` to transmit IPC messages to the server process. Previously, a temporary socket server was used, which was unreliable in some conditions.
|
||||
- Event handler tests have been converted to PHPUnit tests by mocking `Server` and `Plugin` instances. Previously, these required integration tests for these dependencies.
|
||||
- Fixed various deprecation warnings in PHP 8.4.
|
||||
- `netresearch/jsonmapper` is now used at `5.0.0`. The PMMP fork of this library has been removed, as it is no longer needed.
|
||||
|
||||
# 5.23.1
|
||||
Released 5th December 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed signs not creating a tile when placed.
|
||||
|
||||
## Internals
|
||||
- Improved blockstate consistency check to detect tiles disappearing during refactors.
|
||||
|
||||
# 5.23.2
|
||||
Released 9th December 2024.
|
||||
|
||||
## General
|
||||
- Updated translations for Russian and Korean.
|
||||
|
||||
## Fixes
|
||||
- Fixed server build number.
|
||||
- Fixed some crashes being misreported as plugin-involved.
|
||||
|
||||
## Internals
|
||||
- Removed legacy `build/make-release.php` script. This script is no longer used, as all releases should now follow the PR workflow.
|
||||
|
||||
# 5.23.3
|
||||
Released 22nd January 2025.
|
||||
|
||||
## Fixes
|
||||
- Fixed crashes with PHP internal stack frames being flagged as plugin crashes.
|
||||
- Fixed note block instrument sounds in 1.21.50.
|
||||
|
||||
## Internals
|
||||
- Updated GitHub issue templates to use issue forms.
|
108
changelogs/5.24.md
Normal file
108
changelogs/5.24.md
Normal file
@ -0,0 +1,108 @@
|
||||
# 5.24.0
|
||||
Released 22nd January 2025.
|
||||
|
||||
This is a minor feature release, including new gameplay features, performance improvements, and minor API additions.
|
||||
|
||||
**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.
|
||||
|
||||
## Performance
|
||||
- PHP garbage collection is now managed by the server, instead of being automatically triggered by PHP.
|
||||
- The mechanism for GC triggering is designed to mimic PHP's to avoid behavioural changes. Only the place it's triggered from should be significantly different.
|
||||
- This change also avoids unnecessary GCs during object-heavy operations, such as encoding `CraftingDataPacket`. As such, performance during server join should see an improvement.
|
||||
- Timings is now able to directly measure the impact of GC. Previously, GC would show up as random spikes under random timers, skewing timing results.
|
||||
- `ChunkCache` now uses `string` for completed caches directly instead of keeping them wrapped in `CompressBatchPromise`s. This reduces memory usage, improves performance, and reduces GC workload.
|
||||
|
||||
## Configuration
|
||||
- The following settings have been removed from `pocketmine.yml` and will no longer have any effect:
|
||||
- `memory.garbage-collection.collect-async-worker` (now always `true`)
|
||||
- `memory.garbage-collection.low-memory-trigger` (now always `true`)
|
||||
- `memory.max-chunks.trigger-chunk-collect` (now always `true`)
|
||||
- `memory.world-caches.disable-chunk-cache` (now always `true`)
|
||||
- `memory.world-caches.low-memory-trigger` (now always `true`)
|
||||
|
||||
## Gameplay
|
||||
- Added the following new blocks:
|
||||
- All types of pale oak wood, and leaves
|
||||
- Resin
|
||||
- Resin Bricks, Slabs, Stairs, and Walls
|
||||
- Resin Clump
|
||||
- Chiseled Resin Bricks
|
||||
- Some blocks have had their tool tier requirements adjusted to match latest Bedrock updates.
|
||||
- Added the following new items:
|
||||
- Resin Brick
|
||||
- Music Disc - Creator
|
||||
- Music Disc - Creator (Music Box)
|
||||
- Music Disc - Precipice
|
||||
- Music Disc - Relic
|
||||
|
||||
## API
|
||||
### General
|
||||
- Many places had their PHPDoc improved to address issues highlighted by PHPStan 2.x. This may cause new, previously unreported issues to be reported in plugins using PHPStan.
|
||||
|
||||
### `pocketmine`
|
||||
- The following methods have been deprecated:
|
||||
- `MemoryManager->canUseChunkCache()`
|
||||
- `MemoryManager::dumpMemory()` - relocated to `MemoryDump` class
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new enum cases have been added:
|
||||
- `RecordType::DISK_CREATOR`
|
||||
- `RecordType::DISK_CREATOR_MUSIC_BOX`
|
||||
- `RecordType::DISK_PRECIPICE`
|
||||
- `RecordType::DISK_RELIC`
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following new methods have been added:
|
||||
- `public Player->getFlightSpeedMultiplier() : float` - a base multiplier for player's flight speed
|
||||
- `public Player->setFlightSpeedMultiplier(float $flightSpeedMultiplier) : void` - sets the player's flight speed multiplier
|
||||
- The following new constants have been added:
|
||||
- `Player::DEFAULT_FLIGHT_SPEED_MULTIPLIER`
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following new methods have been added:
|
||||
- `public static TextFormat::javaToBedrock(string $string) : string` - removes unsupported Java Edition format codes to prevent them being incorrectly displayed on Bedrock
|
||||
- The following methods have behavioural changes:
|
||||
- `TextFormat::toHTML()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline). This is because the codes previously used for `STRIKETHROUGH` and `UNDERLINE` conflict with the new material codes introduced by Minecraft Bedrock.
|
||||
- `Terminal::toANSI()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline), as above. However, underline and strikethrough can still be used on the terminal using `Terminal::$FORMAT_UNDERLINE` and `Terminal::$FORMAT_STRIKETHROUGH` respectively.
|
||||
- The following new constants have been added:
|
||||
- `TextFormat::MATERIAL_QUARTZ`
|
||||
- `TextFormat::MATERIAL_IRON`
|
||||
- `TextFormat::MATERIAL_NETHERITE`
|
||||
- `TextFormat::MATERIAL_REDSTONE`
|
||||
- `TextFormat::MATERIAL_COPPER`
|
||||
- `TextFormat::MATERIAL_GOLD`
|
||||
- `TextFormat::MATERIAL_EMERALD`
|
||||
- `TextFormat::MATERIAL_DIAMOND`
|
||||
- `TextFormat::MATERIAL_LAPIS`
|
||||
- `TextFormat::MATERIAL_AMETHYST`
|
||||
- The following constants have been deprecated:
|
||||
- `TextFormat::STRIKETHROUGH`
|
||||
- `TextFormat::UNDERLINE`
|
||||
- The following static properties have been added:
|
||||
- `Terminal::$COLOR_MATERIAL_QUARTZ`
|
||||
- `Terminal::$COLOR_MATERIAL_IRON`
|
||||
- `Terminal::$COLOR_MATERIAL_NETHERITE`
|
||||
- `Terminal::$COLOR_MATERIAL_REDSTONE`
|
||||
- `Terminal::$COLOR_MATERIAL_COPPER`
|
||||
- `Terminal::$COLOR_MATERIAL_GOLD`
|
||||
- `Terminal::$COLOR_MATERIAL_EMERALD`
|
||||
- `Terminal::$COLOR_MATERIAL_DIAMOND`
|
||||
- `Terminal::$COLOR_MATERIAL_LAPIS`
|
||||
- `Terminal::$COLOR_MATERIAL_AMETHYST`
|
||||
|
||||
## Tools
|
||||
- Fixed some UI issues in `tools/convert-world.php`
|
||||
|
||||
## Internals
|
||||
- Block cache in `World` is now size-limited. This prevents memory exhaustion when plugins call `getBlock()` many thousands of times with cache misses.
|
||||
- `RakLibServer` now disables PHP GC. As RakLib doesn't generate any unmanaged cycles, GC is just a waste of CPU time in this context.
|
||||
- `MemoryManager` now has the responsibility for triggering cycle GC. It's checked every tick, but GC won't take place unless the GC threshold is exceeded, similar to PHP.
|
||||
- This mechanism could probably do with alterations to better suit PocketMine-MP, but it was chosen to mimic PHP's own GC to minimize behavioural changes for now.
|
||||
- `AsyncTask` now triggers cycle GC after `onRun()` completes. As with `MemoryManager`, this is based on a threshold designed to mimic PHP's own behaviour.
|
||||
- `FormatConverter` now performs world provider GC periodically. This is not needed with current active providers, but was found to be a problem while developing custom providers.
|
||||
- Various internal adjustments were made to avoid returning incorrectly-keyed arrays in the code. These changes shouldn't affect anything as the arrays should have been properly ordered anyway.
|
||||
- Many places that previously used `==` and `!=` have been updated to use strict variants. This kind of change needs to be done carefully to avoid breaking `int|float` comparisons.
|
52
changelogs/5.25.md
Normal file
52
changelogs/5.25.md
Normal file
@ -0,0 +1,52 @@
|
||||
# 5.25.0
|
||||
Released 16th February 2025.
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.60. It also includes some minor API additions supporting new features in this version.
|
||||
|
||||
**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.60.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Documentation
|
||||
- Fixed the documentation of `Utils::getOS()`. It now refers to the `Utils::OS_*` constants instead of a list of hardcoded strings.
|
||||
|
||||
## API
|
||||
### `pocketmine\inventory`
|
||||
This release allows plugins to decide which creative tab they want to add their new items to.
|
||||
It also allows creating new collapsible groups of items, and modifying or removing existing ones.
|
||||
|
||||
- The following new methods have been added:
|
||||
- `public CreativeInventory->getAllEntries() : list<CreativeInventoryEntry>` - returns an array of objects, each containing a creative item and information about its category and collapsible group (if applicable).
|
||||
- `public CreativeInventory->getEntry(int $index) : ?CreativeInventoryEntry` - returns the creative item with the specified identifier, or `null` if not found
|
||||
- The following methods have signature changes:
|
||||
- `CreativeInventory->add()` now accepts two additional optional parameters: `CreativeCategory $category, ?CreativeGroup $group`. If not specified, the item will be added to the Items tab without a group.
|
||||
- The following new classes have been added:
|
||||
- `CreativeCategory` - enum of possible creative inventory categories (each has its own tab on the GUI)
|
||||
- `CreativeGroup` - contains information about a collapsible group of creative items, including tooltip text and icon
|
||||
- `CreativeInventoryEntry` - contains information about a creative inventory item, including its category and collapsible group (if applicable)
|
||||
|
||||
## Internals
|
||||
- `CreativeContentPacket` is no longer fully cached due to the requirement for translation context during construction. The individual items are still cached (which is the most expensive part), but the packet itself is now constructed on demand, and group entries are constructed on the fly. This may affect performance, but this has not been investigated.
|
||||
- `BedrockDataFiles` now includes constants for folders at the top level of `BedrockData` as well as files.
|
||||
- The structure of creative data in `BedrockData` was changed to accommodate item category and grouping information. `creativeitems.json` has been replaced by `creative/*.json`, which contain information about item grouping and also segregates item lists per category.
|
||||
- New information was added to `required_item_list.json` in `BedrockData`, as the server is now required to send item component NBT data in some cases.
|
||||
|
||||
# 5.25.1
|
||||
Released 26th February 2025.
|
||||
|
||||
## Fixes
|
||||
- Fixed confusing exception message when a block-breaking tool has an efficiency value of zero.
|
||||
- Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.)
|
||||
- Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join.
|
||||
|
||||
# 5.25.2
|
||||
Released 4th March 2025.
|
||||
|
||||
## Fixes
|
||||
- Added limits to various `explode()` calls.
|
71
changelogs/5.26.md
Normal file
71
changelogs/5.26.md
Normal file
@ -0,0 +1,71 @@
|
||||
# 5.26.0
|
||||
Released 22nd March 2025.
|
||||
|
||||
This is a minor feature release focused on performance 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.
|
||||
|
||||
## Performance
|
||||
- Significantly improved performance of entity movement. Load testing with item entities showed a 3x increase in the number of entities supported without lag.
|
||||
- Significantly improved performance of on-ground checks for player movement. This still needs further work, but optimisations implemented in this version should improve performance substantially.
|
||||
- Updated `pocketmine/nbt` dependency with performance improvements to `TAG_Compound` and `TAG_List` comparison. This should improve performance of inventory-related actions.
|
||||
- `InventoryTransaction` now avoids useless item clones when processing transactions, which should improve performance of inventory-related actions.
|
||||
|
||||
## Dependencies
|
||||
- `pocketmine/bedrock-protocol` has been updated to `36.2.0`, which adds new functions to access some packet fields.
|
||||
- `pocketmine/nbt` has been updated to `1.1.0`, which improves performance when comparing NBT object trees.
|
||||
|
||||
## Gameplay
|
||||
- Block breaking animation speed now takes into account the following: jumping, being in water, haste, mining fatigue
|
||||
|
||||
## Tools
|
||||
- `blockstate-upgrade-schema-utils.php` now has a new `dump-table` command, which turns a `.bin` palette table file into human-readable text for debugging.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- The following methods have been added:
|
||||
- `public RuntimeBlockStateRegistry->hasStateId(int $stateId) : bool` - checks whether the given state ID is registered
|
||||
|
||||
### `pocketmine\crafting`
|
||||
- The following methods have been deprecated:
|
||||
- `CraftingManager::sort()` - this was implicitly internal anyway
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following constants have been added:
|
||||
- `TextFormat::MATERIAL_RESIN`
|
||||
- The following static properties have been added:
|
||||
- `Terminal::$COLOR_MATERIAL_RESIN`
|
||||
|
||||
### `pocketmine\data\bedrock\block`
|
||||
- `BlockStateToObjectDeserializer` now permits overriding **deserializers** for Bedrock IDs. This may be useful to implement custom state handling, or to implement missing block variants (such as snow cauldron).
|
||||
- This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
|
||||
- Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
|
||||
- If you want to make a custom version of a vanilla block, create a custom type ID for it, exactly as you would for a regular custom block.
|
||||
- The following methods have been added:
|
||||
- `public BlockStateToObjectDeserializer->getDeserializerForId(string $id) : ?(\Closure(BlockStateReader) : Block)`
|
||||
|
||||
### `pocketmine\data\bedrock\item`
|
||||
- `ItemDeserializer` now permits overriding **deserializers** for Bedrock IDs. As above, this may be useful to implement custom data handling, or to implement missing variants of existing items.
|
||||
- This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
|
||||
- Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
|
||||
- As above, if you want to make a custom version of a vanilla item, create a custom type ID for it, exactly as you would for a regular custom item.
|
||||
- The following methods have been added:
|
||||
- `public ItemDeserializer->getDeserializerForId(string $id) : ?(\Closure(SavedItemData) : Item)`
|
||||
|
||||
## Internals
|
||||
- `new $class` is now banned on new internals code by a PHPStan rule. Closures or factory objects should be used instead for greater flexibility and better static analysis.
|
||||
- `CraftingManager` now uses a more stable hash function for recipe output filtering.
|
||||
- `ChunkCache` now accepts `int $dimensionId` in the constructor. This may be useful for plugins which implement the nether.
|
||||
- `RuntimeBlockStateRegistry` now precomputes basic collision info about known states for fast paths.
|
||||
- This permits specialization for common shapes like cubes and collisionless blocks, which allows skipping complex logic in entity movement calculation. This vastly improves performance.
|
||||
- Any block whose class overrides `readStateFromWorld()` or `getModelPositionOffset()` will *not* be optimised.
|
||||
- `Block->recalculateCollisionBoxes()` now has a hard requirement not to depend on anything other than available properties. It must not use `World` or its position.
|
||||
- This change was problematic for `ChorusPlant`, which used nearby blocks to calculate its collision boxes.
|
||||
- Blocks which need nearby blocks should override `readStateFromWorld()` and set dynamic state properties, similar to fences.
|
||||
- This design flaw will be corrected with a major change to `Block` internals currently in planning for a future major version.
|
||||
- `Block->getCollisionBoxes()` may not be called at all during gameplay for blocks with shapes determined to be simple, like cubes and collisionless blocks.
|
||||
- `BlockStateToObjectDeserializer` now checks if the returned blockstate is registered in `RuntimeBlockStateRegistry` to promote earlier error detection (instead of crashing in random code paths).
|
24
changelogs/5.27.md
Normal file
24
changelogs/5.27.md
Normal file
@ -0,0 +1,24 @@
|
||||
# 5.27.0
|
||||
Released 27th March 2025.
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.70.
|
||||
|
||||
**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.
|
||||
|
||||
## Interim releases
|
||||
If you're upgrading from 5.25.x directly to 5.27.0, please also read the following changelogs, as the interim releases contain important changes:
|
||||
- [5.26.0](https://github.com/pmmp/PocketMine-MP/blob/5.26.0/changelogs/5.26.md#5260) - Performance improvements and other internal improvements
|
||||
|
||||
## General
|
||||
- Aded support for Minecraft: Bedrock Edition 1.21.70.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
# 5.27.1
|
||||
Released 6th April 2025.
|
||||
|
||||
## Fixes
|
||||
- Updated RakLib to get ping timestamp handling fixes.
|
34
changelogs/5.28.md
Normal file
34
changelogs/5.28.md
Normal file
@ -0,0 +1,34 @@
|
||||
# 5.28.0
|
||||
Released 9th May 2025.
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.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.21.80.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Fixes
|
||||
- `AvailableEnchantmentRegistry` now requires provided tags to always be `string`. Previously, this wasn't enforced, leading to random crashes in core code related to enchanting.
|
||||
- `Entity->setFireTicks()` and `Entity->setOnFire()` now truncate the fire time to the max value instead of throwing exceptions.
|
||||
|
||||
## Internals
|
||||
- Improved PHPStan error reporting for unsafe foreaches. Foreach on an array with implicit keys now generates different errors than foreach on an array with string keys.
|
||||
|
||||
# 5.28.1
|
||||
Released 17th May 2025.
|
||||
|
||||
## Fixes
|
||||
- Fixed errors when PlayStation players attempt to join due to null `TitleID`.
|
||||
|
||||
# 5.28.2
|
||||
Released 17th May 2025.
|
||||
|
||||
## Fixes
|
||||
- Fixed version constraints which were incorrectly updated during the 1.21.80 update. This led to an unnoticed failure to update BedrockProtocol in the previous patch release.
|
||||
- Actually fixed PlayStation issues this time
|
25
changelogs/5.29.md
Normal file
25
changelogs/5.29.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 5.29.0
|
||||
Released 18th June 2025.
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.90.
|
||||
|
||||
**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.90.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed thread crashes sometimes not reporting proper cause information in crashdumps.
|
||||
- Fixed crash when a plugin replaced a player's held tool with a different tool with a damage exceeding the old tool's max damage during an action.
|
||||
- Fixed performance issue of `PlayerAuthInputPacket` input flags handling (broken change detection).
|
||||
- Fixed `BaseInventory->addItem()` triggering updates on empty slots when no items were added.
|
||||
- Fixed slow check in `SubChunk` block layer garbage collection.
|
||||
|
||||
## Internals
|
||||
- `LoginPacketHandler->processLogin()` signature has changed. This will break any plugins overriding `LoginPacketHandler`. As noted above, this is _not_ covered by the API version guarantee.
|
||||
- Automated branch sync for `minor-next` and `major-next` is now triggered by `repository_dispatch` from a cron job in this repository instead of `RestrictedActions`. The `RestrictedActions` cron job was getting automatically disabled by GitHub due to repo inactivity.
|
73
changelogs/5.30.md
Normal file
73
changelogs/5.30.md
Normal file
@ -0,0 +1,73 @@
|
||||
# 5.30.0
|
||||
Released 18th June 2025.
|
||||
|
||||
This is a minor feature release containing API additions, internals cleanup and user experience 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
|
||||
- Significantly reduced log spam when unknown blocks, tiles and entities are found in saved worlds.
|
||||
- The file name structure for crashdumps has been changed to improve sorting order in file browsers.
|
||||
- Buffering is now skipped on the RakLib layer. In theory this could reduce player network latency by 10 ms (YMMV).
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Many blocks have had their hardness and blast resistance updated to match vanilla.
|
||||
- Implemented Respawn Anchor.
|
||||
- Melon Stem and Pumpkin Stem drop amounts should now match vanilla (using binomial distribution).
|
||||
|
||||
## API
|
||||
## General
|
||||
- Verification of save registration has been added for blocks, entities and tiles. This is intended to make it easier to find mistakes when registering custom things, which previously would produce obscure core crashes.
|
||||
|
||||
### `pocketmine\event\block`
|
||||
- The following classes have been added:
|
||||
- `BlockPreExplodeEvent` - called before a block tries to explode
|
||||
- `BlockExplodeEvent` - called when after a block's explosion calculation has been done, but before any changes are applied
|
||||
|
||||
### `pocketmine\event\entity`
|
||||
- The following classes have been added:
|
||||
- `EntityExtinguishEvent` - called when a burning entity is extinguished by water or other sources
|
||||
- `EntityFrostWalkerEvent` - called every tick upon which an entity wearing Frost Walker boots moves; this can be used to customise or cancel the behaviour of the Frost Walker enchantment
|
||||
|
||||
### `pocketmine\event\player`
|
||||
- The following classes have been added:
|
||||
- `PlayerRespawnAnchorUseEvent` - called when a player interacts with a charged respawn anchor
|
||||
|
||||
### `pocketmine\entity`
|
||||
- The following methods have been added:
|
||||
- `public Entity->getStepHeight() : float`
|
||||
- `public Entity->setStepHeight(float $stepHeight) : void`
|
||||
|
||||
### `pocketmine\world\generator`
|
||||
- Generator execution has been decoupled from `PopulationTask` and async tasks in general. The following classes have been added:
|
||||
- `executor\GeneratorExecutor`
|
||||
- `executor\SyncGeneratorExecutor` - runs a generator on the main thread (used for flat world generation, which doesn't need threads)
|
||||
- `executor\AsyncGeneratorExecutor` - runs a generator inside an async task, as before
|
||||
- `PopulationUtils` - contains population business logic previously baked into `PopulationTask` - this permits the reuse of that logic outside async tasks
|
||||
- The following methods have signature changes:
|
||||
- `GeneratorManager->addGenerator()` now accepts an optional `bool $fast` parameter, defaulting to `false`; setting this to `true` will cause your generator to run on the main thread
|
||||
- The following methods have been added:
|
||||
- `public GeneratorManagerEntry->isFast() : bool` - returns whether this generator should run on the main thread
|
||||
- `PopulationTask` has been marked as `@internal`. In the next major version, it will move to the `generator\executor` namespace; however, for now it stays put because plugins currently have no other way to regenerate chunks.
|
||||
|
||||
## Internals
|
||||
- World data version numbers have been consolidated in `pocketmine\data\bedrock\WorldDataVersions`. This removes the need to modify several different files to support new world versions, and reduces the chances of things getting missed.
|
||||
- Block hardness and blast resistance is now unit-tested against `block_properties_table.json` in `BedrockData`. This file comes from vanilla BDS, so we can use it to verify compliance.
|
||||
- Protocol-layer "server auth block breaking" has been enabled. Functionally, this is no different from the previous system, it just works differently on the network layer.
|
||||
- Various internal classes in the `pocketmine\world\generator` namespace have been moved to the `generator\executor` namespace.
|
||||
- Removed `World->registerGenerator()` and `World->unregisterGenerator()`.
|
||||
- Removed redundant calls to `curl_close()` (obsolete since PHP 8.0).
|
||||
|
||||
# 5.30.1
|
||||
Released 23rd June 2025.
|
||||
|
||||
## Fixes
|
||||
- Fixed accidental break of backwards compatibility in `EntityExplodeEvent` introduced in the previous release.
|
||||
- Fixed placement of player holding block when exploding respawn anchor.
|
||||
- Updated BedrockProtocol to fix incorrect encoding of `ServerScriptDebugDrawerPacket`.
|
||||
- Disabled client-side locator bar, allowing plugins to write their own implementations.
|
14
changelogs/5.31.md
Normal file
14
changelogs/5.31.md
Normal file
@ -0,0 +1,14 @@
|
||||
# 5.31.0
|
||||
Released 8th July 2025.
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.93.
|
||||
|
||||
**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.93.
|
||||
- Removed support for earlier versions.
|
25
changelogs/5.32.md
Normal file
25
changelogs/5.32.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 5.32.0
|
||||
Released 6th August 2025.
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.21.100.
|
||||
|
||||
**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.100.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed deadlock on RakLib thread crash (e.g. due to port binding failure).
|
||||
|
||||
# 5.32.1
|
||||
Released 14th August 2025.
|
||||
|
||||
## Fixes
|
||||
- Hardened checks when processing resource pack sending during player logins.
|
||||
- Fixed content log warning about crafting recipe with missing ID.
|
||||
- Fixed packets in a batch still being processed after one of them caused the session to be terminated.
|
129
changelogs/5.33.md
Normal file
129
changelogs/5.33.md
Normal file
@ -0,0 +1,129 @@
|
||||
# 5.33.0
|
||||
Released 30th August 2025.
|
||||
|
||||
This is a minor feature release containing internals improvements, API improvements and new gameplay features.
|
||||
|
||||
**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.
|
||||
|
||||
## Performance
|
||||
- Worlds now remember when a chunk isn't generated. This reduces world I/O during world generation.
|
||||
- `BlockObjectToStateSerializer` now creates fewer objects in certain cases.
|
||||
|
||||
## Gameplay
|
||||
- The following blocks have been added and/or are now properly supported:
|
||||
- Hanging signs
|
||||
- Illager banners
|
||||
|
||||
## Tools
|
||||
- `generate-bedrock-data-from-packets.php` now represents items as strings directly when only an ID is present. This significantly improves readability in `BedrockData` and reduces file sizes.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- Added (and implemented) interfaces for many common block properties, to allow `instanceof` to be used:
|
||||
- `Ageable`: for blocks with age, such as crops
|
||||
- `AnyFacing`: for blocks which can face up, down, and horizontal directions (not the same as `HorizontalFacing`!)
|
||||
- `Colored`: for blocks with 16 `DyeColor` variants
|
||||
- `CoralMaterial`: for coral blocks, provides access to coral type and dead/alive
|
||||
- `HorizontalFacing`: for blocks which can **only** face horizontal directions (not the same as `AnyFacing`!)
|
||||
- `Lightable`: for light-source blocks which can be turned on and off, e.g. redstone lamp
|
||||
- `MultiAnyFacing`: for blocks which can appear in multiple faces of the same block (including up, down, and horizontal faces), e.g. glow lichen
|
||||
- `PillarRotation`: for blocks which can be oriented on an axis, e.g. logs
|
||||
- `PoweredByRedstone`: for blocks which receive power from a redstone component, e.g. redstone lamp
|
||||
- `SignLikeRotation`: for blocks which can be rotated 16 ways, e.g. signs, banners
|
||||
- `WoodMaterial`: for blocks made from wood
|
||||
- These interfaces have been implemented on many blocks. For the sake of brevity, they are not listed here, but you can expect to see them wherever the corresponding traits were used.
|
||||
- The following classes have been added:
|
||||
- `BaseOminousBanner`
|
||||
- `CeilingCenterHangingSign` - both chains connected to the same point on the block above, can face 16 directions
|
||||
- `CeilingEdgesHangingSign` - each chain connected to separate edges of the block above, can face 4 directions
|
||||
- `OminousFloorBanner` - floor version of illager banner, can face 16 directions
|
||||
- `OminousWallBanner` - wall version of illager banner, can face 4 directions
|
||||
- `WallHangingSign` - hangs from a horizontal beam, can face 4 directions
|
||||
- The following API methods have been added:
|
||||
- `public ChiseledBookshelf->setSlots(list<ChiseledBookshelfSlot> $slots) : $this`
|
||||
- `public static VanillaBlocks` methods:
|
||||
- `ACACIA_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `ACACIA_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `ACACIA_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `BIRCH_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `BIRCH_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `BIRCH_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `CHERRY_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `CHERRY_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `CHERRY_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `CRIMSON_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `CRIMSON_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `CRIMSON_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `DARK_OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `DARK_OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `DARK_OAK_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `JUNGLE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `JUNGLE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `JUNGLE_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `MANGROVE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `MANGROVE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `MANGROVE_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `OAK_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `OMINOUS_FLOOR_BANNER() : OminousFloorBanner`
|
||||
- `OMINOUS_WALL_BANNER() : OminousWallBanner`
|
||||
- `PALE_OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `PALE_OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `PALE_OAK_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `SPRUCE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `SPRUCE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `SPRUCE_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `WARPED_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
|
||||
- `WARPED_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
|
||||
- `WARPED_WALL_HANGING_SIGN() : WallHangingSign`
|
||||
- `public AgeableTrait->getMaxAge() : int` (included by all growable plant-like blocks, e.g. crops)
|
||||
|
||||
### `pocketmine\data\bedrock\block\convert`
|
||||
- A new system for symmetric block serializers and deserializers has been introduced.
|
||||
- This allows registering both a serializer and a deserializer with the same code, meaning way less code
|
||||
- It also eliminates information duplication and potential inconsistencies, improving maintainability.
|
||||
- A proper way to deal with flattened IDs (e.g. color blocks) has been introduced which _doesn't_ require hardcoding a giant mess of IDs
|
||||
- This symmetric system covers 99% of blocks which have a 1:1 association between PM and vanilla blocks, or 1:N where IDs are flattened
|
||||
- However, there are still some special cases which require registering separate serializers and deserializers (usually in cases where the PM implementation deviates from Mojang where Mojang's implementation sucks, such as hanging signs or big dripleaf).
|
||||
- No backwards compatibility breaks are expected as a result of this change. However, it's recommended to migrate old code to this new system for maintainability.
|
||||
- The following new classes have been added:
|
||||
- `BlockSerializerDeserializerRegistrar` - handles unified registration of block serializers and deserializers, based on a provided block model
|
||||
- `FlattenedIdModel` - represents a block with some properties baked into its Minecraft ID, e.g. coral or color blocks
|
||||
- `Model` - represents a regular block with all properties in its `states` NBT
|
||||
- `property\BoolFromStringProperty<TBlock>` - property mapping a bool value from a string NBT state
|
||||
- `property\BoolProperty<TBlock>`
|
||||
- `property\CommonProperties` - singleton containing commonly-used block property definitions and groups, e.g. facing, stair properties
|
||||
- `property\EnumFromRawStateMap<TEnum, TRaw>` - maps a raw NBT value to a PHP `enum` and vice versa
|
||||
- `property\IntFromRawStateMap<TRaw>` - maps a raw NBT value to PM integer constants and vice versa
|
||||
- `property\IntProperty<TBlock>` - an integer range property with a min, max, and optional offset
|
||||
- `property\Property<TBlock>` - interface implemented by all property definitions accepted by a `Model` or `FlattenedIdModel`
|
||||
- `property\StateMap<TValue, TRaw>` - interface implemented by classes accepted by mapping properties, e.g. `BoolFromStringProperty`
|
||||
- `property\StringProperty<TBlock>` - interface implemented by properties whose raw outputs are strings - these can be used as ID components in `FlattenedIdModel`
|
||||
- `property\ValueFromIntProperty<TBlock, TValue>` - property mapping a generic PM value from an int NBT state
|
||||
- `property\ValueFromStringProperty<TBlock, TValue>` - same as above, but for a string NBT state
|
||||
- `property\ValueSetFromIntProperty<TBlock, TOption>` - a property mapping an `int[]` or `enum[]` from a set of flags in NBT states
|
||||
- `property\ValueMappings` - singleton containing commonly-needed `StateMap`s
|
||||
- The following classes have been deprecated:
|
||||
- `BlockStateDeserializerHelper`
|
||||
- `BlockStateSerializerHelper`
|
||||
- The following methods have been deprecated:
|
||||
- All methods for decoding mapped property types in `BlockStateReader`, e.g. `readFacingDirection()`
|
||||
- All methods for encoding mapped property types in `BlockStateWriter`, e.g. `writeFacingDirection()`
|
||||
- All specific blocktype mapping functions in `BlockStateToObjectDeserializer`, e.g. `mapStairs()`
|
||||
- All specific blocktype mapping functions in `BlockObjectToStateSerializer`, e.g. `mapStairs()`
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following hooks have been added:
|
||||
- `public Item->getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction` - allows more complex logic for itemblocks to place blocks, without duplicating their placement conditions (used for hanging signs)
|
||||
|
||||
### `pocketmine\world`
|
||||
- `World->setChunk()` now verifies that blockstate IDs in the provided chunk are all registered in `RuntimeBlockStateRegistry`. This should provide earlier detection for custom block registration errors by plugins.
|
||||
|
||||
## Internals
|
||||
- `BlockStateUpgrader` is now almost entirely independent from `BlockStateData`. It's anticipated that the upgrader library will be separable from the core in the future.
|
||||
- `Block->readStateFromWorld()` is now triggered on chunk load for any position containing a tile. This should allow more effective updating of blocks with properties in their tiles.
|
@ -32,31 +32,35 @@
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"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",
|
||||
"netresearch/jsonmapper": "~v5.0.0",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
|
||||
"pocketmine/bedrock-data": "~6.0.0+bedrock-1.21.100",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.15.0+bedrock-1.21.100",
|
||||
"pocketmine/bedrock-protocol": "~40.0.0+bedrock-1.21.100",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.7.0",
|
||||
"pocketmine/locale-data": "~2.22.0",
|
||||
"pocketmine/locale-data": "~2.25.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/math": "~1.0.0",
|
||||
"pocketmine/nbt": "~1.0.0",
|
||||
"pocketmine/raklib": "~1.1.0",
|
||||
"pocketmine/nbt": "~1.1.0",
|
||||
"pocketmine/raklib": "~1.2.0",
|
||||
"pocketmine/raklib-ipc": "~1.0.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"ramsey/uuid": "~4.9.0",
|
||||
"symfony/filesystem": "~6.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.11.11",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpstan/phpstan": "2.1.17",
|
||||
"phpstan/phpstan-phpunit": "^2.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.0",
|
||||
"phpunit/phpunit": "^10.5.24"
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-ctype": "*",
|
||||
"symfony/polyfill-mbstring": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"pocketmine\\": "src/"
|
||||
|
644
composer.lock
generated
644
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -11,14 +11,17 @@ includes:
|
||||
|
||||
rules:
|
||||
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
|
||||
- pocketmine\phpstan\rules\DisallowDynamicNewRule
|
||||
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
|
||||
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
|
||||
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
|
||||
- pocketmine\phpstan\rules\ExplodeLimitRule
|
||||
- pocketmine\phpstan\rules\UnsafeForeachRule
|
||||
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
|
||||
|
||||
parameters:
|
||||
level: 9
|
||||
checkMissingCallableSignature: true
|
||||
rememberPossiblyImpureFunctionValues: false #risky to remember these, better for performance to avoid repeated calls anyway
|
||||
treatPhpDocTypesAsCertain: false
|
||||
bootstrapFiles:
|
||||
- tests/phpstan/bootstrap.php
|
||||
@ -31,6 +34,7 @@ parameters:
|
||||
paths:
|
||||
- build
|
||||
- src
|
||||
- tests/phpstan/DummyPluginOwned.php
|
||||
- tests/phpstan/rules
|
||||
- tests/phpunit
|
||||
- tests/plugins/TesterPlugin
|
||||
@ -44,6 +48,7 @@ parameters:
|
||||
- pocketmine\DEBUG
|
||||
- pocketmine\IS_DEVELOPMENT_BUILD
|
||||
stubFiles:
|
||||
- tests/phpstan/stubs/chunkutils2.stub
|
||||
- tests/phpstan/stubs/JsonMapper.stub
|
||||
- tests/phpstan/stubs/leveldb.stub
|
||||
- tests/phpstan/stubs/pmmpthread.stub
|
||||
|
@ -54,12 +54,6 @@ memory:
|
||||
#This only affects the main thread. Other threads should fire their own collections
|
||||
period: 36000
|
||||
|
||||
#Fire asynchronous tasks to collect garbage from workers
|
||||
collect-async-worker: true
|
||||
|
||||
#Trigger on low memory
|
||||
low-memory-trigger: true
|
||||
|
||||
#Settings controlling memory dump handling.
|
||||
memory-dump:
|
||||
#Dump memory from async workers as well as the main thread. If you have issues with segfaults when dumping memory, disable this setting.
|
||||
@ -69,16 +63,6 @@ memory:
|
||||
#Cap maximum render distance per player when low memory is triggered. Set to 0 to disable cap.
|
||||
chunk-radius: 4
|
||||
|
||||
#Do chunk garbage collection on trigger
|
||||
trigger-chunk-collect: true
|
||||
|
||||
world-caches:
|
||||
#Disallow adding to world chunk-packet caches when memory is low
|
||||
disable-chunk-cache: true
|
||||
#Clear world caches when memory is low
|
||||
low-memory-trigger: true
|
||||
|
||||
|
||||
network:
|
||||
#Threshold for batching packets, in bytes. Only these packets will be compressed
|
||||
#Set to 0 to compress everything, -1 to disable.
|
||||
|
114
src/GarbageCollectorManager.php
Normal file
114
src/GarbageCollectorManager.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use function gc_collect_cycles;
|
||||
use function gc_disable;
|
||||
use function gc_status;
|
||||
use function hrtime;
|
||||
use function max;
|
||||
use function min;
|
||||
use function number_format;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Allows threads to manually trigger the cyclic garbage collector using a threshold like PHP's own garbage collector,
|
||||
* but triggered at a time that suits the thread instead of in random code pathways.
|
||||
*
|
||||
* The GC trigger behaviour in this class was adapted from Zend/zend_gc.c as of PHP 8.3.14.
|
||||
*/
|
||||
final class GarbageCollectorManager{
|
||||
//TODO: These values could be adjusted to better suit PM, but for now we just want to mirror PHP GC to minimize
|
||||
//behavioural changes.
|
||||
private const GC_THRESHOLD_TRIGGER = 100;
|
||||
private const GC_THRESHOLD_MAX = 1_000_000_000;
|
||||
private const GC_THRESHOLD_DEFAULT = 10_001;
|
||||
private const GC_THRESHOLD_STEP = 10_000;
|
||||
|
||||
private int $threshold = self::GC_THRESHOLD_DEFAULT;
|
||||
private int $collectionTimeTotalNs = 0;
|
||||
private int $runs = 0;
|
||||
|
||||
private \Logger $logger;
|
||||
private TimingsHandler $timings;
|
||||
|
||||
public function __construct(
|
||||
\Logger $logger,
|
||||
?TimingsHandler $parentTimings,
|
||||
){
|
||||
gc_disable();
|
||||
$this->logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector");
|
||||
$this->timings = new TimingsHandler("Cyclic Garbage Collector", $parentTimings);
|
||||
}
|
||||
|
||||
private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{
|
||||
//TODO Very simple heuristic for dynamic GC buffer resizing:
|
||||
//If there are "too few" collections, increase the collection threshold
|
||||
//by a fixed step
|
||||
//Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14
|
||||
if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->threshold){
|
||||
$this->threshold = min(self::GC_THRESHOLD_MAX, $this->threshold + self::GC_THRESHOLD_STEP);
|
||||
}elseif($this->threshold > self::GC_THRESHOLD_DEFAULT){
|
||||
$this->threshold = max(self::GC_THRESHOLD_DEFAULT, $this->threshold - self::GC_THRESHOLD_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
public function getThreshold() : int{ return $this->threshold; }
|
||||
|
||||
public function getCollectionTimeTotalNs() : int{ return $this->collectionTimeTotalNs; }
|
||||
|
||||
public function maybeCollectCycles() : int{
|
||||
$rootsBefore = gc_status()["roots"];
|
||||
if($rootsBefore < $this->threshold){
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->timings->startTiming();
|
||||
|
||||
$start = hrtime(true);
|
||||
$cycles = gc_collect_cycles();
|
||||
$end = hrtime(true);
|
||||
|
||||
$rootsAfter = gc_status()["roots"];
|
||||
$this->adjustGcThreshold($cycles, $rootsAfter);
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
||||
$time = $end - $start;
|
||||
$this->collectionTimeTotalNs += $time;
|
||||
$this->runs++;
|
||||
$this->logger->info(sprintf(
|
||||
"Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms",
|
||||
$this->runs,
|
||||
number_format($time / 1_000_000, 2),
|
||||
$rootsBefore,
|
||||
$rootsAfter,
|
||||
$cycles,
|
||||
number_format($this->collectionTimeTotalNs / 1_000_000, 2)
|
||||
));
|
||||
|
||||
return $cycles;
|
||||
}
|
||||
}
|
305
src/MemoryDump.php
Normal file
305
src/MemoryDump.php
Normal file
@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function arsort;
|
||||
use function count;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function gc_disable;
|
||||
use function gc_enable;
|
||||
use function gc_enabled;
|
||||
use function get_class;
|
||||
use function get_declared_classes;
|
||||
use function get_defined_functions;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
use function is_object;
|
||||
use function is_resource;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function mkdir;
|
||||
use function print_r;
|
||||
use function spl_object_hash;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
final class MemoryDump{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Static memory dumper accessible from any thread.
|
||||
*/
|
||||
public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
|
||||
$hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
|
||||
ini_set('memory_limit', '-1');
|
||||
$gcEnabled = gc_enabled();
|
||||
gc_disable();
|
||||
|
||||
if(!file_exists($outputFolder)){
|
||||
mkdir($outputFolder, 0777, true);
|
||||
}
|
||||
|
||||
$obData = Utils::assumeNotFalse(fopen(Path::join($outputFolder, "objects.js"), "wb+"));
|
||||
|
||||
$objects = [];
|
||||
|
||||
$refCounts = [];
|
||||
|
||||
$instanceCounts = [];
|
||||
|
||||
$staticProperties = [];
|
||||
$staticCount = 0;
|
||||
|
||||
$functionStaticVars = [];
|
||||
$functionStaticVarsCount = 0;
|
||||
|
||||
foreach(get_declared_classes() as $className){
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$staticProperties[$className] = [];
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$property->isInitialized()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$staticCount++;
|
||||
$staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
if(count($staticProperties[$className]) === 0){
|
||||
unset($staticProperties[$className]);
|
||||
}
|
||||
|
||||
foreach($reflection->getMethods() as $method){
|
||||
if($method->getDeclaringClass()->getName() !== $reflection->getName()){
|
||||
continue;
|
||||
}
|
||||
$methodStatics = [];
|
||||
foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
|
||||
$methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($methodStatics) > 0){
|
||||
$functionStaticVars[$className . "::" . $method->getName()] = $methodStatics;
|
||||
$functionStaticVarsCount += count($functionStaticVars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $staticCount static properties");
|
||||
|
||||
$globalVariables = [];
|
||||
$globalCount = 0;
|
||||
|
||||
$ignoredGlobals = [
|
||||
'GLOBALS' => true,
|
||||
'_SERVER' => true,
|
||||
'_REQUEST' => true,
|
||||
'_POST' => true,
|
||||
'_GET' => true,
|
||||
'_FILES' => true,
|
||||
'_ENV' => true,
|
||||
'_COOKIE' => true,
|
||||
'_SESSION' => true
|
||||
];
|
||||
|
||||
foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$globalCount++;
|
||||
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $globalCount global variables");
|
||||
|
||||
foreach(get_defined_functions()["user"] as $function){
|
||||
$reflect = new \ReflectionFunction($function);
|
||||
|
||||
$vars = [];
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
|
||||
$vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($vars) > 0){
|
||||
$functionStaticVars[$function] = $vars;
|
||||
$functionStaticVarsCount += count($vars);
|
||||
}
|
||||
}
|
||||
file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $functionStaticVarsCount function static variables");
|
||||
|
||||
$data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
|
||||
do{
|
||||
$continue = false;
|
||||
foreach(Utils::stringifyKeys($objects) as $hash => $object){
|
||||
if(!is_object($object)){
|
||||
continue;
|
||||
}
|
||||
$continue = true;
|
||||
|
||||
$className = get_class($object);
|
||||
if(!isset($instanceCounts[$className])){
|
||||
$instanceCounts[$className] = 1;
|
||||
}else{
|
||||
$instanceCounts[$className]++;
|
||||
}
|
||||
|
||||
$objects[$hash] = true;
|
||||
$info = [
|
||||
"information" => "$hash@$className",
|
||||
];
|
||||
if($object instanceof \Closure){
|
||||
$info["definition"] = Utils::getNiceClosureName($object);
|
||||
$info["referencedVars"] = [];
|
||||
$reflect = new \ReflectionFunction($object);
|
||||
if(($closureThis = $reflect->getClosureThis()) !== null){
|
||||
$info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
|
||||
$info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}else{
|
||||
$reflection = new \ReflectionObject($object);
|
||||
|
||||
$info["properties"] = [];
|
||||
|
||||
for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if($property->isStatic()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $property->getName();
|
||||
if($reflection !== $original){
|
||||
if($property->isPrivate()){
|
||||
$name = $reflection->getName() . ":" . $name;
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!$property->isInitialized($object)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");
|
||||
}
|
||||
|
||||
}while($continue);
|
||||
|
||||
$logger->info("Wrote " . count($objects) . " objects");
|
||||
|
||||
fclose($obData);
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
|
||||
arsort($instanceCounts, SORT_NUMERIC);
|
||||
file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
|
||||
$logger->info("Finished!");
|
||||
|
||||
ini_set('memory_limit', $hardLimit);
|
||||
if($gcEnabled){
|
||||
gc_enable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object[]|true[] $objects reference parameter
|
||||
* @param int[] $refCounts reference parameter
|
||||
*
|
||||
* @phpstan-param array<string, object|true> $objects
|
||||
* @phpstan-param array<string, int> $refCounts
|
||||
* @phpstan-param-out array<string, object|true> $objects
|
||||
* @phpstan-param-out array<string, int> $refCounts
|
||||
*/
|
||||
private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{
|
||||
if($maxNesting <= 0){
|
||||
return "(error) NESTING LIMIT REACHED";
|
||||
}
|
||||
|
||||
--$maxNesting;
|
||||
|
||||
if(is_object($from)){
|
||||
if(!isset($objects[$hash = spl_object_hash($from)])){
|
||||
$objects[$hash] = $from;
|
||||
$refCounts[$hash] = 0;
|
||||
}
|
||||
|
||||
++$refCounts[$hash];
|
||||
|
||||
$data = "(object) $hash";
|
||||
}elseif(is_array($from)){
|
||||
if($recursion >= 5){
|
||||
return "(error) ARRAY RECURSION LIMIT REACHED";
|
||||
}
|
||||
$data = [];
|
||||
$numeric = 0;
|
||||
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),
|
||||
];
|
||||
$numeric++;
|
||||
}
|
||||
}elseif(is_string($from)){
|
||||
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||
}elseif(is_resource($from)){
|
||||
$data = "(resource) " . print_r($from, true);
|
||||
}elseif(is_float($from)){
|
||||
$data = "(float) $from";
|
||||
}else{
|
||||
$data = $from;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -29,52 +29,24 @@ use pocketmine\scheduler\DumpWorkerMemoryTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\Process;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\YmlServerProperties as Yml;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function arsort;
|
||||
use function count;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function gc_collect_cycles;
|
||||
use function gc_disable;
|
||||
use function gc_enable;
|
||||
use function gc_mem_caches;
|
||||
use function get_class;
|
||||
use function get_declared_classes;
|
||||
use function get_defined_functions;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function intdiv;
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
use function is_object;
|
||||
use function is_resource;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function mb_strtoupper;
|
||||
use function min;
|
||||
use function mkdir;
|
||||
use function preg_match;
|
||||
use function print_r;
|
||||
use function round;
|
||||
use function spl_object_hash;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
class MemoryManager{
|
||||
private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND;
|
||||
private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2;
|
||||
private const DEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
private GarbageCollectorManager $cycleGcManager;
|
||||
|
||||
private int $memoryLimit;
|
||||
private int $globalMemoryLimit;
|
||||
private int $checkRate;
|
||||
@ -88,14 +60,8 @@ class MemoryManager{
|
||||
|
||||
private int $garbageCollectionPeriod;
|
||||
private int $garbageCollectionTicker = 0;
|
||||
private bool $garbageCollectionTrigger;
|
||||
private bool $garbageCollectionAsync;
|
||||
|
||||
private int $lowMemChunkRadiusOverride;
|
||||
private bool $lowMemChunkGC;
|
||||
|
||||
private bool $lowMemDisableChunkCache;
|
||||
private bool $lowMemClearWorldCache;
|
||||
|
||||
private bool $dumpWorkers = true;
|
||||
|
||||
@ -105,6 +71,7 @@ class MemoryManager{
|
||||
private Server $server
|
||||
){
|
||||
$this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager");
|
||||
$this->cycleGcManager = new GarbageCollectorManager($this->logger, Timings::$memoryManager);
|
||||
|
||||
$this->init($server->getConfigGroup());
|
||||
}
|
||||
@ -142,17 +109,10 @@ class MemoryManager{
|
||||
$this->continuousTriggerRate = $config->getPropertyInt(Yml::MEMORY_CONTINUOUS_TRIGGER_RATE, self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
|
||||
|
||||
$this->garbageCollectionPeriod = $config->getPropertyInt(Yml::MEMORY_GARBAGE_COLLECTION_PERIOD, self::DEFAULT_TICKS_PER_GC);
|
||||
$this->garbageCollectionTrigger = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER, true);
|
||||
$this->garbageCollectionAsync = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER, true);
|
||||
|
||||
$this->lowMemChunkRadiusOverride = $config->getPropertyInt(Yml::MEMORY_MAX_CHUNKS_CHUNK_RADIUS, 4);
|
||||
$this->lowMemChunkGC = $config->getPropertyBool(Yml::MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT, true);
|
||||
|
||||
$this->lowMemDisableChunkCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE, true);
|
||||
$this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true);
|
||||
|
||||
$this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true);
|
||||
gc_enable();
|
||||
}
|
||||
|
||||
public function isLowMemory() : bool{
|
||||
@ -163,8 +123,11 @@ class MemoryManager{
|
||||
return $this->globalMemoryLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function canUseChunkCache() : bool{
|
||||
return !$this->lowMemory || !$this->lowMemDisableChunkCache;
|
||||
return !$this->lowMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,26 +143,19 @@ class MemoryManager{
|
||||
public function trigger(int $memory, int $limit, bool $global = false, int $triggerCount = 0) : void{
|
||||
$this->logger->debug(sprintf("%sLow memory triggered, limit %gMB, using %gMB",
|
||||
$global ? "Global " : "", round(($limit / 1024) / 1024, 2), round(($memory / 1024) / 1024, 2)));
|
||||
if($this->lowMemClearWorldCache){
|
||||
foreach($this->server->getWorldManager()->getWorlds() as $world){
|
||||
$world->clearCache(true);
|
||||
}
|
||||
ChunkCache::pruneCaches();
|
||||
foreach($this->server->getWorldManager()->getWorlds() as $world){
|
||||
$world->clearCache(true);
|
||||
}
|
||||
ChunkCache::pruneCaches();
|
||||
|
||||
if($this->lowMemChunkGC){
|
||||
foreach($this->server->getWorldManager()->getWorlds() as $world){
|
||||
$world->doChunkGarbageCollection();
|
||||
}
|
||||
foreach($this->server->getWorldManager()->getWorlds() as $world){
|
||||
$world->doChunkGarbageCollection();
|
||||
}
|
||||
|
||||
$ev = new LowMemoryEvent($memory, $limit, $global, $triggerCount);
|
||||
$ev->call();
|
||||
|
||||
$cycles = 0;
|
||||
if($this->garbageCollectionTrigger){
|
||||
$cycles = $this->triggerGarbageCollector();
|
||||
}
|
||||
$cycles = $this->triggerGarbageCollector();
|
||||
|
||||
$this->logger->debug(sprintf("Freed %gMB, $cycles cycles", round(($ev->getMemoryFreed() / 1024) / 1024, 2)));
|
||||
}
|
||||
@ -239,6 +195,8 @@ class MemoryManager{
|
||||
if($this->garbageCollectionPeriod > 0 && ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
|
||||
$this->garbageCollectionTicker = 0;
|
||||
$this->triggerGarbageCollector();
|
||||
}else{
|
||||
$this->cycleGcManager->maybeCollectCycles();
|
||||
}
|
||||
|
||||
Timings::$memoryManager->stopTiming();
|
||||
@ -247,14 +205,12 @@ class MemoryManager{
|
||||
public function triggerGarbageCollector() : int{
|
||||
Timings::$garbageCollector->startTiming();
|
||||
|
||||
if($this->garbageCollectionAsync){
|
||||
$pool = $this->server->getAsyncPool();
|
||||
if(($w = $pool->shutdownUnusedWorkers()) > 0){
|
||||
$this->logger->debug("Shut down $w idle async pool workers");
|
||||
}
|
||||
foreach($pool->getRunningWorkers() as $i){
|
||||
$pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
|
||||
}
|
||||
$pool = $this->server->getAsyncPool();
|
||||
if(($w = $pool->shutdownUnusedWorkers()) > 0){
|
||||
$this->logger->debug("Shut down $w idle async pool workers");
|
||||
}
|
||||
foreach($pool->getRunningWorkers() as $i){
|
||||
$pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
|
||||
}
|
||||
|
||||
$cycles = gc_collect_cycles();
|
||||
@ -271,7 +227,7 @@ class MemoryManager{
|
||||
public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize) : void{
|
||||
$logger = new \PrefixedLogger($this->server->getLogger(), "Memory Dump");
|
||||
$logger->notice("After the memory dump is done, the server might crash");
|
||||
self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $logger);
|
||||
MemoryDump::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $logger);
|
||||
|
||||
if($this->dumpWorkers){
|
||||
$pool = $this->server->getAsyncPool();
|
||||
@ -283,239 +239,10 @@ class MemoryManager{
|
||||
|
||||
/**
|
||||
* Static memory dumper accessible from any thread.
|
||||
* @deprecated
|
||||
* @see MemoryDump
|
||||
*/
|
||||
public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
|
||||
$hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
|
||||
ini_set('memory_limit', '-1');
|
||||
gc_disable();
|
||||
|
||||
if(!file_exists($outputFolder)){
|
||||
mkdir($outputFolder, 0777, true);
|
||||
}
|
||||
|
||||
$obData = Utils::assumeNotFalse(fopen(Path::join($outputFolder, "objects.js"), "wb+"));
|
||||
|
||||
$objects = [];
|
||||
|
||||
$refCounts = [];
|
||||
|
||||
$instanceCounts = [];
|
||||
|
||||
$staticProperties = [];
|
||||
$staticCount = 0;
|
||||
|
||||
$functionStaticVars = [];
|
||||
$functionStaticVarsCount = 0;
|
||||
|
||||
foreach(get_declared_classes() as $className){
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$staticProperties[$className] = [];
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$property->isInitialized()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$staticCount++;
|
||||
$staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
if(count($staticProperties[$className]) === 0){
|
||||
unset($staticProperties[$className]);
|
||||
}
|
||||
|
||||
foreach($reflection->getMethods() as $method){
|
||||
if($method->getDeclaringClass()->getName() !== $reflection->getName()){
|
||||
continue;
|
||||
}
|
||||
$methodStatics = [];
|
||||
foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
|
||||
$methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($methodStatics) > 0){
|
||||
$functionStaticVars[$className . "::" . $method->getName()] = $methodStatics;
|
||||
$functionStaticVarsCount += count($functionStaticVars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $staticCount static properties");
|
||||
|
||||
$globalVariables = [];
|
||||
$globalCount = 0;
|
||||
|
||||
$ignoredGlobals = [
|
||||
'GLOBALS' => true,
|
||||
'_SERVER' => true,
|
||||
'_REQUEST' => true,
|
||||
'_POST' => true,
|
||||
'_GET' => true,
|
||||
'_FILES' => true,
|
||||
'_ENV' => true,
|
||||
'_COOKIE' => true,
|
||||
'_SESSION' => true
|
||||
];
|
||||
|
||||
foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$globalCount++;
|
||||
$globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $globalCount global variables");
|
||||
|
||||
foreach(get_defined_functions()["user"] as $function){
|
||||
$reflect = new \ReflectionFunction($function);
|
||||
|
||||
$vars = [];
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
|
||||
$vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($vars) > 0){
|
||||
$functionStaticVars[$function] = $vars;
|
||||
$functionStaticVarsCount += count($vars);
|
||||
}
|
||||
}
|
||||
file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
$logger->info("Wrote $functionStaticVarsCount function static variables");
|
||||
|
||||
$data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
|
||||
do{
|
||||
$continue = false;
|
||||
foreach(Utils::stringifyKeys($objects) as $hash => $object){
|
||||
if(!is_object($object)){
|
||||
continue;
|
||||
}
|
||||
$continue = true;
|
||||
|
||||
$className = get_class($object);
|
||||
if(!isset($instanceCounts[$className])){
|
||||
$instanceCounts[$className] = 1;
|
||||
}else{
|
||||
$instanceCounts[$className]++;
|
||||
}
|
||||
|
||||
$objects[$hash] = true;
|
||||
$info = [
|
||||
"information" => "$hash@$className",
|
||||
];
|
||||
if($object instanceof \Closure){
|
||||
$info["definition"] = Utils::getNiceClosureName($object);
|
||||
$info["referencedVars"] = [];
|
||||
$reflect = new \ReflectionFunction($object);
|
||||
if(($closureThis = $reflect->getClosureThis()) !== null){
|
||||
$info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
|
||||
$info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}else{
|
||||
$reflection = new \ReflectionObject($object);
|
||||
|
||||
$info["properties"] = [];
|
||||
|
||||
for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
|
||||
foreach($reflection->getProperties() as $property){
|
||||
if($property->isStatic()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $property->getName();
|
||||
if($reflection !== $original){
|
||||
if($property->isPrivate()){
|
||||
$name = $reflection->getName() . ":" . $name;
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!$property->isInitialized($object)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");
|
||||
}
|
||||
|
||||
}while($continue);
|
||||
|
||||
$logger->info("Wrote " . count($objects) . " objects");
|
||||
|
||||
fclose($obData);
|
||||
|
||||
file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
|
||||
arsort($instanceCounts, SORT_NUMERIC);
|
||||
file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
||||
|
||||
$logger->info("Finished!");
|
||||
|
||||
ini_set('memory_limit', $hardLimit);
|
||||
gc_enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object[] $objects reference parameter
|
||||
* @param int[] $refCounts reference parameter
|
||||
*
|
||||
* @phpstan-param array<string, object> $objects
|
||||
* @phpstan-param array<string, int> $refCounts
|
||||
* @phpstan-param-out array<string, object> $objects
|
||||
* @phpstan-param-out array<string, int> $refCounts
|
||||
*/
|
||||
private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{
|
||||
if($maxNesting <= 0){
|
||||
return "(error) NESTING LIMIT REACHED";
|
||||
}
|
||||
|
||||
--$maxNesting;
|
||||
|
||||
if(is_object($from)){
|
||||
if(!isset($objects[$hash = spl_object_hash($from)])){
|
||||
$objects[$hash] = $from;
|
||||
$refCounts[$hash] = 0;
|
||||
}
|
||||
|
||||
++$refCounts[$hash];
|
||||
|
||||
$data = "(object) $hash";
|
||||
}elseif(is_array($from)){
|
||||
if($recursion >= 5){
|
||||
return "(error) ARRAY RECURSION LIMIT REACHED";
|
||||
}
|
||||
$data = [];
|
||||
$numeric = 0;
|
||||
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),
|
||||
];
|
||||
$numeric++;
|
||||
}
|
||||
}elseif(is_string($from)){
|
||||
$data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
|
||||
}elseif(is_resource($from)){
|
||||
$data = "(resource) " . print_r($from, true);
|
||||
}elseif(is_float($from)){
|
||||
$data = "(float) $from";
|
||||
}else{
|
||||
$data = $from;
|
||||
}
|
||||
|
||||
return $data;
|
||||
MemoryDump::dumpMemory($startingObject, $outputFolder, $maxNesting, $maxStringSize, $logger);
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ JIT_WARNING
|
||||
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
|
||||
if($composerGitHash !== null){
|
||||
//we can't verify dependency versions if we were installed without using git
|
||||
$currentGitHash = explode("-", VersionInfo::GIT_HASH())[0];
|
||||
$currentGitHash = explode("-", VersionInfo::GIT_HASH(), 2)[0];
|
||||
if($currentGitHash !== $composerGitHash){
|
||||
critical_error("Composer dependencies and/or autoloader are out of sync.");
|
||||
critical_error("- Current revision is $currentGitHash");
|
||||
@ -282,6 +282,11 @@ JIT_WARNING
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(defined('pocketmine\ORIGINAL_PHAR_PATH')){
|
||||
//if we're inside a phar cache, \pocketmine\PATH will not include the original phar
|
||||
Filesystem::addCleanedPath(ORIGINAL_PHAR_PATH, Filesystem::CLEAN_PATH_SRC_PREFIX);
|
||||
}
|
||||
|
||||
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
|
||||
$dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd;
|
||||
$pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
|
||||
|
@ -36,6 +36,7 @@ use pocketmine\crafting\CraftingManager;
|
||||
use pocketmine\crafting\CraftingManagerFromDataHelper;
|
||||
use pocketmine\crash\CrashDump;
|
||||
use pocketmine\crash\CrashDumpRenderer;
|
||||
use pocketmine\data\bedrock\BedrockDataFiles;
|
||||
use pocketmine\entity\EntityDataHelper;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\event\HandlerListManager;
|
||||
@ -138,6 +139,7 @@ use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function fopen;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
@ -697,7 +699,7 @@ class Server{
|
||||
|
||||
public function removeOp(string $name) : void{
|
||||
$lowercaseName = strtolower($name);
|
||||
foreach($this->operators->getAll() as $operatorName => $_){
|
||||
foreach(Utils::promoteKeys($this->operators->getAll()) as $operatorName => $_){
|
||||
$operatorName = (string) $operatorName;
|
||||
if($lowercaseName === strtolower($operatorName)){
|
||||
$this->operators->remove($operatorName);
|
||||
@ -918,6 +920,7 @@ class Server{
|
||||
TimingsHandler::getCollectCallbacks()->add(function() : array{
|
||||
$promises = [];
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
/** @phpstan-var PromiseResolver<list<string>> $resolver */
|
||||
$resolver = new PromiseResolver();
|
||||
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
|
||||
|
||||
@ -1003,7 +1006,7 @@ class Server{
|
||||
|
||||
$this->commandMap = new SimpleCommandMap($this);
|
||||
|
||||
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes"));
|
||||
$this->craftingManager = CraftingManagerFromDataHelper::make(BedrockDataFiles::RECIPES);
|
||||
|
||||
$this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger);
|
||||
|
||||
@ -1013,7 +1016,11 @@ class Server{
|
||||
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
|
||||
}
|
||||
try{
|
||||
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile)));
|
||||
$array = yaml_parse(Filesystem::fileGetContents($graylistFile));
|
||||
if(!is_array($array)){
|
||||
throw new \InvalidArgumentException("Expected array for root, but have " . gettype($array));
|
||||
}
|
||||
$pluginGraylist = PluginGraylist::fromArray($array);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
|
||||
$this->forceShutdownExit();
|
||||
@ -1174,7 +1181,7 @@ class Server{
|
||||
|
||||
if($this->worldManager->getDefaultWorld() === null){
|
||||
$default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
|
||||
if(trim($default) == ""){
|
||||
if(trim($default) === ""){
|
||||
$this->logger->warning("level-name cannot be null, using default");
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
|
||||
@ -1611,7 +1618,7 @@ class Server{
|
||||
if(!is_dir($crashFolder)){
|
||||
mkdir($crashFolder);
|
||||
}
|
||||
$crashDumpPath = Path::join($crashFolder, date("D_M_j-H.i.s-T_Y", (int) $dump->getData()->time) . ".log");
|
||||
$crashDumpPath = Path::join($crashFolder, date("Y-m-d_H.i.s_T", (int) $dump->getData()->time) . ".log");
|
||||
|
||||
$fp = @fopen($crashDumpPath, "wb");
|
||||
if(!is_resource($fp)){
|
||||
|
@ -31,8 +31,8 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.21.3";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BASE_VERSION = "5.33.0";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
/**
|
||||
|
@ -75,20 +75,14 @@ final class YmlServerProperties{
|
||||
public const MEMORY_CONTINUOUS_TRIGGER = 'memory.continuous-trigger';
|
||||
public const MEMORY_CONTINUOUS_TRIGGER_RATE = 'memory.continuous-trigger-rate';
|
||||
public const MEMORY_GARBAGE_COLLECTION = 'memory.garbage-collection';
|
||||
public const MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER = 'memory.garbage-collection.collect-async-worker';
|
||||
public const MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER = 'memory.garbage-collection.low-memory-trigger';
|
||||
public const MEMORY_GARBAGE_COLLECTION_PERIOD = 'memory.garbage-collection.period';
|
||||
public const MEMORY_GLOBAL_LIMIT = 'memory.global-limit';
|
||||
public const MEMORY_MAIN_HARD_LIMIT = 'memory.main-hard-limit';
|
||||
public const MEMORY_MAIN_LIMIT = 'memory.main-limit';
|
||||
public const MEMORY_MAX_CHUNKS = 'memory.max-chunks';
|
||||
public const MEMORY_MAX_CHUNKS_CHUNK_RADIUS = 'memory.max-chunks.chunk-radius';
|
||||
public const MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT = 'memory.max-chunks.trigger-chunk-collect';
|
||||
public const MEMORY_MEMORY_DUMP = 'memory.memory-dump';
|
||||
public const MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER = 'memory.memory-dump.dump-async-worker';
|
||||
public const MEMORY_WORLD_CACHES = 'memory.world-caches';
|
||||
public const MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE = 'memory.world-caches.disable-chunk-cache';
|
||||
public const MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER = 'memory.world-caches.low-memory-trigger';
|
||||
public const NETWORK = 'network';
|
||||
public const NETWORK_ASYNC_COMPRESSION = 'network.async-compression';
|
||||
public const NETWORK_ASYNC_COMPRESSION_THRESHOLD = 'network.async-compression-threshold';
|
||||
|
@ -23,9 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\PoweredByRedstone;
|
||||
use pocketmine\block\utils\RailPoweredByRedstoneTrait;
|
||||
|
||||
class ActivatorRail extends StraightOnlyRail{
|
||||
class ActivatorRail extends StraightOnlyRail implements PoweredByRedstone{
|
||||
use RailPoweredByRedstoneTrait;
|
||||
|
||||
//TODO
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AmethystTrait;
|
||||
use pocketmine\block\utils\AnyFacing;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
@ -38,7 +39,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class AmethystCluster extends Transparent{
|
||||
final class AmethystCluster extends Transparent implements AnyFacing{
|
||||
use AmethystTrait;
|
||||
use AnyFacingTrait;
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\inventory\AnvilInventory;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -41,7 +42,7 @@ use pocketmine\world\sound\AnvilFallSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function round;
|
||||
|
||||
class Anvil extends Transparent implements Fallable{
|
||||
class Anvil extends Transparent implements Fallable, HorizontalFacing{
|
||||
use FallableTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
@ -70,9 +71,6 @@ class Anvil extends Transparent implements Fallable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)];
|
||||
}
|
||||
|
@ -87,9 +87,6 @@ class Bamboo extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
//this places the BB at the northwest corner, not the center
|
||||
$inset = 1 - (($this->thick ? 3 : 2) / 16);
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Barrel as TileBarrel;
|
||||
use pocketmine\block\utils\AnyFacing;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -33,7 +34,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function abs;
|
||||
|
||||
class Barrel extends Opaque{
|
||||
class Barrel extends Opaque implements AnyFacing{
|
||||
use AnyFacingTrait;
|
||||
|
||||
protected bool $open = false;
|
||||
|
@ -25,19 +25,19 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Banner as TileBanner;
|
||||
use pocketmine\block\utils\BannerPatternLayer;
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Banner as ItemBanner;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function assert;
|
||||
use function count;
|
||||
|
||||
abstract class BaseBanner extends Transparent{
|
||||
abstract class BaseBanner extends Transparent implements Colored{
|
||||
use ColoredTrait;
|
||||
|
||||
/**
|
||||
@ -50,6 +50,10 @@ abstract class BaseBanner extends Transparent{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileBanner){
|
||||
if($tile->getType() === TileBanner::TYPE_OMINOUS){
|
||||
//illager banner is implemented as a separate block, as it doesn't support base color or custom patterns
|
||||
return $this->getOminousVersion();
|
||||
}
|
||||
$this->color = $tile->getBaseColor();
|
||||
$this->setPatterns($tile->getPatterns());
|
||||
}
|
||||
@ -57,6 +61,13 @@ abstract class BaseBanner extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: make this abstract in PM6 (BC break)
|
||||
*/
|
||||
protected function getOminousVersion() : Block{
|
||||
return VanillaBlocks::AIR();
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
@ -97,9 +108,6 @@ abstract class BaseBanner extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
@ -33,7 +34,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
abstract class BaseBigDripleaf extends Transparent{
|
||||
abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
abstract protected function isHead() : bool;
|
||||
|
@ -24,12 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CoralMaterial;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class BaseCoral extends Transparent{
|
||||
abstract class BaseCoral extends Transparent implements CoralMaterial{
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
90
src/block/BaseOminousBanner.php
Normal file
90
src/block/BaseOminousBanner.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?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\tile\Banner as TileBanner;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function assert;
|
||||
|
||||
abstract class BaseOminousBanner extends Transparent{
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
assert($tile instanceof TileBanner);
|
||||
$tile->setBaseColor(DyeColor::WHITE);
|
||||
$tile->setPatterns([]);
|
||||
$tile->setType(TileBanner::TYPE_OMINOUS);
|
||||
}
|
||||
|
||||
public function isSolid() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 16;
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
return 300;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->isSolid();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
abstract protected function getSupportingFace() : int;
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
return VanillaItems::OMINOUS_BANNER();
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ use pocketmine\block\tile\Sign as TileSign;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SignText;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\block\utils\WoodMaterial;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\block\utils\WoodTypeTrait;
|
||||
use pocketmine\color\Color;
|
||||
@ -34,7 +35,6 @@ use pocketmine\event\block\SignChangeEvent;
|
||||
use pocketmine\item\Dye;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
@ -45,7 +45,7 @@ use function array_map;
|
||||
use function assert;
|
||||
use function strlen;
|
||||
|
||||
abstract class BaseSign extends Transparent{
|
||||
abstract class BaseSign extends Transparent implements WoodMaterial{
|
||||
use WoodTypeTrait;
|
||||
|
||||
protected SignText $text;
|
||||
@ -95,9 +95,6 @@ abstract class BaseSign extends Transparent{
|
||||
return 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
|
@ -24,8 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Bed as TileBed;
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -41,7 +43,7 @@ use pocketmine\utils\TextFormat;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\World;
|
||||
|
||||
class Bed extends Transparent{
|
||||
class Bed extends Transparent implements Colored, HorizontalFacing{
|
||||
use ColoredTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
@ -76,9 +78,6 @@ class Bed extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Bell as TileBell;
|
||||
use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -38,7 +39,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\BellRingSound;
|
||||
|
||||
final class Bell extends Transparent{
|
||||
final class Bell extends Transparent implements HorizontalFacing{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR;
|
||||
|
@ -75,7 +75,10 @@ class Block{
|
||||
protected BlockTypeInfo $typeInfo;
|
||||
protected Position $position;
|
||||
|
||||
/** @var AxisAlignedBB[]|null */
|
||||
/**
|
||||
* @var AxisAlignedBB[]|null
|
||||
* @phpstan-var list<AxisAlignedBB>|null
|
||||
*/
|
||||
protected ?array $collisionBoxes = null;
|
||||
|
||||
private int $requiredBlockItemStateDataBits;
|
||||
@ -907,6 +910,7 @@ class Block{
|
||||
* - anti-cheat checks in plugins
|
||||
*
|
||||
* @return AxisAlignedBB[]
|
||||
* @phpstan-return list<AxisAlignedBB>
|
||||
*/
|
||||
final public function getCollisionBoxes() : array{
|
||||
if($this->collisionBoxes === null){
|
||||
@ -931,6 +935,7 @@ class Block{
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
* @phpstan-return list<AxisAlignedBB>
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()];
|
||||
|
@ -73,7 +73,7 @@ class BlockBreakInfo{
|
||||
return new self(0.0, $toolType, $toolHarvestLevel, 0.0);
|
||||
}
|
||||
|
||||
public static function indestructible(float $blastResistance = 18000000.0) : self{
|
||||
public static function indestructible(float $blastResistance = 18000003.75) : self{
|
||||
return new self(-1.0, BlockToolType::NONE, 0, $blastResistance);
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ class BlockBreakInfo{
|
||||
* Returns whether this block can be instantly broken.
|
||||
*/
|
||||
public function breaksInstantly() : bool{
|
||||
return $this->hardness == 0.0;
|
||||
return $this->hardness === 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,7 +154,7 @@ class BlockBreakInfo{
|
||||
|
||||
$efficiency = $item->getMiningEfficiency(($this->toolType & $item->getBlockToolType()) !== 0);
|
||||
if($efficiency <= 0){
|
||||
throw new \InvalidArgumentException(get_class($item) . " has invalid mining efficiency: expected >= 0, got $efficiency");
|
||||
throw new \InvalidArgumentException(get_class($item) . " must have a positive mining efficiency, but got $efficiency");
|
||||
}
|
||||
|
||||
$base /= $efficiency;
|
||||
|
@ -765,8 +765,65 @@ final class BlockTypeIds{
|
||||
public const COPPER_TRAPDOOR = 10735;
|
||||
public const CHISELED_COPPER = 10736;
|
||||
public const COPPER_GRATE = 10737;
|
||||
public const PALE_OAK_BUTTON = 10738;
|
||||
public const PALE_OAK_DOOR = 10739;
|
||||
public const PALE_OAK_FENCE = 10740;
|
||||
public const PALE_OAK_FENCE_GATE = 10741;
|
||||
public const PALE_OAK_LEAVES = 10742;
|
||||
public const PALE_OAK_LOG = 10743;
|
||||
public const PALE_OAK_PLANKS = 10744;
|
||||
public const PALE_OAK_PRESSURE_PLATE = 10745;
|
||||
public const PALE_OAK_SIGN = 10746;
|
||||
public const PALE_OAK_SLAB = 10747;
|
||||
public const PALE_OAK_STAIRS = 10748;
|
||||
public const PALE_OAK_TRAPDOOR = 10749;
|
||||
public const PALE_OAK_WALL_SIGN = 10750;
|
||||
public const PALE_OAK_WOOD = 10751;
|
||||
public const RESIN = 10752;
|
||||
public const RESIN_BRICK_SLAB = 10753;
|
||||
public const RESIN_BRICK_STAIRS = 10754;
|
||||
public const RESIN_BRICK_WALL = 10755;
|
||||
public const RESIN_BRICKS = 10756;
|
||||
public const RESIN_CLUMP = 10757;
|
||||
public const CHISELED_RESIN_BRICKS = 10758;
|
||||
public const RESPAWN_ANCHOR = 10759;
|
||||
public const OMINOUS_BANNER = 10760;
|
||||
public const OMINOUS_WALL_BANNER = 10761;
|
||||
public const ACACIA_CEILING_CENTER_HANGING_SIGN = 10762;
|
||||
public const ACACIA_CEILING_EDGES_HANGING_SIGN = 10763;
|
||||
public const ACACIA_WALL_HANGING_SIGN = 10764;
|
||||
public const BIRCH_CEILING_CENTER_HANGING_SIGN = 10765;
|
||||
public const BIRCH_CEILING_EDGES_HANGING_SIGN = 10766;
|
||||
public const BIRCH_WALL_HANGING_SIGN = 10767;
|
||||
public const CHERRY_CEILING_CENTER_HANGING_SIGN = 10768;
|
||||
public const CHERRY_CEILING_EDGES_HANGING_SIGN = 10769;
|
||||
public const CHERRY_WALL_HANGING_SIGN = 10770;
|
||||
public const CRIMSON_CEILING_CENTER_HANGING_SIGN = 10771;
|
||||
public const CRIMSON_CEILING_EDGES_HANGING_SIGN = 10772;
|
||||
public const CRIMSON_WALL_HANGING_SIGN = 10773;
|
||||
public const DARK_OAK_CEILING_CENTER_HANGING_SIGN = 10774;
|
||||
public const DARK_OAK_CEILING_EDGES_HANGING_SIGN = 10775;
|
||||
public const DARK_OAK_WALL_HANGING_SIGN = 10776;
|
||||
public const JUNGLE_CEILING_CENTER_HANGING_SIGN = 10777;
|
||||
public const JUNGLE_CEILING_EDGES_HANGING_SIGN = 10778;
|
||||
public const JUNGLE_WALL_HANGING_SIGN = 10779;
|
||||
public const MANGROVE_CEILING_CENTER_HANGING_SIGN = 10780;
|
||||
public const MANGROVE_CEILING_EDGES_HANGING_SIGN = 10781;
|
||||
public const MANGROVE_WALL_HANGING_SIGN = 10782;
|
||||
public const OAK_CEILING_CENTER_HANGING_SIGN = 10783;
|
||||
public const OAK_CEILING_EDGES_HANGING_SIGN = 10784;
|
||||
public const OAK_WALL_HANGING_SIGN = 10785;
|
||||
public const PALE_OAK_CEILING_CENTER_HANGING_SIGN = 10786;
|
||||
public const PALE_OAK_CEILING_EDGES_HANGING_SIGN = 10787;
|
||||
public const PALE_OAK_WALL_HANGING_SIGN = 10788;
|
||||
public const SPRUCE_CEILING_CENTER_HANGING_SIGN = 10789;
|
||||
public const SPRUCE_CEILING_EDGES_HANGING_SIGN = 10790;
|
||||
public const SPRUCE_WALL_HANGING_SIGN = 10791;
|
||||
public const WARPED_CEILING_CENTER_HANGING_SIGN = 10792;
|
||||
public const WARPED_CEILING_EDGES_HANGING_SIGN = 10793;
|
||||
public const WARPED_WALL_HANGING_SIGN = 10794;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10738;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10795;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
@ -31,4 +31,5 @@ final class BlockTypeTags{
|
||||
public const SAND = self::PREFIX . "sand";
|
||||
public const POTTABLE_PLANTS = self::PREFIX . "pottable";
|
||||
public const FIRE = self::PREFIX . "fire";
|
||||
public const HANGING_SIGN = self::PREFIX . "hanging_sign";
|
||||
}
|
||||
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\PillarRotation;
|
||||
use pocketmine\block\utils\PillarRotationTrait;
|
||||
|
||||
class BoneBlock extends Opaque{
|
||||
class BoneBlock extends Opaque implements PillarRotation{
|
||||
use PillarRotationTrait;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnyFacing;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -33,7 +34,7 @@ use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\RedstonePowerOffSound;
|
||||
use pocketmine\world\sound\RedstonePowerOnSound;
|
||||
|
||||
abstract class Button extends Flowable{
|
||||
abstract class Button extends Flowable implements AnyFacing{
|
||||
use AnyFacingTrait;
|
||||
|
||||
protected bool $pressed = false;
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Ageable;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
@ -33,7 +34,7 @@ use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
class Cactus extends Transparent{
|
||||
class Cactus extends Transparent implements Ageable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
@ -43,9 +44,6 @@ class Cactus extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$shrinkSize = 1 / 16;
|
||||
return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)];
|
||||
|
@ -40,9 +40,6 @@ class Cake extends BaseCake{
|
||||
$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CandleTrait;
|
||||
use pocketmine\block\utils\Lightable;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -31,14 +32,11 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class CakeWithCandle extends BaseCake{
|
||||
class CakeWithCandle extends BaseCake implements Lightable{
|
||||
use CandleTrait {
|
||||
onInteract as onInteractCandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
|
@ -23,10 +23,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
class CakeWithDyedCandle extends CakeWithCandle{
|
||||
class CakeWithDyedCandle extends CakeWithCandle implements Colored{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
|
@ -25,7 +25,9 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\CampfireInventory;
|
||||
use pocketmine\block\tile\Campfire as TileCampfire;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\Lightable;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\crafting\FurnaceRecipe;
|
||||
@ -59,7 +61,7 @@ use function count;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
class Campfire extends Transparent{
|
||||
class Campfire extends Transparent implements Lightable, HorizontalFacing{
|
||||
use HorizontalFacingTrait{
|
||||
HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CandleTrait;
|
||||
use pocketmine\block\utils\Lightable;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -35,7 +36,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Candle extends Transparent{
|
||||
class Candle extends Transparent implements Lightable{
|
||||
use CandleTrait {
|
||||
describeBlockOnlyState as encodeLitState;
|
||||
getLightLevel as getBaseLightLevel;
|
||||
|
@ -23,12 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
class Carpet extends Flowable{
|
||||
class Carpet extends Flowable implements Colored{
|
||||
use ColoredTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
@ -36,9 +37,6 @@ class Carpet extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
|
||||
class CarvedPumpkin extends Opaque{
|
||||
class CarvedPumpkin extends Opaque implements HorizontalFacing{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Ageable;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
@ -39,7 +40,7 @@ use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\GlowBerriesPickSound;
|
||||
use function mt_rand;
|
||||
|
||||
class CaveVines extends Flowable{
|
||||
class CaveVines extends Flowable implements Ageable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
|
61
src/block/CeilingCenterHangingSign.php
Normal file
61
src/block/CeilingCenterHangingSign.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?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\SignLikeRotation;
|
||||
use pocketmine\block\utils\SignLikeRotationTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotation{
|
||||
use SignLikeRotationTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
protected function getSupportingFace() : int{
|
||||
return Facing::UP;
|
||||
}
|
||||
|
||||
//TODO: duplicated code :(
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face !== Facing::DOWN){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($player !== null){
|
||||
$this->rotation = self::getRotationFromYaw($player->getLocation()->getYaw());
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::UP);
|
||||
return
|
||||
$supportBlock->getSupportType(Facing::DOWN)->hasCenterSupport() ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::HANGING_SIGN);
|
||||
}
|
||||
}
|
68
src/block/CeilingEdgesHangingSign.php
Normal file
68
src/block/CeilingEdgesHangingSign.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?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\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected function getSupportingFace() : int{
|
||||
return Facing::UP;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face !== Facing::DOWN){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::UP);
|
||||
return
|
||||
$supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL ||
|
||||
(($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()) === Facing::axis($this->facing));
|
||||
}
|
||||
}
|
@ -23,13 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\PillarRotation;
|
||||
use pocketmine\block\utils\PillarRotationTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
final class Chain extends Transparent{
|
||||
final class Chain extends Transparent implements PillarRotation{
|
||||
use PillarRotationTrait;
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
|
@ -24,11 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
final class ChemistryTable extends Opaque{
|
||||
final class ChemistryTable extends Opaque implements HorizontalFacing{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\ChestPairEvent;
|
||||
use pocketmine\item\Item;
|
||||
@ -33,12 +34,9 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class Chest extends Transparent{
|
||||
class Chest extends Transparent implements HorizontalFacing{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
//these are slightly bigger than in PC
|
||||
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
|
||||
use pocketmine\block\utils\ChiseledBookshelfSlot;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Book;
|
||||
@ -38,7 +39,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function spl_object_id;
|
||||
|
||||
class ChiseledBookshelf extends Opaque{
|
||||
class ChiseledBookshelf extends Opaque implements HorizontalFacing{
|
||||
use HorizontalFacingTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
@ -113,6 +114,18 @@ class ChiseledBookshelf extends Opaque{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ChiseledBookshelfSlot[] $slots
|
||||
* @return $this
|
||||
*/
|
||||
public function setSlots(array $slots) : self{
|
||||
$this->slots = [];
|
||||
foreach($slots as $slot){
|
||||
$this->setSlot($slot, true);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last slot interacted by a player or null if no slot has been interacted with yet.
|
||||
*/
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Ageable;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
@ -40,7 +41,7 @@ use function array_rand;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
final class ChorusFlower extends Flowable{
|
||||
final class ChorusFlower extends Flowable implements Ageable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
|
@ -34,11 +34,16 @@ use function mt_rand;
|
||||
final class ChorusPlant extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
/**
|
||||
* @var true[]
|
||||
* @phpstan-var array<int, true>
|
||||
*/
|
||||
protected array $connections = [];
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$bb = AxisAlignedBB::one();
|
||||
foreach($this->getAllSides() as $facing => $block){
|
||||
$id = $block->getTypeId();
|
||||
if($id !== BlockTypeIds::END_STONE && $id !== BlockTypeIds::CHORUS_FLOWER && !$block->hasSameTypeId($this)){
|
||||
foreach(Facing::ALL as $facing){
|
||||
if(!isset($this->connections[$facing])){
|
||||
$bb->trim($facing, 2 / 16);
|
||||
}
|
||||
}
|
||||
@ -46,6 +51,26 @@ final class ChorusPlant extends Flowable{
|
||||
return [$bb];
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
|
||||
$this->collisionBoxes = null;
|
||||
|
||||
foreach(Facing::ALL as $facing){
|
||||
$block = $this->getSide($facing);
|
||||
if(match($block->getTypeId()){
|
||||
BlockTypeIds::END_STONE, BlockTypeIds::CHORUS_FLOWER, $this->getTypeId() => true,
|
||||
default => false
|
||||
}){
|
||||
$this->connections[$facing] = true;
|
||||
}else{
|
||||
unset($this->connections[$facing]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
|
||||
}
|
||||
|
@ -23,8 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Ageable;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -39,7 +41,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
class CocoaBlock extends Flowable{
|
||||
class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{
|
||||
use HorizontalFacingTrait;
|
||||
use AgeableTrait;
|
||||
|
||||
@ -50,9 +52,6 @@ class CocoaBlock extends Flowable{
|
||||
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
|
||||
class Concrete extends Opaque{
|
||||
class Concrete extends Opaque implements Colored{
|
||||
use ColoredTrait;
|
||||
}
|
||||
|
@ -24,12 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
class ConcretePowder extends Opaque implements Fallable{
|
||||
class ConcretePowder extends Opaque implements Fallable, Colored{
|
||||
use ColoredTrait;
|
||||
use FallableTrait {
|
||||
onNearbyBlockChange as protected startFalling;
|
||||
|
@ -26,11 +26,13 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\block\utils\Lightable;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstone;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
class CopperBulb extends Opaque implements CopperMaterial{
|
||||
class CopperBulb extends Opaque implements CopperMaterial, Lightable, PoweredByRedstone{
|
||||
use CopperTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
use LightableTrait{
|
||||
|
@ -24,11 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CoralMaterial;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
final class CoralBlock extends Opaque{
|
||||
final class CoralBlock extends Opaque implements CoralMaterial{
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Ageable;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
@ -34,7 +35,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class Crops extends Flowable{
|
||||
abstract class Crops extends Flowable implements Ageable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitter;
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -36,7 +37,7 @@ use function max;
|
||||
use function round;
|
||||
use const M_PI;
|
||||
|
||||
class DaylightSensor extends Transparent{
|
||||
class DaylightSensor extends Transparent implements AnalogRedstoneSignalEmitter{
|
||||
use AnalogRedstoneSignalEmitterTrait;
|
||||
|
||||
protected bool $inverted = false;
|
||||
@ -62,9 +63,6 @@ class DaylightSensor extends Transparent{
|
||||
return 300;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)];
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -34,7 +35,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\DoorSound;
|
||||
|
||||
class Door extends Transparent{
|
||||
class Door extends Transparent implements HorizontalFacing{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected bool $top = false;
|
||||
@ -95,9 +96,6 @@ class Door extends Transparent{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
//TODO: doors are 0.1825 blocks thick, instead of 0.1875 like JE (https://bugs.mojang.com/browse/MCPE-19214)
|
||||
return [AxisAlignedBB::one()->trim($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)];
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Ageable;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -37,7 +38,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class DoublePitcherCrop extends DoublePlant{
|
||||
final class DoublePitcherCrop extends DoublePlant implements Ageable{
|
||||
use AgeableTrait {
|
||||
describeBlockOnlyState as describeAge;
|
||||
}
|
||||
|
@ -23,9 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Colored;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
|
||||
class DyedCandle extends Candle{
|
||||
class DyedCandle extends Candle implements Colored{
|
||||
use ColoredTrait;
|
||||
|
||||
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user