mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 11:16:57 +00:00
Compare commits
373 Commits
Author | SHA1 | Date | |
---|---|---|---|
a42f68d3cf | |||
b6f3f6120b | |||
96d3f4f78b | |||
6756203aec | |||
adb78679c5 | |||
b60731607d | |||
0058bfcd23 | |||
9f31b479e1 | |||
eb161f8e1c | |||
80f8a27094 | |||
341c480e13 | |||
971ad04299 | |||
025b72e2f2 | |||
cf538afb84 | |||
7ff91337ad | |||
63e5867727 | |||
c38779f1fd | |||
14bae72dd5 | |||
f35b43613d | |||
3d0fa10437 | |||
76e5ea385b | |||
5a351d3caf | |||
bb93d4f8de | |||
66818e178a | |||
f782a478c4 | |||
c06b4830d1 | |||
6d64ef8d74 | |||
c5498bb3fa | |||
59334d652f | |||
5c81da9b1e | |||
b08affc4c6 | |||
cb49f1e835 | |||
f6a0f4aa82 | |||
de09c8c082 | |||
0ac87989dc | |||
13ea984b12 | |||
3878f58847 | |||
d7a35a5302 | |||
d850a84d0d | |||
22d9260a3b | |||
3c90ed13b9 | |||
396efbac7e | |||
00644dd529 | |||
c2c210e25a | |||
86c4e936cb | |||
3b103dcd62 | |||
48dfc5b232 | |||
39360f127a | |||
25890e76e2 | |||
810bdeb965 | |||
b6b3dcc1aa | |||
e0818e7e52 | |||
d0c4463773 | |||
7521160f53 | |||
4f5190e3f8 | |||
3c941dd992 | |||
89f457b397 | |||
2bdbb9794c | |||
898009a91b | |||
793f93afdb | |||
64506ea3ae | |||
4a35516441 | |||
74b9922a28 | |||
088fbf65b1 | |||
fa70127241 | |||
fc76d04dcb | |||
15ae323bcb | |||
353bae87a7 | |||
dc7be3fd52 | |||
f28a0740a0 | |||
df990fb47a | |||
98ca28d96e | |||
db896b9faf | |||
76c234e4e6 | |||
2ef3962028 | |||
1e5941c98c | |||
9ca7c0c883 | |||
b4a8f8391b | |||
142d750b9f | |||
948b0b4cbc | |||
d6b596a8ac | |||
eab2d4d704 | |||
8114551600 | |||
8e1d1993c5 | |||
82bf5f6193 | |||
76a86e51f7 | |||
d1e803685a | |||
0a884aa5fb | |||
c410e676b3 | |||
8f1f5fde47 | |||
0f268df2e0 | |||
86108e7010 | |||
5ea448ef36 | |||
389e7767d1 | |||
4c268acc00 | |||
e34a4f6b98 | |||
3925e598d6 | |||
383ec8a8e3 | |||
95313e0a90 | |||
e9a87978a6 | |||
612e9e162c | |||
fdc3faadc9 | |||
9644e72acf | |||
2c678dcf0d | |||
7bd9a2b2e0 | |||
5c26deb517 | |||
8fcb44de7d | |||
bd2e7db3b9 | |||
f4480c07ee | |||
c09e2301c8 | |||
8e9f787d33 | |||
89833b3b68 | |||
a10a656a5d | |||
2659ed8d91 | |||
9c01ecbe7e | |||
6a55021779 | |||
6ada261b04 | |||
14a6779e08 | |||
cce99b07af | |||
c3d80d711d | |||
1a8b33dafe | |||
baa094a2d1 | |||
f2ff510597 | |||
562179bdd6 | |||
5c12bee874 | |||
99606bbe23 | |||
a1d50de12e | |||
4252c5914b | |||
0659d2fbef | |||
10612acace | |||
1d810f8aeb | |||
414104851a | |||
c0bed03a2a | |||
d25c84acff | |||
55994e08db | |||
6f5d4d6b80 | |||
df1ef7fe0c | |||
20a25a69df | |||
faca610594 | |||
91603dc2d6 | |||
af90e18b18 | |||
ab5b4d112b | |||
a30b1fb6d5 | |||
20b4723728 | |||
d1ced0ffc6 | |||
2164dbae67 | |||
6c92a2e88b | |||
97deadc59f | |||
0c3b136a8d | |||
79b7e08e60 | |||
2540dacdd7 | |||
f1078e3909 | |||
2f43b054de | |||
23b5d64535 | |||
9afa0e5483 | |||
4eaea54b0e | |||
6b51bf4a80 | |||
cba8d86c4f | |||
2e834c8f5c | |||
f9873e9108 | |||
074baf7e1c | |||
2e0dd574e0 | |||
e16d8e31af | |||
3c93a57397 | |||
e2e927b328 | |||
a8dab25201 | |||
3de2b7969e | |||
8f486ea65d | |||
6b971b1761 | |||
6f36fa504b | |||
8e73842a93 | |||
e71e18fc88 | |||
e1bacb5c6d | |||
44697e784a | |||
65529ff2ce | |||
c346c45d42 | |||
c433fad0a7 | |||
8fad5a6e30 | |||
7a6f279825 | |||
10b72c895d | |||
d520928888 | |||
27767e7ddb | |||
243c12de7c | |||
372545e47e | |||
8913b48700 | |||
6ee4a0e090 | |||
8bd8da4bc6 | |||
9ba4144a71 | |||
9da7c6af27 | |||
109312284c | |||
51934614bc | |||
9e89f65094 | |||
9562711b84 | |||
30b49e0d22 | |||
a975868fc3 | |||
b38b932845 | |||
43cb19ebca | |||
769cc91543 | |||
278f37d3e0 | |||
02a6ca84a9 | |||
b8703d5dff | |||
37c2d78731 | |||
b7663e5815 | |||
1d0ffa06f8 | |||
768cfe3953 | |||
2822465f33 | |||
5da48f429f | |||
dbd0d04549 | |||
0f92ec6d2a | |||
791b4d8ef3 | |||
0b7ff6f2e7 | |||
af092b01e1 | |||
d811217755 | |||
d7f86f0240 | |||
5fe1d2e396 | |||
ddbb5363ef | |||
d3704bfae4 | |||
07f034d2da | |||
331ae5498f | |||
eebd90ec42 | |||
8e47a40b4c | |||
0f9fdf6442 | |||
5364e4de68 | |||
1099e2044b | |||
80d48161d3 | |||
fbe8485696 | |||
a150f39b02 | |||
71d17c50d6 | |||
3a18bdd6a0 | |||
96857c65b6 | |||
9826abd83e | |||
48c8c2a8c3 | |||
c83b7d9b69 | |||
c66dc7b273 | |||
5bf7350ee5 | |||
636cc1c199 | |||
62b6405371 | |||
9ecf23d3ee | |||
187f36fe38 | |||
dc89b48354 | |||
e6d1c1dfbc | |||
5a8812b1dc | |||
4b8e4123af | |||
45a4252c26 | |||
feaaa925a7 | |||
5221db1178 | |||
47321114eb | |||
a27c9409f1 | |||
854f851525 | |||
9003b38be3 | |||
a6a93f822f | |||
d4851a8f1f | |||
480a513f30 | |||
4fd3bee360 | |||
41fd7545e3 | |||
82dddde159 | |||
bc709efb77 | |||
cd98e6a23e | |||
cb591a98f4 | |||
e9d1af0aee | |||
0f545c410a | |||
1c2ed0836f | |||
6cf30dc813 | |||
f7d9247d39 | |||
3380aa3ac2 | |||
6a9cad8fb7 | |||
c9e598cdb9 | |||
a6e5b6e158 | |||
22a6b817d7 | |||
2cdf97b7b5 | |||
836cb67850 | |||
ab37df4484 | |||
93969197f7 | |||
946a1036f1 | |||
43410cdafb | |||
a99e15012c | |||
b22a2ef914 | |||
f7f7be896e | |||
254281cd5e | |||
5dfceeea98 | |||
4b9a142a5d | |||
0bacf51729 | |||
33f6b441d8 | |||
11b59498d9 | |||
d71a543d10 | |||
5e0c3333cf | |||
0f941410f6 | |||
504cc3bf8b | |||
6bd1491b8b | |||
658786f2f6 | |||
d34f3f1af3 | |||
8650c187f9 | |||
4b4820cf53 | |||
d33acc4fd0 | |||
f7de6eb59f | |||
75a0627bf2 | |||
8752e363c9 | |||
9ed1b5ca7f | |||
1cbb31f1db | |||
1393b4c4e2 | |||
2921c86b3c | |||
9c3a929b65 | |||
9abaa42cd7 | |||
77b9feb3c0 | |||
e0e2e1775f | |||
d2d65ce6cc | |||
ff2e982f22 | |||
daf56e990b | |||
3f5e83a322 | |||
5ecc5ed7e0 | |||
cd80ae00d4 | |||
beb5d72299 | |||
0eef634aab | |||
0ea166a551 | |||
6417cff618 | |||
a71af952ba | |||
93dd05a03e | |||
98f903783c | |||
5d47ea4337 | |||
c242d6213a | |||
4ad1093fd7 | |||
fc0782df02 | |||
bfaa224f6b | |||
de88f0fce1 | |||
9b078854c4 | |||
42f8e061a5 | |||
1455c38dbe | |||
75df6973df | |||
4763360e9e | |||
0299191e64 | |||
2664a1b4d8 | |||
4249c00c3e | |||
517c4e5143 | |||
69c343bb9b | |||
70df1579a8 | |||
ea9f9aa250 | |||
34a899e28b | |||
b80868040e | |||
a7f1181335 | |||
bf8a8b386e | |||
4b518f2a58 | |||
60b1f0a6e9 | |||
5934399a0d | |||
b42132a7c3 | |||
c05697f506 | |||
d4fe1b8ece | |||
9b2653fb6f | |||
ed88684e71 | |||
cbb9c4f298 | |||
660d42e8d1 | |||
dbeceb02f9 | |||
fd77dd0066 | |||
87ce87112b | |||
1d71f5edb3 | |||
0f620157e8 | |||
2323601f98 | |||
d34b94302f | |||
ec4c61e113 | |||
231e491bb9 | |||
69cdc6f13a | |||
dfeb62491a | |||
178eedb536 | |||
4975da2aae | |||
5946ec8819 | |||
abf0dee426 | |||
30f5a8fac6 | |||
f704061618 | |||
23dc6e09d8 | |||
15b7fc978e | |||
bb396174ba | |||
dcef3cba21 | |||
5f8a9f8747 | |||
84e41e6967 |
@ -4,3 +4,7 @@ root = yes
|
||||
[*]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
8
.github/FUNDING.yml
vendored
Normal file
8
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: pocketminemp
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
custom: https://github.com/pmmp/PocketMine-MP#donate
|
48
.github/ISSUE_TEMPLATE.md
vendored
48
.github/ISSUE_TEMPLATE.md
vendored
@ -1,48 +0,0 @@
|
||||
### Issue description
|
||||
<!---
|
||||
THIS ISSUE TRACKER IS FOR BUG REPORTING, NOT FOR HELP & SUPPORT. If you need help, use the links below.
|
||||
- http://pmmp.readthedocs.io/en/rtfd/ - Documentation
|
||||
- https://forums.pmmp.io - PMMP Forums
|
||||
|
||||
Any issues requesting updates to new versions of MCPE will be treated as spam.
|
||||
Please do not create issues for missing/un-implemented gameplay features - they will be closed.
|
||||
-->
|
||||
|
||||
<!--- Write a short description about the issue -->
|
||||
|
||||
<!--- If you are reporting a regression or unexpected behaviour, please include the below information: -->
|
||||
- Expected result: What were you expecting to happen?
|
||||
- Actual result: What actually happened?
|
||||
|
||||
### Steps to reproduce the issue
|
||||
<!--- help us find the problem by adding steps to reproduce the issue -->
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
### OS and versions
|
||||
<!--- use the 'version' command in PocketMine-MP
|
||||
|
||||
NOTE: LATEST is not a valid version. PocketMine-MP version should include Jenkins build number and/or git commit hash.
|
||||
|
||||
NO support whatsoever will be provided for third-party modified variants of PocketMine-MP. Issues relating to third-party modifications will be closed as spam.
|
||||
|
||||
Note that 32-bit platforms are no longer supported by PocketMine-MP and issues concerning 32-bit platforms will be closed.
|
||||
-->
|
||||
* PocketMine-MP: <!-- LATEST IS NOT A VALID VERSION -->
|
||||
* PHP:
|
||||
* Server OS:
|
||||
* Game version: PE/Win10 (delete as appropriate)
|
||||
|
||||
### Plugins
|
||||
- Test on a clean server without plugins: is the issue reproducible without any plugins loaded?
|
||||
|
||||
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
|
||||
- Paste your list of plugins here (use the 'plugins' command in PocketMine-MP)
|
||||
|
||||
### Crashdump, backtrace or other files
|
||||
- Do not paste crashdumps into an issue - please use our Crash Archive at https://crash.pmmp.io for submitting crash reports to not spam the issue tracker. Add links to your reports in the Crash Archive here.
|
||||
- Please use gist or anything else to add other files and add links here
|
||||
|
||||
* ...
|
19
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
name: API change request
|
||||
about: Suggest a change, addition or removal to the plugin API
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- tell us what you want -->
|
||||
## Description
|
||||
|
||||
|
||||
<!--- explain why you want this and why it's a good idea -->
|
||||
## Justification
|
||||
|
||||
|
||||
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
|
||||
## Alternative methods
|
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
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:
|
||||
* Server OS:
|
||||
* Game version: PE/Win10 (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 -->
|
15
.github/ISSUE_TEMPLATE/crash.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/crash.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
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 -->
|
||||
Link to crashdump:
|
||||
|
||||
<!--- write additional information about the crash to help us find the problem -->
|
||||
### Additional comments (optional)
|
14
.github/ISSUE_TEMPLATE/help---support.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/help---support.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Help & support
|
||||
about: We don't accept support requests here. Try the links on the README.
|
||||
title: ''
|
||||
labels: Support request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
We don't accept support requests on the issue tracker. Please try the following links instead:
|
||||
|
||||
Documentation: http://pmmp.rtfd.io
|
||||
Forums: https://forums.pmmp.io
|
||||
Discord: https://discord.gg/bge7dYQ
|
12
.github/ISSUE_TEMPLATE/security-dos-vulnerability.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/security-dos-vulnerability.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
name: Security/DoS vulnerability
|
||||
about: 'Bug or exploit that can be used to attack servers (hint: don''t report it
|
||||
on a public issue tracker)'
|
||||
title: ''
|
||||
labels: 'Auto: Spam'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please DO NOT report security vulnerabilities here.
|
||||
Instead, send an email to team@pmmp.io or contact a developer directly, IN PRIVATE.
|
6
.github/support.yml
vendored
6
.github/support.yml
vendored
@ -5,7 +5,11 @@ supportLabel: "Support request"
|
||||
# Comment to post on issues marked as support requests. Add a link
|
||||
# to a support page, or set to `false` to disable
|
||||
supportComment: >
|
||||
This issue tracker is not a support forum. Please use the [forums](https://forums.pmmp.io) for support.
|
||||
Thanks, but this issue tracker not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
|
||||
|
||||
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bge7dYQ) | [Forums](https://forums.pmmp.io)
|
||||
|
||||
# Whether to close issues marked as support requests
|
||||
close: true
|
||||
# Whether to lock issues marked as support requests
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@ server.properties
|
||||
/pocketmine.yml
|
||||
memory_dumps/*
|
||||
resource_packs/
|
||||
server.lock
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
|
@ -2,13 +2,14 @@ language: php
|
||||
|
||||
php:
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
before_script:
|
||||
# - pecl install channel://pecl.php.net/pthreads-3.1.6
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.0.2
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.0.4
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout c8cfacda84f21032d6014b53e72bf345ac901dac
|
||||
- git checkout 6ca019c58b4fa09ee2ff490f2444e34bef0773d0
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
|
13
README.md
13
README.md
@ -8,15 +8,20 @@ __A highly customisable, open source server software for Minecraft: Bedrock Edit
|
||||
Head over to the [documentation site](http://pmmp.readthedocs.org/).
|
||||
If you don't find what you're looking for there, [talk to a human](#discussion). Please do not use our issue tracker for support requests.
|
||||
|
||||
### Docker
|
||||
We provide an official docker image on Docker Hub: [`pmmp/pocketmine-mp`](https://hub.docker.com/r/pmmp/pocketmine-mp).
|
||||
|
||||
### Discussion
|
||||
- [Forums](https://forums.pmmp.io/)
|
||||
- [Community Discord](https://discord.gg/bge7dYQ)
|
||||
|
||||
### Plugins
|
||||
There are a very wide range of already-written plugins available which you can use to customise your server. Check out [Poggit](https://poggit.pmmp.io), or just search GitHub.
|
||||
|
||||
### For developers
|
||||
* [Latest API documentation](https://jenkins.pmmp.io/job/PocketMine-MP-doc/doxygen/) - Doxygen documentation generated from development
|
||||
* [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - A development tools plugin for creating plugins.
|
||||
* [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - Development tools plugin for creating plugins
|
||||
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
|
||||
### Can I contribute?
|
||||
Yes you can! Contributions are welcomed provided that they comply with our [Contributing Guidelines](CONTRIBUTING.md). Please ensure you read the relevant sections of the guidelines carefully before making a Pull Request or opening an Issue.
|
||||
@ -27,6 +32,12 @@ Yes you can! Contributions are welcomed provided that they comply with our [Cont
|
||||
|
||||
**Note: Please avoid development builds unless there is no other alternative for what you need.** Development builds are subject to changes at any time without notice, and it is likely that your server or plugins might break without warning.
|
||||
|
||||
### Donate
|
||||
Donations help support the development of the project and pay for our expenses.
|
||||
- Bitcoin Cash (BCH): `qz9p8dqkv0r7aahdatu5uewqfkvstrglv58f8yle07`
|
||||
- Bitcoin (BTC): `1PVAyDJ2g7kcjCxAC3C89oxpV2ZYcLad8T`
|
||||
- [Patreon](https://www.patreon.com/pocketminemp)
|
||||
|
||||
## Licensing information
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -27,10 +27,11 @@
|
||||
"pocketmine/raklib": "^0.12.0",
|
||||
"pocketmine/spl": "^0.3.0",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/nbt": "^0.2.1",
|
||||
"pocketmine/nbt": "^0.2.6",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"daverandom/callback-validator": "dev-master"
|
||||
"daverandom/callback-validator": "dev-master",
|
||||
"adhocore/json-comment": "^0.0.7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
108
composer.lock
generated
108
composer.lock
generated
@ -4,8 +4,52 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2d120a3dd7d68958809c3662d1cb7c99",
|
||||
"content-hash": "2f5313e4ebd7b62c785cf683b27464b4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
"version": "v0.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adhocore/php-json-comment.git",
|
||||
"reference": "135356c7e7336ef59924f1d921c770045f937a76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/135356c7e7336ef59924f1d921c770045f937a76",
|
||||
"reference": "135356c7e7336ef59924f1d921c770045f937a76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ahc\\Json\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jitendra Adhikari",
|
||||
"email": "jiten.adhikary@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Lightweight JSON comment stripper library for PHP",
|
||||
"keywords": [
|
||||
"comment",
|
||||
"json",
|
||||
"strip-comment"
|
||||
],
|
||||
"time": "2018-08-01T12:27:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "daverandom/callback-validator",
|
||||
"version": "dev-master",
|
||||
@ -48,16 +92,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be"
|
||||
"reference": "33f511715d22418c03368b49b45a6c25d6b33806"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/54efeb978be0ff9335022729fe63c1e2077bf1be",
|
||||
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/33f511715d22418c03368b49b45a6c25d6b33806",
|
||||
"reference": "33f511715d22418c03368b49b45a6c25d6b33806",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -75,23 +119,23 @@
|
||||
],
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/master",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.8",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2018-08-26T18:11:05+00:00"
|
||||
"time": "2019-01-16T17:31:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6"
|
||||
"reference": "b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/ee299f5c9c444ca526c9c691b920f321458cf0b6",
|
||||
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c",
|
||||
"reference": "b755d3fb7025c4ddb7d9d6df0ba7e0b65125e51c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -109,23 +153,23 @@
|
||||
],
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.1",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.2",
|
||||
"issues": "https://github.com/pmmp/Math/issues"
|
||||
},
|
||||
"time": "2018-08-15T15:43:27+00:00"
|
||||
"time": "2019-01-04T15:42:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "291bf5cc2a94500eada1edbda51d15bed25a1e1c"
|
||||
"reference": "2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/291bf5cc2a94500eada1edbda51d15bed25a1e1c",
|
||||
"reference": "291bf5cc2a94500eada1edbda51d15bed25a1e1c",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a",
|
||||
"reference": "2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -150,23 +194,23 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.3",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.7",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2018-12-03T16:08:28+00:00"
|
||||
"time": "2019-03-29T19:39:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "922da28efd828e2af6c19db1676ea9b6a267071c"
|
||||
"reference": "fc1ccc8e61b9033e5372436b2e28a7a95388373f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/922da28efd828e2af6c19db1676ea9b6a267071c",
|
||||
"reference": "922da28efd828e2af6c19db1676ea9b6a267071c",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/fc1ccc8e61b9033e5372436b2e28a7a95388373f",
|
||||
"reference": "fc1ccc8e61b9033e5372436b2e28a7a95388373f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -191,23 +235,23 @@
|
||||
],
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.0",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.4",
|
||||
"issues": "https://github.com/pmmp/RakLib/issues"
|
||||
},
|
||||
"time": "2018-06-13T10:06:14+00:00"
|
||||
"time": "2019-05-02T14:53:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Snooze.git",
|
||||
"reference": "3cc9d0164230889acb08e22cc126133809e9d346"
|
||||
"reference": "b7bd231bdb75e69300cac89ccd515fc731c38c40"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/3cc9d0164230889acb08e22cc126133809e9d346",
|
||||
"reference": "3cc9d0164230889acb08e22cc126133809e9d346",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/b7bd231bdb75e69300cac89ccd515fc731c38c40",
|
||||
"reference": "b7bd231bdb75e69300cac89ccd515fc731c38c40",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -225,10 +269,10 @@
|
||||
],
|
||||
"description": "Thread notification management library for code using the pthreads extension",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.1.0",
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.1.1",
|
||||
"issues": "https://github.com/pmmp/Snooze/issues"
|
||||
},
|
||||
"time": "2018-06-13T09:36:11+00:00"
|
||||
"time": "2019-01-04T15:54:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
@ -258,7 +302,7 @@
|
||||
],
|
||||
"description": "Standard library files required by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/SPL/tree/master"
|
||||
"source": "https://github.com/pmmp/SPL/tree/0.3.2"
|
||||
},
|
||||
"time": "2018-08-12T15:17:39+00:00"
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
10
doxygen/index.md
Normal file
10
doxygen/index.md
Normal file
@ -0,0 +1,10 @@
|
||||
## PocketMine-MP API Documentation
|
||||
|
||||
This site contains auto-generated API documentation for PocketMine-MP (and dependencies which are not outsourced). Documentation here is regenerated for every release.
|
||||
|
||||
This site can be accessed via https://apidoc.pmmp.io.
|
||||
|
||||
### Additional developer resources
|
||||
- [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - Development tools plugin for creating plugins
|
||||
- [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
- [DeveloperDocs](https://github.com/pmmp/DeveloperDocs/) - Reference, guides and specifications for the PocketMine-MP API
|
@ -30,9 +30,70 @@ use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use raklib\RakLib;
|
||||
use function base64_encode;
|
||||
use function date;
|
||||
use function error_get_last;
|
||||
use function fclose;
|
||||
use function file;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function get_loaded_extensions;
|
||||
use function implode;
|
||||
use function is_dir;
|
||||
use function is_resource;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function max;
|
||||
use function mkdir;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
use function ob_start;
|
||||
use function php_uname;
|
||||
use function phpinfo;
|
||||
use function phpversion;
|
||||
use function preg_replace;
|
||||
use function str_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function time;
|
||||
use function zend_version;
|
||||
use function zlib_encode;
|
||||
use const E_COMPILE_ERROR;
|
||||
use const E_COMPILE_WARNING;
|
||||
use const E_CORE_ERROR;
|
||||
use const E_CORE_WARNING;
|
||||
use const E_DEPRECATED;
|
||||
use const E_ERROR;
|
||||
use const E_NOTICE;
|
||||
use const E_PARSE;
|
||||
use const E_RECOVERABLE_ERROR;
|
||||
use const E_STRICT;
|
||||
use const E_USER_DEPRECATED;
|
||||
use const E_USER_ERROR;
|
||||
use const E_USER_NOTICE;
|
||||
use const E_USER_WARNING;
|
||||
use const E_WARNING;
|
||||
use const FILE_IGNORE_NEW_LINES;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const PHP_EOL;
|
||||
use const PHP_OS;
|
||||
|
||||
class CrashDump{
|
||||
|
||||
/**
|
||||
* Crashdump data format version, used by the crash archive to decide how to decode the crashdump
|
||||
* This should be incremented when backwards incompatible changes are introduced, such as fields being removed or
|
||||
* having their content changed, version format changing, etc.
|
||||
* It is not necessary to increase this when adding new fields.
|
||||
*/
|
||||
private const FORMAT_VERSION = 2;
|
||||
|
||||
private const PLUGIN_INVOLVEMENT_NONE = "none";
|
||||
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
|
||||
private const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
private $fp;
|
||||
@ -54,6 +115,7 @@ class CrashDump{
|
||||
if(!is_resource($this->fp)){
|
||||
throw new \RuntimeException("Could not create Crash Dump");
|
||||
}
|
||||
$this->data["format_version"] = self::FORMAT_VERSION;
|
||||
$this->data["time"] = $this->time;
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time));
|
||||
$this->addLine();
|
||||
@ -85,7 +147,11 @@ class CrashDump{
|
||||
$this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
|
||||
$this->addLine();
|
||||
$this->addLine("===BEGIN CRASH DUMP===");
|
||||
$this->encodedData = zlib_encode(json_encode($this->data, JSON_UNESCAPED_SLASHES), ZLIB_ENCODING_DEFLATE, 9);
|
||||
$json = json_encode($this->data, JSON_UNESCAPED_SLASHES);
|
||||
if($json === false){
|
||||
throw new \RuntimeException("Failed to encode crashdump JSON: " . json_last_error_msg());
|
||||
}
|
||||
$this->encodedData = zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9);
|
||||
foreach(str_split(base64_encode($this->encodedData), 76) as $line){
|
||||
$this->addLine($line);
|
||||
}
|
||||
@ -150,7 +216,7 @@ class CrashDump{
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = (array) error_get_last();
|
||||
$error["trace"] = Utils::getTrace(4); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$errorConversion = [
|
||||
E_ERROR => "E_ERROR",
|
||||
E_WARNING => "E_WARNING",
|
||||
@ -177,6 +243,9 @@ class CrashDump{
|
||||
}
|
||||
|
||||
if(isset($lastError)){
|
||||
if(isset($lastError["trace"])){
|
||||
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
|
||||
}
|
||||
$this->data["lastError"] = $lastError;
|
||||
}
|
||||
|
||||
@ -188,24 +257,16 @@ class CrashDump{
|
||||
$this->addLine("Line: " . $error["line"]);
|
||||
$this->addLine("Type: " . $error["type"]);
|
||||
|
||||
if(strpos($error["file"], "src/pocketmine/") === false and strpos($error["file"], "vendor/pocketmine/") === false and file_exists($error["fullFile"])){
|
||||
$this->addLine();
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
$this->data["plugin"] = true;
|
||||
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||
if(strpos($error["file"], $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_NONE;
|
||||
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
|
||||
foreach($error["trace"] as $frame){
|
||||
if(!isset($frame["file"])){
|
||||
continue; //PHP core
|
||||
}
|
||||
if($this->determinePluginFromFile($frame["file"], false)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->data["plugin"] = false;
|
||||
}
|
||||
|
||||
$this->addLine();
|
||||
@ -222,20 +283,48 @@ class CrashDump{
|
||||
|
||||
$this->addLine();
|
||||
$this->addLine("Backtrace:");
|
||||
foreach(($this->data["trace"] = $error["trace"]) as $line){
|
||||
foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){
|
||||
$this->addLine($line);
|
||||
}
|
||||
$this->addLine();
|
||||
}
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
$frameCleanPath = Utils::cleanPath($filePath); //this will be empty in phar stub
|
||||
if($frameCleanPath !== "" and strpos($frameCleanPath, "src/pocketmine/") === false and strpos($frameCleanPath, "vendor/pocketmine/") === false and file_exists($filePath)){
|
||||
$this->addLine();
|
||||
if($crashFrame){
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT;
|
||||
}else{
|
||||
$this->addLine("A PLUGIN WAS INVOLVED IN THIS CRASH");
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function generalData(){
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
$this->data["general"] = [];
|
||||
$this->data["general"]["name"] = $this->server->getName();
|
||||
$this->data["general"]["version"] = $version->getFullVersion(false);
|
||||
$this->data["general"]["build"] = $version->getBuild();
|
||||
$this->data["general"]["base_version"] = \pocketmine\BASE_VERSION;
|
||||
$this->data["general"]["build"] = \pocketmine\BUILD_NUMBER;
|
||||
$this->data["general"]["is_dev"] = \pocketmine\IS_DEVELOPMENT_BUILD;
|
||||
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
|
||||
$this->data["general"]["api"] = \pocketmine\BASE_VERSION;
|
||||
$this->data["general"]["git"] = \pocketmine\GIT_COMMIT;
|
||||
$this->data["general"]["raklib"] = RakLib::VERSION;
|
||||
$this->data["general"]["uname"] = php_uname("a");
|
||||
|
@ -28,6 +28,39 @@ use pocketmine\scheduler\DumpWorkerMemoryTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\Utils;
|
||||
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 get_class;
|
||||
use function get_declared_classes;
|
||||
use function implode;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_resource;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
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 strtoupper;
|
||||
use function substr;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
class MemoryManager{
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine;
|
||||
use pocketmine\block\Bed;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\block\UnknownBlock;
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\entity\Effect;
|
||||
@ -101,7 +102,9 @@ use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableEntityIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\BookEditPacket;
|
||||
@ -120,6 +123,7 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
@ -158,6 +162,41 @@ use pocketmine\tile\Tile;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\UUID;
|
||||
use function abs;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function base64_decode;
|
||||
use function ceil;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function floor;
|
||||
use function fmod;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function lcg_value;
|
||||
use function max;
|
||||
use function microtime;
|
||||
use function min;
|
||||
use function preg_match;
|
||||
use function round;
|
||||
use function spl_object_hash;
|
||||
use function sqrt;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
use function trim;
|
||||
use function ucfirst;
|
||||
use const M_PI;
|
||||
use const M_SQRT3;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
|
||||
/**
|
||||
@ -276,7 +315,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var int */
|
||||
protected $spawnThreshold;
|
||||
/** @var int */
|
||||
protected $chunkLoadCount = 0;
|
||||
protected $spawnChunkLoadCount = 0;
|
||||
/** @var int */
|
||||
protected $chunksPerTick;
|
||||
|
||||
@ -325,6 +364,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var Form[] */
|
||||
protected $forms = [];
|
||||
|
||||
/** @var float */
|
||||
protected $lastRightClickTime = 0.0;
|
||||
/** @var Vector3|null */
|
||||
protected $lastRightClickPos = null;
|
||||
|
||||
/**
|
||||
* @return TranslationContainer|string
|
||||
*/
|
||||
@ -349,7 +393,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function isBanned() : bool{
|
||||
return $this->server->getNameBans()->isBanned($this->iusername);
|
||||
return $this->server->getNameBans()->isBanned($this->username);
|
||||
}
|
||||
|
||||
public function setBanned(bool $value){
|
||||
@ -362,14 +406,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function isWhitelisted() : bool{
|
||||
return $this->server->isWhitelisted($this->iusername);
|
||||
return $this->server->isWhitelisted($this->username);
|
||||
}
|
||||
|
||||
public function setWhitelisted(bool $value){
|
||||
if($value){
|
||||
$this->server->addWhitelist($this->iusername);
|
||||
$this->server->addWhitelist($this->username);
|
||||
}else{
|
||||
$this->server->removeWhitelist($this->iusername);
|
||||
$this->server->removeWhitelist($this->username);
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,8 +478,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function setFlying(bool $value){
|
||||
$this->flying = $value;
|
||||
$this->sendSettings();
|
||||
if($this->flying !== $value){
|
||||
$this->flying = $value;
|
||||
$this->resetFallDistance();
|
||||
$this->sendSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public function isFlying() : bool{
|
||||
@ -647,14 +694,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$this->perm->recalculatePermissions();
|
||||
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
}
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
}
|
||||
|
||||
if($this->spawned){
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
}
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
}
|
||||
|
||||
$this->sendCommandData();
|
||||
}
|
||||
}
|
||||
@ -669,12 +716,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
public function sendCommandData(){
|
||||
$pk = new AvailableCommandsPacket();
|
||||
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
|
||||
if(isset($pk->commandData[$command->getName()]) or $command->getName() === "help"){
|
||||
if(isset($pk->commandData[$command->getName()]) or $command->getName() === "help" or !$command->testPermissionSilent($this)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = new CommandData();
|
||||
$data->commandName = $command->getName();
|
||||
//TODO: commands containing uppercase letters in the name crash 1.9.0 client
|
||||
$data->commandName = strtolower($command->getName());
|
||||
$data->commandDescription = $this->server->getLanguage()->translateString($command->getDescription());
|
||||
$data->flags = 0;
|
||||
$data->permission = 0;
|
||||
@ -962,8 +1010,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$this->usedChunks[Level::chunkHash($x, $z)] = true;
|
||||
$this->chunkLoadCount++;
|
||||
|
||||
$this->dataPacket($payload);
|
||||
|
||||
if($this->spawned){
|
||||
@ -974,8 +1020,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
if($this->chunkLoadCount >= $this->spawnThreshold and !$this->spawned){
|
||||
$this->doFirstSpawn();
|
||||
if($this->spawnChunkLoadCount !== -1 and ++$this->spawnChunkLoadCount >= $this->spawnThreshold){
|
||||
$this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN);
|
||||
$this->spawnChunkLoadCount = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1013,10 +1060,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
Timings::$playerChunkSendTimer->stopTiming();
|
||||
}
|
||||
|
||||
protected function doFirstSpawn(){
|
||||
public function doFirstSpawn(){
|
||||
if($this->spawned){
|
||||
return; //avoid player spawning twice (this can only happen on 3.x with a custom malicious client)
|
||||
}
|
||||
$this->spawned = true;
|
||||
|
||||
$this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN);
|
||||
$this->setImmobile(false);
|
||||
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
@ -1037,7 +1086,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$this->noDamageTicks = 60;
|
||||
|
||||
foreach($this->usedChunks as $index => $c){
|
||||
foreach($this->usedChunks as $index => $hasSent){
|
||||
if(!$hasSent){
|
||||
continue; //this will happen when the chunk is ready to send
|
||||
}
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){
|
||||
if($entity !== $this and !$entity->isClosed() and $entity->isAlive() and !$entity->isFlaggedForDespawn()){
|
||||
@ -1071,8 +1123,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
Timings::$playerChunkOrderTimer->startTiming();
|
||||
|
||||
$this->nextChunkOrderRun = 200;
|
||||
|
||||
$radius = $this->server->getAllowedViewDistance($this->viewDistance);
|
||||
$radiusSquared = $radius ** 2;
|
||||
|
||||
@ -1149,6 +1199,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$this->loadQueue = $newOrder;
|
||||
if(!empty($this->loadQueue) or !empty($unloadChunks)){
|
||||
$pk = new NetworkChunkPublisherUpdatePacket();
|
||||
$pk->x = $this->getFloorX();
|
||||
$pk->y = $this->getFloorY();
|
||||
$pk->z = $this->getFloorZ();
|
||||
$pk->radius = $this->viewDistance * 16; //blocks, not chunks >.>
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
Timings::$playerChunkOrderTimer->stopTiming();
|
||||
}
|
||||
@ -1361,19 +1419,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$this->allowFlight = $this->isCreative();
|
||||
if($this->isSpectator()){
|
||||
$this->flying = true;
|
||||
$this->setFlying(true);
|
||||
$this->keepMovement = true;
|
||||
$this->despawnFromAll();
|
||||
}else{
|
||||
$this->keepMovement = $this->allowMovementCheats;
|
||||
if($this->isSurvival()){
|
||||
$this->flying = false;
|
||||
$this->setFlying(false);
|
||||
}
|
||||
$this->spawnToAll();
|
||||
}
|
||||
|
||||
$this->resetFallDistance();
|
||||
|
||||
$this->namedtag->setInt("playerGameType", $this->gamemode);
|
||||
if(!$client){ //Gamemode changed by server, do not send for client changes
|
||||
$this->sendGamemode();
|
||||
@ -1599,7 +1655,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}else{
|
||||
$this->broadcastMovement();
|
||||
|
||||
$distance = $from->distance($to);
|
||||
$distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
|
||||
//TODO: check swimming (adds 0.015 exhaustion in MCPE)
|
||||
if($this->isSprinting()){
|
||||
$this->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING);
|
||||
@ -1630,6 +1686,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->newPosition = null;
|
||||
}
|
||||
|
||||
public function fall(float $fallDistance) : void{
|
||||
if(!$this->flying){
|
||||
parent::fall($fallDistance);
|
||||
}
|
||||
}
|
||||
|
||||
public function jump() : void{
|
||||
(new PlayerJumpEvent($this))->call();
|
||||
parent::jump();
|
||||
@ -1762,7 +1824,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->nextChunkOrderRun-- <= 0){
|
||||
if($this->nextChunkOrderRun !== PHP_INT_MAX and $this->nextChunkOrderRun-- <= 0){
|
||||
$this->nextChunkOrderRun = PHP_INT_MAX;
|
||||
$this->orderChunks();
|
||||
}
|
||||
|
||||
@ -1871,7 +1934,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!$this->server->isWhitelisted($this->iusername) and $this->kick("Server is white-listed", false)){
|
||||
if(!$this->server->isWhitelisted($this->username) and $this->kick("Server is white-listed", false)){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2001,7 +2064,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case ResourcePackClientResponsePacket::STATUS_SEND_PACKS:
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
foreach($packet->packIds as $uuid){
|
||||
$pack = $manager->getPackById(substr($uuid, 0, strpos($uuid, "_"))); //dirty hack for mojang's dirty hack for versions
|
||||
//dirty hack for mojang's dirty hack for versions
|
||||
$splitPos = strpos($uuid, "_");
|
||||
if($splitPos !== false){
|
||||
$uuid = substr($uuid, 0, $splitPos);
|
||||
}
|
||||
|
||||
$pack = $manager->getPackById($uuid);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
//Client requested a resource pack but we don't have it available on the server
|
||||
$this->close("", "disconnectionScreen.resourcePack", true);
|
||||
@ -2041,6 +2110,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var float[] $pos */
|
||||
$pos = $this->namedtag->getListTag("Pos")->getAllValues();
|
||||
$this->level->registerChunkLoader($this, ((int) floor($pos[0])) >> 4, ((int) floor($pos[2])) >> 4, true);
|
||||
$this->usedChunks[Level::chunkHash(((int) floor($pos[0])) >> 4, ((int) floor($pos[2])) >> 4)] = false;
|
||||
|
||||
parent::__construct($this->level, $this->namedtag);
|
||||
$ev = new PlayerLoginEvent($this, "Plugin reason");
|
||||
@ -2087,12 +2157,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableEntityIdentifiersPacket());
|
||||
$this->sendDataPacket(new BiomeDefinitionListPacket());
|
||||
|
||||
$this->level->sendTime($this);
|
||||
|
||||
$this->sendAttributes(true);
|
||||
$this->setNameTagVisible();
|
||||
$this->setNameTagAlwaysVisible();
|
||||
$this->setCanClimb();
|
||||
$this->setImmobile(); //disable pre-spawn movement
|
||||
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logIn", [
|
||||
TextFormat::AQUA . $this->username . TextFormat::WHITE,
|
||||
@ -2252,7 +2326,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if($action !== null){
|
||||
$actions[] = $action;
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
}catch(\UnexpectedValueException $e){
|
||||
$this->server->getLogger()->debug("Unhandled inventory action from " . $this->getName() . ": " . $e->getMessage());
|
||||
$this->sendAllInventories();
|
||||
return false;
|
||||
@ -2322,6 +2396,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$type = $packet->trData->actionType;
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$spamBug = ($this->lastRightClickPos !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickPos = clone $packet->trData->clickPos;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
|
||||
$this->setUsingItem(false);
|
||||
|
||||
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()){
|
||||
@ -2619,6 +2706,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){
|
||||
//TODO HACK: silence useless spam (MCPE 1.8)
|
||||
//this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang
|
||||
//hacks, it also sends it when changing the held item now, which causes us to think the inventory was closed
|
||||
//when it wasn't.
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->doCloseInventory();
|
||||
|
||||
@ -2642,6 +2736,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
public function handleBlockPickRequest(BlockPickRequestPacket $packet) : bool{
|
||||
$block = $this->level->getBlockAt($packet->blockX, $packet->blockY, $packet->blockZ);
|
||||
if($block instanceof UnknownBlock){
|
||||
return true;
|
||||
}
|
||||
|
||||
$item = $block->getPickedItem();
|
||||
if($packet->addUserData){
|
||||
@ -2866,8 +2963,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->sendSettings();
|
||||
}else{
|
||||
}else{ //don't use setFlying() here, to avoid feedback loops
|
||||
$this->flying = $ev->isFlying();
|
||||
$this->resetFallDistance();
|
||||
}
|
||||
|
||||
$handled = true;
|
||||
@ -2897,7 +2995,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$t = $this->level->getTile($pos);
|
||||
if($t instanceof Spawnable){
|
||||
$nbt = new NetworkLittleEndianNBTStream();
|
||||
$compound = $nbt->read($packet->namedtag);
|
||||
$_ = 0;
|
||||
$compound = $nbt->read($packet->namedtag, false, $_, 512);
|
||||
|
||||
if(!($compound instanceof CompoundTag)){
|
||||
throw new \InvalidArgumentException("Expected " . CompoundTag::class . " in block entity NBT, got " . (is_object($compound) ? get_class($compound) : gettype($compound)));
|
||||
@ -3213,7 +3312,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the title duration settings.
|
||||
* Resets the title duration settings to defaults and removes any existing titles.
|
||||
*/
|
||||
public function resetTitles(){
|
||||
$pk = new SetTitlePacket();
|
||||
@ -3379,92 +3478,82 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
*/
|
||||
final public function close($message = "", string $reason = "generic reason", bool $notify = true) : void{
|
||||
if($this->isConnected() and !$this->closed){
|
||||
if($notify and strlen($reason) > 0){
|
||||
$pk = new DisconnectPacket();
|
||||
$pk->message = $reason;
|
||||
$this->directDataPacket($pk);
|
||||
}
|
||||
$this->interface->close($this, $notify ? $reason : "");
|
||||
$this->sessionAdapter = null;
|
||||
|
||||
try{
|
||||
if($notify and strlen($reason) > 0){
|
||||
$pk = new DisconnectPacket();
|
||||
$pk->message = $reason;
|
||||
$this->directDataPacket($pk);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
|
||||
$this->stopSleep();
|
||||
|
||||
if($this->spawned){
|
||||
$ev = new PlayerQuitEvent($this, $message, $reason);
|
||||
$ev->call();
|
||||
if($ev->getQuitMessage() != ""){
|
||||
$this->server->broadcastMessage($ev->getQuitMessage());
|
||||
}
|
||||
$this->interface->close($this, $notify ? $reason : "");
|
||||
$this->sessionAdapter = null;
|
||||
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$this->stopSleep();
|
||||
|
||||
if($this->spawned){
|
||||
$ev = new PlayerQuitEvent($this, $message, $reason);
|
||||
$ev->call();
|
||||
if($ev->getQuitMessage() != ""){
|
||||
$this->server->broadcastMessage($ev->getQuitMessage());
|
||||
if($this->isValid()){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
$this->level->unregisterChunkLoader($this, $chunkX, $chunkZ);
|
||||
foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){
|
||||
$entity->despawnFrom($this);
|
||||
}
|
||||
unset($this->usedChunks[$index]);
|
||||
}
|
||||
}
|
||||
$this->usedChunks = [];
|
||||
$this->loadQueue = [];
|
||||
|
||||
try{
|
||||
$this->save();
|
||||
}catch(\Throwable $e){
|
||||
$this->server->getLogger()->critical("Failed to save player data for " . $this->getName());
|
||||
$this->server->getLogger()->logException($e);
|
||||
if($this->loggedIn){
|
||||
$this->server->onPlayerLogout($this);
|
||||
foreach($this->server->getOnlinePlayers() as $player){
|
||||
if(!$player->canSee($this)){
|
||||
$player->showPlayer($this);
|
||||
}
|
||||
}
|
||||
$this->hiddenPlayers = [];
|
||||
}
|
||||
|
||||
if($this->isValid()){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
$this->level->unregisterChunkLoader($this, $chunkX, $chunkZ);
|
||||
foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){
|
||||
$entity->despawnFrom($this);
|
||||
}
|
||||
unset($this->usedChunks[$index]);
|
||||
}
|
||||
}
|
||||
$this->usedChunks = [];
|
||||
$this->loadQueue = [];
|
||||
$this->removeAllWindows(true);
|
||||
$this->windows = [];
|
||||
$this->windowIndex = [];
|
||||
$this->cursorInventory = null;
|
||||
$this->craftingGrid = null;
|
||||
|
||||
if($this->loggedIn){
|
||||
$this->server->onPlayerLogout($this);
|
||||
foreach($this->server->getOnlinePlayers() as $player){
|
||||
if(!$player->canSee($this)){
|
||||
$player->showPlayer($this);
|
||||
}
|
||||
}
|
||||
$this->hiddenPlayers = [];
|
||||
}
|
||||
if($this->constructed){
|
||||
parent::close();
|
||||
}
|
||||
$this->spawned = false;
|
||||
|
||||
$this->removeAllWindows(true);
|
||||
$this->windows = [];
|
||||
$this->windowIndex = [];
|
||||
$this->cursorInventory = null;
|
||||
$this->craftingGrid = null;
|
||||
if($this->loggedIn){
|
||||
$this->loggedIn = false;
|
||||
$this->server->removeOnlinePlayer($this);
|
||||
}
|
||||
|
||||
if($this->constructed){
|
||||
parent::close();
|
||||
}
|
||||
$this->spawned = false;
|
||||
$this->server->removePlayer($this);
|
||||
|
||||
if($this->loggedIn){
|
||||
$this->loggedIn = false;
|
||||
$this->server->removeOnlinePlayer($this);
|
||||
}
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [
|
||||
TextFormat::AQUA . $this->getName() . TextFormat::WHITE,
|
||||
$this->ip,
|
||||
$this->port,
|
||||
$this->getServer()->getLanguage()->translateString($reason)
|
||||
]));
|
||||
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [
|
||||
TextFormat::AQUA . $this->getName() . TextFormat::WHITE,
|
||||
$this->ip,
|
||||
$this->port,
|
||||
$this->getServer()->getLanguage()->translateString($reason)
|
||||
]));
|
||||
$this->spawnPosition = null;
|
||||
|
||||
$this->spawnPosition = null;
|
||||
|
||||
if($this->perm !== null){
|
||||
$this->perm->clearPermissions();
|
||||
$this->perm = null;
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
$this->server->getLogger()->logException($e);
|
||||
}finally{
|
||||
$this->server->removePlayer($this);
|
||||
if($this->perm !== null){
|
||||
$this->perm->clearPermissions();
|
||||
$this->perm = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3551,7 +3640,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
if($this->inventory !== null){
|
||||
$this->inventory->setHeldItemIndex(0, false); //This is already handled when sending contents, don't send it twice
|
||||
$this->inventory->setHeldItemIndex(0);
|
||||
$this->inventory->clearAll();
|
||||
}
|
||||
if($this->armorInventory !== null){
|
||||
@ -3559,16 +3648,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: allow this number to be manipulated during PlayerDeathEvent
|
||||
$this->level->dropExperience($this, $this->getXpDropAmount());
|
||||
$this->setXpAndProgress(0, 0);
|
||||
|
||||
if($ev->getDeathMessage() != ""){
|
||||
$this->server->broadcast($ev->getDeathMessage(), Server::BROADCAST_CHANNEL_USERS);
|
||||
$this->server->broadcastMessage($ev->getDeathMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function onDeathUpdate(int $tickDiff) : bool{
|
||||
if(parent::onDeathUpdate($tickDiff)){
|
||||
$this->despawnFromAll(); //non-player entities rely on close() to do this for them
|
||||
}
|
||||
|
||||
parent::onDeathUpdate($tickDiff);
|
||||
return false; //never flag players for despawn
|
||||
}
|
||||
|
||||
@ -3822,17 +3912,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @param Inventory $inventory
|
||||
* @param bool $force Forces removal of permanent windows such as normal inventory, cursor
|
||||
*
|
||||
* @throws \BadMethodCallException if trying to remove a fixed inventory window without the `force` parameter as true
|
||||
* @throws \InvalidArgumentException if trying to remove a fixed inventory window without the `force` parameter as true
|
||||
*/
|
||||
public function removeWindow(Inventory $inventory, bool $force = false){
|
||||
$id = $this->windows[$hash = spl_object_hash($inventory)] ?? null;
|
||||
|
||||
if($id !== null and !$force and isset($this->permanentWindows[$id])){
|
||||
throw new \BadMethodCallException("Cannot remove fixed window $id (" . get_class($inventory) . ") from " . $this->getName());
|
||||
throw new \InvalidArgumentException("Cannot remove fixed window $id (" . get_class($inventory) . ") from " . $this->getName());
|
||||
}
|
||||
|
||||
$inventory->close($this);
|
||||
if($id !== null){
|
||||
$inventory->close($this);
|
||||
unset($this->windows[$hash], $this->windowIndex[$id], $this->permanentWindows[$id]);
|
||||
}
|
||||
}
|
||||
@ -3877,9 +3967,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
public function onChunkChanged(Chunk $chunk){
|
||||
if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
$this->usedChunks[$hash] = false;
|
||||
if(!$this->spawned){
|
||||
$this->nextChunkOrderRun = 0;
|
||||
}
|
||||
$this->nextChunkOrderRun = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,10 +36,7 @@ namespace pocketmine {
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.4.0";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
|
||||
const MIN_PHP_VERSION = "7.2.0";
|
||||
|
||||
@ -177,11 +174,10 @@ namespace pocketmine {
|
||||
ini_set("default_charset", "utf-8");
|
||||
|
||||
ini_set("memory_limit", '-1');
|
||||
define('pocketmine\START_TIME', microtime(true));
|
||||
|
||||
define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR);
|
||||
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard"]);
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
|
||||
define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR);
|
||||
define('pocketmine\PLUGIN_PATH', isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : realpath(getcwd()) . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
|
||||
@ -190,9 +186,25 @@ namespace pocketmine {
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
}
|
||||
|
||||
define('pocketmine\LOCK_FILE_PATH', \pocketmine\DATA . 'server.lock');
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\LOCK_FILE_PATH, "cb"));
|
||||
if(!flock(\pocketmine\LOCK_FILE, LOCK_EX | LOCK_NB)){
|
||||
critical_error("Another " . \pocketmine\NAME . " instance is already using this folder (" . realpath(\pocketmine\DATA) . ").");
|
||||
critical_error("Please stop the other server first before running a new one.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//Logger has a dependency on timezone
|
||||
$tzError = Timezone::init();
|
||||
|
||||
if(isset($opts["enable-ansi"])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts["disable-ansi"])){
|
||||
Terminal::init(false);
|
||||
}else{
|
||||
Terminal::init();
|
||||
}
|
||||
|
||||
$logger = new MainLogger(\pocketmine\DATA . "server.log");
|
||||
$logger->registerStatic();
|
||||
|
||||
@ -204,6 +216,9 @@ namespace pocketmine {
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning(PHP_EOL . PHP_EOL . PHP_EOL . "\tYou are running " . \pocketmine\NAME . " with xdebug enabled. This has a major impact on performance." . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
if(!extension_loaded("pocketmine_chunkutils")){
|
||||
$logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance.");
|
||||
}
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged " . \pocketmine\NAME . " installation detected. Consider using a phar in production for better performance.");
|
||||
@ -245,6 +260,8 @@ namespace pocketmine {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move this to a Server field
|
||||
define('pocketmine\START_TIME', microtime(true));
|
||||
ThreadManager::init();
|
||||
new Server($autoloader, $logger, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
|
||||
|
||||
|
@ -98,7 +98,6 @@ use pocketmine\tile\Tile;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\updater\AutoUpdater;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\Config;
|
||||
use pocketmine\utils\Internet;
|
||||
use pocketmine\utils\MainLogger;
|
||||
@ -106,6 +105,75 @@ use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\UUID;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_shift;
|
||||
use function array_sum;
|
||||
use function asort;
|
||||
use function assert;
|
||||
use function base64_encode;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function define;
|
||||
use function explode;
|
||||
use function extension_loaded;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function getmypid;
|
||||
use function getopt;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_dir;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function is_subclass_of;
|
||||
use function json_decode;
|
||||
use function max;
|
||||
use function microtime;
|
||||
use function min;
|
||||
use function mkdir;
|
||||
use function ob_end_flush;
|
||||
use function pcntl_signal;
|
||||
use function pcntl_signal_dispatch;
|
||||
use function preg_replace;
|
||||
use function random_bytes;
|
||||
use function random_int;
|
||||
use function realpath;
|
||||
use function register_shutdown_function;
|
||||
use function rename;
|
||||
use function round;
|
||||
use function scandir;
|
||||
use function sleep;
|
||||
use function spl_object_hash;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function str_replace;
|
||||
use function stripos;
|
||||
use function strlen;
|
||||
use function strrpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
use function time;
|
||||
use function touch;
|
||||
use function trim;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const INT32_MAX;
|
||||
use const INT32_MIN;
|
||||
use const PHP_EOL;
|
||||
use const PHP_INT_MAX;
|
||||
use const PTHREADS_INHERIT_NONE;
|
||||
use const SCANDIR_SORT_NONE;
|
||||
use const SIGHUP;
|
||||
use const SIGINT;
|
||||
use const SIGTERM;
|
||||
|
||||
/**
|
||||
* The class that manages everything
|
||||
@ -225,15 +293,6 @@ class Server{
|
||||
/** @var int */
|
||||
public $networkCompressionLevel = 7;
|
||||
|
||||
/** @var bool */
|
||||
private $autoTickRate = true;
|
||||
/** @var int */
|
||||
private $autoTickRateLimit = 20;
|
||||
/** @var bool */
|
||||
private $alwaysTickPlayers = false;
|
||||
/** @var int */
|
||||
private $baseTickRate = 1;
|
||||
|
||||
/** @var int */
|
||||
private $autoSaveTicker = 0;
|
||||
/** @var int */
|
||||
@ -739,6 +798,7 @@ class Server{
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOfflinePlayerData(string $name) : bool{
|
||||
$name = strtolower($name);
|
||||
return file_exists($this->getDataPath() . "players/$name.dat");
|
||||
}
|
||||
|
||||
@ -986,7 +1046,7 @@ class Server{
|
||||
*/
|
||||
public function unloadLevel(Level $level, bool $forceUnload = false) : bool{
|
||||
if($level === $this->getDefaultLevel() and !$forceUnload){
|
||||
throw new \InvalidStateException("The default level cannot be unloaded while running, please switch levels.");
|
||||
throw new \InvalidStateException("The default world cannot be unloaded while running, please switch worlds.");
|
||||
}
|
||||
|
||||
return $level->unload($forceUnload);
|
||||
@ -1012,7 +1072,7 @@ class Server{
|
||||
*/
|
||||
public function loadLevel(string $name) : bool{
|
||||
if(trim($name) === ""){
|
||||
throw new LevelException("Invalid empty level name");
|
||||
throw new LevelException("Invalid empty world name");
|
||||
}
|
||||
if($this->isLevelLoaded($name)){
|
||||
return true;
|
||||
@ -1031,16 +1091,24 @@ class Server{
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @var LevelProvider $provider
|
||||
* @see LevelProvider::__construct()
|
||||
*/
|
||||
$provider = new $providerClass($path);
|
||||
try{
|
||||
GeneratorManager::getGenerator($provider->getGenerator(), true);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, "Unknown generator \"" . $provider->getGenerator() . "\""]));
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @see LevelProvider::__construct() */
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$level = new Level($this, $name, $provider);
|
||||
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
(new LevelLoadEvent($level))->call();
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1059,7 +1127,7 @@ class Server{
|
||||
return false;
|
||||
}
|
||||
|
||||
$seed = $seed ?? Binary::readInt(random_bytes(4));
|
||||
$seed = $seed ?? random_int(INT32_MIN, INT32_MAX);
|
||||
|
||||
if(!isset($options["preset"])){
|
||||
$options["preset"] = $this->getConfigString("generator-settings", "");
|
||||
@ -1072,7 +1140,7 @@ class Server{
|
||||
if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){
|
||||
$providerClass = LevelProviderManager::getProviderByName("pmanvil");
|
||||
if($providerClass === null){
|
||||
throw new \InvalidStateException("Default level provider has not been registered");
|
||||
throw new \InvalidStateException("Default world provider has not been registered");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1084,8 +1152,6 @@ class Server{
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
|
||||
(new LevelInitEvent($level))->call();
|
||||
|
||||
(new LevelLoadEvent($level))->call();
|
||||
@ -1449,8 +1515,6 @@ class Server{
|
||||
"announce-player-achievements" => true,
|
||||
"spawn-protection" => 16,
|
||||
"max-players" => 20,
|
||||
"spawn-animals" => true,
|
||||
"spawn-mobs" => true,
|
||||
"gamemode" => 0,
|
||||
"force-gamemode" => false,
|
||||
"hardcore" => false,
|
||||
@ -1485,7 +1549,7 @@ class Server{
|
||||
return;
|
||||
}
|
||||
|
||||
if(((int) ini_get('zend.assertions')) > 0 and ((bool) $this->getProperty("debug.assertions.warn-if-enabled", true)) !== false){
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
$this->logger->warning("Debugging assertions are enabled, this may impact on performance. To disable them, set `zend.assertions = -1` in php.ini.");
|
||||
}
|
||||
|
||||
@ -1525,11 +1589,6 @@ class Server{
|
||||
}
|
||||
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
|
||||
|
||||
$this->autoTickRate = (bool) $this->getProperty("level-settings.auto-tick-rate", true);
|
||||
$this->autoTickRateLimit = (int) $this->getProperty("level-settings.auto-tick-rate-limit", 20);
|
||||
$this->alwaysTickPlayers = (bool) $this->getProperty("level-settings.always-tick-players", false);
|
||||
$this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1);
|
||||
|
||||
$this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes();
|
||||
|
||||
|
||||
@ -1628,12 +1687,17 @@ class Server{
|
||||
Entity::init();
|
||||
Tile::init();
|
||||
BlockFactory::init();
|
||||
BlockFactory::registerStaticRuntimeIdMappings();
|
||||
Enchantment::init();
|
||||
ItemFactory::init();
|
||||
Item::initCreativeItems();
|
||||
Biome::init();
|
||||
|
||||
LevelProviderManager::init();
|
||||
if(extension_loaded("leveldb")){
|
||||
$this->logger->debug($this->getLanguage()->translateString("pocketmine.debug.enable"));
|
||||
}
|
||||
GeneratorManager::registerDefaultGenerators();
|
||||
|
||||
$this->craftingManager = new CraftingManager();
|
||||
|
||||
$this->resourceManager = new ResourcePackManager($this->getDataPath() . "resource_packs" . DIRECTORY_SEPARATOR, $this->logger);
|
||||
@ -1655,15 +1719,10 @@ class Server{
|
||||
|
||||
$this->network->registerInterface(new RakLibInterface($this));
|
||||
|
||||
LevelProviderManager::init();
|
||||
if(extension_loaded("leveldb")){
|
||||
$this->logger->debug($this->getLanguage()->translateString("pocketmine.debug.enable"));
|
||||
}
|
||||
|
||||
GeneratorManager::registerDefaultGenerators();
|
||||
|
||||
foreach((array) $this->getProperty("worlds", []) as $name => $options){
|
||||
if(!is_array($options)){
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
continue;
|
||||
}
|
||||
if(!$this->loadLevel($name)){
|
||||
@ -1968,7 +2027,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function reload(){
|
||||
$this->logger->info("Saving levels...");
|
||||
$this->logger->info("Saving worlds...");
|
||||
|
||||
foreach($this->levels as $level){
|
||||
$level->save();
|
||||
@ -2046,7 +2105,7 @@ class Server{
|
||||
$player->close($player->getLeaveMessage(), $this->getProperty("settings.shutdown-message", "Server closed"));
|
||||
}
|
||||
|
||||
$this->getLogger()->debug("Unloading all levels");
|
||||
$this->getLogger()->debug("Unloading all worlds");
|
||||
foreach($this->getLevels() as $level){
|
||||
$this->unloadLevel($level, true);
|
||||
}
|
||||
@ -2078,9 +2137,6 @@ class Server{
|
||||
$this->network->unregisterInterface($interface);
|
||||
}
|
||||
}
|
||||
|
||||
$this->getLogger()->debug("Collecting cycles");
|
||||
gc_collect_cycles();
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
$this->logger->emergency("Crashed while crashing, killing process");
|
||||
@ -2151,6 +2207,7 @@ class Server{
|
||||
* @param array|null $trace
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null){
|
||||
while(@ob_end_flush()){}
|
||||
global $lastError;
|
||||
|
||||
if($trace === null){
|
||||
@ -2173,7 +2230,7 @@ class Server{
|
||||
"fullFile" => $e->getFile(),
|
||||
"file" => $errfile,
|
||||
"line" => $errline,
|
||||
"trace" => Utils::getTrace(0, $trace)
|
||||
"trace" => $trace
|
||||
];
|
||||
|
||||
global $lastExceptionError, $lastError;
|
||||
@ -2182,6 +2239,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function crashDump(){
|
||||
while(@ob_end_flush()){}
|
||||
if(!$this->isRunning){
|
||||
return;
|
||||
}
|
||||
@ -2212,6 +2270,7 @@ class Server{
|
||||
if(is_string($plugin)){
|
||||
$p = $this->pluginManager->getPlugin($plugin);
|
||||
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
|
||||
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
|
||||
$report = false;
|
||||
}
|
||||
}
|
||||
@ -2254,6 +2313,7 @@ class Server{
|
||||
//Force minimum uptime to be >= 120 seconds, to reduce the impact of spammy crash loops
|
||||
$spacing = ((int) \pocketmine\START_TIME) - time() + 120;
|
||||
if($spacing > 0){
|
||||
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
sleep($spacing);
|
||||
}
|
||||
@Utils::kill(getmypid());
|
||||
@ -2361,8 +2421,6 @@ class Server{
|
||||
foreach($this->players as $p){
|
||||
if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){
|
||||
$p->close("", "Login timeout");
|
||||
}elseif($this->alwaysTickPlayers and $p->spawned){
|
||||
$p->onUpdate($currentTick);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2372,32 +2430,13 @@ class Server{
|
||||
// Level unloaded during the tick of a level earlier in this loop, perhaps by plugin
|
||||
continue;
|
||||
}
|
||||
if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$tickMs = (microtime(true) - $levelTime) * 1000;
|
||||
$level->tickRateTime = $tickMs;
|
||||
|
||||
if($this->autoTickRate){
|
||||
if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){
|
||||
$level->setTickRate($r = $level->getTickRate() - 1);
|
||||
if($r > $this->baseTickRate){
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
$this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks");
|
||||
}elseif($tickMs >= 50){
|
||||
if($level->getTickRate() === $this->baseTickRate){
|
||||
$level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50))));
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){
|
||||
$level->setTickRate($level->getTickRate() + 1);
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
if($tickMs >= 50){
|
||||
$this->getLogger()->debug(sprintf("World \"%s\" took too long to tick: %gms (%g ticks)", $level->getName(), $tickMs, round($tickMs / 50, 2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2488,12 +2527,10 @@ class Server{
|
||||
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
|
||||
$this->queryHandler->handle($interface, $address, $port, $payload);
|
||||
}else{
|
||||
$this->logger->debug("Unhandled raw packet from $address $port: " . bin2hex($payload));
|
||||
$this->logger->debug("Unhandled raw packet from $address $port: " . base64_encode($payload));
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
$this->logger->logException($e);
|
||||
|
||||
$this->getNetwork()->blockAddress($address, 600);
|
||||
}
|
||||
@ -2552,7 +2589,11 @@ class Server{
|
||||
|
||||
if($this->autoSave and ++$this->autoSaveTicker >= $this->autoSaveTicks){
|
||||
$this->autoSaveTicker = 0;
|
||||
$this->getLogger()->debug("[Auto Save] Saving worlds...");
|
||||
$start = microtime(true);
|
||||
$this->doAutoSave();
|
||||
$time = (microtime(true) - $start);
|
||||
$this->getLogger()->debug("[Auto Save] Save completed in " . ($time >= 1 ? round($time, 3) . "s" : round($time * 1000) . "ms"));
|
||||
}
|
||||
|
||||
if($this->sendUsageTicker > 0 and --$this->sendUsageTicker === 0){
|
||||
@ -2584,10 +2625,9 @@ class Server{
|
||||
|
||||
TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate);
|
||||
|
||||
array_shift($this->tickAverage);
|
||||
$this->tickAverage[] = $this->currentTPS;
|
||||
array_shift($this->useAverage);
|
||||
$this->useAverage[] = $this->currentUse;
|
||||
$idx = $this->tickCounter % 20;
|
||||
$this->tickAverage[$idx] = $this->currentTPS;
|
||||
$this->useAverage[$idx] = $this->currentUse;
|
||||
|
||||
if(($this->nextTick - $tickTime) < -1){
|
||||
$this->nextTick = $tickTime;
|
||||
|
@ -28,7 +28,7 @@ namespace pocketmine;
|
||||
*/
|
||||
abstract class Thread extends \Thread{
|
||||
|
||||
/** @var \ClassLoader */
|
||||
/** @var \ClassLoader|null */
|
||||
protected $classLoader;
|
||||
/** @var string|null */
|
||||
protected $composerAutoloaderPath;
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function spl_object_hash;
|
||||
|
||||
class ThreadManager extends \Volatile{
|
||||
|
||||
|
27
src/pocketmine/VersionInfo.php
Normal file
27
src/pocketmine/VersionInfo.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.8.4";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
@ -28,7 +28,7 @@ namespace pocketmine;
|
||||
*/
|
||||
abstract class Worker extends \Worker{
|
||||
|
||||
/** @var \ClassLoader */
|
||||
/** @var \ClassLoader|null */
|
||||
protected $classLoader;
|
||||
/** @var string|null */
|
||||
protected $composerAutoloaderPath;
|
||||
|
@ -26,6 +26,13 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function array_map;
|
||||
use function array_reverse;
|
||||
use function array_search;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
|
||||
abstract class BaseRail extends Flowable{
|
||||
|
||||
@ -98,7 +105,7 @@ abstract class BaseRail extends Flowable{
|
||||
$meta = array_search(array_reverse($connections), $lookup, true);
|
||||
}
|
||||
if($meta === false){
|
||||
throw new \InvalidArgumentException("No meta value matches connections " . implode(", ", array_map('dechex', $connections)));
|
||||
throw new \InvalidArgumentException("No meta value matches connections " . implode(", ", array_map('\dechex', $connections)));
|
||||
}
|
||||
|
||||
return $meta;
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Beetroot extends Crops{
|
||||
|
||||
|
@ -36,8 +36,12 @@ use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\metadata\Metadatable;
|
||||
use pocketmine\metadata\MetadataValue;
|
||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use function array_merge;
|
||||
use function get_class;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class Block extends Position implements BlockIds, Metadatable{
|
||||
|
||||
@ -114,7 +118,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
* @return int
|
||||
*/
|
||||
public function getRuntimeId() : int{
|
||||
return BlockFactory::toStaticRuntimeId($this->getId(), $this->getDamage());
|
||||
return RuntimeBlockMapping::toStaticRuntimeId($this->getId(), $this->getDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,6 +270,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
* @param Item $item
|
||||
*
|
||||
* @return float
|
||||
* @throws \InvalidArgumentException if the item efficiency is not a positive number
|
||||
*/
|
||||
public function getBreakTime(Item $item) : float{
|
||||
$base = $this->getHardness();
|
||||
@ -277,7 +282,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
|
||||
$efficiency = $item->getMiningEfficiency($this);
|
||||
if($efficiency <= 0){
|
||||
throw new \RuntimeException("Item efficiency is invalid");
|
||||
throw new \InvalidArgumentException(get_class($item) . " has invalid mining efficiency: expected >= 0, got $efficiency");
|
||||
}
|
||||
|
||||
$base /= $efficiency;
|
||||
|
@ -25,6 +25,8 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use function min;
|
||||
|
||||
/**
|
||||
* Manages block registration and instance creation
|
||||
@ -48,15 +50,6 @@ class BlockFactory{
|
||||
/** @var \SplFixedArray<float> */
|
||||
public static $blastResistance = null;
|
||||
|
||||
/** @var int[] */
|
||||
public static $staticRuntimeIdMap = [];
|
||||
|
||||
/** @var int[] */
|
||||
public static $legacyIdMap = [];
|
||||
|
||||
/** @var int */
|
||||
private static $lastRuntimeId = 0;
|
||||
|
||||
/**
|
||||
* Initializes the block factory. By default this is called only once on server start, however you may wish to use
|
||||
* this if you need to reset the block factory back to its original defaults for whatever reason.
|
||||
@ -167,7 +160,7 @@ class BlockFactory{
|
||||
self::registerBlock(new Cake());
|
||||
//TODO: REPEATER_BLOCK
|
||||
//TODO: POWERED_REPEATER
|
||||
//TODO: INVISIBLEBEDROCK
|
||||
self::registerBlock(new InvisibleBedrock());
|
||||
self::registerBlock(new Trapdoor());
|
||||
//TODO: MONSTER_EGG
|
||||
self::registerBlock(new StoneBricks());
|
||||
@ -315,13 +308,13 @@ class BlockFactory{
|
||||
self::registerBlock(new Stonecutter());
|
||||
self::registerBlock(new GlowingObsidian());
|
||||
self::registerBlock(new NetherReactor());
|
||||
//TODO: INFO_UPDATE
|
||||
//TODO: INFO_UPDATE2
|
||||
self::registerBlock(new InfoUpdate(Block::INFO_UPDATE, 0, "update!"));
|
||||
self::registerBlock(new InfoUpdate(Block::INFO_UPDATE2, 0, "ate!upd"));
|
||||
//TODO: MOVINGBLOCK
|
||||
//TODO: OBSERVER
|
||||
//TODO: STRUCTURE_BLOCK
|
||||
|
||||
//TODO: RESERVED6
|
||||
self::registerBlock(new Reserved6(Block::RESERVED6, 0, "reserved6"));
|
||||
|
||||
for($id = 0, $size = self::$fullList->getSize() >> 4; $id < $size; ++$id){
|
||||
if(self::$fullList[$id << 4] === null){
|
||||
@ -423,16 +416,9 @@ class BlockFactory{
|
||||
return $b !== null and !($b instanceof UnknownBlock);
|
||||
}
|
||||
|
||||
public static function registerStaticRuntimeIdMappings() : void{
|
||||
/** @var mixed[] $runtimeIdMap */
|
||||
$runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
|
||||
foreach($runtimeIdMap as $k => $obj){
|
||||
self::registerMapping($k, $obj["id"], $obj["data"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
@ -440,15 +426,11 @@ class BlockFactory{
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
* if still not found, return update! block
|
||||
*/
|
||||
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
|
||||
return RuntimeBlockMapping::toStaticRuntimeId($id, $meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @internal
|
||||
*
|
||||
* @param int $runtimeId
|
||||
@ -456,13 +438,6 @@ class BlockFactory{
|
||||
* @return int[] [id, meta]
|
||||
*/
|
||||
public static function fromStaticRuntimeId(int $runtimeId) : array{
|
||||
$v = self::$legacyIdMap[$runtimeId];
|
||||
return [$v >> 4, $v & 0xf];
|
||||
}
|
||||
|
||||
private static function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{
|
||||
self::$staticRuntimeIdMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId;
|
||||
self::$legacyIdMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta;
|
||||
self::$lastRuntimeId = max(self::$lastRuntimeId, $staticRuntimeId);
|
||||
return RuntimeBlockMapping::fromStaticRuntimeId($runtimeId);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
class BrownMushroomBlock extends RedMushroomBlock{
|
||||
|
||||
|
@ -96,9 +96,12 @@ class Cactus extends Transparent{
|
||||
if($b->getId() === self::AIR){
|
||||
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
|
||||
if($ev->isCancelled()){
|
||||
break;
|
||||
}
|
||||
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->meta = 0;
|
||||
|
@ -46,7 +46,7 @@ class Carpet extends Flowable{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Carpet";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Carpet";
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Carrot extends Crops{
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use function mt_rand;
|
||||
|
||||
class CoalOre extends Solid{
|
||||
|
||||
|
@ -30,6 +30,18 @@ use pocketmine\math\Vector3;
|
||||
class CobblestoneWall extends Transparent{
|
||||
public const NONE_MOSSY_WALL = 0;
|
||||
public const MOSSY_WALL = 1;
|
||||
public const GRANITE_WALL = 2;
|
||||
public const DIORITE_WALL = 3;
|
||||
public const ANDESITE_WALL = 4;
|
||||
public const SANDSTONE_WALL = 5;
|
||||
public const BRICK_WALL = 6;
|
||||
public const STONE_BRICK_WALL = 7;
|
||||
public const MOSSY_STONE_BRICK_WALL = 8;
|
||||
public const NETHER_BRICK_WALL = 9;
|
||||
public const END_STONE_BRICK_WALL = 10;
|
||||
public const PRISMARINE_WALL = 11;
|
||||
public const RED_SANDSTONE_WALL = 12;
|
||||
public const RED_NETHER_BRICK_WALL = 13;
|
||||
|
||||
protected $id = self::COBBLESTONE_WALL;
|
||||
|
||||
@ -50,11 +62,23 @@ class CobblestoneWall extends Transparent{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
if($this->meta === 0x01){
|
||||
return "Mossy Cobblestone Wall";
|
||||
}
|
||||
|
||||
return "Cobblestone Wall";
|
||||
static $names = [
|
||||
self::NONE_MOSSY_WALL => "Cobblestone",
|
||||
self::MOSSY_WALL => "Mossy Cobblestone",
|
||||
self::GRANITE_WALL => "Granite",
|
||||
self::DIORITE_WALL => "Diorite",
|
||||
self::ANDESITE_WALL => "Andesite",
|
||||
self::SANDSTONE_WALL => "Sandstone",
|
||||
self::BRICK_WALL => "Brick",
|
||||
self::STONE_BRICK_WALL => "Stone Brick",
|
||||
self::MOSSY_STONE_BRICK_WALL => "Mossy Stone Brick",
|
||||
self::NETHER_BRICK_WALL => "Nether Brick",
|
||||
self::END_STONE_BRICK_WALL => "End Stone Brick",
|
||||
self::PRISMARINE_WALL => "Prismarine",
|
||||
self::RED_SANDSTONE_WALL => "Red Sandstone",
|
||||
self::RED_NETHER_BRICK_WALL => "Red Nether Brick"
|
||||
];
|
||||
return ($names[$this->getVariant()] ?? "Unknown") . " Wall";
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
|
@ -35,7 +35,7 @@ class Concrete extends Solid{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Concrete";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Concrete";
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
|
@ -34,7 +34,7 @@ class ConcretePowder extends Fallable{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Concrete Powder";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Concrete Powder";
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class Crops extends Flowable{
|
||||
|
||||
@ -42,7 +43,7 @@ abstract class Crops extends Flowable{
|
||||
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
|
||||
if($this->meta < 7 and $item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
|
||||
$block = clone $this;
|
||||
$block->meta += mt_rand(2, 5);
|
||||
if($block->meta > 7){
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class DeadBush extends Flowable{
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use function mt_rand;
|
||||
|
||||
class DiamondOre extends Solid{
|
||||
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class DoublePlant extends Flowable{
|
||||
public const BITFLAG_TOP = 0x08;
|
||||
@ -38,7 +39,7 @@ class DoublePlant extends Flowable{
|
||||
}
|
||||
|
||||
public function canBeReplaced() : bool{
|
||||
return $this->meta === 2 or $this->meta === 3; //grass or fern
|
||||
return $this->getVariant() === 2 or $this->getVariant() === 3; //grass or fern
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
@ -94,11 +95,11 @@ class DoublePlant extends Flowable{
|
||||
}
|
||||
|
||||
public function getToolType() : int{
|
||||
return ($this->meta === 2 or $this->meta === 3) ? BlockToolType::TYPE_SHEARS : BlockToolType::TYPE_NONE;
|
||||
return ($this->getVariant() === 2 or $this->getVariant() === 3) ? BlockToolType::TYPE_SHEARS : BlockToolType::TYPE_NONE;
|
||||
}
|
||||
|
||||
public function getToolHarvestLevel() : int{
|
||||
return ($this->meta === 2 or $this->meta === 3) ? 1 : 0; //only grass or fern require shears
|
||||
return ($this->getVariant() === 2 or $this->getVariant() === 3) ? 1 : 0; //only grass or fern require shears
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use function mt_rand;
|
||||
|
||||
class EmeraldOre extends Solid{
|
||||
|
||||
|
@ -31,6 +31,8 @@ use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
class Fire extends Flowable{
|
||||
|
||||
|
@ -56,7 +56,7 @@ class Flower extends Flowable{
|
||||
self::TYPE_PINK_TULIP => "Pink Tulip",
|
||||
self::TYPE_OXEYE_DAISY => "Oxeye Daisy"
|
||||
];
|
||||
return $names[$this->meta] ?? "Unknown";
|
||||
return $names[$this->getVariant()] ?? "Unknown";
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Glowstone extends Transparent{
|
||||
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\level\generator\object\TallGrass as TallGrassObject;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Random;
|
||||
use function mt_rand;
|
||||
|
||||
class Grass extends Solid{
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Gravel extends Fallable{
|
||||
|
||||
|
@ -56,7 +56,7 @@ class Ice extends Transparent{
|
||||
}
|
||||
|
||||
public function onBreak(Item $item, Player $player = null) : bool{
|
||||
if(!$item->hasEnchantment(Enchantment::SILK_TOUCH)){
|
||||
if(($player === null or $player->isSurvival()) and !$item->hasEnchantment(Enchantment::SILK_TOUCH)){
|
||||
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::WATER), true);
|
||||
}
|
||||
return parent::onBreak($item, $player);
|
||||
|
31
src/pocketmine/block/InfoUpdate.php
Normal file
31
src/pocketmine/block/InfoUpdate.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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;
|
||||
|
||||
class InfoUpdate extends Solid{
|
||||
|
||||
public function getHardness() : float{
|
||||
return 1;
|
||||
}
|
||||
}
|
51
src/pocketmine/block/InvisibleBedrock.php
Normal file
51
src/pocketmine/block/InvisibleBedrock.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?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\item\Item;
|
||||
|
||||
class InvisibleBedrock extends Transparent{
|
||||
|
||||
protected $id = self::INVISIBLE_BEDROCK;
|
||||
|
||||
public function __construct(){
|
||||
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return "Invisible Bedrock";
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function getBlastResistance() : float{
|
||||
return 18000000;
|
||||
}
|
||||
|
||||
public function isBreakable(Item $item) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
@ -44,4 +44,8 @@ class IronTrapdoor extends Trapdoor{
|
||||
public function getToolHarvestLevel() : int{
|
||||
return TieredTool::TIER_WOODEN;
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
return 0; //TODO: remove this hack on 4.0
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\ItemFrame as TileItemFrame;
|
||||
use pocketmine\tile\Tile;
|
||||
use function lcg_value;
|
||||
|
||||
class ItemFrame extends Flowable{
|
||||
protected $id = Block::ITEM_FRAME_BLOCK;
|
||||
@ -64,7 +65,7 @@ class ItemFrame extends Flowable{
|
||||
2 => Vector3::SIDE_NORTH,
|
||||
3 => Vector3::SIDE_SOUTH
|
||||
];
|
||||
if(!$this->getSide($sides[$this->meta])->isSolid()){
|
||||
if(isset($sides[$this->meta]) and !$this->getSide($sides[$this->meta])->isSolid()){
|
||||
$this->level->useBreakOn($this);
|
||||
}
|
||||
}
|
||||
@ -111,4 +112,8 @@ class ItemFrame extends Flowable{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0.25;
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +58,10 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity) : void{
|
||||
$entity->resetFallDistance();
|
||||
$entity->onGround = true;
|
||||
if($entity->asVector3()->floor()->distanceSquared($this) < 1){ //entity coordinates must be inside block
|
||||
$entity->resetFallDistance();
|
||||
$entity->onGround = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use function mt_rand;
|
||||
|
||||
class LapisOre extends Solid{
|
||||
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class Leaves extends Transparent{
|
||||
public const OAK = 0;
|
||||
@ -187,7 +188,7 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
public function canDropApples() : bool{
|
||||
return $this->meta === self::OAK;
|
||||
return $this->getVariant() === self::OAK;
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
|
@ -44,6 +44,6 @@ class Leaves2 extends Leaves{
|
||||
}
|
||||
|
||||
public function canDropApples() : bool{
|
||||
return $this->meta === self::DARK_OAK;
|
||||
return $this->getVariant() === self::DARK_OAK;
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,12 @@ use pocketmine\event\block\BlockFormEvent;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\sound\FizzSound;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use function array_fill;
|
||||
use function lcg_value;
|
||||
use function min;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
|
||||
@ -435,7 +438,7 @@ abstract class Liquid extends Transparent{
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->level->setBlock($this, $ev->getNewState(), true, true);
|
||||
$this->level->broadcastLevelSoundEvent($this->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_FIZZ, (int) ((2.6 + (lcg_value() - lcg_value()) * 0.8) * 1000));
|
||||
$this->level->addSound(new FizzSound($this->add(0.5, 0.5, 0.5), 2.6 + (lcg_value() - lcg_value()) * 0.8));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Melon extends Transparent{
|
||||
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use function mt_rand;
|
||||
|
||||
class MelonStem extends Crops{
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\TieredTool;
|
||||
use function mt_rand;
|
||||
|
||||
class MonsterSpawner extends Transparent{
|
||||
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use function mt_rand;
|
||||
|
||||
class Mycelium extends Solid{
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use function mt_rand;
|
||||
|
||||
class NetherQuartzOre extends Solid{
|
||||
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class NetherWartPlant extends Flowable{
|
||||
protected $id = Block::NETHER_WART_PLANT;
|
||||
|
@ -46,4 +46,6 @@ class NoteBlock extends Solid{
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_AXE;
|
||||
}
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class PackedIce extends Solid{
|
||||
|
||||
protected $id = self::PACKED_ICE;
|
||||
@ -46,4 +48,8 @@ class PackedIce extends Solid{
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_PICKAXE;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Potato extends Crops{
|
||||
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use function mt_rand;
|
||||
|
||||
class PumpkinStem extends Crops{
|
||||
|
||||
|
@ -55,7 +55,7 @@ class Quartz extends Solid{
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
if($this->meta !== self::NORMAL){
|
||||
if($this->getVariant() !== self::NORMAL){
|
||||
$this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face);
|
||||
}
|
||||
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
class RedMushroomBlock extends Solid{
|
||||
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class RedstoneOre extends Solid{
|
||||
|
||||
|
31
src/pocketmine/block/Reserved6.php
Normal file
31
src/pocketmine/block/Reserved6.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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;
|
||||
|
||||
class Reserved6 extends Solid{
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ class Sand extends Fallable{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
if($this->meta === 0x01){
|
||||
if($this->getVariant() === 0x01){
|
||||
return "Red Sand";
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\level\generator\object\Tree;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Random;
|
||||
use function mt_rand;
|
||||
|
||||
class Sapling extends Flowable{
|
||||
public const OAK = 0;
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Sign as TileSign;
|
||||
use pocketmine\tile\Tile;
|
||||
use function floor;
|
||||
|
||||
class SignPost extends Transparent{
|
||||
|
||||
|
@ -78,7 +78,7 @@ abstract class Slab extends Transparent{
|
||||
}
|
||||
}else{ //TODO: collision
|
||||
if($blockReplace->getId() === $this->id){
|
||||
if($blockReplace->getVariant() === $this->meta){
|
||||
if($blockReplace->getVariant() === $this->getVariant()){
|
||||
$this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true);
|
||||
|
||||
return true;
|
||||
|
@ -30,6 +30,6 @@ class StainedClay extends HardenedClay{
|
||||
protected $id = self::STAINED_CLAY;
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Clay";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Stained Clay";
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ class StainedGlass extends Glass{
|
||||
protected $id = self::STAINED_GLASS;
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Glass";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Stained Glass";
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ class StainedGlassPane extends GlassPane{
|
||||
protected $id = self::STAINED_GLASS_PANE;
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Stained Glass Pane";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Stained Glass Pane";
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ abstract class Stair extends Transparent{
|
||||
2 => 1,
|
||||
3 => 3
|
||||
];
|
||||
$this->meta = $faces[$player->getDirection()] & 0x03;
|
||||
$this->meta = $player !== null ? $faces[$player->getDirection()] & 0x03 : 0;
|
||||
if(($clickVector->y > 0.5 and $face !== Vector3::SIDE_UP) or $face === Vector3::SIDE_DOWN){
|
||||
$this->meta |= 0x04; //Upside-down stairs
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Banner as TileBanner;
|
||||
use pocketmine\tile\Tile;
|
||||
use function floor;
|
||||
|
||||
class StandingBanner extends Transparent{
|
||||
|
||||
|
@ -26,6 +26,12 @@ namespace pocketmine\block;
|
||||
class StoneSlab2 extends StoneSlab{
|
||||
public const TYPE_RED_SANDSTONE = 0;
|
||||
public const TYPE_PURPUR = 1;
|
||||
public const TYPE_PRISMARINE = 2;
|
||||
public const TYPE_DARK_PRISMARINE = 3;
|
||||
public const TYPE_PRISMARINE_BRICKS = 4;
|
||||
public const TYPE_MOSSY_COBBLESTONE = 5;
|
||||
public const TYPE_SMOOTH_SANDSTONE = 6;
|
||||
public const TYPE_RED_NETHER_BRICK = 7;
|
||||
|
||||
protected $id = self::STONE_SLAB2;
|
||||
|
||||
@ -36,7 +42,13 @@ class StoneSlab2 extends StoneSlab{
|
||||
public function getName() : string{
|
||||
static $names = [
|
||||
self::TYPE_RED_SANDSTONE => "Red Sandstone",
|
||||
self::TYPE_PURPUR => "Purpur"
|
||||
self::TYPE_PURPUR => "Purpur",
|
||||
self::TYPE_PRISMARINE => "Prismarine",
|
||||
self::TYPE_DARK_PRISMARINE => "Dark Prismarine",
|
||||
self::TYPE_PRISMARINE_BRICKS => "Prismarine Bricks",
|
||||
self::TYPE_MOSSY_COBBLESTONE => "Mossy Cobblestone",
|
||||
self::TYPE_SMOOTH_SANDSTONE => "Smooth Sandstone",
|
||||
self::TYPE_RED_NETHER_BRICK => "Red Nether Brick"
|
||||
];
|
||||
|
||||
return (($this->meta & 0x08) > 0 ? "Upper " : "") . ($names[$this->getVariant()] ?? "") . " Slab";
|
||||
|
@ -37,6 +37,10 @@ class Stonecutter extends Solid{
|
||||
return "Stonecutter";
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 3.5;
|
||||
}
|
||||
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_PICKAXE;
|
||||
}
|
||||
|
@ -50,9 +50,11 @@ class Sugarcane extends Flowable{
|
||||
if($b->getId() === self::AIR){
|
||||
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
|
||||
if($ev->isCancelled()){
|
||||
break;
|
||||
}
|
||||
$this->getLevel()->setBlock($b, $ev->getNewState(), true);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,16 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\item\FlintSteel;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Random;
|
||||
use function cos;
|
||||
use function sin;
|
||||
use const M_PI;
|
||||
|
||||
class TNT extends Solid{
|
||||
|
||||
@ -48,8 +53,10 @@ class TNT extends Solid{
|
||||
}
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
if($item instanceof FlintSteel){
|
||||
$item->applyDamage(1);
|
||||
if($item instanceof FlintSteel or $item->hasEnchantment(Enchantment::FIRE_ASPECT)){
|
||||
if($item instanceof Durable){
|
||||
$item->applyDamage(1);
|
||||
}
|
||||
$this->ignite();
|
||||
return true;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function mt_rand;
|
||||
|
||||
class TallGrass extends Flowable{
|
||||
|
||||
@ -50,8 +51,8 @@ class TallGrass extends Flowable{
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
$down = $this->getSide(Vector3::SIDE_DOWN);
|
||||
if($down->getId() === self::GRASS){
|
||||
$down = $this->getSide(Vector3::SIDE_DOWN)->getId();
|
||||
if($down === self::GRASS or $down === self::DIRT){
|
||||
$this->getLevel()->setBlock($blockReplace, $this, true);
|
||||
|
||||
return true;
|
||||
|
@ -45,8 +45,8 @@ class Torch extends Flowable{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$below = $this->getSide(Vector3::SIDE_DOWN);
|
||||
$side = $this->getDamage();
|
||||
$faces = [
|
||||
$meta = $this->getDamage();
|
||||
static $faces = [
|
||||
0 => Vector3::SIDE_DOWN,
|
||||
1 => Vector3::SIDE_WEST,
|
||||
2 => Vector3::SIDE_EAST,
|
||||
@ -54,8 +54,9 @@ class Torch extends Flowable{
|
||||
4 => Vector3::SIDE_SOUTH,
|
||||
5 => Vector3::SIDE_DOWN
|
||||
];
|
||||
$face = $faces[$meta] ?? Vector3::SIDE_DOWN;
|
||||
|
||||
if($this->getSide($faces[$side])->isTransparent() and !($faces[$side] === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
|
||||
if($this->getSide($face)->isTransparent() and !($face === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ class UnknownBlock extends Transparent{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function canBePlaced() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use function max;
|
||||
use function min;
|
||||
|
||||
class Vine extends Flowable{
|
||||
public const FLAG_SOUTH = 0x01;
|
||||
|
@ -67,7 +67,7 @@ class Water extends Liquid{
|
||||
|
||||
public function onEntityCollide(Entity $entity) : void{
|
||||
$entity->resetFallDistance();
|
||||
if($entity->fireTicks > 0){
|
||||
if($entity->isOnFire()){
|
||||
$entity->extinguish();
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Wheat extends Crops{
|
||||
|
||||
|
@ -43,7 +43,7 @@ class Wool extends Solid{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->meta) . " Wool";
|
||||
return ColorBlockMetaHelper::getColorFromMeta($this->getVariant()) . " Wool";
|
||||
}
|
||||
|
||||
public function getBreakTime(Item $item) : float{
|
||||
|
@ -26,12 +26,15 @@ declare(strict_types=1);
|
||||
*/
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\command\utils\CommandException;
|
||||
use pocketmine\lang\TextContainer;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function explode;
|
||||
use function str_replace;
|
||||
|
||||
abstract class Command{
|
||||
|
||||
@ -90,6 +93,7 @@ abstract class Command{
|
||||
* @param string[] $args
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CommandException
|
||||
*/
|
||||
abstract public function execute(CommandSender $sender, string $commandLabel, array $args);
|
||||
|
||||
|
@ -26,6 +26,22 @@ namespace pocketmine\command;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\Thread;
|
||||
use pocketmine\utils\Utils;
|
||||
use function extension_loaded;
|
||||
use function fclose;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function fstat;
|
||||
use function getopt;
|
||||
use function is_resource;
|
||||
use function microtime;
|
||||
use function preg_replace;
|
||||
use function readline;
|
||||
use function readline_add_history;
|
||||
use function stream_isatty;
|
||||
use function stream_select;
|
||||
use function trim;
|
||||
use function usleep;
|
||||
use const STDIN;
|
||||
|
||||
class CommandReader extends Thread{
|
||||
|
||||
|
@ -31,6 +31,9 @@ use pocketmine\permission\PermissionAttachmentInfo;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function explode;
|
||||
use function trim;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class ConsoleCommandSender implements CommandSender{
|
||||
|
||||
|
@ -25,6 +25,11 @@ namespace pocketmine\command;
|
||||
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function count;
|
||||
use function ord;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
|
||||
class FormattedCommandAlias extends Command{
|
||||
private $formatStrings = [];
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\lang\TextContainer;
|
||||
use function trim;
|
||||
|
||||
class RemoteConsoleCommandSender extends ConsoleCommandSender{
|
||||
|
||||
|
@ -66,6 +66,16 @@ use pocketmine\command\defaults\VersionCommand;
|
||||
use pocketmine\command\defaults\WhitelistCommand;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\Server;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function min;
|
||||
use function preg_match_all;
|
||||
use function stripslashes;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
class SimpleCommandMap implements CommandMap{
|
||||
|
||||
@ -90,9 +100,11 @@ class SimpleCommandMap implements CommandMap{
|
||||
new DefaultGamemodeCommand("defaultgamemode"),
|
||||
new DeopCommand("deop"),
|
||||
new DifficultyCommand("difficulty"),
|
||||
new DumpMemoryCommand("dumpmemory"),
|
||||
new EffectCommand("effect"),
|
||||
new EnchantCommand("enchant"),
|
||||
new GamemodeCommand("gamemode"),
|
||||
new GarbageCollectorCommand("gc"),
|
||||
new GiveCommand("give"),
|
||||
new HelpCommand("help"),
|
||||
new KickCommand("kick"),
|
||||
@ -112,6 +124,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
new SeedCommand("seed"),
|
||||
new SetWorldSpawnCommand("setworldspawn"),
|
||||
new SpawnpointCommand("spawnpoint"),
|
||||
new StatusCommand("status"),
|
||||
new StopCommand("stop"),
|
||||
new TeleportCommand("tp"),
|
||||
new TellCommand("tell"),
|
||||
@ -122,14 +135,6 @@ class SimpleCommandMap implements CommandMap{
|
||||
new VersionCommand("version"),
|
||||
new WhitelistCommand("whitelist")
|
||||
]);
|
||||
|
||||
if($this->server->getProperty("debug.commands", false)){
|
||||
$this->registerAll("pocketmine", [
|
||||
new StatusCommand("status"),
|
||||
new GarbageCollectorCommand("gc"),
|
||||
new DumpMemoryCommand("dumpmemory")
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -242,7 +247,16 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
|
||||
public function dispatch(CommandSender $sender, string $commandLine) : bool{
|
||||
$args = array_map("stripslashes", str_getcsv($commandLine, " "));
|
||||
$args = [];
|
||||
preg_match_all('/"((?:\\\\.|[^\\\\"])*)"|(\S+)/u', $commandLine, $matches);
|
||||
foreach($matches[0] as $k => $_){
|
||||
for($i = 1; $i <= 2; ++$i){
|
||||
if($matches[$i][$k] !== ""){
|
||||
$args[$k] = stripslashes($matches[$i][$k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sentCommandLabel = "";
|
||||
$target = $this->matchCommand($sentCommandLabel, $args);
|
||||
|
||||
|
@ -28,6 +28,9 @@ use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\Player;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
class BanCommand extends VanillaCommand{
|
||||
|
||||
|
@ -28,6 +28,10 @@ use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\Player;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function preg_match;
|
||||
|
||||
class BanIpCommand extends VanillaCommand{
|
||||
|
||||
|
@ -27,6 +27,10 @@ use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\permission\BanEntry;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function strtolower;
|
||||
|
||||
class BanListCommand extends VanillaCommand{
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user