mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-16 22:35:06 +00:00
Compare commits
164 Commits
Author | SHA1 | Date | |
---|---|---|---|
c3c81b09e8 | |||
08f9873c32 | |||
e3700cab50 | |||
c1ed182112 | |||
5f3a2a5096 | |||
8ccaf907d1 | |||
6b5c405939 | |||
9fc9609694 | |||
1055b7580a | |||
3385087c56 | |||
1e4a1565bb | |||
8aaa6dd176 | |||
07dff9c9e8 | |||
75a39491be | |||
a10e4b6481 | |||
68c6b87678 | |||
e20c031aa1 | |||
9832fe899f | |||
55f3477ed9 | |||
2c17f82eb8 | |||
e6e2c54ec9 | |||
abce512860 | |||
0093732d49 | |||
9eb2a46942 | |||
1402571055 | |||
34bb86d2bf | |||
b41960dfec | |||
0b2fc84827 | |||
22b9e70372 | |||
a222636476 | |||
fb586cc562 | |||
f3f22ba48b | |||
a2e6e2e5b9 | |||
1aaaadb909 | |||
53a740433f | |||
8491d3c6c0 | |||
d637370b83 | |||
f655eda3b3 | |||
af432c1a7f | |||
9fcc9f4338 | |||
41c5f63565 | |||
73b1fba53c | |||
8e17aed4f4 | |||
1f461977d4 | |||
e4888d7102 | |||
450ad42202 | |||
eb935ca80f | |||
1c5d3b43be | |||
decc188302 | |||
8fa5c7cdab | |||
7dd3a70d2e | |||
dbb5a32a96 | |||
9474324f75 | |||
ada37899aa | |||
f1440324a7 | |||
73659318f6 | |||
f868c1d8c6 | |||
114f444ec3 | |||
19a1792184 | |||
6a3ec70c72 | |||
ccd2cdd324 | |||
c7a358a56f | |||
b3390458b4 | |||
b4d55e4384 | |||
932116fa52 | |||
7b5c30bc2c | |||
c14eb63f9b | |||
48dcf0e32c | |||
7f3de835e4 | |||
63fcf9879a | |||
224a69b11a | |||
d0d16cdeb7 | |||
18b711aca8 | |||
b0936a50c1 | |||
82d6fc3890 | |||
3c614b505d | |||
15ba642258 | |||
edea793a98 | |||
1da7e3586b | |||
538b698a00 | |||
128eb500eb | |||
7d200247f8 | |||
8b52a5cd9e | |||
c9163a1505 | |||
2d697c5f04 | |||
364f408eb1 | |||
660e2b8173 | |||
9facb98327 | |||
7e42a03db3 | |||
ee26d6d570 | |||
94a17f59d2 | |||
ed4088755f | |||
006f78c0a7 | |||
55cc5a6651 | |||
390cc3060a | |||
ca69f08da0 | |||
eac0564792 | |||
628d77f8d7 | |||
fe543a4789 | |||
31cd096b4b | |||
78cc5ba635 | |||
4b9d170954 | |||
d94391af57 | |||
ff89d4d055 | |||
a6b030f2b3 | |||
56d7039086 | |||
8f804f6f34 | |||
4f13e446a1 | |||
6ec340359b | |||
c3bca9e172 | |||
c028bb9055 | |||
f151047b5e | |||
77566db766 | |||
dd2e6ea33f | |||
ee6d551729 | |||
04b815a87a | |||
d138a15a32 | |||
7a2cfa92b6 | |||
912fd3f5c6 | |||
3906600d44 | |||
3f7abf29a8 | |||
fe3e2cc90a | |||
5eca90d478 | |||
338bb3fe6d | |||
f485f7fb46 | |||
63eba3eb53 | |||
914eb62e94 | |||
a85814d0c9 | |||
eb2e472b01 | |||
6553852d99 | |||
540476365f | |||
e9169cfa67 | |||
537e194161 | |||
f7f5af607c | |||
b293d7bf1f | |||
efafc2c6ca | |||
2a528b4afb | |||
999eab0c84 | |||
33a0fb9061 | |||
904b0acfff | |||
093b1e1b18 | |||
d7f69c5e24 | |||
d60fca0a1c | |||
0e87ee1e0e | |||
03ecc98a24 | |||
a5aeabd836 | |||
d3ab516ba4 | |||
aa916b2c49 | |||
7ce33d9375 | |||
14f2368454 | |||
07194e3884 | |||
58278f22f3 | |||
7dcd2592d4 | |||
6887fcd590 | |||
c168818311 | |||
b50efbc15a | |||
94d98fb5c4 | |||
ae564e445d | |||
fe94379a93 | |||
79acc4fed4 | |||
be4e091d40 | |||
857c2edc2c | |||
b1ab69ac6c | |||
e95a920fb8 |
14
.github/workflows/build-docker-image.yml
vendored
14
.github/workflows/build-docker-image.yml
vendored
@ -12,16 +12,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@ -53,7 +53,7 @@ jobs:
|
|||||||
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Build image for tag
|
- name: Build image for tag
|
||||||
uses: docker/build-push-action@v4.1.1
|
uses: docker/build-push-action@v5.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
@ -66,7 +66,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build image for major tag
|
- name: Build image for major tag
|
||||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||||
uses: docker/build-push-action@v4.1.1
|
uses: docker/build-push-action@v5.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
@ -79,7 +79,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build image for minor tag
|
- name: Build image for minor tag
|
||||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||||
uses: docker/build-push-action@v4.1.1
|
uses: docker/build-push-action@v5.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
@ -92,7 +92,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build image for latest tag
|
- name: Build image for latest tag
|
||||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||||
uses: docker/build-push-action@v4.1.1
|
uses: docker/build-push-action@v5.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
context: ./pocketmine-mp
|
context: ./pocketmine-mp
|
||||||
|
2
.github/workflows/discord-release-notify.yml
vendored
2
.github/workflows/discord-release-notify.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP and tools
|
- name: Setup PHP and tools
|
||||||
uses: shivammathur/setup-php@2.25.5
|
uses: shivammathur/setup-php@2.27.1
|
||||||
with:
|
with:
|
||||||
php-version: 8.1
|
php-version: 8.1
|
||||||
|
|
||||||
|
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@2.25.5
|
uses: shivammathur/setup-php@2.27.1
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
|
|
||||||
|
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@ -152,9 +152,6 @@ jobs:
|
|||||||
- name: Regenerate KnownTranslation APIs
|
- name: Regenerate KnownTranslation APIs
|
||||||
run: php build/generate-known-translation-apis.php
|
run: php build/generate-known-translation-apis.php
|
||||||
|
|
||||||
- name: Regenerate RuntimeEnum(De)serializer
|
|
||||||
run: php build/generate-runtime-enum-serializers.php
|
|
||||||
|
|
||||||
- name: Regenerate BedrockData available files constants
|
- name: Regenerate BedrockData available files constants
|
||||||
run: php build/generate-bedrockdata-path-consts.php
|
run: php build/generate-bedrockdata-path-consts.php
|
||||||
|
|
||||||
@ -176,7 +173,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP and tools
|
- name: Setup PHP and tools
|
||||||
uses: shivammathur/setup-php@2.25.5
|
uses: shivammathur/setup-php@2.27.1
|
||||||
with:
|
with:
|
||||||
php-version: 8.1
|
php-version: 8.1
|
||||||
tools: php-cs-fixer:3.17
|
tools: php-cs-fixer:3.17
|
||||||
|
@ -88,45 +88,58 @@ Depending on the changes, maintainers might ask you to make changes to the PR to
|
|||||||
### Requirements
|
### Requirements
|
||||||
The following are required as a minimum for pull requests. PRs that don't meet these requirements will be declined unless updated to meet them.
|
The following are required as a minimum for pull requests. PRs that don't meet these requirements will be declined unless updated to meet them.
|
||||||
|
|
||||||
#### Licensing
|
- **All code must be licensed under the [LGPLv3 license](LICENSE)** as per PocketMine-MP's own license, or a compatible license.
|
||||||
PocketMine-MP is licensed under [LGPLv3 license](LICENSE).
|
- By proposing a pull request, you agree to your code being distributed within PocketMine-MP under the same license.
|
||||||
By proposing a pull request, you agree to your code being distributed within PocketMine-MP under the same license.
|
- If you take code from other projects, that code MUST be licensed under an LGPL-compatible license.
|
||||||
If you take code from other projects, that code MUST be licensed under an LGPL-compatible license.
|
- **PRs should be about ONE thing**
|
||||||
|
- If you want to make multiple changes, those changes should each be contributed as separate pull requests. **DO NOT** mix unrelated changes.
|
||||||
#### PRs should be about exactly ONE thing
|
- **Do not include unnecessary changes.** This makes the code diff larger and more noisy, making it harder to review.
|
||||||
If you want to make multiple changes, those changes should each be contributed as separate pull requests. **DO NOT** mix unrelated changes.
|
- Don't change things that aren't related to the PR's objective
|
||||||
|
- Don't reformat or rearrange existing code without a good reason related to the PR's objective
|
||||||
#### PRs must not include unnecessary/unrelated changes
|
- Don't rewrite existing code just to make it "look nicer"
|
||||||
Do not include changes which aren't strictly necessary. This makes it harder to review a PR, because the code diff becomes larger and harder to review.
|
- Don't change PhpDocs to native types in code you didn't write, unless that's the objective of the PR
|
||||||
This means:
|
- **Test code changes, and tell us what tests have been done.**
|
||||||
- don't reformat or rearrange existing code
|
- Where possible, PHPUnit tests should be written for new or changed code. If that's not possible (e.g. for in-game functionality), the code must be tested manually and details of the tests done must be provided.
|
||||||
- don't change things that aren't related to the PR's objective
|
- **Simply saying "Tested" is not acceptable** and could lead to your PR being declined.
|
||||||
- don't rewrite existing code just to make it "look nicer"
|
- **Code, comments and documentation must be written in American English.** English is the shared languages of all current maintainers.
|
||||||
- don't change PhpDocs to native types in code you didn't write
|
- **Code must be in the PocketMine-MP style.**
|
||||||
|
- It's your responsibility to ensure your code matches the formatting and styling of the rest of the code.
|
||||||
#### Tests must be provided
|
- If you use PhpStorm, a `Project` code style is provided, which you can use to automatically format new code.
|
||||||
Where possible, PHPUnit tests should be written for new or changed code.
|
- You can also use [`php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to format your code.
|
||||||
If that's not possible (e.g. for in-game functionality), the code must be tested manually and details of the tests done must be provided.
|
- **Use `final` and `private` wherever possible**.
|
||||||
**Simply saying "Tested" is not acceptable** and will lead to your PR being declined.
|
- Changing from `private` to `protected` or `final` to non-`final` doesn't break backwards compatibility, but the opposite does.
|
||||||
|
- `private` and `final` also enable certain performance optimizations which are otherwise not possible.
|
||||||
#### Comments and documentation must be written in American English
|
- `private` members can be freely changed, added and removed in the future, so it's ideal for internal functions. Abusing `protected` makes internal improvements inconvenient.
|
||||||
English is the shared languages of all current maintainers.
|
- "Let's leave it protected/public in case someone needs it for ... idk what" is **not a valid reason to expose things**. If there isn't a clear reason for something to be accessible from the outside, don't expose it.
|
||||||
|
- **This is a lesson learned through years of experience.** You may not like it, but it's for the best.
|
||||||
#### Code must be in the PocketMine-MP style
|
- **Immutable things are almost always preferred.**
|
||||||
It's your responsibility to ensure your code matches the formatting and styling of the rest of the code.
|
- Do not add unnecessary setters or public writable properties to classes. As above, "Let's leave it in case someone needs it" is **not a valid reason to expose things**.
|
||||||
If you use PhpStorm, a `Project` code style is provided, which you can use to automatically format new code.
|
- Mutable classes and properties are unpredictable, since code has no way to know if the object it's working with might be randomly modified by another part of the code. This makes it harder to maintain code and debug issues.
|
||||||
You can also use [`php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to format your code.
|
- Most classes exist only to hold some data. These are called "data transfer objects" (DTOs). These types of classes should pretty much always be immutable.
|
||||||
|
- Make use of `final`, `private` and `readonly` modifiers.
|
||||||
|
|
||||||
### Recommendations
|
### Recommendations
|
||||||
|
- **Be patient.** Reviewing pull requests takes a lot of time and energy, and maintainers are often unavailable or busy. Your PR might not receive attention for a while.
|
||||||
|
- Remember, PRs with small diffs are much easier to review. Small PRs are generally reviewed and merged much faster than large ones.
|
||||||
|
- **Start small.** Try fixing minor bugs or doing something isolated (e.g. adding a new block or item) before attempting larger changes.
|
||||||
|
- This helps you get familiar with the codebase, the contribution process, and the expectations of maintainers.
|
||||||
|
- Check out the [issues page]() for something that you could tackle without too much effort.
|
||||||
|
- **Do not copy-paste other people's code**. Many PRs involve discussion about the changes, and changes are often requested by reviewers. If you don't understand the code you're copy-pasting, your PR is likely to fail.
|
||||||
- **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE.
|
- **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE.
|
||||||
- **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode.
|
- **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode.
|
||||||
|
- **Do not make large pull requests without an RFC.**
|
||||||
|
- Large changes should be discussed beforehand using the [RFC / Change Proposal](#rfcs--change-proposals) process.
|
||||||
|
- Large changes are much harder to review, and are more likely to be declined if maintainers don't have a good idea what you're trying to do in advance.
|
||||||
- **Create a new branch on your fork for each pull request.** This allows you to use the same fork to make multiple pull requests at the same time.
|
- **Create a new branch on your fork for each pull request.** This allows you to use the same fork to make multiple pull requests at the same time.
|
||||||
|
- **Make your PR diff as small as possible.** Smaller PRs are **much more likely** to be accepted, as they are easier to review.
|
||||||
|
- Avoid moving code around in files if possible.
|
||||||
|
- Don't make random CS changes. This makes the diff noisier and harder to review.
|
||||||
- **Use descriptive commit titles.** You can see an example [here](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
- **Use descriptive commit titles.** You can see an example [here](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||||
- **Do not include multiple unrelated changes in one commit.** An atomic style for commits is preferred - this means that changes included in a commit should be part of a single distinct change set. See [this link](https://www.freshconsulting.com/atomic-commits/) for more information on atomic commits. See the [documentation on `git add`](https://git-scm.com/docs/git-add) for information on how to isolate local changes for committing.
|
- **Split unrelated changes into multiple commits.**
|
||||||
- **Your pull request will be checked and discussed in due time.** Since the team is scattered all around the world, your PR may not receive any attention for some time.
|
- An atomic style for commits is preferred - this means that changes included in a commit should be part of a single distinct change set.
|
||||||
- **Do not make large pull requests without an RFC.** Large changes should be discussed beforehand using the [RFC / Change Proposal](#rfcs--change-proposals) process. Large changes are much harder to review and are more likely to be declined if maintainers don't have a good idea what you're trying to do in advance.
|
- If you need to use "and" or "multiple changes" in your commit message, the commit probably needs to be split up. There are exceptions, but this is a good rule of thumb.
|
||||||
- **Do not copy-paste code**. There are potential license issues implicit with copy-pasting, and copy-paste usually indicates a lack of understanding of the actual code. Copy-pasted code is obvious a mile off and **any PR like this is likely to be closed**. If you want to use somebody else's code from a Git repository, **use [GIT's cherry-pick feature](https://git-scm.com/docs/git-cherry-pick)** to cherry-pick the commit.
|
- See [this link](https://www.freshconsulting.com/atomic-commits/) for more information on atomic commits.
|
||||||
|
- See the [documentation on `git add -i` or `git add -p`](https://git-scm.com/docs/git-add) for information on how to split up local changes for committing.
|
||||||
|
|
||||||
|
|
||||||
**Thanks for contributing to PocketMine-MP!**
|
**Thanks for contributing to PocketMine-MP!**
|
||||||
|
@ -1,265 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* ____ _ _ __ __ _ __ __ ____
|
|
||||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
|
||||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
|
||||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
|
||||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* @author PocketMine Team
|
|
||||||
* @link http://www.pocketmine.net/
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace pocketmine\build\generate_runtime_enum_serializers;
|
|
||||||
|
|
||||||
use pocketmine\block\utils\BellAttachmentType;
|
|
||||||
use pocketmine\block\utils\CopperOxidation;
|
|
||||||
use pocketmine\block\utils\CoralType;
|
|
||||||
use pocketmine\block\utils\DirtType;
|
|
||||||
use pocketmine\block\utils\DripleafState;
|
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
use pocketmine\block\utils\FroglightType;
|
|
||||||
use pocketmine\block\utils\LeverFacing;
|
|
||||||
use pocketmine\block\utils\MobHeadType;
|
|
||||||
use pocketmine\block\utils\MushroomBlockType;
|
|
||||||
use pocketmine\block\utils\SlabType;
|
|
||||||
use pocketmine\item\MedicineType;
|
|
||||||
use pocketmine\item\PotionType;
|
|
||||||
use pocketmine\item\SuspiciousStewType;
|
|
||||||
use function array_key_first;
|
|
||||||
use function array_keys;
|
|
||||||
use function array_map;
|
|
||||||
use function ceil;
|
|
||||||
use function count;
|
|
||||||
use function dirname;
|
|
||||||
use function file_put_contents;
|
|
||||||
use function implode;
|
|
||||||
use function ksort;
|
|
||||||
use function lcfirst;
|
|
||||||
use function log;
|
|
||||||
use function ob_get_clean;
|
|
||||||
use function ob_start;
|
|
||||||
use const SORT_STRING;
|
|
||||||
|
|
||||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[] $memberNames
|
|
||||||
* @phpstan-param list<string> $memberNames
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
* @phpstan-return list<string>
|
|
||||||
*/
|
|
||||||
function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
|
|
||||||
$bits = getBitsRequired($memberNames);
|
|
||||||
$lines = [];
|
|
||||||
|
|
||||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
|
||||||
$lines[] = "\t\$this->writeInt($bits, match(\$value){";
|
|
||||||
|
|
||||||
foreach($memberNames as $key => $memberName){
|
|
||||||
$lines[] = "\t\t$memberName => $key,";
|
|
||||||
}
|
|
||||||
$lines[] = "\t\tdefault => throw new \pocketmine\utils\AssumptionFailedError(\"All $virtualTypeName cases should be covered\")";
|
|
||||||
$lines[] = "\t});";
|
|
||||||
$lines[] = "}";
|
|
||||||
|
|
||||||
return $lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[] $memberNames
|
|
||||||
* @phpstan-param list<string> $memberNames
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
* @phpstan-return list<string>
|
|
||||||
*/
|
|
||||||
function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
|
|
||||||
$bits = getBitsRequired($memberNames);
|
|
||||||
$lines = [];
|
|
||||||
|
|
||||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
|
||||||
$lines[] = "\t\$value = match(\$this->readInt($bits)){";
|
|
||||||
|
|
||||||
foreach($memberNames as $key => $memberName){
|
|
||||||
$lines[] = "\t\t$key => $memberName,";
|
|
||||||
}
|
|
||||||
$lines[] = "\t\tdefault => throw new InvalidSerializedRuntimeDataException(\"Invalid serialized value for $virtualTypeName\")";
|
|
||||||
$lines[] = "\t};";
|
|
||||||
$lines[] = "}";
|
|
||||||
|
|
||||||
return $lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildInterfaceFunc(string $nativeTypeName, string $functionName) : string{
|
|
||||||
return "public function $functionName(\\$nativeTypeName &\$value) : void;";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[] $memberNames
|
|
||||||
* @phpstan-param list<string> $memberNames
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
* @phpstan-return list<string>
|
|
||||||
*/
|
|
||||||
function buildSizeCalculationFunc(string $nativeTypeName, string $functionName, array $memberNames) : array{
|
|
||||||
$lines = [];
|
|
||||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
|
||||||
$lines[] = "\t\$this->addBits(" . getBitsRequired($memberNames) . ");";
|
|
||||||
$lines[] = "}";
|
|
||||||
|
|
||||||
return $lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed[] $members
|
|
||||||
*/
|
|
||||||
function getBitsRequired(array $members) : int{
|
|
||||||
return (int) ceil(log(count($members), 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param object[] $members
|
|
||||||
* @phpstan-param array<string, object> $members
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
* @phpstan-return list<string>
|
|
||||||
*/
|
|
||||||
function stringifyEnumMembers(array $members, string $enumClass) : array{
|
|
||||||
ksort($members, SORT_STRING);
|
|
||||||
return array_map(fn(string $enumCaseName) => "\\$enumClass::$enumCaseName()", array_keys($members));
|
|
||||||
}
|
|
||||||
|
|
||||||
$enumsUsed = [
|
|
||||||
BellAttachmentType::getAll(),
|
|
||||||
CopperOxidation::getAll(),
|
|
||||||
CoralType::getAll(),
|
|
||||||
DirtType::getAll(),
|
|
||||||
DripleafState::getAll(),
|
|
||||||
DyeColor::getAll(),
|
|
||||||
FroglightType::getAll(),
|
|
||||||
LeverFacing::getAll(),
|
|
||||||
MedicineType::getAll(),
|
|
||||||
MushroomBlockType::getAll(),
|
|
||||||
MobHeadType::getAll(),
|
|
||||||
SlabType::getAll(),
|
|
||||||
SuspiciousStewType::getAll(),
|
|
||||||
PotionType::getAll()
|
|
||||||
];
|
|
||||||
|
|
||||||
$readerFuncs = [
|
|
||||||
"" => [
|
|
||||||
"abstract protected function readInt(int \$bits) : int;"
|
|
||||||
]
|
|
||||||
];
|
|
||||||
$writerFuncs = [
|
|
||||||
"" => [
|
|
||||||
"abstract protected function writeInt(int \$bits, int \$value) : void;"
|
|
||||||
]
|
|
||||||
];
|
|
||||||
$interfaceFuncs = [];
|
|
||||||
$sizeCalculationFuncs = [
|
|
||||||
"" => [
|
|
||||||
"abstract protected function addBits(int \$bits) : void;"
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach($enumsUsed as $enumMembers){
|
|
||||||
if(count($enumMembers) === 0){
|
|
||||||
throw new \InvalidArgumentException("Enum members cannot be empty");
|
|
||||||
}
|
|
||||||
$reflect = new \ReflectionClass($enumMembers[array_key_first($enumMembers)]);
|
|
||||||
$virtualTypeName = $reflect->getShortName();
|
|
||||||
$nativeTypeName = $reflect->getName();
|
|
||||||
$functionName = lcfirst($virtualTypeName);
|
|
||||||
|
|
||||||
$stringifiedMembers = stringifyEnumMembers($enumMembers, $nativeTypeName);
|
|
||||||
$writerFuncs[$functionName] = buildWriterFunc(
|
|
||||||
$virtualTypeName,
|
|
||||||
$nativeTypeName,
|
|
||||||
$stringifiedMembers,
|
|
||||||
$functionName
|
|
||||||
);
|
|
||||||
$readerFuncs[$functionName] = buildReaderFunc(
|
|
||||||
$virtualTypeName,
|
|
||||||
$nativeTypeName,
|
|
||||||
$stringifiedMembers,
|
|
||||||
$functionName
|
|
||||||
);
|
|
||||||
$interfaceFuncs[$functionName] = [buildInterfaceFunc(
|
|
||||||
$nativeTypeName,
|
|
||||||
$functionName
|
|
||||||
)];
|
|
||||||
$sizeCalculationFuncs[$functionName] = buildSizeCalculationFunc(
|
|
||||||
$nativeTypeName,
|
|
||||||
$functionName,
|
|
||||||
$stringifiedMembers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[][] $functions
|
|
||||||
* @phpstan-param array<string, list<string>> $functions
|
|
||||||
*/
|
|
||||||
function printFunctions(array $functions, string $className, string $classType) : void{
|
|
||||||
ksort($functions, SORT_STRING);
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
echo <<<'HEADER'
|
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* ____ _ _ __ __ _ __ __ ____
|
|
||||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
|
||||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
|
||||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
|
||||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* @author PocketMine Team
|
|
||||||
* @link http://www.pocketmine.net/
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace pocketmine\data\runtime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is auto-generated. Do not edit it manually.
|
|
||||||
* @see build/generate-runtime-enum-serializers.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
HEADER;
|
|
||||||
|
|
||||||
echo "$classType $className{\n\n";
|
|
||||||
echo implode("\n\n", array_map(fn(array $functionLines) => "\t" . implode("\n\t", $functionLines), $functions));
|
|
||||||
echo "\n\n}\n";
|
|
||||||
|
|
||||||
file_put_contents(dirname(__DIR__) . '/src/data/runtime/' . $className . '.php', ob_get_clean());
|
|
||||||
}
|
|
||||||
|
|
||||||
printFunctions($writerFuncs, "RuntimeEnumSerializerTrait", "trait");
|
|
||||||
printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait", "trait");
|
|
||||||
printFunctions($interfaceFuncs, "RuntimeEnumDescriber", "interface");
|
|
||||||
printFunctions($sizeCalculationFuncs, "RuntimeEnumSizeCalculatorTrait", "trait");
|
|
||||||
|
|
||||||
echo "Done. Don't forget to run CS fixup after generating code.\n";
|
|
Submodule build/php updated: 3331f8c0d5...3c3c483baa
17
changelogs/4.24.md
Normal file
17
changelogs/4.24.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 4.24.0
|
||||||
|
Released 20th September 2023.
|
||||||
|
|
||||||
|
**For Minecraft: Bedrock Edition 1.20.30**
|
||||||
|
|
||||||
|
This is a support release for Minecraft: Bedrock Edition 1.20.30.
|
||||||
|
|
||||||
|
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
|
||||||
|
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||||
|
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.20.30.
|
||||||
|
- Removed support for older versions.
|
||||||
|
- Updated 4.x obsoletion message.
|
16
changelogs/4.25.md
Normal file
16
changelogs/4.25.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 4.25.0
|
||||||
|
Released 26th October 2023.
|
||||||
|
|
||||||
|
**For Minecraft: Bedrock Edition 1.20.40**
|
||||||
|
|
||||||
|
This is a support release for Minecraft: Bedrock Edition 1.20.40.
|
||||||
|
|
||||||
|
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
|
||||||
|
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||||
|
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.20.40.
|
||||||
|
- Removed support for older versions.
|
34
changelogs/5.6.md
Normal file
34
changelogs/5.6.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# 5.6.0
|
||||||
|
Released 20th September 2023.
|
||||||
|
|
||||||
|
**For Minecraft: Bedrock Edition 1.20.30**
|
||||||
|
|
||||||
|
This is a support release for Minecraft: Bedrock Edition 1.20.30.
|
||||||
|
|
||||||
|
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||||
|
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||||
|
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.20.30.
|
||||||
|
- Removed support for older versions.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed support conditions for hanging roots, cave vines and dead bushes.
|
||||||
|
- Fixed connection conditions for fences, glass panes, iron bars, and walls.
|
||||||
|
|
||||||
|
# 5.6.1
|
||||||
|
Released 20th October 2023.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Improved performance of cactus growth by disabling neighbour updates when only the age property was updated. While this isn't a perfect solution, it provides significant performance gains for servers with large cactus farms.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed `tools/generate-bedrock-data-from-packets.php` incorrectly interpreting network meta as blockstates in some cases (broken crafting recipes).
|
||||||
|
- Fixed crafting recipes involving beds, skulls and some other items not working correctly (incorrectly interpreted data).
|
||||||
|
- Fixed crashes when flower pot or cauldron blockentities exist in places where they shouldn't (leftovers from upgraded PM3 worlds).
|
||||||
|
- Fixed `Entity->broadcastSound()` not firing `WorldSoundEvent` (bypassing internal sound system).
|
||||||
|
- Fixed wooden signs, buttons and doors not being able to be used as furnace fuel.
|
||||||
|
- Fixed bone meal and tools only working when used on the top side of dirt and grass. Bone meal now works from any side, and tools work on any side except the bottom.
|
27
changelogs/5.7.md
Normal file
27
changelogs/5.7.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# 5.7.0
|
||||||
|
Released 26th October 2023.
|
||||||
|
|
||||||
|
**For Minecraft: Bedrock Edition 1.20.40**
|
||||||
|
|
||||||
|
This is a support release for Minecraft: Bedrock Edition 1.20.40.
|
||||||
|
|
||||||
|
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||||
|
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||||
|
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Added support for Minecraft: Bedrock Edition 1.20.40.
|
||||||
|
- Removed support for older versions.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed `cartography_table`, `smithing_table`, `stripped_cherry_log` and `stripped_cherry_wood` not working in `StringToItemParser`.
|
||||||
|
- Fixed `Promise<null>::onCompletion()` always calling the reject handler if the promise was already completed.
|
||||||
|
|
||||||
|
# 5.7.1
|
||||||
|
Released 1st November 2023.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed non-reentrant-safe code in `PermissionManager` and various other subscriber subsystems.
|
||||||
|
- These issues caused server crashes when deleting a subscriber indirectly triggered the deletion of other subscribers (e.g. due to the GC activating in `unset()`).
|
138
changelogs/5.8.md
Normal file
138
changelogs/5.8.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# 5.8.0
|
||||||
|
Released 1st November 2023.
|
||||||
|
|
||||||
|
**Borked release, forgot to merge branches.**
|
||||||
|
|
||||||
|
# 5.8.1
|
||||||
|
Released 1st November 2023.
|
||||||
|
|
||||||
|
**For Minecraft: Bedrock Edition 1.20.40**
|
||||||
|
|
||||||
|
This is a minor feature release, including new gameplay features, various performance improvements to internal `World` and `Block` systems, and changes to the API.
|
||||||
|
|
||||||
|
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||||
|
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||||
|
|
||||||
|
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||||
|
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||||
|
|
||||||
|
## General
|
||||||
|
- Neighbour block updates now have a separate timer for timings. Previously, these were counted under `Scheduled Block Updates`, which was misleading.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- `LightUpdate` now avoids attempting to propagate back in the same direction the light came from. This produces a small performance improvement of around 6% in light propagation.
|
||||||
|
- Improved worst-case (non-cached) performance of `World::getCollisionBlocks()` (and its successor `World::getBlockCollisionBlocks()`).
|
||||||
|
- While 5.5.0 introduced caching at the `World` level for AABBs, the cache was rarely useful due to entity and player movement being too unpredictable. This meant that most users saw a performance degradation with lots of moving entities, except in specific situations.
|
||||||
|
- Performance for fetching non-cached AABBs for a cell is now improved by 2x. Overall performance benefit to a server depends on the number of entities and players.
|
||||||
|
- Added cache for hydrated farmland blocks to remember the last known location of nearby water.
|
||||||
|
- If nearby water sources are not changed, this cache allows hydrated farmland to completely avoid checking up to 161 nearby blocks for water after the first check.
|
||||||
|
- Tests with large wheat farms showed a 25% performance improvement in overall server performance compared to previous 5.x versions.
|
||||||
|
- Migrated various internal enums to native PHP 8.1 enums. Bypassing magic `__callStatic()` accessors improved performance in many areas, although it's hard to quantify the exact benefit.
|
||||||
|
- Made use of `Facing::OFFSET` constant in various places to avoid unnecessary `Vector3` and `Position` object allocations. Many pathways benefit from this, including neighbour block updates (due to faster `Block::getSide()` and less useless objects).
|
||||||
|
- Avoided clearing block AABB caches except when strictly necessary. Previously, the cache was wiped every time blocks were read from the world, making them mostly useless.
|
||||||
|
- Avoided random updates on blocks which have reached their final state, such as fully-grown crops. This produces a minimal performance improvement.
|
||||||
|
- Removed useless checks in some `World` hot paths.
|
||||||
|
|
||||||
|
## API
|
||||||
|
### General
|
||||||
|
- All enums have been migrated to native PHP 8.1 enums.
|
||||||
|
- For now, the old APIs and accessors are still usable (via `LegacyEnumShimTrait`), but these will be removed in the next major release.
|
||||||
|
- `EnumTrait` has been deprecated, and will be removed in the next major release.
|
||||||
|
- Migration for most plugin developers will simply involve deleting `()` from the end of enum case usages, which is a trivial change and also improves performance.
|
||||||
|
- Plugin usages of `EnumTrait` are encouraged to move to native enums, optionally using `LegacyEnumShimTrait` to provide backwards compatibility.
|
||||||
|
- See [this code](https://github.com/pmmp/PocketMine-MP/blob/9832fe899f13a8ea47cc9d73de7088f7775a12f5/src/block/utils/DyeColor.php#L85-L107) for an example of how to associate properties with enum cases (since native enums don't support this directly).
|
||||||
|
- Thanks to improvements in `RuntimeDataDescriber`, any native enum can now be used as a custom block's state property.
|
||||||
|
|
||||||
|
### `pocketmine\block`
|
||||||
|
- The following classes have been added:
|
||||||
|
- `utils\AgeableTrait` - used by blocks which have an age property, such as crops
|
||||||
|
- `utils\StaticSupportTrait` - used by blocks which have the same support requirements regardless of their state, such as crops
|
||||||
|
|
||||||
|
### `pocketmine\data\runtime`
|
||||||
|
- The following API methods have been added:
|
||||||
|
- `public RuntimeDataDescriber->boundedIntAuto(int $min, int $max, int &$value) : void` - similar to `boundedInt()`, but automatically calculates the needed number of bits based on the given min/max
|
||||||
|
- `public RuntimeDataDescriber->enum(T extends \UnitEnum &$case) : void` - describes any native PHP 8.1 enum case
|
||||||
|
- `public RuntimeDataDescriber->enumSet(array<int, T extends \UnitEnum> &$set, array<int, T extends \UnitEnum> $allCases) : void` - describes a set of enum cases (similar to bitflags)
|
||||||
|
- The following API methods have been deprecated:
|
||||||
|
- `RuntimeDataDescriber->bellAttachmentType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->boundedInt()` - use `boundedIntAuto()` instead
|
||||||
|
- `RuntimeDataDescriber->brewingStandSlots()` - use `enumSet()` instead
|
||||||
|
- `RuntimeDataDescriber->copperOxidation()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->coralType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->dirtType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->dripleafState()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->dyeColor()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->froglightType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->leverFacing()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->medicineType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->mobHeadType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->mushroomBlockType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->potionType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->slabType()` - use `enum()` instead
|
||||||
|
- `RuntimeDataDescriber->suspiciousStewType()` - use `enum()` instead
|
||||||
|
|
||||||
|
### `pocketmine\player`
|
||||||
|
- `TitleID` is now included in `PlayerInfo` metadata for plugin consumption.
|
||||||
|
|
||||||
|
### `pocketmine\world`
|
||||||
|
- The following API methods have been added:
|
||||||
|
- `public World->getBlockCollisionBoxes(AxisAlignedBB $bb) : list<AxisAlignedBB>` - similar to `getCollisionBoxes` but exclusively for blocks, avoiding the need for conditionally useless parameters
|
||||||
|
- The following API methods have been deprecated:
|
||||||
|
- `World->getCollisionBoxes()` - use `getBlockCollisionBoxes()` instead (alongside `getCollidingEntities()` if entity collision boxes are also required)
|
||||||
|
|
||||||
|
### `pocketmine\utils`
|
||||||
|
- The following classes have been deprecated:
|
||||||
|
- `EnumTrait` - use native PHP 8.1 enums instead
|
||||||
|
- The following classes have been added:
|
||||||
|
- `LegacyEnumShimTrait` - can be `use`d by native PHP 8.1 enums to provide the same API as `EnumTrait`
|
||||||
|
|
||||||
|
## Gameplay
|
||||||
|
### Blocks
|
||||||
|
- Implemented the following blocks:
|
||||||
|
- Amethyst
|
||||||
|
- Amethyst Cluster
|
||||||
|
- Chiseled Bookshelf
|
||||||
|
- Crimson Roots
|
||||||
|
- Pitcher Crop
|
||||||
|
- Pitcher Plant
|
||||||
|
- Torchflower
|
||||||
|
- Torchflower Crop
|
||||||
|
- Warped Roots
|
||||||
|
|
||||||
|
### Items
|
||||||
|
- Implemented the following items:
|
||||||
|
- Pitcher Pod
|
||||||
|
- Torchflower Seeds
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- `Farmland` block now has an extra property (`waterPositionIndex`) stored in its blockstate ID to track the position of nearby water. This property is not saved to disk, and is only used for caching.
|
||||||
|
- The format of internal blockstate ID has been updated.
|
||||||
|
- The lower `11` bits are now reserved for state data (previously `8` bits). This increase facilitates the new cache for `Farmland` blocks.
|
||||||
|
- The state data bits are now XOR'd with the `xxh3` of the block's type ID, instead of being directly XOR'd with the type ID.
|
||||||
|
- This XOR improves the distribution of the lower bits of the blockstate ID, which is important for hashtable indexing to minimize collisions.
|
||||||
|
- Previously, the lower bits were XOR'd with the type ID directly, but the effectiveness of this reduced as more state data bits were added.
|
||||||
|
- `xxh3` produces consistently good results for this purpose regardless of the number of state data bits allocated.
|
||||||
|
- Hash collisions with blockstate IDs are reduced by 50% with this change, which is a happy side effect.
|
||||||
|
- This is backwards-incompatible, so anyone saving internal blockstate IDs on disk or in a DB will be burned by this change (though they shouldn't have been doing that anyway).
|
||||||
|
- Removed code generation step for `RuntimeDataDescriber` enum serialization. All described enums now use PHP 8.1 native enums, which can be described without codegen using `RuntimeDataDescriber->enum()`.
|
||||||
|
- Added `DeprecatedLegacyEnumAccessRule` custom PHPStan rule to flag legacy `EnumTrait` case accessors.
|
||||||
|
- Cleaned up remaining hardcoded `Config` keys in `SetupWizard`. These usages now use auto-generated constants like the rest of the codebase.
|
||||||
|
|
||||||
|
# 5.8.2
|
||||||
|
Released 9th November 2023.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Improved performance of small packet zero-compression (unintended use of slow zlib compressor instead of fast libdeflate one).
|
||||||
|
- This affected the majority of outbound packets, as most packets are below the 256-byte threshold for compression.
|
||||||
|
- This faster method is over 20x faster than the old method, producing noticeable performance gains for large servers.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fixed melons and pumpkins not growing.
|
||||||
|
- Fixed melon and pumpkin stems not attaching to the grown melon/pumpkin.
|
||||||
|
- Fixed iron and gold ores not being affected by the Fortune enchantment.
|
||||||
|
- Fixed ancient debris burning in lava.
|
||||||
|
- Fixed sign (front) text loading from vanilla world saves (back text is not yet supported).
|
||||||
|
|
||||||
|
## Internals
|
||||||
|
- Removed bogus optimization from `tools/generate-blockstate-upgrade-schema.php` that could cause incorrect `remappedStates` generation when some of the states stayed under the old ID.
|
||||||
|
- Fixed possible crash in `BlockStateUpgrader` name flattening rule handling with invalid blockstate NBT data.
|
@ -33,10 +33,10 @@
|
|||||||
"composer-runtime-api": "^2.0",
|
"composer-runtime-api": "^2.0",
|
||||||
"adhocore/json-comment": "~1.2.0",
|
"adhocore/json-comment": "~1.2.0",
|
||||||
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
|
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
|
||||||
"pocketmine/bedrock-block-upgrade-schema": "~3.1.0+bedrock-1.20.10",
|
"pocketmine/bedrock-block-upgrade-schema": "~3.3.0+bedrock-1.20.40",
|
||||||
"pocketmine/bedrock-data": "~2.4.0+bedrock-1.20.10",
|
"pocketmine/bedrock-data": "~2.6.0+bedrock-1.20.40",
|
||||||
"pocketmine/bedrock-item-upgrade-schema": "~1.4.0+bedrock-1.20.10",
|
"pocketmine/bedrock-item-upgrade-schema": "~1.5.0+bedrock-1.20.30",
|
||||||
"pocketmine/bedrock-protocol": "~23.0.2+bedrock-1.20.10",
|
"pocketmine/bedrock-protocol": "~25.0.0+bedrock-1.20.40",
|
||||||
"pocketmine/binaryutils": "^0.2.1",
|
"pocketmine/binaryutils": "^0.2.1",
|
||||||
"pocketmine/callback-validator": "^1.0.2",
|
"pocketmine/callback-validator": "^1.0.2",
|
||||||
"pocketmine/color": "^0.3.0",
|
"pocketmine/color": "^0.3.0",
|
||||||
@ -52,10 +52,10 @@
|
|||||||
"symfony/filesystem": "~6.3.0"
|
"symfony/filesystem": "~6.3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "1.10.16",
|
"phpstan/phpstan": "1.10.41",
|
||||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||||
"phpunit/phpunit": "^10.1"
|
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
147
composer.lock
generated
147
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "9237955fd97ba7c1697d80314fa9ad6f",
|
"content-hash": "5c19f4766fd04be0cbd38d9f4681864e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/json-comment",
|
"name": "adhocore/json-comment",
|
||||||
@ -122,16 +122,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||||
"version": "3.1.0",
|
"version": "3.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||||
"reference": "6d4ae416043337946a22fc31e8065ca2c21f472d"
|
"reference": "ee46b9367af262bbddd9f122d4d5b5b495b892e7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/6d4ae416043337946a22fc31e8065ca2c21f472d",
|
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/ee46b9367af262bbddd9f122d4d5b5b495b892e7",
|
||||||
"reference": "6d4ae416043337946a22fc31e8065ca2c21f472d",
|
"reference": "ee46b9367af262bbddd9f122d4d5b5b495b892e7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -142,22 +142,22 @@
|
|||||||
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.1.0"
|
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.3.0"
|
||||||
},
|
},
|
||||||
"time": "2023-07-12T12:05:36+00:00"
|
"time": "2023-10-16T16:11:02+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/bedrock-data",
|
"name": "pocketmine/bedrock-data",
|
||||||
"version": "2.4.0+bedrock-1.20.10",
|
"version": "2.6.0+bedrock-1.20.40",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/BedrockData.git",
|
"url": "https://github.com/pmmp/BedrockData.git",
|
||||||
"reference": "f98bd1cae46d2920058acf3b23c0bedeac79f4ab"
|
"reference": "37e780d28b470230bda3579b04cb50d406e3fbe6"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/f98bd1cae46d2920058acf3b23c0bedeac79f4ab",
|
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/37e780d28b470230bda3579b04cb50d406e3fbe6",
|
||||||
"reference": "f98bd1cae46d2920058acf3b23c0bedeac79f4ab",
|
"reference": "37e780d28b470230bda3579b04cb50d406e3fbe6",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -168,22 +168,22 @@
|
|||||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.10"
|
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.40"
|
||||||
},
|
},
|
||||||
"time": "2023-07-12T11:51:54+00:00"
|
"time": "2023-10-26T10:39:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||||
"version": "1.4.0",
|
"version": "1.5.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||||
"reference": "60d199afe5e371fd189b21d685ec1fed6ba54230"
|
"reference": "3edc9ebbad9a4f2d9c8f53b3a5ba44d4a792ad93"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/60d199afe5e371fd189b21d685ec1fed6ba54230",
|
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/3edc9ebbad9a4f2d9c8f53b3a5ba44d4a792ad93",
|
||||||
"reference": "60d199afe5e371fd189b21d685ec1fed6ba54230",
|
"reference": "3edc9ebbad9a4f2d9c8f53b3a5ba44d4a792ad93",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -194,32 +194,32 @@
|
|||||||
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
||||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.4.0"
|
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.5.0"
|
||||||
},
|
},
|
||||||
"time": "2023-07-12T12:08:37+00:00"
|
"time": "2023-09-01T19:58:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/bedrock-protocol",
|
"name": "pocketmine/bedrock-protocol",
|
||||||
"version": "23.0.4+bedrock-1.20.10",
|
"version": "25.0.0+bedrock-1.20.40",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||||
"reference": "ae0d8f4d49506674b7ff622f7c81ce241dc49adb"
|
"reference": "69c36c96f6835e93fc278071aa2bb9829abe5cf8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/ae0d8f4d49506674b7ff622f7c81ce241dc49adb",
|
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/69c36c96f6835e93fc278071aa2bb9829abe5cf8",
|
||||||
"reference": "ae0d8f4d49506674b7ff622f7c81ce241dc49adb",
|
"reference": "69c36c96f6835e93fc278071aa2bb9829abe5cf8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"netresearch/jsonmapper": "^4.0",
|
"netresearch/jsonmapper": "^4.0",
|
||||||
"php": "^8.0",
|
"php": "^8.1",
|
||||||
"pocketmine/binaryutils": "^0.2.0",
|
"pocketmine/binaryutils": "^0.2.0",
|
||||||
"pocketmine/color": "^0.2.0 || ^0.3.0",
|
"pocketmine/color": "^0.2.0 || ^0.3.0",
|
||||||
"pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0",
|
"pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0",
|
||||||
"pocketmine/nbt": "^0.3.0 || ^1.0.0",
|
"pocketmine/nbt": "^1.0.0",
|
||||||
"ramsey/uuid": "^4.1"
|
"ramsey/uuid": "^4.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
@ -241,9 +241,9 @@
|
|||||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.4+bedrock-1.20.10"
|
"source": "https://github.com/pmmp/BedrockProtocol/tree/25.0.0+bedrock-1.20.40"
|
||||||
},
|
},
|
||||||
"time": "2023-09-06T07:36:44+00:00"
|
"time": "2023-10-26T11:03:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/binaryutils",
|
"name": "pocketmine/binaryutils",
|
||||||
@ -1378,16 +1378,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "1.10.16",
|
"version": "1.10.41",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "352bdbb960bb523e3d71b834862589f910921c23"
|
"reference": "c6174523c2a69231df55bdc65b61655e72876d76"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/352bdbb960bb523e3d71b834862589f910921c23",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6174523c2a69231df55bdc65b61655e72876d76",
|
||||||
"reference": "352bdbb960bb523e3d71b834862589f910921c23",
|
"reference": "c6174523c2a69231df55bdc65b61655e72876d76",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1436,20 +1436,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-06-05T08:21:46+00:00"
|
"time": "2023-11-05T12:57:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-phpunit",
|
"name": "phpstan/phpstan-phpunit",
|
||||||
"version": "1.3.14",
|
"version": "1.3.15",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||||
"reference": "614acc10c522e319639bf38b0698a4a566665f04"
|
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/614acc10c522e319639bf38b0698a4a566665f04",
|
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
|
||||||
"reference": "614acc10c522e319639bf38b0698a4a566665f04",
|
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1486,27 +1486,27 @@
|
|||||||
"description": "PHPUnit extensions and rules for PHPStan",
|
"description": "PHPUnit extensions and rules for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.14"
|
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15"
|
||||||
},
|
},
|
||||||
"time": "2023-08-25T09:46:39+00:00"
|
"time": "2023-10-09T18:58:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-strict-rules",
|
"name": "phpstan/phpstan-strict-rules",
|
||||||
"version": "1.5.1",
|
"version": "1.5.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||||
"reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6"
|
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6",
|
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542",
|
||||||
"reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6",
|
"reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"phpstan/phpstan": "^1.10"
|
"phpstan/phpstan": "^1.10.34"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"nikic/php-parser": "^4.13.0",
|
"nikic/php-parser": "^4.13.0",
|
||||||
@ -1535,22 +1535,22 @@
|
|||||||
"description": "Extra strict and opinionated rules for PHPStan",
|
"description": "Extra strict and opinionated rules for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1"
|
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2"
|
||||||
},
|
},
|
||||||
"time": "2023-03-29T14:47:40+00:00"
|
"time": "2023-10-30T14:35:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "10.1.4",
|
"version": "10.1.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
"reference": "cd59bb34756a16ca8253ce9b2909039c227fff71"
|
"reference": "355324ca4980b8916c18b9db29f3ef484078f26e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cd59bb34756a16ca8253ce9b2909039c227fff71",
|
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e",
|
||||||
"reference": "cd59bb34756a16ca8253ce9b2909039c227fff71",
|
"reference": "355324ca4980b8916c18b9db29f3ef484078f26e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1607,7 +1607,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.4"
|
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1615,7 +1615,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-08-31T14:04:38+00:00"
|
"time": "2023-10-04T15:34:17+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-file-iterator",
|
"name": "phpunit/php-file-iterator",
|
||||||
@ -1862,16 +1862,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "10.3.3",
|
"version": "10.3.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4"
|
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503",
|
||||||
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
|
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1885,7 +1885,7 @@
|
|||||||
"phar-io/manifest": "^2.0.3",
|
"phar-io/manifest": "^2.0.3",
|
||||||
"phar-io/version": "^3.0.2",
|
"phar-io/version": "^3.0.2",
|
||||||
"php": ">=8.1",
|
"php": ">=8.1",
|
||||||
"phpunit/php-code-coverage": "^10.1.1",
|
"phpunit/php-code-coverage": "^10.1.5",
|
||||||
"phpunit/php-file-iterator": "^4.0",
|
"phpunit/php-file-iterator": "^4.0",
|
||||||
"phpunit/php-invoker": "^4.0",
|
"phpunit/php-invoker": "^4.0",
|
||||||
"phpunit/php-text-template": "^3.0",
|
"phpunit/php-text-template": "^3.0",
|
||||||
@ -1895,7 +1895,7 @@
|
|||||||
"sebastian/comparator": "^5.0",
|
"sebastian/comparator": "^5.0",
|
||||||
"sebastian/diff": "^5.0",
|
"sebastian/diff": "^5.0",
|
||||||
"sebastian/environment": "^6.0",
|
"sebastian/environment": "^6.0",
|
||||||
"sebastian/exporter": "^5.0",
|
"sebastian/exporter": "^5.1",
|
||||||
"sebastian/global-state": "^6.0.1",
|
"sebastian/global-state": "^6.0.1",
|
||||||
"sebastian/object-enumerator": "^5.0",
|
"sebastian/object-enumerator": "^5.0",
|
||||||
"sebastian/recursion-context": "^5.0",
|
"sebastian/recursion-context": "^5.0",
|
||||||
@ -1943,7 +1943,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.3"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1959,7 +1959,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-09-05T04:34:51+00:00"
|
"time": "2023-09-19T05:42:37+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/cli-parser",
|
"name": "sebastian/cli-parser",
|
||||||
@ -2207,16 +2207,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/complexity",
|
"name": "sebastian/complexity",
|
||||||
"version": "3.0.1",
|
"version": "3.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/complexity.git",
|
"url": "https://github.com/sebastianbergmann/complexity.git",
|
||||||
"reference": "c70b73893e10757af9c6a48929fa6a333b56a97a"
|
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a",
|
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957",
|
||||||
"reference": "c70b73893e10757af9c6a48929fa6a333b56a97a",
|
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -2229,7 +2229,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "3.0-dev"
|
"dev-main": "3.1-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -2253,7 +2253,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/complexity/issues",
|
"issues": "https://github.com/sebastianbergmann/complexity/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/complexity/security/policy",
|
"security": "https://github.com/sebastianbergmann/complexity/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1"
|
"source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2261,7 +2261,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-08-31T09:55:53+00:00"
|
"time": "2023-09-28T11:50:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/diff",
|
"name": "sebastian/diff",
|
||||||
@ -2396,16 +2396,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/exporter",
|
"name": "sebastian/exporter",
|
||||||
"version": "5.0.0",
|
"version": "5.1.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||||
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0"
|
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
|
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
|
||||||
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
|
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -2419,7 +2419,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "5.0-dev"
|
"dev-main": "5.1-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -2461,7 +2461,8 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0"
|
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
|
||||||
|
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2469,7 +2470,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-02-03T07:06:49+00:00"
|
"time": "2023-09-24T13:22:09+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/global-state",
|
"name": "sebastian/global-state",
|
||||||
|
@ -10,6 +10,7 @@ includes:
|
|||||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
|
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
|
||||||
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
|
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
|
||||||
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
|
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
|
||||||
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
|
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
|
||||||
|
@ -360,7 +360,7 @@ class MemoryManager{
|
|||||||
'_SESSION' => true
|
'_SESSION' => true
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach(Utils::stringifyKeys($GLOBALS) as $varName => $value){
|
foreach($GLOBALS as $varName => $value){
|
||||||
if(isset($ignoredGlobals[$varName])){
|
if(isset($ignoredGlobals[$varName])){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,13 @@ namespace pocketmine {
|
|||||||
$messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
|
$messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(($libdeflate_version = phpversion("libdeflate")) !== false){
|
||||||
|
//make sure level 0 compression is available
|
||||||
|
if(version_compare($libdeflate_version, "0.2.0") < 0 || version_compare($libdeflate_version, "0.3.0") >= 0){
|
||||||
|
$messages[] = "php-libdeflate ^0.2.0 is required, while you have $libdeflate_version.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(extension_loaded("pocketmine")){
|
if(extension_loaded("pocketmine")){
|
||||||
$messages[] = "The native PocketMine extension is no longer supported.";
|
$messages[] = "The native PocketMine extension is no longer supported.";
|
||||||
}
|
}
|
||||||
|
@ -390,7 +390,7 @@ class Server{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getGamemode() : GameMode{
|
public function getGamemode() : GameMode{
|
||||||
return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE, GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL();
|
return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE)) ?? GameMode::SURVIVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForceGamemode() : bool{
|
public function getForceGamemode() : bool{
|
||||||
@ -570,6 +570,7 @@ class Server{
|
|||||||
$playerPromiseResolver = new PromiseResolver();
|
$playerPromiseResolver = new PromiseResolver();
|
||||||
|
|
||||||
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
|
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
|
||||||
|
/** @see Player::__construct() */
|
||||||
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
|
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
|
||||||
if(!$player->hasPlayedBefore()){
|
if(!$player->hasPlayedBefore()){
|
||||||
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
|
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
|
||||||
@ -817,7 +818,7 @@ class Server{
|
|||||||
ServerProperties::ENABLE_IPV6 => true,
|
ServerProperties::ENABLE_IPV6 => true,
|
||||||
ServerProperties::WHITELIST => false,
|
ServerProperties::WHITELIST => false,
|
||||||
ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS,
|
ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS,
|
||||||
ServerProperties::GAME_MODE => GameMode::SURVIVAL()->name(),
|
ServerProperties::GAME_MODE => GameMode::SURVIVAL->name, //TODO: this probably shouldn't use the enum name directly
|
||||||
ServerProperties::FORCE_GAME_MODE => false,
|
ServerProperties::FORCE_GAME_MODE => false,
|
||||||
ServerProperties::HARDCORE => false,
|
ServerProperties::HARDCORE => false,
|
||||||
ServerProperties::PVP => true,
|
ServerProperties::PVP => true,
|
||||||
@ -1019,7 +1020,7 @@ class Server{
|
|||||||
$this->forceShutdownExit();
|
$this->forceShutdownExit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
|
if(!$this->enablePlugins(PluginEnableOrder::STARTUP)){
|
||||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
||||||
$this->forceShutdownExit();
|
$this->forceShutdownExit();
|
||||||
return;
|
return;
|
||||||
@ -1030,7 +1031,7 @@ class Server{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
|
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD)){
|
||||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
||||||
$this->forceShutdownExit();
|
$this->forceShutdownExit();
|
||||||
return;
|
return;
|
||||||
@ -1258,9 +1259,10 @@ class Server{
|
|||||||
*/
|
*/
|
||||||
public function unsubscribeFromBroadcastChannel(string $channelId, CommandSender $subscriber) : void{
|
public function unsubscribeFromBroadcastChannel(string $channelId, CommandSender $subscriber) : void{
|
||||||
if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){
|
if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){
|
||||||
unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]);
|
if(count($this->broadcastSubscribers[$channelId]) === 1){
|
||||||
if(count($this->broadcastSubscribers[$channelId]) === 0){
|
|
||||||
unset($this->broadcastSubscribers[$channelId]);
|
unset($this->broadcastSubscribers[$channelId]);
|
||||||
|
}else{
|
||||||
|
unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1385,14 +1387,14 @@ class Server{
|
|||||||
public function enablePlugins(PluginEnableOrder $type) : bool{
|
public function enablePlugins(PluginEnableOrder $type) : bool{
|
||||||
$allSuccess = true;
|
$allSuccess = true;
|
||||||
foreach($this->pluginManager->getPlugins() as $plugin){
|
foreach($this->pluginManager->getPlugins() as $plugin){
|
||||||
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
|
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder() === $type){
|
||||||
if(!$this->pluginManager->enablePlugin($plugin)){
|
if(!$this->pluginManager->enablePlugin($plugin)){
|
||||||
$allSuccess = false;
|
$allSuccess = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($type->equals(PluginEnableOrder::POSTWORLD())){
|
if($type === PluginEnableOrder::POSTWORLD){
|
||||||
$this->commandMap->registerServerAliases();
|
$this->commandMap->registerServerAliases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
|||||||
|
|
||||||
final class VersionInfo{
|
final class VersionInfo{
|
||||||
public const NAME = "PocketMine-MP";
|
public const NAME = "PocketMine-MP";
|
||||||
public const BASE_VERSION = "5.5.0";
|
public const BASE_VERSION = "5.8.2";
|
||||||
public const IS_DEVELOPMENT_BUILD = false;
|
public const IS_DEVELOPMENT_BUILD = false;
|
||||||
public const BUILD_CHANNEL = "stable";
|
public const BUILD_CHANNEL = "stable";
|
||||||
|
|
||||||
|
133
src/block/AmethystCluster.php
Normal file
133
src/block/AmethystCluster.php
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AmethystTrait;
|
||||||
|
use pocketmine\block\utils\AnyFacingTrait;
|
||||||
|
use pocketmine\block\utils\FortuneDropHelper;
|
||||||
|
use pocketmine\block\utils\SupportType;
|
||||||
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\item\VanillaItems;
|
||||||
|
use pocketmine\math\Axis;
|
||||||
|
use pocketmine\math\AxisAlignedBB;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
|
use pocketmine\world\BlockTransaction;
|
||||||
|
|
||||||
|
final class AmethystCluster extends Transparent{
|
||||||
|
use AmethystTrait;
|
||||||
|
use AnyFacingTrait;
|
||||||
|
|
||||||
|
public const STAGE_SMALL_BUD = 0;
|
||||||
|
public const STAGE_MEDIUM_BUD = 1;
|
||||||
|
public const STAGE_LARGE_BUD = 2;
|
||||||
|
public const STAGE_CLUSTER = 3;
|
||||||
|
|
||||||
|
private int $stage = self::STAGE_CLUSTER;
|
||||||
|
|
||||||
|
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||||
|
$w->boundedIntAuto(self::STAGE_SMALL_BUD, self::STAGE_CLUSTER, $this->stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStage() : int{ return $this->stage; }
|
||||||
|
|
||||||
|
public function setStage(int $stage) : self{
|
||||||
|
if($stage < self::STAGE_SMALL_BUD || $stage > self::STAGE_CLUSTER){
|
||||||
|
throw new \InvalidArgumentException("Size must be in range " . self::STAGE_SMALL_BUD . " ... " . self::STAGE_CLUSTER);
|
||||||
|
}
|
||||||
|
$this->stage = $stage;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLightLevel() : int{
|
||||||
|
return match($this->stage){
|
||||||
|
self::STAGE_SMALL_BUD => 1,
|
||||||
|
self::STAGE_MEDIUM_BUD => 2,
|
||||||
|
self::STAGE_LARGE_BUD => 4,
|
||||||
|
self::STAGE_CLUSTER => 5,
|
||||||
|
default => throw new AssumptionFailedError("Invalid stage $this->stage"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recalculateCollisionBoxes() : array{
|
||||||
|
$myAxis = Facing::axis($this->facing);
|
||||||
|
|
||||||
|
$box = AxisAlignedBB::one();
|
||||||
|
foreach([Axis::Y, Axis::Z, Axis::X] as $axis){
|
||||||
|
if($axis === $myAxis){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$box->squash($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16);
|
||||||
|
}
|
||||||
|
$box->trim($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16));
|
||||||
|
|
||||||
|
return [$box];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canBeSupportedAt(Block $block, int $facing) : bool{
|
||||||
|
return $block->getAdjacentSupportType($facing) === SupportType::FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSupportType(int $facing) : SupportType{
|
||||||
|
return SupportType::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
|
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->facing = $face;
|
||||||
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onNearbyBlockChange() : void{
|
||||||
|
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
|
||||||
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAffectedBySilkTouch() : bool{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
|
if($this->stage === self::STAGE_CLUSTER){
|
||||||
|
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 4, maxBase: 4))];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||||
|
if($this->stage === self::STAGE_CLUSTER){
|
||||||
|
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 2))];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ class Anvil extends Transparent implements Fallable{
|
|||||||
private int $damage = self::UNDAMAGED;
|
private int $damage = self::UNDAMAGED;
|
||||||
|
|
||||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(2, self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
|
$w->boundedIntAuto(self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
@ -78,7 +78,7 @@ class Anvil extends Transparent implements Fallable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
use pocketmine\event\block\StructureGrowEvent;
|
use pocketmine\event\block\StructureGrowEvent;
|
||||||
@ -46,6 +47,7 @@ use function mt_rand;
|
|||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
|
|
||||||
class Bamboo extends Transparent{
|
class Bamboo extends Transparent{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const NO_LEAVES = 0;
|
public const NO_LEAVES = 0;
|
||||||
public const SMALL_LEAVES = 1;
|
public const SMALL_LEAVES = 1;
|
||||||
@ -56,7 +58,7 @@ class Bamboo extends Transparent{
|
|||||||
protected int $leafSize = self::NO_LEAVES;
|
protected int $leafSize = self::NO_LEAVES;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(2, self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
|
$w->boundedIntAuto(self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
|
||||||
$w->bool($this->thick);
|
$w->bool($this->thick);
|
||||||
$w->bool($this->ready);
|
$w->bool($this->ready);
|
||||||
}
|
}
|
||||||
@ -95,7 +97,7 @@ class Bamboo extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getOffsetSeed(int $x, int $y, int $z) : int{
|
private static function getOffsetSeed(int $x, int $y, int $z) : int{
|
||||||
@ -120,12 +122,14 @@ class Bamboo extends Transparent{
|
|||||||
return new Vector3($retX, 0, $retZ);
|
return new Vector3($retX, 0, $retZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
$supportBlock = $block->getSide(Facing::DOWN);
|
||||||
return
|
return
|
||||||
$block->getTypeId() === BlockTypeIds::GRAVEL ||
|
$supportBlock->hasSameTypeId($this) ||
|
||||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||||
$block->hasTypeTag(BlockTypeTags::SAND);
|
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||||
|
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function seekToTop() : Bamboo{
|
private function seekToTop() : Bamboo{
|
||||||
@ -153,14 +157,6 @@ class Bamboo extends Transparent{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
$world = $this->position->getWorld();
|
|
||||||
$below = $world->getBlock($this->position->down());
|
|
||||||
if(!$this->canBeSupportedBy($below) && !$below->hasSameTypeId($this)){
|
|
||||||
$world->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
|
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||||
|
@ -23,17 +23,21 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
use pocketmine\event\block\StructureGrowEvent;
|
use pocketmine\event\block\StructureGrowEvent;
|
||||||
use pocketmine\item\Bamboo as ItemBamboo;
|
use pocketmine\item\Bamboo as ItemBamboo;
|
||||||
use pocketmine\item\Fertilizer;
|
use pocketmine\item\Fertilizer;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\world\BlockTransaction;
|
use pocketmine\world\BlockTransaction;
|
||||||
|
|
||||||
final class BambooSapling extends Flowable{
|
final class BambooSapling extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
private bool $ready = false;
|
private bool $ready = false;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
@ -48,19 +52,13 @@ final class BambooSapling extends Flowable{
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
$supportBlock = $block->getSide(Facing::DOWN);
|
||||||
return
|
return
|
||||||
$block->getTypeId() === BlockTypeIds::GRAVEL ||
|
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||||
$block->hasTypeTag(BlockTypeTags::SAND);
|
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
|
||||||
}
|
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if(!$this->canBeSupportedBy($blockReplace->position->getWorld()->getBlock($blockReplace->position->down()))){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
@ -73,13 +71,6 @@ final class BambooSapling extends Flowable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
$world = $this->position->getWorld();
|
|
||||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
|
||||||
$world->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function grow(?Player $player) : bool{
|
private function grow(?Player $player) : bool{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
|||||||
use pocketmine\block\tile\Banner as TileBanner;
|
use pocketmine\block\tile\Banner as TileBanner;
|
||||||
use pocketmine\block\utils\BannerPatternLayer;
|
use pocketmine\block\utils\BannerPatternLayer;
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\item\Banner as ItemBanner;
|
use pocketmine\item\Banner as ItemBanner;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
@ -48,11 +47,6 @@ abstract class BaseBanner extends Transparent{
|
|||||||
*/
|
*/
|
||||||
protected array $patterns = [];
|
protected array $patterns = [];
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::BLACK();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function readStateFromWorld() : Block{
|
public function readStateFromWorld() : Block{
|
||||||
parent::readStateFromWorld();
|
parent::readStateFromWorld();
|
||||||
$tile = $this->position->getWorld()->getTile($this->position);
|
$tile = $this->position->getWorld()->getTile($this->position);
|
||||||
@ -111,7 +105,7 @@ abstract class BaseBanner extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedBy(Block $block) : bool{
|
||||||
|
@ -131,6 +131,6 @@ abstract class BaseBigDripleaf extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\entity\effect\EffectInstance;
|
use pocketmine\entity\effect\EffectInstance;
|
||||||
use pocketmine\entity\FoodSource;
|
use pocketmine\entity\FoodSource;
|
||||||
@ -31,27 +32,16 @@ use pocketmine\item\Item;
|
|||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
|
|
||||||
abstract class BaseCake extends Transparent implements FoodSource{
|
abstract class BaseCake extends Transparent implements FoodSource{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
$down = $this->getSide(Facing::DOWN);
|
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
|
||||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){ //Replace with common break method
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\CoralType;
|
|
||||||
use pocketmine\block\utils\CoralTypeTrait;
|
use pocketmine\block\utils\CoralTypeTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
@ -33,11 +32,6 @@ use function mt_rand;
|
|||||||
abstract class BaseCoral extends Transparent{
|
abstract class BaseCoral extends Transparent{
|
||||||
use CoralTypeTrait;
|
use CoralTypeTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->coralType = CoralType::TUBE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
if(!$this->dead){
|
if(!$this->dead){
|
||||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
||||||
@ -78,6 +72,6 @@ abstract class BaseCoral extends Transparent{
|
|||||||
protected function recalculateCollisionBoxes() : array{ return []; }
|
protected function recalculateCollisionBoxes() : array{ return []; }
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ abstract class BaseSign extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected function getSupportingFace() : int;
|
abstract protected function getSupportingFace() : int;
|
||||||
@ -172,13 +172,13 @@ abstract class BaseSign extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
|
$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
|
||||||
ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
|
ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
|
||||||
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
|
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
|
||||||
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
|
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
|
||||||
default => null
|
default => null
|
||||||
};
|
};
|
||||||
if($dyeColor !== null){
|
if($dyeColor !== null){
|
||||||
$color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
|
$color = $dyeColor === DyeColor::BLACK ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
|
||||||
if(
|
if(
|
||||||
$color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
|
$color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
|
||||||
$this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
|
$this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
|
||||||
@ -271,4 +271,8 @@ abstract class BaseSign extends Transparent{
|
|||||||
public function asItem() : Item{
|
public function asItem() : Item{
|
||||||
return ($this->asItemCallback)();
|
return ($this->asItemCallback)();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFuelTime() : int{
|
||||||
|
return $this->woodType->isFlammable() ? 200 : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,11 +48,6 @@ class Bed extends Transparent{
|
|||||||
protected bool $occupied = false;
|
protected bool $occupied = false;
|
||||||
protected bool $head = false;
|
protected bool $head = false;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::RED();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->horizontalFacing($this->facing);
|
$w->horizontalFacing($this->facing);
|
||||||
$w->bool($this->occupied);
|
$w->bool($this->occupied);
|
||||||
@ -65,6 +60,8 @@ class Bed extends Transparent{
|
|||||||
$tile = $this->position->getWorld()->getTile($this->position);
|
$tile = $this->position->getWorld()->getTile($this->position);
|
||||||
if($tile instanceof TileBed){
|
if($tile instanceof TileBed){
|
||||||
$this->color = $tile->getColor();
|
$this->color = $tile->getColor();
|
||||||
|
}else{
|
||||||
|
$this->color = DyeColor::RED; //legacy pre-1.1 beds don't have tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -87,7 +84,7 @@ class Bed extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isHeadPart() : bool{
|
public function isHeadPart() : bool{
|
||||||
@ -209,7 +206,7 @@ class Bed extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMaxStackSize() : int{ return 1; }
|
public function getMaxStackSize() : int{ return 1; }
|
||||||
|
@ -35,32 +35,26 @@ use pocketmine\math\Facing;
|
|||||||
use pocketmine\math\RayTraceResult;
|
use pocketmine\math\RayTraceResult;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
use pocketmine\world\BlockTransaction;
|
||||||
use pocketmine\world\sound\BellRingSound;
|
use pocketmine\world\sound\BellRingSound;
|
||||||
|
|
||||||
final class Bell extends Transparent{
|
final class Bell extends Transparent{
|
||||||
use HorizontalFacingTrait;
|
use HorizontalFacingTrait;
|
||||||
|
|
||||||
private BellAttachmentType $attachmentType;
|
private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->attachmentType = BellAttachmentType::FLOOR();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->bellAttachmentType($this->attachmentType);
|
$w->enum($this->attachmentType);
|
||||||
$w->horizontalFacing($this->facing);
|
$w->horizontalFacing($this->facing);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function recalculateCollisionBoxes() : array{
|
protected function recalculateCollisionBoxes() : array{
|
||||||
if($this->attachmentType->equals(BellAttachmentType::FLOOR())){
|
if($this->attachmentType === BellAttachmentType::FLOOR){
|
||||||
return [
|
return [
|
||||||
AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16)
|
AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
|
if($this->attachmentType === BellAttachmentType::CEILING){
|
||||||
return [
|
return [
|
||||||
AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4)
|
AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4)
|
||||||
];
|
];
|
||||||
@ -72,12 +66,12 @@ final class Bell extends Transparent{
|
|||||||
->trim(Facing::DOWN, 1 / 4);
|
->trim(Facing::DOWN, 1 / 4);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
$this->attachmentType->equals(BellAttachmentType::ONE_WALL()) ? $box->trim($this->facing, 3 / 16) : $box
|
$this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trim($this->facing, 3 / 16) : $box
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
|
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
|
||||||
@ -89,7 +83,7 @@ final class Bell extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||||
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
|
return $block->getAdjacentSupportType($face) !== SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
@ -100,15 +94,15 @@ final class Bell extends Transparent{
|
|||||||
if($player !== null){
|
if($player !== null){
|
||||||
$this->setFacing(Facing::opposite($player->getHorizontalFacing()));
|
$this->setFacing(Facing::opposite($player->getHorizontalFacing()));
|
||||||
}
|
}
|
||||||
$this->setAttachmentType(BellAttachmentType::FLOOR());
|
$this->setAttachmentType(BellAttachmentType::FLOOR);
|
||||||
}elseif($face === Facing::DOWN){
|
}elseif($face === Facing::DOWN){
|
||||||
$this->setAttachmentType(BellAttachmentType::CEILING());
|
$this->setAttachmentType(BellAttachmentType::CEILING);
|
||||||
}else{
|
}else{
|
||||||
$this->setFacing($face);
|
$this->setFacing($face);
|
||||||
$this->setAttachmentType(
|
$this->setAttachmentType(
|
||||||
$this->canBeSupportedAt($blockReplace, $face) ?
|
$this->canBeSupportedAt($blockReplace, $face) ?
|
||||||
BellAttachmentType::TWO_WALLS() :
|
BellAttachmentType::TWO_WALLS :
|
||||||
BellAttachmentType::ONE_WALL()
|
BellAttachmentType::ONE_WALL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
@ -116,11 +110,10 @@ final class Bell extends Transparent{
|
|||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
foreach(match($this->attachmentType){
|
foreach(match($this->attachmentType){
|
||||||
BellAttachmentType::CEILING() => [Facing::UP],
|
BellAttachmentType::CEILING => [Facing::UP],
|
||||||
BellAttachmentType::FLOOR() => [Facing::DOWN],
|
BellAttachmentType::FLOOR => [Facing::DOWN],
|
||||||
BellAttachmentType::ONE_WALL() => [Facing::opposite($this->facing)],
|
BellAttachmentType::ONE_WALL => [Facing::opposite($this->facing)],
|
||||||
BellAttachmentType::TWO_WALLS() => [$this->facing, Facing::opposite($this->facing)],
|
BellAttachmentType::TWO_WALLS => [$this->facing, Facing::opposite($this->facing)]
|
||||||
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
|
|
||||||
} as $supportBlockDirection){
|
} as $supportBlockDirection){
|
||||||
if(!$this->canBeSupportedAt($this, $supportBlockDirection)){
|
if(!$this->canBeSupportedAt($this, $supportBlockDirection)){
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
@ -159,10 +152,9 @@ final class Bell extends Transparent{
|
|||||||
|
|
||||||
private function isValidFaceToRing(int $faceHit) : bool{
|
private function isValidFaceToRing(int $faceHit) : bool{
|
||||||
return match($this->attachmentType){
|
return match($this->attachmentType){
|
||||||
BellAttachmentType::CEILING() => true,
|
BellAttachmentType::CEILING => true,
|
||||||
BellAttachmentType::FLOOR() => Facing::axis($faceHit) === Facing::axis($this->facing),
|
BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing),
|
||||||
BellAttachmentType::ONE_WALL(), BellAttachmentType::TWO_WALLS() => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
|
BellAttachmentType::ONE_WALL, BellAttachmentType::TWO_WALLS => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
|
||||||
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,22 +30,16 @@ use pocketmine\entity\projectile\Projectile;
|
|||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\RayTraceResult;
|
use pocketmine\math\RayTraceResult;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
|
||||||
use pocketmine\world\sound\DripleafTiltDownSound;
|
use pocketmine\world\sound\DripleafTiltDownSound;
|
||||||
use pocketmine\world\sound\DripleafTiltUpSound;
|
use pocketmine\world\sound\DripleafTiltUpSound;
|
||||||
|
|
||||||
class BigDripleafHead extends BaseBigDripleaf{
|
class BigDripleafHead extends BaseBigDripleaf{
|
||||||
|
|
||||||
protected DripleafState $leafState;
|
protected DripleafState $leafState = DripleafState::STABLE;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->leafState = DripleafState::STABLE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
parent::describeBlockOnlyState($w);
|
parent::describeBlockOnlyState($w);
|
||||||
$w->dripleafState($this->leafState);
|
$w->enum($this->leafState);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isHead() : bool{
|
protected function isHead() : bool{
|
||||||
@ -76,20 +70,20 @@ class BigDripleafHead extends BaseBigDripleaf{
|
|||||||
|
|
||||||
private function getLeafTopOffset() : float{
|
private function getLeafTopOffset() : float{
|
||||||
return match($this->leafState){
|
return match($this->leafState){
|
||||||
DripleafState::STABLE(), DripleafState::UNSTABLE() => 1 / 16,
|
DripleafState::STABLE, DripleafState::UNSTABLE => 1 / 16,
|
||||||
DripleafState::PARTIAL_TILT() => 3 / 16,
|
DripleafState::PARTIAL_TILT => 3 / 16,
|
||||||
default => 0
|
default => 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onEntityInside(Entity $entity) : bool{
|
public function onEntityInside(Entity $entity) : bool{
|
||||||
if(!$entity instanceof Projectile && $this->leafState->equals(DripleafState::STABLE())){
|
if(!$entity instanceof Projectile && $this->leafState === DripleafState::STABLE){
|
||||||
//the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath
|
//the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath
|
||||||
$intersection = AxisAlignedBB::one()
|
$intersection = AxisAlignedBB::one()
|
||||||
->offset($this->position->x, $this->position->y, $this->position->z)
|
->offset($this->position->x, $this->position->y, $this->position->z)
|
||||||
->trim(Facing::DOWN, 1 - $this->getLeafTopOffset());
|
->trim(Facing::DOWN, 1 - $this->getLeafTopOffset());
|
||||||
if($entity->getBoundingBox()->intersectsWith($intersection)){
|
if($entity->getBoundingBox()->intersectsWith($intersection)){
|
||||||
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE());
|
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,22 +91,21 @@ class BigDripleafHead extends BaseBigDripleaf{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||||
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
|
if($this->leafState !== DripleafState::FULL_TILT){
|
||||||
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT());
|
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT);
|
||||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onScheduledUpdate() : void{
|
public function onScheduledUpdate() : void{
|
||||||
if(!$this->leafState->equals(DripleafState::STABLE())){
|
if($this->leafState !== DripleafState::STABLE){
|
||||||
if($this->leafState->equals(DripleafState::FULL_TILT())){
|
if($this->leafState === DripleafState::FULL_TILT){
|
||||||
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE()));
|
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE));
|
||||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
|
$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
|
||||||
}else{
|
}else{
|
||||||
$this->setTiltAndScheduleTick(match($this->leafState->id()){
|
$this->setTiltAndScheduleTick(match($this->leafState){
|
||||||
DripleafState::UNSTABLE()->id() => DripleafState::PARTIAL_TILT(),
|
DripleafState::UNSTABLE => DripleafState::PARTIAL_TILT,
|
||||||
DripleafState::PARTIAL_TILT()->id() => DripleafState::FULL_TILT(),
|
DripleafState::PARTIAL_TILT => DripleafState::FULL_TILT,
|
||||||
default => throw new AssumptionFailedError("All types should be covered")
|
|
||||||
});
|
});
|
||||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
||||||
}
|
}
|
||||||
@ -120,7 +113,7 @@ class BigDripleafHead extends BaseBigDripleaf{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function recalculateCollisionBoxes() : array{
|
protected function recalculateCollisionBoxes() : array{
|
||||||
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
|
if($this->leafState !== DripleafState::FULL_TILT){
|
||||||
return [
|
return [
|
||||||
AxisAlignedBB::one()
|
AxisAlignedBB::one()
|
||||||
->trim(Facing::DOWN, 11 / 16)
|
->trim(Facing::DOWN, 11 / 16)
|
||||||
|
@ -42,7 +42,6 @@ use pocketmine\item\enchantment\ItemEnchantmentTags;
|
|||||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\ItemBlock;
|
use pocketmine\item\ItemBlock;
|
||||||
use pocketmine\math\Axis;
|
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\RayTraceResult;
|
use pocketmine\math\RayTraceResult;
|
||||||
@ -50,22 +49,26 @@ use pocketmine\math\Vector3;
|
|||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
|
use pocketmine\utils\Binary;
|
||||||
use pocketmine\world\BlockTransaction;
|
use pocketmine\world\BlockTransaction;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\Position;
|
use pocketmine\world\Position;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use function count;
|
use function count;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
|
use function hash;
|
||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
|
|
||||||
class Block{
|
class Block{
|
||||||
public const INTERNAL_STATE_DATA_BITS = 8;
|
public const INTERNAL_STATE_DATA_BITS = 11;
|
||||||
public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
|
public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
* Hardcoded int is `Binary::readLong(hash('xxh3', Binary::writeLLong(BlockTypeIds::AIR), binary: true))`
|
||||||
|
* TODO: it would be much easier if we could just make this 0 or some other easy value
|
||||||
*/
|
*/
|
||||||
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (BlockTypeIds::AIR & self::INTERNAL_STATE_DATA_MASK);
|
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (-7482769108513497636 & self::INTERNAL_STATE_DATA_MASK);
|
||||||
|
|
||||||
protected BlockIdentifier $idInfo;
|
protected BlockIdentifier $idInfo;
|
||||||
protected string $fallbackName;
|
protected string $fallbackName;
|
||||||
@ -80,6 +83,23 @@ class Block{
|
|||||||
|
|
||||||
private Block $defaultState;
|
private Block $defaultState;
|
||||||
|
|
||||||
|
private int $stateIdXorMask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the mask to be XOR'd with the state data.
|
||||||
|
* This is to improve distribution of the state data bits, which occupy the least significant bits of the state ID.
|
||||||
|
* Improved distribution improves PHP array performance when using the state ID as a key, as PHP arrays use some of
|
||||||
|
* the lower bits of integer keys directly without hashing.
|
||||||
|
*
|
||||||
|
* The type ID is included in the XOR mask. This is not necessary to improve distribution, but it reduces the number
|
||||||
|
* of operations required to compute the state ID (micro optimization).
|
||||||
|
*/
|
||||||
|
private static function computeStateIdXorMask(int $typeId) : int{
|
||||||
|
return
|
||||||
|
$typeId << self::INTERNAL_STATE_DATA_BITS |
|
||||||
|
(Binary::readLong(hash('xxh3', Binary::writeLLong($typeId), binary: true)) & self::INTERNAL_STATE_DATA_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name English name of the block type (TODO: implement translations)
|
* @param string $name English name of the block type (TODO: implement translations)
|
||||||
*/
|
*/
|
||||||
@ -97,6 +117,9 @@ class Block{
|
|||||||
$this->describeBlockOnlyState($calculator);
|
$this->describeBlockOnlyState($calculator);
|
||||||
$this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
|
$this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
|
||||||
|
|
||||||
|
$this->stateIdXorMask = self::computeStateIdXorMask($idInfo->getBlockTypeId());
|
||||||
|
|
||||||
|
//this must be done last, otherwise the defaultState could have uninitialized fields
|
||||||
$defaultState = clone $this;
|
$defaultState = clone $this;
|
||||||
$this->defaultState = $defaultState;
|
$this->defaultState = $defaultState;
|
||||||
$defaultState->defaultState = $defaultState;
|
$defaultState->defaultState = $defaultState;
|
||||||
@ -152,13 +175,7 @@ class Block{
|
|||||||
* {@link RuntimeBlockStateRegistry::fromStateId()}.
|
* {@link RuntimeBlockStateRegistry::fromStateId()}.
|
||||||
*/
|
*/
|
||||||
public function getStateId() : int{
|
public function getStateId() : int{
|
||||||
$typeId = $this->getTypeId();
|
return $this->encodeFullState() ^ $this->stateIdXorMask;
|
||||||
//TODO: this XOR mask improves hashtable distribution, but it's only effective if the number of unique block
|
|
||||||
//type IDs is larger than the number of available state data bits. We should probably hash (e.g. using xxhash)
|
|
||||||
//the type ID to create a better mask.
|
|
||||||
//Alternatively, we could hash the whole state ID, but this is currently problematic, since we currently need
|
|
||||||
//to be able to recover the state data from the state ID because of UnknownBlock.
|
|
||||||
return ($typeId << self::INTERNAL_STATE_DATA_BITS) | ($this->encodeFullState() ^ ($typeId & self::INTERNAL_STATE_DATA_MASK));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,12 +245,6 @@ class Block{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function decodeFullState(int $data) : void{
|
|
||||||
$reader = new RuntimeDataReader($this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits, $data);
|
|
||||||
$this->decodeBlockItemState($reader->readInt($this->requiredBlockItemStateDataBits));
|
|
||||||
$this->decodeBlockOnlyState($reader->readInt($this->requiredBlockOnlyStateDataBits));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function encodeBlockItemState() : int{
|
private function encodeBlockItemState() : int{
|
||||||
$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits);
|
$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits);
|
||||||
|
|
||||||
@ -304,34 +315,44 @@ class Block{
|
|||||||
if($bits > Block::INTERNAL_STATE_DATA_BITS){
|
if($bits > Block::INTERNAL_STATE_DATA_BITS){
|
||||||
throw new \LogicException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits");
|
throw new \LogicException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits");
|
||||||
}
|
}
|
||||||
for($stateData = 0; $stateData < (1 << $bits); ++$stateData){
|
for($blockItemStateData = 0; $blockItemStateData < (1 << $this->requiredBlockItemStateDataBits); ++$blockItemStateData){
|
||||||
$v = clone $this;
|
$withType = clone $this;
|
||||||
try{
|
try{
|
||||||
$v->decodeFullState($stateData);
|
$withType->decodeBlockItemState($blockItemStateData);
|
||||||
if($v->encodeFullState() !== $stateData){
|
$encoded = $withType->encodeBlockItemState();
|
||||||
throw new \LogicException(static::class . "::decodeStateData() accepts invalid state data (returned " . $v->encodeFullState() . " for input $stateData)");
|
if($encoded !== $blockItemStateData){
|
||||||
|
throw new \LogicException(static::class . "::decodeBlockItemState() accepts invalid inputs (returned $encoded for input $blockItemStateData)");
|
||||||
}
|
}
|
||||||
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
|
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield $v;
|
for($blockOnlyStateData = 0; $blockOnlyStateData < (1 << $this->requiredBlockOnlyStateDataBits); ++$blockOnlyStateData){
|
||||||
|
$withState = clone $withType;
|
||||||
|
try{
|
||||||
|
$withState->decodeBlockOnlyState($blockOnlyStateData);
|
||||||
|
$encoded = $withState->encodeBlockOnlyState();
|
||||||
|
if($encoded !== $blockOnlyStateData){
|
||||||
|
throw new \LogicException(static::class . "::decodeBlockOnlyState() accepts invalid inputs (returned $encoded for input $blockOnlyStateData)");
|
||||||
|
}
|
||||||
|
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $withState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when this block is created, set, or has a neighbouring block update, to re-detect dynamic properties which
|
* Called when this block is created, set, or has a neighbouring block update, to re-detect dynamic properties which
|
||||||
* are not saved on the world.
|
* are not saved in the blockstate ID.
|
||||||
*
|
* If any such properties are updated, don't forget to clear things like AABB caches if necessary.
|
||||||
* Clears any cached precomputed objects, such as bounding boxes. Remove any outdated precomputed things such as
|
|
||||||
* AABBs and force recalculation.
|
|
||||||
*
|
*
|
||||||
* A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g.
|
* A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g.
|
||||||
* data from a block entity).
|
* data from a block entity).
|
||||||
*/
|
*/
|
||||||
public function readStateFromWorld() : Block{
|
public function readStateFromWorld() : Block{
|
||||||
$this->collisionBoxes = null;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,6 +592,7 @@ class Block{
|
|||||||
*/
|
*/
|
||||||
final public function position(World $world, int $x, int $y, int $z) : void{
|
final public function position(World $world, int $x, int $y, int $z) : void{
|
||||||
$this->position = new Position($x, $y, $z, $world);
|
$this->position = new Position($x, $y, $z, $world);
|
||||||
|
$this->collisionBoxes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -721,8 +743,14 @@ class Block{
|
|||||||
* @return Block
|
* @return Block
|
||||||
*/
|
*/
|
||||||
public function getSide(int $side, int $step = 1){
|
public function getSide(int $side, int $step = 1){
|
||||||
if($this->position->isValid()){
|
$position = $this->position;
|
||||||
return $this->position->getWorld()->getBlock($this->position->getSide($side, $step));
|
if($position->isValid()){
|
||||||
|
[$dx, $dy, $dz] = Facing::OFFSET[$side] ?? [0, 0, 0];
|
||||||
|
return $position->getWorld()->getBlockAt(
|
||||||
|
$position->x + ($dx * $step),
|
||||||
|
$position->y + ($dy * $step),
|
||||||
|
$position->z + ($dz * $step)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \LogicException("Block does not have a valid world");
|
throw new \LogicException("Block does not have a valid world");
|
||||||
@ -736,8 +764,14 @@ class Block{
|
|||||||
*/
|
*/
|
||||||
public function getHorizontalSides() : \Generator{
|
public function getHorizontalSides() : \Generator{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
foreach($this->position->sidesAroundAxis(Axis::Y) as $vector3){
|
foreach(Facing::HORIZONTAL as $facing){
|
||||||
yield $world->getBlock($vector3);
|
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
|
||||||
|
//TODO: yield Facing as the key?
|
||||||
|
yield $world->getBlockAt(
|
||||||
|
$this->position->x + $dx,
|
||||||
|
$this->position->y + $dy,
|
||||||
|
$this->position->z + $dz
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,8 +783,13 @@ class Block{
|
|||||||
*/
|
*/
|
||||||
public function getAllSides() : \Generator{
|
public function getAllSides() : \Generator{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
foreach($this->position->sides() as $vector3){
|
foreach(Facing::OFFSET as [$dx, $dy, $dz]){
|
||||||
yield $world->getBlock($vector3);
|
//TODO: yield Facing as the key?
|
||||||
|
yield $world->getBlockAt(
|
||||||
|
$this->position->x + $dx,
|
||||||
|
$this->position->y + $dy,
|
||||||
|
$this->position->z + $dz
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -877,7 +916,7 @@ class Block{
|
|||||||
* blocks placed on the given face can be supported by this block.
|
* blocks placed on the given face can be supported by this block.
|
||||||
*/
|
*/
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::FULL();
|
return SupportType::FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getAdjacentSupportType(int $facing) : SupportType{
|
protected function getAdjacentSupportType(int $facing) : SupportType{
|
||||||
|
@ -562,10 +562,10 @@ final class BlockTypeIds{
|
|||||||
public const WEIGHTED_PRESSURE_PLATE_HEAVY = 10532;
|
public const WEIGHTED_PRESSURE_PLATE_HEAVY = 10532;
|
||||||
public const WEIGHTED_PRESSURE_PLATE_LIGHT = 10533;
|
public const WEIGHTED_PRESSURE_PLATE_LIGHT = 10533;
|
||||||
public const WHEAT = 10534;
|
public const WHEAT = 10534;
|
||||||
|
public const BUDDING_AMETHYST = 10535;
|
||||||
public const WHITE_TULIP = 10536;
|
public const WHITE_TULIP = 10536;
|
||||||
public const WOOL = 10537;
|
public const WOOL = 10537;
|
||||||
|
public const AMETHYST_CLUSTER = 10538;
|
||||||
public const GLAZED_TERRACOTTA = 10539;
|
public const GLAZED_TERRACOTTA = 10539;
|
||||||
public const AMETHYST = 10540;
|
public const AMETHYST = 10540;
|
||||||
public const ANCIENT_DEBRIS = 10541;
|
public const ANCIENT_DEBRIS = 10541;
|
||||||
@ -737,8 +737,16 @@ final class BlockTypeIds{
|
|||||||
public const BIG_DRIPLEAF_HEAD = 10707;
|
public const BIG_DRIPLEAF_HEAD = 10707;
|
||||||
public const BIG_DRIPLEAF_STEM = 10708;
|
public const BIG_DRIPLEAF_STEM = 10708;
|
||||||
public const PINK_PETALS = 10709;
|
public const PINK_PETALS = 10709;
|
||||||
|
public const CRIMSON_ROOTS = 10710;
|
||||||
|
public const WARPED_ROOTS = 10711;
|
||||||
|
public const CHISELED_BOOKSHELF = 10712;
|
||||||
|
public const TORCHFLOWER = 10713;
|
||||||
|
public const TORCHFLOWER_CROP = 10714;
|
||||||
|
public const PITCHER_PLANT = 10715;
|
||||||
|
public const PITCHER_CROP = 10716;
|
||||||
|
public const DOUBLE_PITCHER_CROP = 10717;
|
||||||
|
|
||||||
public const FIRST_UNUSED_BLOCK_ID = 10710;
|
public const FIRST_UNUSED_BLOCK_ID = 10718;
|
||||||
|
|
||||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ use pocketmine\math\Facing;
|
|||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use function array_key_exists;
|
use function array_key_exists;
|
||||||
|
use function spl_object_id;
|
||||||
|
|
||||||
class BrewingStand extends Transparent{
|
class BrewingStand extends Transparent{
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ class BrewingStand extends Transparent{
|
|||||||
protected array $slots = [];
|
protected array $slots = [];
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->brewingStandSlots($this->slots);
|
$w->enumSet($this->slots, BrewingStandSlot::cases());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function recalculateCollisionBoxes() : array{
|
protected function recalculateCollisionBoxes() : array{
|
||||||
@ -61,18 +62,18 @@ class BrewingStand extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasSlot(BrewingStandSlot $slot) : bool{
|
public function hasSlot(BrewingStandSlot $slot) : bool{
|
||||||
return array_key_exists($slot->id(), $this->slots);
|
return array_key_exists(spl_object_id($slot), $this->slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setSlot(BrewingStandSlot $slot, bool $occupied) : self{
|
public function setSlot(BrewingStandSlot $slot, bool $occupied) : self{
|
||||||
if($occupied){
|
if($occupied){
|
||||||
$this->slots[$slot->id()] = $slot;
|
$this->slots[spl_object_id($slot)] = $slot;
|
||||||
}else{
|
}else{
|
||||||
unset($this->slots[$slot->id()]);
|
unset($this->slots[spl_object_id($slot)]);
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -89,7 +90,7 @@ class BrewingStand extends Transparent{
|
|||||||
public function setSlots(array $slots) : self{
|
public function setSlots(array $slots) : self{
|
||||||
$this->slots = [];
|
$this->slots = [];
|
||||||
foreach($slots as $slot){
|
foreach($slots as $slot){
|
||||||
$this->slots[$slot->id()] = $slot;
|
$this->slots[spl_object_id($slot)] = $slot;
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -114,7 +115,7 @@ class BrewingStand extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
$changed = false;
|
$changed = false;
|
||||||
foreach(BrewingStandSlot::getAll() as $slot){
|
foreach(BrewingStandSlot::cases() as $slot){
|
||||||
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
|
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
|
||||||
if($occupied !== $this->hasSlot($slot)){
|
if($occupied !== $this->hasSlot($slot)){
|
||||||
$this->setSlot($slot, $occupied);
|
$this->setSlot($slot, $occupied);
|
||||||
|
68
src/block/BuddingAmethyst.php
Normal file
68
src/block/BuddingAmethyst.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AmethystTrait;
|
||||||
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
|
use function array_rand;
|
||||||
|
use function mt_rand;
|
||||||
|
|
||||||
|
final class BuddingAmethyst extends Opaque{
|
||||||
|
use AmethystTrait;
|
||||||
|
|
||||||
|
public function ticksRandomly() : bool{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRandomTick() : void{
|
||||||
|
if(mt_rand(1, 5) === 1){
|
||||||
|
$face = Facing::ALL[array_rand(Facing::ALL)];
|
||||||
|
|
||||||
|
$adjacent = $this->getSide($face);
|
||||||
|
//TODO: amethyst buds can spawn in water - we need waterlogging support for this
|
||||||
|
|
||||||
|
$newStage = null;
|
||||||
|
|
||||||
|
if($adjacent->getTypeId() === BlockTypeIds::AIR){
|
||||||
|
$newStage = AmethystCluster::STAGE_SMALL_BUD;
|
||||||
|
}elseif(
|
||||||
|
$adjacent->getTypeId() === BlockTypeIds::AMETHYST_CLUSTER &&
|
||||||
|
$adjacent instanceof AmethystCluster &&
|
||||||
|
$adjacent->getStage() < AmethystCluster::STAGE_CLUSTER &&
|
||||||
|
$adjacent->getFacing() === $face
|
||||||
|
){
|
||||||
|
$newStage = $adjacent->getStage() + 1;
|
||||||
|
}
|
||||||
|
if($newStage !== null){
|
||||||
|
BlockEventHelper::grow($adjacent, VanillaBlocks::AMETHYST_CLUSTER()->setStage($newStage)->setFacing($face), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -23,39 +23,22 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
|
||||||
use pocketmine\entity\Entity;
|
use pocketmine\entity\Entity;
|
||||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||||
use pocketmine\event\entity\EntityDamageEvent;
|
use pocketmine\event\entity\EntityDamageEvent;
|
||||||
use pocketmine\item\Item;
|
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
|
|
||||||
class Cactus extends Transparent{
|
class Cactus extends Transparent{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 15;
|
public const MAX_AGE = 15;
|
||||||
|
|
||||||
protected int $age = 0;
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasEntityCollision() : bool{
|
public function hasEntityCollision() : bool{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -69,7 +52,7 @@ class Cactus extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onEntityInside(Entity $entity) : bool{
|
public function onEntityInside(Entity $entity) : bool{
|
||||||
@ -78,23 +61,18 @@ class Cactus extends Transparent{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return $block->hasSameTypeId($this) || $block->hasTypeTag(BlockTypeTags::SAND);
|
$supportBlock = $block->getSide(Facing::DOWN);
|
||||||
}
|
if(!$supportBlock->hasSameTypeId($this) && !$supportBlock->hasTypeTag(BlockTypeTags::SAND)){
|
||||||
|
return false;
|
||||||
public function onNearbyBlockChange() : void{
|
}
|
||||||
$world = $this->position->getWorld();
|
foreach(Facing::HORIZONTAL as $side){
|
||||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
if($block->getSide($side)->isSolid()){
|
||||||
$world->useBreakOn($this->position);
|
return false;
|
||||||
}else{
|
|
||||||
foreach(Facing::HORIZONTAL as $side){
|
|
||||||
$b = $this->getSide($side);
|
|
||||||
if($b->isSolid()){
|
|
||||||
$world->useBreakOn($this->position);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ticksRandomly() : bool{
|
public function ticksRandomly() : bool{
|
||||||
@ -117,25 +95,11 @@ class Cactus extends Transparent{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->age = 0;
|
$this->age = 0;
|
||||||
$world->setBlock($this->position, $this);
|
$world->setBlock($this->position, $this, update: false);
|
||||||
}else{
|
}else{
|
||||||
++$this->age;
|
++$this->age;
|
||||||
$world->setBlock($this->position, $this);
|
$world->setBlock($this->position, $this, update: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
|
||||||
foreach(Facing::HORIZONTAL as $side){
|
|
||||||
if($this->getSide($side)->isSolid()){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class Cake extends BaseCake{
|
|||||||
protected int $bites = 0;
|
protected int $bites = 0;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(3, 0, self::MAX_BITES, $this->bites);
|
$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +30,7 @@ class CakeWithDyedCandle extends CakeWithCandle{
|
|||||||
use ColoredTrait;
|
use ColoredTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||||
$this->color = DyeColor::WHITE();
|
$this->color = DyeColor::WHITE;
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
parent::__construct($idInfo, $name, $typeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class Candle extends Transparent{
|
|||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$this->encodeLitState($w);
|
$this->encodeLitState($w);
|
||||||
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCount() : int{ return $this->count; }
|
public function getCount() : int{ return $this->count; }
|
||||||
@ -91,7 +91,7 @@ class Candle extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
||||||
|
@ -24,21 +24,13 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\Item;
|
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
|
|
||||||
class Carpet extends Flowable{
|
class Carpet extends Flowable{
|
||||||
use ColoredTrait;
|
use ColoredTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::WHITE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isSolid() : bool{
|
public function isSolid() : bool{
|
||||||
return true;
|
return true;
|
||||||
@ -51,19 +43,8 @@ class Carpet extends Flowable{
|
|||||||
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
|
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
$down = $this->getSide(Facing::DOWN);
|
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
|
||||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFlameEncouragement() : int{
|
public function getFlameEncouragement() : int{
|
||||||
|
@ -61,7 +61,7 @@ final class Cauldron extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
|
return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,7 +83,7 @@ final class Cauldron extends Transparent{
|
|||||||
}elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){
|
}elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){
|
||||||
//TODO: powder snow cauldron
|
//TODO: powder snow cauldron
|
||||||
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
|
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
|
||||||
if($item->getType()->equals(PotionType::WATER())){
|
if($item->getType() === PotionType::WATER){
|
||||||
$this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
$this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
||||||
}else{
|
}else{
|
||||||
$this->fill(PotionCauldron::POTION_FILL_AMOUNT, VanillaBlocks::POTION_CAULDRON()->setPotionItem($item), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
$this->fill(PotionCauldron::POTION_FILL_AMOUNT, VanillaBlocks::POTION_CAULDRON()->setPotionItem($item), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
||||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
use pocketmine\entity\Entity;
|
use pocketmine\entity\Entity;
|
||||||
@ -38,14 +40,16 @@ use pocketmine\world\sound\GlowBerriesPickSound;
|
|||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
class CaveVines extends Flowable{
|
class CaveVines extends Flowable{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 25;
|
public const MAX_AGE = 25;
|
||||||
|
|
||||||
protected int $age = 0;
|
|
||||||
protected bool $berries = false;
|
protected bool $berries = false;
|
||||||
protected bool $head = false;
|
protected bool $head = false;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
|
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
|
||||||
$w->bool($this->berries);
|
$w->bool($this->berries);
|
||||||
$w->bool($this->head);
|
$w->bool($this->head);
|
||||||
}
|
}
|
||||||
@ -66,19 +70,6 @@ class CaveVines extends Flowable{
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAge() : int{
|
|
||||||
return $this->age;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canClimb() : bool{
|
public function canClimb() : bool{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -88,19 +79,11 @@ class CaveVines extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return $block->getAdjacentSupportType(Facing::UP)->equals(SupportType::FULL()) || $block->hasSameTypeId($this);
|
$supportBlock = $block->getSide(Facing::UP);
|
||||||
}
|
return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || $supportBlock->hasSameTypeId($this);
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if(!$this->canBeSupportedAt($blockReplace)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->age = mt_rand(0, self::MAX_AGE);
|
$this->age = mt_rand(0, self::MAX_AGE);
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
@ -176,6 +159,6 @@ class CaveVines extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ final class Chain extends Transparent{
|
|||||||
use PillarRotationTrait;
|
use PillarRotationTrait;
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function recalculateCollisionBoxes() : array{
|
protected function recalculateCollisionBoxes() : array{
|
||||||
|
@ -45,7 +45,7 @@ class Chest extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onPostPlace() : void{
|
public function onPostPlace() : void{
|
||||||
|
134
src/block/ChiseledBookshelf.php
Normal file
134
src/block/ChiseledBookshelf.php
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
|
||||||
|
use pocketmine\block\utils\ChiseledBookshelfSlot;
|
||||||
|
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||||
|
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||||
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
|
use pocketmine\item\Book;
|
||||||
|
use pocketmine\item\EnchantedBook;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\item\WritableBookBase;
|
||||||
|
use pocketmine\math\Axis;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use function spl_object_id;
|
||||||
|
|
||||||
|
class ChiseledBookshelf extends Opaque{
|
||||||
|
use HorizontalFacingTrait;
|
||||||
|
use FacesOppositePlacingPlayerTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ChiseledBookshelfSlot[]
|
||||||
|
* @phpstan-var array<int, ChiseledBookshelfSlot>
|
||||||
|
*/
|
||||||
|
private array $slots = [];
|
||||||
|
|
||||||
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
|
$w->horizontalFacing($this->facing);
|
||||||
|
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given slot is displayed as occupied.
|
||||||
|
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
|
||||||
|
*/
|
||||||
|
public function hasSlot(ChiseledBookshelfSlot $slot) : bool{
|
||||||
|
return isset($this->slots[spl_object_id($slot)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the given slot is displayed as occupied.
|
||||||
|
*
|
||||||
|
* This doesn't modify the bookshelf's inventory, so you can use this to make invisible
|
||||||
|
* books or display books that aren't actually in the bookshelf.
|
||||||
|
*
|
||||||
|
* To modify the contents of the bookshelf inventory, access the tile inventory.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setSlot(ChiseledBookshelfSlot $slot, bool $occupied) : self{
|
||||||
|
if($occupied){
|
||||||
|
$this->slots[spl_object_id($slot)] = $slot;
|
||||||
|
}else{
|
||||||
|
unset($this->slots[spl_object_id($slot)]);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns which slots of the bookshelf are displayed as occupied.
|
||||||
|
* As above, these values do not necessarily reflect the contents of the bookshelf inventory,
|
||||||
|
* although they usually will unless modified by plugins.
|
||||||
|
*
|
||||||
|
* @return ChiseledBookshelfSlot[]
|
||||||
|
* @phpstan-return array<int, ChiseledBookshelfSlot>
|
||||||
|
*/
|
||||||
|
public function getSlots() : array{
|
||||||
|
return $this->slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
if($face !== $this->facing){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$x = Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX();
|
||||||
|
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
|
||||||
|
Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
|
||||||
|
$clickVector->y
|
||||||
|
);
|
||||||
|
$tile = $this->position->getWorld()->getTile($this->position);
|
||||||
|
if(!$tile instanceof TileChiseledBookshelf){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$inventory = $tile->getInventory();
|
||||||
|
if(!$inventory->isSlotEmpty($slot->value)){
|
||||||
|
$returnedItems[] = $inventory->getItem($slot->value);
|
||||||
|
$inventory->clear($slot->value);
|
||||||
|
$this->setSlot($slot, false);
|
||||||
|
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
|
||||||
|
//TODO: type tags like blocks would be better for this
|
||||||
|
$inventory->setItem($slot->value, $item->pop());
|
||||||
|
$this->setSlot($slot, true);
|
||||||
|
}else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position->getWorld()->setBlock($this->position, $this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAffectedBySilkTouch() : bool{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -23,52 +23,38 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\entity\projectile\Projectile;
|
use pocketmine\entity\projectile\Projectile;
|
||||||
use pocketmine\event\block\StructureGrowEvent;
|
use pocketmine\event\block\StructureGrowEvent;
|
||||||
use pocketmine\item\Item;
|
|
||||||
use pocketmine\math\Axis;
|
use pocketmine\math\Axis;
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\RayTraceResult;
|
use pocketmine\math\RayTraceResult;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
use pocketmine\world\BlockTransaction;
|
||||||
use pocketmine\world\Position;
|
|
||||||
use pocketmine\world\sound\ChorusFlowerDieSound;
|
use pocketmine\world\sound\ChorusFlowerDieSound;
|
||||||
use pocketmine\world\sound\ChorusFlowerGrowSound;
|
use pocketmine\world\sound\ChorusFlowerGrowSound;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use function array_rand;
|
use function array_rand;
|
||||||
|
use function min;
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
final class ChorusFlower extends Flowable{
|
final class ChorusFlower extends Flowable{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const MIN_AGE = 0;
|
public const MIN_AGE = 0;
|
||||||
public const MAX_AGE = 5;
|
public const MAX_AGE = 5;
|
||||||
|
|
||||||
private const MAX_STEM_HEIGHT = 5;
|
private const MAX_STEM_HEIGHT = 5;
|
||||||
|
|
||||||
private int $age = self::MIN_AGE;
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(3, self::MIN_AGE, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < self::MIN_AGE || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in the range " . self::MIN_AGE . " ... " . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function recalculateCollisionBoxes() : array{
|
protected function recalculateCollisionBoxes() : array{
|
||||||
return [AxisAlignedBB::one()];
|
return [AxisAlignedBB::one()];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Position $position) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
$position = $block->getPosition();
|
||||||
$world = $position->getWorld();
|
$world = $position->getWorld();
|
||||||
$down = $world->getBlock($position->down());
|
$down = $world->getBlock($position->down());
|
||||||
|
|
||||||
@ -93,25 +79,10 @@ final class ChorusFlower extends Flowable{
|
|||||||
return $plantAdjacent;
|
return $plantAdjacent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if(!$this->canBeSupportedAt($blockReplace->getPosition())){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedAt($this->position)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
$this->position->getWorld()->useBreakOn($this->position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-return array{int, bool}
|
* @phpstan-return array{int, bool}
|
||||||
*/
|
*/
|
||||||
@ -181,11 +152,13 @@ final class ChorusFlower extends Flowable{
|
|||||||
if($tx === null){
|
if($tx === null){
|
||||||
$tx = new BlockTransaction($this->position->getWorld());
|
$tx = new BlockTransaction($this->position->getWorld());
|
||||||
}
|
}
|
||||||
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge($this->getAge() + $ageChange));
|
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->getAge() + $ageChange)));
|
||||||
|
|
||||||
return $tx;
|
return $tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
|
||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
|
|
||||||
|
@ -23,18 +23,16 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
use pocketmine\math\Axis;
|
use pocketmine\math\Axis;
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
use pocketmine\world\Position;
|
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
final class ChorusPlant extends Flowable{
|
final class ChorusPlant extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
protected function recalculateCollisionBoxes() : array{
|
protected function recalculateCollisionBoxes() : array{
|
||||||
$bb = AxisAlignedBB::one();
|
$bb = AxisAlignedBB::one();
|
||||||
@ -52,7 +50,8 @@ final class ChorusPlant extends Flowable{
|
|||||||
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
|
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canStay(Position $position) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
$position = $block->getPosition();
|
||||||
$world = $position->getWorld();
|
$world = $position->getWorld();
|
||||||
|
|
||||||
$down = $world->getBlock($position->down());
|
$down = $world->getBlock($position->down());
|
||||||
@ -72,24 +71,7 @@ final class ChorusPlant extends Flowable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->canBeSupportedBy($down)){
|
return $this->canBeSupportedBy($down);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if(!$this->canStay($blockReplace->getPosition())){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canStay($this->position)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDropsForCompatibleTool(Item $item) : array{
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
@ -41,25 +42,13 @@ use function mt_rand;
|
|||||||
|
|
||||||
class CocoaBlock extends Transparent{
|
class CocoaBlock extends Transparent{
|
||||||
use HorizontalFacingTrait;
|
use HorizontalFacingTrait;
|
||||||
|
use AgeableTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 2;
|
public const MAX_AGE = 2;
|
||||||
|
|
||||||
protected int $age = 0;
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->horizontalFacing($this->facing);
|
$w->horizontalFacing($this->facing);
|
||||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,11 +66,11 @@ class CocoaBlock extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canAttachTo(Block $block) : bool{
|
private function canAttachTo(Block $block) : bool{
|
||||||
return $block instanceof Wood && $block->getWoodType()->equals(WoodType::JUNGLE());
|
return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
@ -110,7 +99,7 @@ class CocoaBlock extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function ticksRandomly() : bool{
|
public function ticksRandomly() : bool{
|
||||||
return true;
|
return $this->age < self::MAX_AGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
|
|
||||||
class Concrete extends Opaque{
|
class Concrete extends Opaque{
|
||||||
use ColoredTrait;
|
use ColoredTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::WHITE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
|||||||
|
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
use pocketmine\block\utils\Fallable;
|
use pocketmine\block\utils\Fallable;
|
||||||
use pocketmine\block\utils\FallableTrait;
|
use pocketmine\block\utils\FallableTrait;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
@ -36,11 +35,6 @@ class ConcretePowder extends Opaque implements Fallable{
|
|||||||
onNearbyBlockChange as protected startFalling;
|
onNearbyBlockChange as protected startFalling;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::WHITE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
if(($water = $this->getAdjacentWater()) !== null){
|
if(($water = $this->getAdjacentWater()) !== null){
|
||||||
BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);
|
BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);
|
||||||
|
@ -23,14 +23,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\CopperOxidation;
|
|
||||||
use pocketmine\block\utils\CopperTrait;
|
use pocketmine\block\utils\CopperTrait;
|
||||||
|
|
||||||
class CopperStairs extends Stair{
|
class CopperStairs extends Stair{
|
||||||
use CopperTrait;
|
use CopperTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->oxidation = CopperOxidation::NONE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,29 +23,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\item\Item;
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
|
|
||||||
final class Coral extends BaseCoral{
|
final class Coral extends BaseCoral{
|
||||||
|
use StaticSupportTrait;
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if(!$this->canBeSupportedAt($blockReplace)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
$world = $this->position->getWorld();
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$world->useBreakOn($this->position);
|
|
||||||
}else{
|
|
||||||
parent::onNearbyBlockChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\CoralType;
|
|
||||||
use pocketmine\block\utils\CoralTypeTrait;
|
use pocketmine\block\utils\CoralTypeTrait;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
@ -32,11 +31,6 @@ use function mt_rand;
|
|||||||
final class CoralBlock extends Opaque{
|
final class CoralBlock extends Opaque{
|
||||||
use CoralTypeTrait;
|
use CoralTypeTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->coralType = CoralType::TUBE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
if(!$this->dead){
|
if(!$this->dead){
|
||||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
||||||
|
@ -23,51 +23,34 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\Fertilizer;
|
use pocketmine\item\Fertilizer;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
abstract class Crops extends Flowable{
|
abstract class Crops extends Flowable{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 7;
|
public const MAX_AGE = 7;
|
||||||
|
|
||||||
protected int $age = 0;
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(3, 0, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if($blockReplace->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND){
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
|
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
|
||||||
$block = clone $this;
|
$block = clone $this;
|
||||||
$block->age += mt_rand(2, 5);
|
$tempAge = $block->age + mt_rand(2, 5);
|
||||||
if($block->age > self::MAX_AGE){
|
if($tempAge > self::MAX_AGE){
|
||||||
$block->age = self::MAX_AGE;
|
$tempAge = self::MAX_AGE;
|
||||||
}
|
}
|
||||||
|
$block->age = $tempAge;
|
||||||
if(BlockEventHelper::grow($this, $block, $player)){
|
if(BlockEventHelper::grow($this, $block, $player)){
|
||||||
$item->pop();
|
$item->pop();
|
||||||
}
|
}
|
||||||
@ -78,14 +61,8 @@ abstract class Crops extends Flowable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::FARMLAND){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ticksRandomly() : bool{
|
public function ticksRandomly() : bool{
|
||||||
return true;
|
return $this->age < self::MAX_AGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
|
@ -42,7 +42,7 @@ class DaylightSensor extends Transparent{
|
|||||||
protected bool $inverted = false;
|
protected bool $inverted = false;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(4, 0, 15, $this->signalStrength);
|
$w->boundedIntAuto(0, 15, $this->signalStrength);
|
||||||
$w->bool($this->inverted);
|
$w->bool($this->inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ class DaylightSensor extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
@ -23,29 +23,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
class DeadBush extends Flowable{
|
class DeadBush extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||||
return [
|
return [
|
||||||
@ -65,14 +50,21 @@ class DeadBush extends Flowable{
|
|||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
$blockId = $block->getTypeId();
|
$supportBlock = $block->getSide(Facing::DOWN);
|
||||||
return $blockId === BlockTypeIds::SAND
|
return
|
||||||
|| $blockId === BlockTypeIds::RED_SAND
|
$supportBlock->hasTypeTag(BlockTypeTags::SAND) ||
|
||||||
|| $blockId === BlockTypeIds::PODZOL
|
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||||
|| $blockId === BlockTypeIds::MYCELIUM
|
match($supportBlock->getTypeId()){
|
||||||
|| $blockId === BlockTypeIds::DIRT
|
//can't use DIRT tag here because it includes farmland
|
||||||
|| $blockId === BlockTypeIds::HARDENED_CLAY
|
BlockTypeIds::PODZOL,
|
||||||
|| $blockId === BlockTypeIds::STAINED_CLAY;
|
BlockTypeIds::MYCELIUM,
|
||||||
|
BlockTypeIds::DIRT,
|
||||||
|
BlockTypeIds::GRASS,
|
||||||
|
BlockTypeIds::HARDENED_CLAY,
|
||||||
|
BlockTypeIds::STAINED_CLAY => true,
|
||||||
|
//TODO: moss block
|
||||||
|
default => false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,15 +38,10 @@ use pocketmine\world\sound\ItemUseOnBlockSound;
|
|||||||
use pocketmine\world\sound\WaterSplashSound;
|
use pocketmine\world\sound\WaterSplashSound;
|
||||||
|
|
||||||
class Dirt extends Opaque{
|
class Dirt extends Opaque{
|
||||||
protected DirtType $dirtType;
|
protected DirtType $dirtType = DirtType::NORMAL;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->dirtType = DirtType::NORMAL();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||||
$w->dirtType($this->dirtType);
|
$w->enum($this->dirtType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDirtType() : DirtType{ return $this->dirtType; }
|
public function getDirtType() : DirtType{ return $this->dirtType; }
|
||||||
@ -59,19 +54,24 @@ class Dirt extends Opaque{
|
|||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
if($face === Facing::UP && $item instanceof Hoe){
|
if($face !== Facing::DOWN && $item instanceof Hoe){
|
||||||
|
$up = $this->getSide(Facing::UP);
|
||||||
|
if($up->getTypeId() !== BlockTypeIds::AIR){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$item->applyDamage(1);
|
$item->applyDamage(1);
|
||||||
|
|
||||||
$newBlock = $this->dirtType->equals(DirtType::NORMAL()) ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
|
$newBlock = $this->dirtType === DirtType::NORMAL ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
|
||||||
$center = $this->position->add(0.5, 0.5, 0.5);
|
$center = $this->position->add(0.5, 0.5, 0.5);
|
||||||
$world->addSound($center, new ItemUseOnBlockSound($newBlock));
|
$world->addSound($center, new ItemUseOnBlockSound($newBlock));
|
||||||
$world->setBlock($this->position, $newBlock);
|
$world->setBlock($this->position, $newBlock);
|
||||||
if($this->dirtType->equals(DirtType::ROOTED())){
|
if($this->dirtType === DirtType::ROOTED){
|
||||||
$world->dropItem($center, VanillaBlocks::HANGING_ROOTS()->asItem());
|
$world->dropItem($center, VanillaBlocks::HANGING_ROOTS()->asItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}elseif($this->dirtType->equals(DirtType::ROOTED()) && $item instanceof Fertilizer){
|
}elseif($this->dirtType === DirtType::ROOTED && $item instanceof Fertilizer){
|
||||||
$down = $this->getSide(Facing::DOWN);
|
$down = $this->getSide(Facing::DOWN);
|
||||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
if($down->getTypeId() !== BlockTypeIds::AIR){
|
||||||
return true;
|
return true;
|
||||||
@ -80,7 +80,7 @@ class Dirt extends Opaque{
|
|||||||
$item->pop();
|
$item->pop();
|
||||||
$world->setBlock($down->position, VanillaBlocks::HANGING_ROOTS());
|
$world->setBlock($down->position, VanillaBlocks::HANGING_ROOTS());
|
||||||
//TODO: bonemeal particles, growth sounds
|
//TODO: bonemeal particles, growth sounds
|
||||||
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType()->equals(PotionType::WATER())){
|
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType() === PotionType::WATER){
|
||||||
$item->pop();
|
$item->pop();
|
||||||
$world->setBlock($this->position, VanillaBlocks::MUD());
|
$world->setBlock($this->position, VanillaBlocks::MUD());
|
||||||
$world->addSound($this->position, new WaterSplashSound(0.5));
|
$world->addSound($this->position, new WaterSplashSound(0.5));
|
||||||
|
@ -51,6 +51,8 @@ class Door extends Transparent{
|
|||||||
public function readStateFromWorld() : Block{
|
public function readStateFromWorld() : Block{
|
||||||
parent::readStateFromWorld();
|
parent::readStateFromWorld();
|
||||||
|
|
||||||
|
$this->collisionBoxes = null;
|
||||||
|
|
||||||
//copy door properties from other half
|
//copy door properties from other half
|
||||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||||
if($other instanceof Door && $other->hasSameTypeId($this)){
|
if($other instanceof Door && $other->hasSameTypeId($this)){
|
||||||
@ -102,7 +104,7 @@ class Door extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
|
110
src/block/DoublePitcherCrop.php
Normal file
110
src/block/DoublePitcherCrop.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
|
use pocketmine\event\block\StructureGrowEvent;
|
||||||
|
use pocketmine\item\Fertilizer;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\math\Axis;
|
||||||
|
use pocketmine\math\AxisAlignedBB;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\world\BlockTransaction;
|
||||||
|
use function mt_rand;
|
||||||
|
|
||||||
|
final class DoublePitcherCrop extends DoublePlant{
|
||||||
|
use AgeableTrait {
|
||||||
|
describeBlockOnlyState as describeAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const MAX_AGE = 1;
|
||||||
|
|
||||||
|
public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
|
parent::describeBlockOnlyState($w);
|
||||||
|
$this->describeAge($w);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recalculateCollisionBoxes() : array{
|
||||||
|
if($this->top){
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
//the pod exists only in the bottom half of the plant
|
||||||
|
return [
|
||||||
|
AxisAlignedBB::one()
|
||||||
|
->trim(Facing::UP, 11 / 16)
|
||||||
|
->squash(Axis::X, 3 / 16)
|
||||||
|
->squash(Axis::Z, 3 / 16)
|
||||||
|
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function grow(?Player $player) : bool{
|
||||||
|
if($this->age >= self::MAX_AGE){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bottom = $this->top ? $this->getSide(Facing::DOWN) : $this;
|
||||||
|
$top = $this->top ? $this : $this->getSide(Facing::UP);
|
||||||
|
if($top->getTypeId() !== BlockTypeIds::AIR && !$top->hasSameTypeId($this)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newState = (clone $this)->setAge($this->age + 1);
|
||||||
|
|
||||||
|
$tx = new BlockTransaction($this->position->getWorld());
|
||||||
|
$tx->addBlock($bottom->position, (clone $newState)->setTop(false));
|
||||||
|
$tx->addBlock($top->position, (clone $newState)->setTop(true));
|
||||||
|
|
||||||
|
$ev = new StructureGrowEvent($bottom, $tx, $player);
|
||||||
|
$ev->call();
|
||||||
|
|
||||||
|
return !$ev->isCancelled() && $tx->apply();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
if($item instanceof Fertilizer && $this->grow($player)){
|
||||||
|
$item->pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ticksRandomly() : bool{
|
||||||
|
return $this->age < self::MAX_AGE && !$this->top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRandomTick() : void{
|
||||||
|
//TODO: the growth speed is influenced by farmland and nearby crops
|
||||||
|
//only the bottom half of the plant can grow randomly
|
||||||
|
if(mt_rand(0, 2) === 0 && !$this->top){
|
||||||
|
$this->grow(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -50,7 +50,7 @@ class DragonEgg extends Transparent implements Fallable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
|
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
|
||||||
if($player !== null && !$player->getGamemode()->equals(GameMode::CREATIVE())){
|
if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
|
||||||
$this->teleport();
|
$this->teleport();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -82,6 +82,6 @@ class DragonEgg extends Transparent implements Fallable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,19 +24,13 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
|
|
||||||
class DyedCandle extends Candle{
|
class DyedCandle extends Candle{
|
||||||
use ColoredTrait;
|
use ColoredTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::WHITE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
||||||
$result = parent::getCandleIfCompatibleType($block);
|
$result = parent::getCandleIfCompatibleType($block);
|
||||||
//different coloured candles can't be combined in the same block
|
//different coloured candles can't be combined in the same block
|
||||||
return $result instanceof DyedCandle && $result->color->equals($this->color) ? $result : null;
|
return $result instanceof DyedCandle && $result->color === $this->color ? $result : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
|
|
||||||
final class DyedShulkerBox extends ShulkerBox{
|
final class DyedShulkerBox extends ShulkerBox{
|
||||||
use ColoredTrait;
|
use ColoredTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::WHITE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class EnchantingTable extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
@ -49,7 +49,7 @@ class EnderChest extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
@ -31,15 +31,40 @@ use pocketmine\event\entity\EntityTrampleFarmlandEvent;
|
|||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
|
use function intdiv;
|
||||||
use function lcg_value;
|
use function lcg_value;
|
||||||
|
|
||||||
class Farmland extends Transparent{
|
class Farmland extends Transparent{
|
||||||
public const MAX_WETNESS = 7;
|
public const MAX_WETNESS = 7;
|
||||||
|
|
||||||
|
private const WATER_SEARCH_HORIZONTAL_LENGTH = 9;
|
||||||
|
|
||||||
|
private const WATER_SEARCH_VERTICAL_LENGTH = 2;
|
||||||
|
|
||||||
|
private const WATER_POSITION_INDEX_UNKNOWN = -1;
|
||||||
|
/** Total possible options for water X/Z indexes */
|
||||||
|
private const WATER_POSITION_INDICES_TOTAL = (self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2) * 2;
|
||||||
|
|
||||||
protected int $wetness = 0; //"moisture" blockstate property in PC
|
protected int $wetness = 0; //"moisture" blockstate property in PC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached value indicating the relative coordinates of the most recently found water block.
|
||||||
|
*
|
||||||
|
* If this is set to a non-unknown value, the farmland block will check the relative coordinates indicated by
|
||||||
|
* this value for water, before searching the entire 9x2x9 grid around the farmland. This significantly benefits
|
||||||
|
* hydrating or fully hydrated farmland, avoiding the need for costly searches on every random tick.
|
||||||
|
*
|
||||||
|
* If the coordinates indicated don't contain water, the full 9x2x9 volume will be searched as before. A new index
|
||||||
|
* will be recorded if water is found, otherwise it will be set to unknown and future searches will search the full
|
||||||
|
* 9x2x9 volume again.
|
||||||
|
*
|
||||||
|
* This property is not exposed to the API or saved on disk. It is only used by PocketMine-MP at runtime as a cache.
|
||||||
|
*/
|
||||||
|
private int $waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(3, 0, self::MAX_WETNESS, $this->wetness);
|
$w->boundedIntAuto(0, self::MAX_WETNESS, $this->wetness);
|
||||||
|
$w->boundedIntAuto(-1, self::WATER_POSITION_INDICES_TOTAL - 1, $this->waterPositionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWetness() : int{ return $this->wetness; }
|
public function getWetness() : int{ return $this->wetness; }
|
||||||
@ -53,6 +78,22 @@ class Farmland extends Transparent{
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function getWaterPositionIndex() : int{ return $this->waterPositionIndex; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setWaterPositionIndex(int $waterPositionIndex) : self{
|
||||||
|
if($waterPositionIndex < -1 || $waterPositionIndex >= self::WATER_POSITION_INDICES_TOTAL){
|
||||||
|
throw new \InvalidArgumentException("Water XZ index must be in range -1 ... " . (self::WATER_POSITION_INDICES_TOTAL - 1));
|
||||||
|
}
|
||||||
|
$this->waterPositionIndex = $waterPositionIndex;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return AxisAlignedBB[]
|
* @return AxisAlignedBB[]
|
||||||
*/
|
*/
|
||||||
@ -72,6 +113,11 @@ class Farmland extends Transparent{
|
|||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
|
|
||||||
|
//this property may be updated by canHydrate() - track this so we know if we need to set the block again
|
||||||
|
$oldWaterPositionIndex = $this->waterPositionIndex;
|
||||||
|
$changed = false;
|
||||||
|
|
||||||
if(!$this->canHydrate()){
|
if(!$this->canHydrate()){
|
||||||
if($this->wetness > 0){
|
if($this->wetness > 0){
|
||||||
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
|
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
|
||||||
@ -79,9 +125,11 @@ class Farmland extends Transparent{
|
|||||||
if(!$event->isCancelled()){
|
if(!$event->isCancelled()){
|
||||||
$this->wetness = $event->getNewHydration();
|
$this->wetness = $event->getNewHydration();
|
||||||
$world->setBlock($this->position, $this, false);
|
$world->setBlock($this->position, $this, false);
|
||||||
|
$changed = true;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$world->setBlock($this->position, VanillaBlocks::DIRT());
|
$world->setBlock($this->position, VanillaBlocks::DIRT());
|
||||||
|
$changed = true;
|
||||||
}
|
}
|
||||||
}elseif($this->wetness < self::MAX_WETNESS){
|
}elseif($this->wetness < self::MAX_WETNESS){
|
||||||
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
|
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
|
||||||
@ -89,8 +137,14 @@ class Farmland extends Transparent{
|
|||||||
if(!$event->isCancelled()){
|
if(!$event->isCancelled()){
|
||||||
$this->wetness = $event->getNewHydration();
|
$this->wetness = $event->getNewHydration();
|
||||||
$world->setBlock($this->position, $this, false);
|
$world->setBlock($this->position, $this, false);
|
||||||
|
$changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!$changed && $oldWaterPositionIndex !== $this->waterPositionIndex){
|
||||||
|
//ensure the water square index is saved regardless of whether anything else happened
|
||||||
|
$world->setBlock($this->position, $this, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onEntityLand(Entity $entity) : ?float{
|
public function onEntityLand(Entity $entity) : ?float{
|
||||||
@ -105,19 +159,39 @@ class Farmland extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function canHydrate() : bool{
|
protected function canHydrate() : bool{
|
||||||
//TODO: check rain
|
$world = $this->position->getWorld();
|
||||||
$start = $this->position->add(-4, 0, -4);
|
|
||||||
$end = $this->position->add(4, 1, 4);
|
$startX = $this->position->getFloorX() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
|
||||||
for($y = $start->y; $y <= $end->y; ++$y){
|
$startY = $this->position->getFloorY();
|
||||||
for($z = $start->z; $z <= $end->z; ++$z){
|
$startZ = $this->position->getFloorZ() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
|
||||||
for($x = $start->x; $x <= $end->x; ++$x){
|
|
||||||
if($this->position->getWorld()->getBlockAt($x, $y, $z) instanceof Water){
|
if($this->waterPositionIndex !== self::WATER_POSITION_INDEX_UNKNOWN){
|
||||||
|
$raw = $this->waterPositionIndex;
|
||||||
|
$x = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
|
||||||
|
$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
|
||||||
|
$z = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
|
||||||
|
$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
|
||||||
|
$y = $raw % self::WATER_SEARCH_VERTICAL_LENGTH;
|
||||||
|
|
||||||
|
if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//no water found at cached position - search the whole area
|
||||||
|
//y will increment after x/z have been exhausted, as usually water will be at the same Y as the farmland
|
||||||
|
for($y = 0; $y < self::WATER_SEARCH_VERTICAL_LENGTH; $y++){
|
||||||
|
for($x = 0; $x < self::WATER_SEARCH_HORIZONTAL_LENGTH; $x++){
|
||||||
|
for($z = 0; $z < self::WATER_SEARCH_HORIZONTAL_LENGTH; $z++){
|
||||||
|
if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
|
||||||
|
$this->waterPositionIndex = $x + ($z * self::WATER_SEARCH_HORIZONTAL_LENGTH) + ($y * self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +40,11 @@ class Fence extends Transparent{
|
|||||||
public function readStateFromWorld() : Block{
|
public function readStateFromWorld() : Block{
|
||||||
parent::readStateFromWorld();
|
parent::readStateFromWorld();
|
||||||
|
|
||||||
|
$this->collisionBoxes = null;
|
||||||
|
|
||||||
foreach(Facing::HORIZONTAL as $facing){
|
foreach(Facing::HORIZONTAL as $facing){
|
||||||
$block = $this->getSide($facing);
|
$block = $this->getSide($facing);
|
||||||
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
|
if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){
|
||||||
$this->connections[$facing] = true;
|
$this->connections[$facing] = true;
|
||||||
}else{
|
}else{
|
||||||
unset($this->connections[$facing]);
|
unset($this->connections[$facing]);
|
||||||
@ -98,6 +100,6 @@ class Fence extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ class FenceGate extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkInWall() : bool{
|
private function checkInWall() : bool{
|
||||||
|
@ -38,7 +38,7 @@ abstract class FillableCauldron extends Transparent{
|
|||||||
private int $fillLevel = self::MIN_FILL_LEVEL;
|
private int $fillLevel = self::MIN_FILL_LEVEL;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(3, self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
|
$w->boundedIntAuto(self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFillLevel() : int{ return $this->fillLevel; }
|
public function getFillLevel() : int{ return $this->fillLevel; }
|
||||||
@ -64,7 +64,7 @@ abstract class FillableCauldron extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
|
return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function withFillLevel(int $fillLevel) : Block{
|
protected function withFillLevel(int $fillLevel) : Block{
|
||||||
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
|
||||||
use pocketmine\event\block\BlockBurnEvent;
|
use pocketmine\event\block\BlockBurnEvent;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
@ -36,31 +36,16 @@ use function min;
|
|||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
class Fire extends BaseFire{
|
class Fire extends BaseFire{
|
||||||
|
use AgeableTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 15;
|
public const MAX_AGE = 15;
|
||||||
|
|
||||||
protected int $age = 0;
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getFireDamage() : int{
|
protected function getFireDamage() : int{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedBy(Block $block) : bool{
|
||||||
return $block->getSupportType(Facing::UP)->equals(SupportType::FULL());
|
return $block->getSupportType(Facing::UP) === SupportType::FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
@ -35,6 +36,8 @@ use function atan2;
|
|||||||
use function rad2deg;
|
use function rad2deg;
|
||||||
|
|
||||||
final class FloorCoralFan extends BaseCoral{
|
final class FloorCoralFan extends BaseCoral{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
private int $axis = Axis::X;
|
private int $axis = Axis::X;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
@ -53,9 +56,6 @@ final class FloorCoralFan extends BaseCoral{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if(!$this->canBeSupportedAt($blockReplace)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if($player !== null){
|
if($player !== null){
|
||||||
$playerBlockPos = $player->getPosition()->floor();
|
$playerBlockPos = $player->getPosition()->floor();
|
||||||
$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
|
$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
|
||||||
@ -73,15 +73,6 @@ final class FloorCoralFan extends BaseCoral{
|
|||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
$world = $this->position->getWorld();
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$world->useBreakOn($this->position);
|
|
||||||
}else{
|
|
||||||
parent::onNearbyBlockChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,6 @@ abstract class Flowable extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,28 +23,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\item\Item;
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
|
|
||||||
class Flower extends Flowable{
|
class Flower extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
$down = $this->getSide(Facing::DOWN);
|
$supportBlock = $block->getSide(Facing::DOWN);
|
||||||
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
|
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
$down = $this->getSide(Facing::DOWN);
|
|
||||||
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFlameEncouragement() : int{
|
public function getFlameEncouragement() : int{
|
||||||
|
@ -24,15 +24,16 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
|
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
use function assert;
|
use function assert;
|
||||||
|
|
||||||
class FlowerPot extends Flowable{
|
class FlowerPot extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
protected ?Block $plant = null;
|
protected ?Block $plant = null;
|
||||||
|
|
||||||
@ -89,20 +90,6 @@ class FlowerPot extends Flowable{
|
|||||||
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
|
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if(!$this->canBeSupportedAt($blockReplace)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||||
}
|
}
|
||||||
|
@ -28,15 +28,10 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
|
|||||||
|
|
||||||
final class Froglight extends SimplePillar{
|
final class Froglight extends SimplePillar{
|
||||||
|
|
||||||
private FroglightType $froglightType;
|
private FroglightType $froglightType = FroglightType::OCHRE;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->froglightType = FroglightType::OCHRE();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||||
$w->froglightType($this->froglightType);
|
$w->enum($this->froglightType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFroglightType() : FroglightType{ return $this->froglightType; }
|
public function getFroglightType() : FroglightType{ return $this->froglightType; }
|
||||||
|
@ -23,30 +23,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
class FrostedIce extends Ice{
|
class FrostedIce extends Ice{
|
||||||
|
use AgeableTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 3;
|
public const MAX_AGE = 3;
|
||||||
|
|
||||||
protected int $age = 0;
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40));
|
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40));
|
||||||
}
|
}
|
||||||
|
@ -24,15 +24,9 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
use pocketmine\block\utils\ColoredTrait;
|
use pocketmine\block\utils\ColoredTrait;
|
||||||
use pocketmine\block\utils\DyeColor;
|
|
||||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||||
|
|
||||||
class GlazedTerracotta extends Opaque{
|
class GlazedTerracotta extends Opaque{
|
||||||
use ColoredTrait;
|
use ColoredTrait;
|
||||||
use FacesOppositePlacingPlayerTrait;
|
use FacesOppositePlacingPlayerTrait;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->color = DyeColor::BLACK();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ class GlowLichen extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canBeReplaced() : bool{
|
public function canBeReplaced() : bool{
|
||||||
@ -121,7 +121,7 @@ class GlowLichen extends Transparent{
|
|||||||
$changed = false;
|
$changed = false;
|
||||||
|
|
||||||
foreach($this->faces as $face){
|
foreach($this->faces as $face){
|
||||||
if(!$this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
|
if($this->getAdjacentSupportType($face) !== SupportType::FULL){
|
||||||
unset($this->faces[$face]);
|
unset($this->faces[$face]);
|
||||||
$changed = true;
|
$changed = true;
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ class GlowLichen extends Transparent{
|
|||||||
$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
|
$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
|
||||||
$supportFace = Facing::opposite($spreadFace);
|
$supportFace = Facing::opposite($spreadFace);
|
||||||
|
|
||||||
if(!$supportBlock->getSupportType($supportFace)->equals(SupportType::FULL())){
|
if($supportBlock->getSupportType($supportFace) !== SupportType::FULL){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ class GlowLichen extends Transparent{
|
|||||||
private function getAvailableFaces() : array{
|
private function getAvailableFaces() : array{
|
||||||
$faces = [];
|
$faces = [];
|
||||||
foreach(Facing::ALL as $face){
|
foreach(Facing::ALL as $face){
|
||||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
|
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
|
||||||
$faces[$face] = $face;
|
$faces[$face] = $face;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\FortuneDropHelper;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
|
|
||||||
final class GoldOre extends Opaque{
|
final class GoldOre extends Opaque{
|
||||||
|
|
||||||
public function getDropsForCompatibleTool(Item $item) : array{
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
return [VanillaItems::RAW_GOLD()];
|
return [VanillaItems::RAW_GOLD()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAffectedBySilkTouch() : bool{ return true; }
|
public function isAffectedBySilkTouch() : bool{ return true; }
|
||||||
|
@ -69,7 +69,7 @@ class Grass extends Opaque{
|
|||||||
$b = $world->getBlockAt($x, $y, $z);
|
$b = $world->getBlockAt($x, $y, $z);
|
||||||
if(
|
if(
|
||||||
!($b instanceof Dirt) ||
|
!($b instanceof Dirt) ||
|
||||||
!$b->getDirtType()->equals(DirtType::NORMAL()) ||
|
$b->getDirtType() !== DirtType::NORMAL ||
|
||||||
$world->getFullLightAt($x, $y + 1, $z) < 4 ||
|
$world->getFullLightAt($x, $y + 1, $z) < 4 ||
|
||||||
$world->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2
|
$world->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2
|
||||||
){
|
){
|
||||||
@ -82,7 +82,7 @@ class Grass extends Opaque{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
if($face !== Facing::UP){
|
if($this->getSide(Facing::UP)->getTypeId() !== BlockTypeIds::AIR){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
@ -91,20 +91,23 @@ class Grass extends Opaque{
|
|||||||
TallGrassObject::growGrass($world, $this->position, new Random(mt_rand()), 8, 2);
|
TallGrassObject::growGrass($world, $this->position, new Random(mt_rand()), 8, 2);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}elseif($item instanceof Hoe){
|
}
|
||||||
$item->applyDamage(1);
|
if($face !== Facing::DOWN){
|
||||||
$newBlock = VanillaBlocks::FARMLAND();
|
if($item instanceof Hoe){
|
||||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
$item->applyDamage(1);
|
||||||
$world->setBlock($this->position, $newBlock);
|
$newBlock = VanillaBlocks::FARMLAND();
|
||||||
|
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
||||||
|
$world->setBlock($this->position, $newBlock);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}elseif($item instanceof Shovel && $this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR){
|
}elseif($item instanceof Shovel){
|
||||||
$item->applyDamage(1);
|
$item->applyDamage(1);
|
||||||
$newBlock = VanillaBlocks::GRASS_PATH();
|
$newBlock = VanillaBlocks::GRASS_PATH();
|
||||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
||||||
$world->setBlock($this->position, $newBlock);
|
$world->setBlock($this->position, $newBlock);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -23,30 +23,16 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
|
|
||||||
final class HangingRoots extends Flowable{
|
final class HangingRoots extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
private function canBeSupportedBy(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return $block->isSolid(); //TODO: not sure if this is the correct logic
|
return $block->getAdjacentSupportType(Facing::UP)->hasCenterSupport(); //weird I know, but they can be placed on the bottom of fences
|
||||||
}
|
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if(!$blockReplace->getSide(Facing::UP)->isSolid()){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedBy($this->getSide(Facing::UP))){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||||
|
@ -68,9 +68,9 @@ class Hopper extends Transparent{
|
|||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return match($facing){
|
return match($facing){
|
||||||
Facing::UP => SupportType::FULL(),
|
Facing::UP => SupportType::FULL,
|
||||||
Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER() : SupportType::NONE(),
|
Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER : SupportType::NONE,
|
||||||
default => SupportType::NONE()
|
default => SupportType::NONE
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,13 +23,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\FortuneDropHelper;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\VanillaItems;
|
use pocketmine\item\VanillaItems;
|
||||||
|
|
||||||
final class IronOre extends Opaque{
|
final class IronOre extends Opaque{
|
||||||
|
|
||||||
public function getDropsForCompatibleTool(Item $item) : array{
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
return [VanillaItems::RAW_IRON()];
|
return [VanillaItems::RAW_IRON()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAffectedBySilkTouch() : bool{ return true; }
|
public function isAffectedBySilkTouch() : bool{ return true; }
|
||||||
|
@ -164,7 +164,7 @@ class ItemFrame extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||||
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
|
return $block->getAdjacentSupportType($face) !== SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
public function onNearbyBlockChange() : void{
|
||||||
|
@ -66,7 +66,7 @@ class Ladder extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
@ -85,6 +85,6 @@ class Ladder extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||||
return $block->getAdjacentSupportType($face)->equals(SupportType::FULL());
|
return $block->getAdjacentSupportType($face) === SupportType::FULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ class Lantern extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
|
@ -33,12 +33,13 @@ use pocketmine\item\VanillaItems;
|
|||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
use pocketmine\world\BlockTransaction;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
class Leaves extends Transparent{
|
class Leaves extends Transparent{
|
||||||
|
private const MAX_LOG_DISTANCE = 4;
|
||||||
|
|
||||||
protected LeavesType $leavesType; //immutable for now
|
protected LeavesType $leavesType; //immutable for now
|
||||||
protected bool $noDecay = false;
|
protected bool $noDecay = false;
|
||||||
protected bool $checkDecay = false;
|
protected bool $checkDecay = false;
|
||||||
@ -92,7 +93,7 @@ class Leaves extends Transparent{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($block instanceof Leaves && $distance <= 4){
|
if($block instanceof Leaves && $distance <= self::MAX_LOG_DISTANCE){
|
||||||
foreach(Facing::ALL as $side){
|
foreach(Facing::ALL as $side){
|
||||||
if($this->findLog($pos->getSide($side), $visited, $distance + 1)){
|
if($this->findLog($pos->getSide($side), $visited, $distance + 1)){
|
||||||
return true;
|
return true;
|
||||||
@ -111,7 +112,7 @@ class Leaves extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function ticksRandomly() : bool{
|
public function ticksRandomly() : bool{
|
||||||
return true;
|
return !$this->noDecay && $this->checkDecay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
@ -147,23 +148,22 @@ class Leaves extends Transparent{
|
|||||||
if(FortuneDropHelper::bonusChanceDivisor($item, 20, 4)){ //Saplings
|
if(FortuneDropHelper::bonusChanceDivisor($item, 20, 4)){ //Saplings
|
||||||
// TODO: according to the wiki, the jungle saplings have a different drop rate
|
// TODO: according to the wiki, the jungle saplings have a different drop rate
|
||||||
$sapling = (match($this->leavesType){
|
$sapling = (match($this->leavesType){
|
||||||
LeavesType::ACACIA() => VanillaBlocks::ACACIA_SAPLING(),
|
LeavesType::ACACIA => VanillaBlocks::ACACIA_SAPLING(),
|
||||||
LeavesType::BIRCH() => VanillaBlocks::BIRCH_SAPLING(),
|
LeavesType::BIRCH => VanillaBlocks::BIRCH_SAPLING(),
|
||||||
LeavesType::DARK_OAK() => VanillaBlocks::DARK_OAK_SAPLING(),
|
LeavesType::DARK_OAK => VanillaBlocks::DARK_OAK_SAPLING(),
|
||||||
LeavesType::JUNGLE() => VanillaBlocks::JUNGLE_SAPLING(),
|
LeavesType::JUNGLE => VanillaBlocks::JUNGLE_SAPLING(),
|
||||||
LeavesType::OAK() => VanillaBlocks::OAK_SAPLING(),
|
LeavesType::OAK => VanillaBlocks::OAK_SAPLING(),
|
||||||
LeavesType::SPRUCE() => VanillaBlocks::SPRUCE_SAPLING(),
|
LeavesType::SPRUCE => VanillaBlocks::SPRUCE_SAPLING(),
|
||||||
LeavesType::MANGROVE(), //TODO: mangrove propagule
|
LeavesType::MANGROVE, //TODO: mangrove propagule
|
||||||
LeavesType::AZALEA(), LeavesType::FLOWERING_AZALEA() => null, //TODO: azalea
|
LeavesType::AZALEA, LeavesType::FLOWERING_AZALEA => null, //TODO: azalea
|
||||||
LeavesType::CHERRY() => null, //TODO: cherry
|
LeavesType::CHERRY => null, //TODO: cherry
|
||||||
default => throw new AssumptionFailedError("Unreachable")
|
|
||||||
})?->asItem();
|
})?->asItem();
|
||||||
if($sapling !== null){
|
if($sapling !== null){
|
||||||
$drops[] = $sapling;
|
$drops[] = $sapling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(
|
if(
|
||||||
($this->leavesType->equals(LeavesType::OAK()) || $this->leavesType->equals(LeavesType::DARK_OAK())) &&
|
($this->leavesType === LeavesType::OAK || $this->leavesType === LeavesType::DARK_OAK) &&
|
||||||
FortuneDropHelper::bonusChanceDivisor($item, 200, 20)
|
FortuneDropHelper::bonusChanceDivisor($item, 200, 20)
|
||||||
){ //Apples
|
){ //Apples
|
||||||
$drops[] = VanillaItems::APPLE();
|
$drops[] = VanillaItems::APPLE();
|
||||||
@ -188,6 +188,6 @@ class Leaves extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ class Lectern extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isProducingSignal() : bool{ return $this->producingSignal; }
|
public function isProducingSignal() : bool{ return $this->producingSignal; }
|
||||||
|
@ -36,16 +36,11 @@ use pocketmine\world\sound\RedstonePowerOffSound;
|
|||||||
use pocketmine\world\sound\RedstonePowerOnSound;
|
use pocketmine\world\sound\RedstonePowerOnSound;
|
||||||
|
|
||||||
class Lever extends Flowable{
|
class Lever extends Flowable{
|
||||||
protected LeverFacing $facing;
|
protected LeverFacing $facing = LeverFacing::UP_AXIS_X;
|
||||||
protected bool $activated = false;
|
protected bool $activated = false;
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->facing = LeverFacing::UP_AXIS_X();
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->leverFacing($this->facing);
|
$w->enum($this->facing);
|
||||||
$w->bool($this->activated);
|
$w->bool($this->activated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,12 +72,12 @@ class Lever extends Flowable{
|
|||||||
return $x;
|
return $x;
|
||||||
};
|
};
|
||||||
$this->facing = match($face){
|
$this->facing = match($face){
|
||||||
Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X(), LeverFacing::DOWN_AXIS_Z()),
|
Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X, LeverFacing::DOWN_AXIS_Z),
|
||||||
Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X(), LeverFacing::UP_AXIS_Z()),
|
Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X, LeverFacing::UP_AXIS_Z),
|
||||||
Facing::NORTH => LeverFacing::NORTH(),
|
Facing::NORTH => LeverFacing::NORTH,
|
||||||
Facing::SOUTH => LeverFacing::SOUTH(),
|
Facing::SOUTH => LeverFacing::SOUTH,
|
||||||
Facing::WEST => LeverFacing::WEST(),
|
Facing::WEST => LeverFacing::WEST,
|
||||||
Facing::EAST => LeverFacing::EAST(),
|
Facing::EAST => LeverFacing::EAST,
|
||||||
default => throw new AssumptionFailedError("Bad facing value"),
|
default => throw new AssumptionFailedError("Bad facing value"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ final class Light extends Flowable{
|
|||||||
private int $level = self::MAX_LIGHT_LEVEL;
|
private int $level = self::MAX_LIGHT_LEVEL;
|
||||||
|
|
||||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(4, self::MIN_LIGHT_LEVEL, self::MAX_LIGHT_LEVEL, $this->level);
|
$w->boundedIntAuto(self::MIN_LIGHT_LEVEL, self::MAX_LIGHT_LEVEL, $this->level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLightLevel() : int{ return $this->level; }
|
public function getLightLevel() : int{ return $this->level; }
|
||||||
|
@ -49,7 +49,7 @@ abstract class Liquid extends Transparent{
|
|||||||
protected bool $still = false;
|
protected bool $still = false;
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->boundedInt(3, 0, self::MAX_DECAY, $this->decay);
|
$w->boundedIntAuto(0, self::MAX_DECAY, $this->decay);
|
||||||
$w->bool($this->falling);
|
$w->bool($this->falling);
|
||||||
$w->bool($this->still);
|
$w->bool($this->still);
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ abstract class Liquid extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDropsForCompatibleTool(Item $item) : array{
|
public function getDropsForCompatibleTool(Item $item) : array{
|
||||||
@ -165,23 +165,22 @@ abstract class Liquid extends Transparent{
|
|||||||
|
|
||||||
$vX = $vY = $vZ = 0;
|
$vX = $vY = $vZ = 0;
|
||||||
|
|
||||||
|
$x = $this->position->getFloorX();
|
||||||
|
$y = $this->position->getFloorY();
|
||||||
|
$z = $this->position->getFloorZ();
|
||||||
|
|
||||||
$decay = $this->getEffectiveFlowDecay($this);
|
$decay = $this->getEffectiveFlowDecay($this);
|
||||||
|
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
|
|
||||||
foreach(Facing::HORIZONTAL as $j){
|
foreach(Facing::HORIZONTAL as $j){
|
||||||
$x = $this->position->x;
|
[$dx, $dy, $dz] = Facing::OFFSET[$j];
|
||||||
$y = $this->position->y;
|
|
||||||
$z = $this->position->z;
|
|
||||||
|
|
||||||
match($j){
|
$sideX = $x + $dx;
|
||||||
Facing::WEST => --$x,
|
$sideY = $y + $dy;
|
||||||
Facing::EAST => ++$x,
|
$sideZ = $z + $dz;
|
||||||
Facing::NORTH => --$z,
|
|
||||||
Facing::SOUTH => ++$z
|
|
||||||
};
|
|
||||||
|
|
||||||
$sideBlock = $world->getBlockAt($x, $y, $z);
|
$sideBlock = $world->getBlockAt($sideX, $sideY, $sideZ);
|
||||||
$blockDecay = $this->getEffectiveFlowDecay($sideBlock);
|
$blockDecay = $this->getEffectiveFlowDecay($sideBlock);
|
||||||
|
|
||||||
if($blockDecay < 0){
|
if($blockDecay < 0){
|
||||||
@ -189,21 +188,21 @@ abstract class Liquid extends Transparent{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$blockDecay = $this->getEffectiveFlowDecay($world->getBlockAt($x, $y - 1, $z));
|
$blockDecay = $this->getEffectiveFlowDecay($world->getBlockAt($sideX, $sideY - 1, $sideZ));
|
||||||
|
|
||||||
if($blockDecay >= 0){
|
if($blockDecay >= 0){
|
||||||
$realDecay = $blockDecay - ($decay - 8);
|
$realDecay = $blockDecay - ($decay - 8);
|
||||||
$vX += ($x - $this->position->x) * $realDecay;
|
$vX += $dx * $realDecay;
|
||||||
$vY += ($y - $this->position->y) * $realDecay;
|
$vY += $dy * $realDecay;
|
||||||
$vZ += ($z - $this->position->z) * $realDecay;
|
$vZ += $dz * $realDecay;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}else{
|
}else{
|
||||||
$realDecay = $blockDecay - $decay;
|
$realDecay = $blockDecay - $decay;
|
||||||
$vX += ($x - $this->position->x) * $realDecay;
|
$vX += $dx * $realDecay;
|
||||||
$vY += ($y - $this->position->y) * $realDecay;
|
$vY += $dy * $realDecay;
|
||||||
$vZ += ($z - $this->position->z) * $realDecay;
|
$vZ += $dz * $realDecay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,10 +210,10 @@ abstract class Liquid extends Transparent{
|
|||||||
|
|
||||||
if($this->falling){
|
if($this->falling){
|
||||||
foreach(Facing::HORIZONTAL as $facing){
|
foreach(Facing::HORIZONTAL as $facing){
|
||||||
$pos = $this->position->getSide($facing);
|
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
|
||||||
if(
|
if(
|
||||||
!$this->canFlowInto($world->getBlockAt($pos->x, $pos->y, $pos->z)) ||
|
!$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy, $z + $dz)) ||
|
||||||
!$this->canFlowInto($world->getBlockAt($pos->x, $pos->y + 1, $pos->z))
|
!$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy + 1, $z + $dz))
|
||||||
){
|
){
|
||||||
$vector = $vector->normalize()->add(0, -6, 0);
|
$vector = $vector->normalize()->add(0, -6, 0);
|
||||||
break;
|
break;
|
||||||
@ -260,13 +259,17 @@ abstract class Liquid extends Transparent{
|
|||||||
|
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
|
|
||||||
|
$x = $this->position->getFloorX();
|
||||||
|
$y = $this->position->getFloorY();
|
||||||
|
$z = $this->position->getFloorZ();
|
||||||
|
|
||||||
if(!$this->isSource()){
|
if(!$this->isSource()){
|
||||||
$smallestFlowDecay = -100;
|
$smallestFlowDecay = -100;
|
||||||
$this->adjacentSources = 0;
|
$this->adjacentSources = 0;
|
||||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x, $this->position->y, $this->position->z - 1), $smallestFlowDecay);
|
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x, $y, $z - 1), $smallestFlowDecay);
|
||||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x, $this->position->y, $this->position->z + 1), $smallestFlowDecay);
|
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x, $y, $z + 1), $smallestFlowDecay);
|
||||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x - 1, $this->position->y, $this->position->z), $smallestFlowDecay);
|
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x - 1, $y, $z), $smallestFlowDecay);
|
||||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x + 1, $this->position->y, $this->position->z), $smallestFlowDecay);
|
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x + 1, $y, $z), $smallestFlowDecay);
|
||||||
|
|
||||||
$newDecay = $smallestFlowDecay + $multiplier;
|
$newDecay = $smallestFlowDecay + $multiplier;
|
||||||
$falling = false;
|
$falling = false;
|
||||||
@ -275,13 +278,13 @@ abstract class Liquid extends Transparent{
|
|||||||
$newDecay = -1;
|
$newDecay = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->getEffectiveFlowDecay($world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)) >= 0){
|
if($this->getEffectiveFlowDecay($world->getBlockAt($x, $y + 1, $z)) >= 0){
|
||||||
$falling = true;
|
$falling = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$minAdjacentSources = $this->getMinAdjacentSourcesToFormSource();
|
$minAdjacentSources = $this->getMinAdjacentSourcesToFormSource();
|
||||||
if($minAdjacentSources !== null && $this->adjacentSources >= $minAdjacentSources){
|
if($minAdjacentSources !== null && $this->adjacentSources >= $minAdjacentSources){
|
||||||
$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
|
$bottomBlock = $world->getBlockAt($x, $y - 1, $z);
|
||||||
if($bottomBlock->isSolid() || ($bottomBlock instanceof Liquid && $bottomBlock->hasSameTypeId($this) && $bottomBlock->isSource())){
|
if($bottomBlock->isSolid() || ($bottomBlock instanceof Liquid && $bottomBlock->hasSameTypeId($this) && $bottomBlock->isSource())){
|
||||||
$newDecay = 0;
|
$newDecay = 0;
|
||||||
$falling = false;
|
$falling = false;
|
||||||
@ -290,17 +293,17 @@ abstract class Liquid extends Transparent{
|
|||||||
|
|
||||||
if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
|
if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
|
||||||
if(!$falling && $newDecay < 0){
|
if(!$falling && $newDecay < 0){
|
||||||
$world->setBlock($this->position, VanillaBlocks::AIR());
|
$world->setBlockAt($x, $y, $z, VanillaBlocks::AIR());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->falling = $falling;
|
$this->falling = $falling;
|
||||||
$this->decay = $falling ? 0 : $newDecay;
|
$this->decay = $falling ? 0 : $newDecay;
|
||||||
$world->setBlock($this->position, $this); //local block update will cause an update to be scheduled
|
$world->setBlockAt($x, $y, $z, $this); //local block update will cause an update to be scheduled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
|
$bottomBlock = $world->getBlockAt($x, $y - 1, $z);
|
||||||
|
|
||||||
$this->flowIntoBlock($bottomBlock, 0, true);
|
$this->flowIntoBlock($bottomBlock, 0, true);
|
||||||
|
|
||||||
@ -313,8 +316,9 @@ abstract class Liquid extends Transparent{
|
|||||||
|
|
||||||
if($adjacentDecay <= self::MAX_DECAY){
|
if($adjacentDecay <= self::MAX_DECAY){
|
||||||
$calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), $this->canFlowInto(...));
|
$calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), $this->canFlowInto(...));
|
||||||
foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){
|
foreach($calculator->getOptimalFlowDirections($x, $y, $z) as $facing){
|
||||||
$this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false);
|
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
|
||||||
|
$this->flowIntoBlock($world->getBlockAt($x + $dx, $y + $dy, $z + $dz), $adjacentDecay, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,18 +39,13 @@ class MobHead extends Flowable{
|
|||||||
public const MIN_ROTATION = 0;
|
public const MIN_ROTATION = 0;
|
||||||
public const MAX_ROTATION = 15;
|
public const MAX_ROTATION = 15;
|
||||||
|
|
||||||
protected MobHeadType $mobHeadType;
|
protected MobHeadType $mobHeadType = MobHeadType::SKELETON;
|
||||||
|
|
||||||
protected int $facing = Facing::NORTH;
|
protected int $facing = Facing::NORTH;
|
||||||
protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
|
protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
|
||||||
$this->mobHeadType = MobHeadType::SKELETON(); //TODO: this should be a parameter
|
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||||
$w->mobHeadType($this->mobHeadType);
|
$w->enum($this->mobHeadType);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
|
@ -42,6 +42,6 @@ class MonsterSpawner extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class Mycelium extends Opaque{
|
|||||||
$z = mt_rand($this->position->z - 1, $this->position->z + 1);
|
$z = mt_rand($this->position->z - 1, $this->position->z + 1);
|
||||||
$world = $this->position->getWorld();
|
$world = $this->position->getWorld();
|
||||||
$block = $world->getBlockAt($x, $y, $z);
|
$block = $world->getBlockAt($x, $y, $z);
|
||||||
if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){
|
if($block instanceof Dirt && $block->getDirtType() === DirtType::NORMAL){
|
||||||
if($block->getSide(Facing::UP) instanceof Transparent){
|
if($block->getSide(Facing::UP) instanceof Transparent){
|
||||||
BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this);
|
BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this);
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class NetherPortal extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDrops(Item $item) : array{
|
public function getDrops(Item $item) : array{
|
||||||
|
39
src/block/NetherRoots.php
Normal file
39
src/block/NetherRoots.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
|
|
||||||
|
final class NetherRoots extends Flowable{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
//TODO: nylium, moss
|
||||||
|
$supportBlock = $block->getSide(Facing::DOWN);
|
||||||
|
return
|
||||||
|
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||||
|
$supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||||
|
}
|
||||||
|
}
|
@ -23,9 +23,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\FortuneDropHelper;
|
use pocketmine\block\utils\FortuneDropHelper;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
|
||||||
use pocketmine\entity\Entity;
|
use pocketmine\entity\Entity;
|
||||||
use pocketmine\event\block\StructureGrowEvent;
|
use pocketmine\event\block\StructureGrowEvent;
|
||||||
use pocketmine\item\Fertilizer;
|
use pocketmine\item\Fertilizer;
|
||||||
@ -41,44 +42,23 @@ use function mt_rand;
|
|||||||
* This class is used for Weeping & Twisting vines, because they have same behaviour
|
* This class is used for Weeping & Twisting vines, because they have same behaviour
|
||||||
*/
|
*/
|
||||||
class NetherVines extends Flowable{
|
class NetherVines extends Flowable{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 25;
|
public const MAX_AGE = 25;
|
||||||
|
|
||||||
/** Direction the vine grows towards. */
|
/** Direction the vine grows towards. */
|
||||||
private int $growthFace;
|
private int $growthFace;
|
||||||
|
|
||||||
protected int $age = 0;
|
|
||||||
|
|
||||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $growthFace){
|
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $growthFace){
|
||||||
$this->growthFace = $growthFace;
|
$this->growthFace = $growthFace;
|
||||||
parent::__construct($idInfo, $name, $typeInfo);
|
parent::__construct($idInfo, $name, $typeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{
|
|
||||||
return $this->age;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAffectedBySilkTouch() : bool{
|
public function isAffectedBySilkTouch() : bool{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ticksRandomly() : bool{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canClimb() : bool{
|
public function canClimb() : bool{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -88,12 +68,6 @@ class NetherVines extends Flowable{
|
|||||||
return $supportBlock->getSupportType($this->growthFace)->hasCenterSupport() || $supportBlock->hasSameTypeId($this);
|
return $supportBlock->getSupportType($this->growthFace)->hasCenterSupport() || $supportBlock->hasSameTypeId($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the block at the end of the vine structure furthest from the supporting block.
|
* Returns the block at the end of the vine structure furthest from the supporting block.
|
||||||
*/
|
*/
|
||||||
@ -106,9 +80,6 @@ class NetherVines extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if(!$this->canBeSupportedAt($blockReplace)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->age = mt_rand(0, self::MAX_AGE - 1);
|
$this->age = mt_rand(0, self::MAX_AGE - 1);
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||||
}
|
}
|
||||||
@ -123,8 +94,12 @@ class NetherVines extends Flowable{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ticksRandomly() : bool{
|
||||||
|
return $this->age < self::MAX_AGE;
|
||||||
|
}
|
||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
if(mt_rand(1, 10) === 1 && $this->age < self::MAX_AGE){
|
if($this->age < self::MAX_AGE && mt_rand(1, 10) === 1){
|
||||||
if($this->getSide($this->growthFace)->canBeReplaced()){
|
if($this->getSide($this->growthFace)->canBeReplaced()){
|
||||||
$this->grow(null);
|
$this->grow(null);
|
||||||
}
|
}
|
||||||
@ -184,6 +159,6 @@ class NetherVines extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,53 +23,26 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\FortuneDropHelper;
|
use pocketmine\block\utils\FortuneDropHelper;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
use function mt_rand;
|
use function mt_rand;
|
||||||
|
|
||||||
class NetherWartPlant extends Flowable{
|
class NetherWartPlant extends Flowable{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
public const MAX_AGE = 3;
|
public const MAX_AGE = 3;
|
||||||
|
|
||||||
protected int $age = 0;
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::SOUL_SAND;
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
|
||||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAge() : int{ return $this->age; }
|
|
||||||
|
|
||||||
/** @return $this */
|
|
||||||
public function setAge(int $age) : self{
|
|
||||||
if($age < 0 || $age > self::MAX_AGE){
|
|
||||||
throw new \InvalidArgumentException("Age must be in range 0 ..." . self::MAX_AGE);
|
|
||||||
}
|
|
||||||
$this->age = $age;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
$down = $this->getSide(Facing::DOWN);
|
|
||||||
if($down->getTypeId() === BlockTypeIds::SOUL_SAND){
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::SOUL_SAND){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ticksRandomly() : bool{
|
public function ticksRandomly() : bool{
|
||||||
return true;
|
return $this->age < self::MAX_AGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRandomTick() : void{
|
public function onRandomTick() : void{
|
||||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
|||||||
|
|
||||||
use pocketmine\block\utils\BlockEventHelper;
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||||
use pocketmine\item\Fertilizer;
|
use pocketmine\item\Fertilizer;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
@ -35,6 +36,9 @@ use pocketmine\world\BlockTransaction;
|
|||||||
|
|
||||||
class PinkPetals extends Flowable{
|
class PinkPetals extends Flowable{
|
||||||
use HorizontalFacingTrait;
|
use HorizontalFacingTrait;
|
||||||
|
use StaticSupportTrait {
|
||||||
|
canBePlacedAt as supportedWhenPlacedAt;
|
||||||
|
}
|
||||||
|
|
||||||
public const MIN_COUNT = 1;
|
public const MIN_COUNT = 1;
|
||||||
public const MAX_COUNT = 4;
|
public const MAX_COUNT = 4;
|
||||||
@ -43,7 +47,7 @@ class PinkPetals extends Flowable{
|
|||||||
|
|
||||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||||
$w->horizontalFacing($this->facing);
|
$w->horizontalFacing($this->facing);
|
||||||
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCount() : int{
|
public function getCount() : int{
|
||||||
@ -65,20 +69,11 @@ class PinkPetals extends Flowable{
|
|||||||
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||||
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
|
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
|
||||||
$this->count = $blockReplace->getCount() + 1;
|
$this->count = $blockReplace->getCount() + 1;
|
||||||
$this->facing = $blockReplace->getFacing();
|
$this->facing = $blockReplace->getFacing();
|
||||||
|
105
src/block/PitcherCrop.php
Normal file
105
src/block/PitcherCrop.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\AgeableTrait;
|
||||||
|
use pocketmine\block\utils\BlockEventHelper;
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
|
use pocketmine\event\block\StructureGrowEvent;
|
||||||
|
use pocketmine\item\Fertilizer;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\math\Axis;
|
||||||
|
use pocketmine\math\AxisAlignedBB;
|
||||||
|
use pocketmine\math\Facing;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\world\BlockTransaction;
|
||||||
|
use function mt_rand;
|
||||||
|
|
||||||
|
final class PitcherCrop extends Flowable{
|
||||||
|
use AgeableTrait;
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
|
public const MAX_AGE = 2;
|
||||||
|
|
||||||
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
|
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recalculateCollisionBoxes() : array{
|
||||||
|
$widthTrim = $this->age === 0 ? 5 : 3;
|
||||||
|
$heightTrim = $this->age === 0 ? 13 : 11;
|
||||||
|
return [
|
||||||
|
AxisAlignedBB::one()
|
||||||
|
->trim(Facing::UP, $heightTrim / 16)
|
||||||
|
->squash(Axis::X, $widthTrim / 16)
|
||||||
|
->squash(Axis::Z, $widthTrim / 16)
|
||||||
|
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function grow(?Player $player) : bool{
|
||||||
|
if($this->age > self::MAX_AGE){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->age === self::MAX_AGE){
|
||||||
|
$up = $this->getSide(Facing::UP);
|
||||||
|
if($up->getTypeId() !== BlockTypeIds::AIR){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tx = new BlockTransaction($this->position->getWorld());
|
||||||
|
$tx->addBlock($this->position, VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(false));
|
||||||
|
$tx->addBlock($this->position->up(), VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(true));
|
||||||
|
|
||||||
|
$ev = new StructureGrowEvent($this, $tx, $player);
|
||||||
|
$ev->call();
|
||||||
|
|
||||||
|
return !$ev->isCancelled() && $tx->apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlockEventHelper::grow($this, (clone $this)->setAge($this->age + 1), $player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||||
|
if($item instanceof Fertilizer && $this->grow($player)){
|
||||||
|
$item->pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ticksRandomly() : bool{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRandomTick() : void{
|
||||||
|
//TODO: the growth speed is influenced by farmland and nearby crops
|
||||||
|
if(mt_rand(0, 2) === 0){
|
||||||
|
$this->grow(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,21 +23,19 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block;
|
namespace pocketmine\block;
|
||||||
|
|
||||||
|
use pocketmine\block\utils\StaticSupportTrait;
|
||||||
use pocketmine\block\utils\SupportType;
|
use pocketmine\block\utils\SupportType;
|
||||||
use pocketmine\entity\Entity;
|
use pocketmine\entity\Entity;
|
||||||
use pocketmine\event\block\PressurePlateUpdateEvent;
|
use pocketmine\event\block\PressurePlateUpdateEvent;
|
||||||
use pocketmine\item\Item;
|
|
||||||
use pocketmine\math\Axis;
|
use pocketmine\math\Axis;
|
||||||
use pocketmine\math\AxisAlignedBB;
|
use pocketmine\math\AxisAlignedBB;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\player\Player;
|
|
||||||
use pocketmine\world\BlockTransaction;
|
|
||||||
use pocketmine\world\sound\PressurePlateActivateSound;
|
use pocketmine\world\sound\PressurePlateActivateSound;
|
||||||
use pocketmine\world\sound\PressurePlateDeactivateSound;
|
use pocketmine\world\sound\PressurePlateDeactivateSound;
|
||||||
use function count;
|
use function count;
|
||||||
|
|
||||||
abstract class PressurePlate extends Transparent{
|
abstract class PressurePlate extends Transparent{
|
||||||
|
use StaticSupportTrait;
|
||||||
|
|
||||||
private readonly int $deactivationDelayTicks;
|
private readonly int $deactivationDelayTicks;
|
||||||
|
|
||||||
@ -60,24 +58,11 @@ abstract class PressurePlate extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportType(int $facing) : SupportType{
|
public function getSupportType(int $facing) : SupportType{
|
||||||
return SupportType::NONE();
|
return SupportType::NONE;
|
||||||
}
|
|
||||||
|
|
||||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
|
||||||
if($this->canBeSupportedAt($blockReplace)){
|
|
||||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canBeSupportedAt(Block $block) : bool{
|
private function canBeSupportedAt(Block $block) : bool{
|
||||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
|
||||||
}
|
|
||||||
|
|
||||||
public function onNearbyBlockChange() : void{
|
|
||||||
if(!$this->canBeSupportedAt($this)){
|
|
||||||
$this->position->getWorld()->useBreakOn($this->position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasEntityCollision() : bool{
|
public function hasEntityCollision() : bool{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user