mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-13 04:45:08 +00:00
Compare commits
559 Commits
5.0.0-ALPH
...
5.0.0-ALPH
Author | SHA1 | Date | |
---|---|---|---|
da4315df05 | |||
83bfe790fa | |||
16f90f4120 | |||
ec59dc1c80 | |||
973b8130db | |||
99faeb8d05 | |||
1305fd5fb2 | |||
7a137f932f | |||
63e8b1cf3a | |||
65bce762ff | |||
529700bb8b | |||
f38b15cf83 | |||
0efd928db6 | |||
9809909072 | |||
de3af9e660 | |||
a30c649607 | |||
d7ebabd771 | |||
be1087c071 | |||
b27c47335c | |||
58eec637c1 | |||
4f86ea9933 | |||
6e2685cbbb | |||
b2017c8462 | |||
bf44edd179 | |||
44e288554a | |||
ffa88aff67 | |||
aa374083d8 | |||
1785cbb6b5 | |||
3d75094874 | |||
8c0d3943d8 | |||
d02c6668b2 | |||
880d01daea | |||
50b70708fb | |||
ba4d038972 | |||
437fa615b8 | |||
0ee6cdb058 | |||
97d6a79b25 | |||
8b5e4c1c16 | |||
c5d716dc9d | |||
4d79aced07 | |||
95d0a3bf41 | |||
cf707e15c2 | |||
1308cda5c2 | |||
214a5ddc15 | |||
9f7dfe3355 | |||
9b55d18393 | |||
84f9136b95 | |||
31465525e3 | |||
74613b9b09 | |||
1cefe24414 | |||
4357c110c8 | |||
8bf85d4a18 | |||
b5e6dec0c6 | |||
a3306914cc | |||
3b32ea1b0b | |||
7ec32f981e | |||
b0c87e9d06 | |||
99996b62d6 | |||
fed2a6d917 | |||
8e600b4a78 | |||
1d4b6dc66e | |||
1cb6d9f5af | |||
18c2e90574 | |||
142ccc7e87 | |||
174c9a48f5 | |||
0a9b52618d | |||
7ae6425d05 | |||
b5cfab497d | |||
ca3b5c38b7 | |||
bc8def3be1 | |||
774e23137e | |||
3984d220bb | |||
43bc3c7b25 | |||
eb62dc3294 | |||
28d8526d8d | |||
0b497654f2 | |||
279056fe2f | |||
cd233b123b | |||
64dd5e3bf6 | |||
4e5cc57560 | |||
d476a4c1aa | |||
95263795a8 | |||
2db86d151f | |||
8f20b9da91 | |||
b0c6e8d8e0 | |||
f0358b09b7 | |||
80a432d9ff | |||
ec9f9a469f | |||
d450264e1c | |||
5e92f55d35 | |||
58c1bfe5d2 | |||
d03caafc2b | |||
641d35a30f | |||
642630a4d2 | |||
b6982a84ef | |||
d634b3de18 | |||
d79e6354a0 | |||
ff63983de4 | |||
588215044e | |||
5c1e9a35a9 | |||
aad9f5fb45 | |||
a8dca190c6 | |||
fdb07cdbcd | |||
3b6ff3c42b | |||
e5f5fe80f9 | |||
36ab34df29 | |||
23ae0c7cac | |||
1bc5e225ab | |||
dda8ff18b1 | |||
6562335120 | |||
0c463a8721 | |||
a66f966b08 | |||
cca22046ab | |||
858d3dce8e | |||
d19f0bf7be | |||
ff2391e74a | |||
39e10da88d | |||
34839da757 | |||
b3b8516661 | |||
90305a0b92 | |||
613ce251c5 | |||
beed6efd4e | |||
e5bc4deb12 | |||
2fcff13578 | |||
b4b8ef1c6b | |||
9650b7f03a | |||
a205d64732 | |||
a3502a711d | |||
83ddcce987 | |||
732dac6fc1 | |||
d5e3636908 | |||
ef100b248b | |||
d03bbb0426 | |||
93e661aa4e | |||
50efcf7424 | |||
a7ac6070dc | |||
069062f122 | |||
bf7014e0ec | |||
824ed0a56a | |||
b3ccf41307 | |||
a39938e6b6 | |||
d5bf88acc0 | |||
2d0602d19f | |||
1a5cc8212c | |||
3a2a23b236 | |||
1a8c8af523 | |||
1e9d83f014 | |||
68e862b6fa | |||
c915334c2b | |||
6153a2ac70 | |||
ed452b9ccd | |||
c19880e045 | |||
01bad344a0 | |||
cdbdcb5d67 | |||
29301614e8 | |||
2fdc46c165 | |||
bfd1b2c635 | |||
d9638cef96 | |||
1671405cd0 | |||
fe982c697b | |||
1572b31b8d | |||
fba4895a17 | |||
b6f6671a81 | |||
6da467b142 | |||
44af519cd6 | |||
fb31e6085e | |||
e4548da173 | |||
0d5287bf0b | |||
a9361b3f8b | |||
6e4c62744e | |||
9a0ead6deb | |||
d74824c8d5 | |||
d4eb73abe9 | |||
d1d5020c53 | |||
9f6c6b2b71 | |||
bd01a919e5 | |||
7864294336 | |||
2a910c1cc2 | |||
cd04a3db2e | |||
572def9245 | |||
20f5bed926 | |||
14d17a9546 | |||
b74c092d9b | |||
53cae8911d | |||
7bcc663b60 | |||
b3bda788d9 | |||
2cc8a56e68 | |||
b70f7afbd6 | |||
92783a6db9 | |||
57deb60355 | |||
92e47b98f8 | |||
b84c110819 | |||
e1b0e00c6f | |||
774df0fa4c | |||
f88ae93897 | |||
4fadb63f67 | |||
c83f0896ac | |||
0d29a138fb | |||
421379fc77 | |||
293cea7714 | |||
15645759e9 | |||
8dc0d506f4 | |||
8c651ce4b4 | |||
db7cee6f22 | |||
6ae7cb288e | |||
7df2719fce | |||
10b8dcfdd1 | |||
c1fbac412e | |||
3feaa18f6c | |||
1c6a2b66f7 | |||
41970feb57 | |||
0edf2ea6a4 | |||
cd4bb91676 | |||
2be527060f | |||
6f68c6d8a0 | |||
ac16378410 | |||
1f9dfa77bf | |||
0c7f8470b9 | |||
fc56c041f3 | |||
d6bbf8217d | |||
22486dd75e | |||
37ec1193ea | |||
1366a43c22 | |||
383dc2a2b9 | |||
bda0ca23b4 | |||
b21cd82e94 | |||
1c7b1e9e5d | |||
4b41b2f9ae | |||
b87e4d8bd3 | |||
a6cc611e9f | |||
86a2f8e360 | |||
def2f8c145 | |||
6f4ea886b0 | |||
ed7c95549d | |||
cfb0cad7e0 | |||
83e5b0adb6 | |||
4650a3bb22 | |||
5e5661de75 | |||
e2f1b10165 | |||
455f9fa92e | |||
dec188d4ad | |||
2db3498891 | |||
ab0202ba29 | |||
9295afe8b9 | |||
a7dfa0907c | |||
f448b2e685 | |||
6a0c54f850 | |||
77a18d0aea | |||
140a809c40 | |||
cb7c136035 | |||
1bc8fb1851 | |||
f6a9949942 | |||
18c9ccb01c | |||
b6289e5807 | |||
24ac543313 | |||
83a136a176 | |||
3f7d8a3777 | |||
3c55db531d | |||
1609b11c8e | |||
cf9610c710 | |||
590eb74703 | |||
c24370b8ac | |||
93d4475111 | |||
e4fc523251 | |||
b39eaaf91f | |||
7804172846 | |||
7d29ac8293 | |||
481bda8cd5 | |||
d1c75da14b | |||
89e29448ee | |||
66e70e5b0e | |||
785dc71256 | |||
d459afaa54 | |||
db586233da | |||
23e98a30f5 | |||
5bc7ca6569 | |||
f39d2a9be3 | |||
2a982d48ad | |||
47d98af6ac | |||
9f97654f6f | |||
f80ffd8de0 | |||
82ba7903c8 | |||
441b06f6c7 | |||
112974758e | |||
313850eec4 | |||
a82923acb4 | |||
67887afd6b | |||
3d03bb1301 | |||
c063198b89 | |||
f3ca6de1eb | |||
88eafdd614 | |||
6dd5fec4ea | |||
6866c86d39 | |||
a735a69870 | |||
a0ea74c08f | |||
ca4b8a5827 | |||
f88c4d9a8c | |||
66cd156d80 | |||
222049927a | |||
d72e947d15 | |||
770cca2efa | |||
033dac3d16 | |||
1ee02d7e02 | |||
85678aa356 | |||
1d253bc8c2 | |||
973a56ab28 | |||
9e0b4621be | |||
ffb3af3e0d | |||
b3f03d7ae6 | |||
2585160ca2 | |||
14d3e6c7d5 | |||
65ec318c30 | |||
a25cb3741a | |||
b7a15b6e01 | |||
456439566b | |||
d5762d3f44 | |||
5c5d96d00b | |||
4dabac8420 | |||
5b89833d5c | |||
fb25e05416 | |||
7fd4c12ea1 | |||
78b5be8dd0 | |||
0a92e91a30 | |||
b3a13a2f21 | |||
08b9495bce | |||
4bd2325828 | |||
64ac20173b | |||
ca3612e4ff | |||
6799dcff51 | |||
5779622235 | |||
d4c4ae3d7a | |||
c16893cbac | |||
7f175b47e6 | |||
0e73ffe555 | |||
1ffd38b37b | |||
bd13f39156 | |||
0c446c276c | |||
0284e65f60 | |||
b0d787b3d3 | |||
65e3ed43d5 | |||
75eba9c9ed | |||
b5a049d1fe | |||
bd9fcffe62 | |||
feffbc2c5b | |||
53b51c99b4 | |||
5cb77c8365 | |||
bf8befc40b | |||
f75ca312cc | |||
d144832928 | |||
709a869045 | |||
ac056044ce | |||
fc8434308b | |||
5426b41447 | |||
af2babec23 | |||
fedd541663 | |||
1ecb10acba | |||
5d5366a7c8 | |||
717ab1989a | |||
83db186b6a | |||
6a4e5aba8b | |||
c13170a00b | |||
98778052bb | |||
e86e8254a8 | |||
1b852ac290 | |||
10b799fadb | |||
bc5008334a | |||
d6af2b12f4 | |||
ad2d59923c | |||
792c1b62b7 | |||
e90abecf38 | |||
575dd47db7 | |||
e4a5defabb | |||
6e8f11d5e8 | |||
c9626c610b | |||
b65e0f64f6 | |||
8fb7fff6b9 | |||
5c8d8ff61f | |||
99b55f7427 | |||
dce8bd6d21 | |||
8fa81242d6 | |||
223de3ad23 | |||
2f4a9469b6 | |||
8e97e9dcda | |||
304bb84af2 | |||
cb020988d4 | |||
eeb95872ea | |||
1b0d353fa3 | |||
4d34885b15 | |||
c5b2488fc1 | |||
d62df585f2 | |||
19d7c2b552 | |||
d4f96a155a | |||
43a3151de3 | |||
f7ab0a3b92 | |||
bf4f6e5d53 | |||
036e06e889 | |||
9343a0b800 | |||
14b4644b03 | |||
464b65b25c | |||
f1c571a528 | |||
15586ed80e | |||
0f8ad8ecf7 | |||
82b9afef77 | |||
2fc84f6c67 | |||
566f5935a3 | |||
44e4dabf6e | |||
8acc535218 | |||
e9a1cb7ce5 | |||
a21419d120 | |||
c2b599166c | |||
df7a1fcba6 | |||
d77a95e4af | |||
5c72807b16 | |||
5c6927e16c | |||
9abbb85a93 | |||
554182b2cb | |||
d669a6f0c7 | |||
723ae9eca0 | |||
79125b8426 | |||
6ba3b39541 | |||
f4de4bd971 | |||
c8a8e33fc1 | |||
16ed16722a | |||
42f9336f7a | |||
5d9f783037 | |||
01ca14c314 | |||
608c6ed6db | |||
c26631d06d | |||
b75bc61a64 | |||
3724479be3 | |||
eb916fe43d | |||
5e3b3a0700 | |||
e3640907ba | |||
d9b050fb68 | |||
817591910b | |||
6a2315a63d | |||
89b784734e | |||
b751207969 | |||
cffa3b8a72 | |||
91e91b1d9f | |||
102406ee79 | |||
4419161a49 | |||
38e495babf | |||
17635e770b | |||
b13f333b2e | |||
a7313ed9d9 | |||
e10a624444 | |||
b20e04539d | |||
4852f8029a | |||
bedf79e2cd | |||
f6c9bf5cd1 | |||
2d2df22ee7 | |||
2940547026 | |||
24e72ec109 | |||
47adcf763d | |||
67682cbf27 | |||
c1acf44337 | |||
466307a43f | |||
44c4118080 | |||
c4f85e526b | |||
6cee428287 | |||
bcba064d69 | |||
2fa92e5e02 | |||
dbc0b9634b | |||
86647683bc | |||
fa201b081c | |||
de3bbb71fa | |||
040516054f | |||
64f0e58e60 | |||
62f21516d1 | |||
c553f7cf06 | |||
eccfb3bbe2 | |||
87b840ff97 | |||
f64e306fb8 | |||
9a8902d1fe | |||
b36b65927c | |||
afaf9dbc88 | |||
b0a492c063 | |||
6d4279671e | |||
cf34f88a67 | |||
b8d1b00985 | |||
8660dfe576 | |||
172bd9a129 | |||
012b668537 | |||
fec89b7803 | |||
4f2f9b4352 | |||
b3f8b5ff37 | |||
81edb1bed4 | |||
d0ff6d2e36 | |||
4afd3dcabf | |||
4e3964ffce | |||
c7f5215a51 | |||
66f2116e57 | |||
c15c59ae0d | |||
07786dc4bc | |||
259f44e57c | |||
93254523e6 | |||
2b61c025c2 | |||
e5804df24b | |||
e00f8e3a32 | |||
c8320fe849 | |||
e2855aadff | |||
21ed5a450f | |||
ba2baba7cc | |||
c7133bc2e6 | |||
d4f4fda442 | |||
4d6ec66270 | |||
baf75089f5 | |||
b4ce5ed515 | |||
eb8fb63409 | |||
705df7d508 | |||
91719051e2 | |||
d321094081 | |||
323d31005f | |||
0c7370e564 | |||
3dd4c42fd3 | |||
f1a63098bd | |||
75d7adfb2d | |||
7dd8876515 | |||
d0067cfac5 | |||
eafc23c756 | |||
20cb67461f | |||
8b2d941502 | |||
dea0207e4e | |||
4b1052022c | |||
32f9fcd4e9 | |||
9d535e2917 | |||
3ccd288afd | |||
06655bee78 | |||
2ba51567d8 | |||
14933a731b | |||
a22276e679 | |||
260e54e4b1 | |||
a44c089f98 | |||
5e70ae2066 | |||
ad7528e3f3 | |||
99ff78a8a5 | |||
9ffee7cfc3 | |||
ccb3c3cb05 | |||
151f2c3f3a | |||
66d655731a | |||
54a773be0c | |||
d894c5e97f | |||
419b21281d | |||
56e6a55645 | |||
c67e42a723 | |||
3e4f01d85e | |||
da9937933b | |||
2142eb3cc9 | |||
690efb09e3 | |||
e4d24e1edd | |||
4d6fb2b925 | |||
0ad2985247 | |||
976502e3db | |||
b0c76f4db5 | |||
8886a023f1 | |||
ae70c63798 | |||
986daab511 | |||
eb404bddb4 |
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -85,7 +85,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
100
.github/workflows/discord-release-embed.php
vendored
Normal file
100
.github/workflows/discord-release-embed.php
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\utils\Internet;
|
||||
use function dirname;
|
||||
use function fwrite;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const STDERR;
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* @phpstan-return array<string, mixed>
|
||||
*/
|
||||
function generateDiscordEmbed(string $version, string $channel, string $description, string $detailsUrl, string $sourceUrl, string $pharDownloadUrl, string $buildLogUrl) : array{
|
||||
return [
|
||||
"embeds" => [
|
||||
[
|
||||
"title" => "New PocketMine-MP release: $version ($channel)",
|
||||
"description" => <<<DESCRIPTION
|
||||
$description
|
||||
|
||||
[Details]($detailsUrl) | [Source Code]($sourceUrl) | [Build Log]($buildLogUrl) | [Download]($pharDownloadUrl)
|
||||
DESCRIPTION,
|
||||
"url" => $detailsUrl,
|
||||
"color" => $channel === "stable" ? 0x57ab5a : 0xc69026
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if(count($argv) !== 5){
|
||||
fwrite(STDERR, "Required arguments: github repo, version, API token\n");
|
||||
exit(1);
|
||||
}
|
||||
[, $repo, $tagName, $token, $hookURL] = $argv;
|
||||
|
||||
$result = Internet::getURL('https://api.github.com/repos/' . $repo . '/releases/tags/' . $tagName, extraHeaders: [
|
||||
'Authorization: token ' . $token
|
||||
]);
|
||||
if($result === null){
|
||||
fwrite(STDERR, "failed to access GitHub API\n");
|
||||
return;
|
||||
}
|
||||
if($result->getCode() !== 200){
|
||||
fwrite(STDERR, "Error accessing GitHub API: " . $result->getCode() . "\n");
|
||||
fwrite(STDERR, $result->getBody() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$releaseInfoJson = json_decode($result->getBody(), true, JSON_THROW_ON_ERROR);
|
||||
if(!is_array($releaseInfoJson)){
|
||||
fwrite(STDERR, "Invalid release JSON returned from GitHub API\n");
|
||||
exit(1);
|
||||
}
|
||||
$buildInfoPath = 'https://github.com/' . $repo . '/releases/download/' . $tagName . '/build_info.json';
|
||||
|
||||
$buildInfoResult = Internet::getURL($buildInfoPath, extraHeaders: [
|
||||
'Authorization: token ' . $token
|
||||
]);
|
||||
if($buildInfoResult === null){
|
||||
fwrite(STDERR, "missing build_info.json\n");
|
||||
exit(1);
|
||||
}
|
||||
if($buildInfoResult->getCode() !== 200){
|
||||
fwrite(STDERR, "error accessing build_info.json: " . $buildInfoResult->getCode() . "\n");
|
||||
fwrite(STDERR, $buildInfoResult->getBody() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$buildInfoJson = json_decode($buildInfoResult->getBody(), true, JSON_THROW_ON_ERROR);
|
||||
if(!is_array($buildInfoJson)){
|
||||
fwrite(STDERR, "invalid build_info.json\n");
|
||||
exit(1);
|
||||
}
|
||||
$detailsUrl = $buildInfoJson["details_url"];
|
||||
$sourceUrl = $buildInfoJson["source_url"];
|
||||
$pharDownloadUrl = $buildInfoJson["download_url"];
|
||||
$buildLogUrl = $buildInfoJson["build_log_url"];
|
||||
|
||||
$description = $releaseInfoJson["body"];
|
||||
|
||||
$discordPayload = generateDiscordEmbed($buildInfoJson["base_version"], $buildInfoJson["channel"], $description, $detailsUrl, $sourceUrl, $pharDownloadUrl, $buildLogUrl);
|
||||
|
||||
$response = Internet::postURL(
|
||||
$hookURL,
|
||||
json_encode($discordPayload, JSON_THROW_ON_ERROR),
|
||||
extraHeaders: ['Content-Type: application/json']
|
||||
);
|
||||
if($response?->getCode() !== 204){
|
||||
fwrite(STDERR, "failed to send Discord webhook\n");
|
||||
fwrite(STDERR, $response?->getBody() ?? "no response body\n");
|
||||
exit(1);
|
||||
}
|
38
.github/workflows/discord-release-notify.yml
vendored
Normal file
38
.github/workflows/discord-release-notify.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Notify Discord webhook of release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.22.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
|
||||
|
||||
- name: Get actual tag name
|
||||
id: tag-name
|
||||
run: echo ::set-output name=TAG_NAME::$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
|
||||
|
||||
- name: Run webhook post script
|
||||
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
|
8
.github/workflows/draft-release.yml
vendored
8
.github/workflows/draft-release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.20.0
|
||||
uses: shivammathur/setup-php@2.22.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
|
||||
@ -57,7 +57,7 @@ jobs:
|
||||
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
|
||||
|
||||
- name: Generate build info
|
||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} > build_info.json
|
||||
run: php build/generate-build-info-json.php ${{ github.sha }} ${{ steps.get-pm-version.outputs.PM_VERSION }} ${{ github.repository }} ${{ steps.build-number.outputs.BUILD_NUMBER }} ${{ github.run_id }} > build_info.json
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
@ -69,7 +69,7 @@ jobs:
|
||||
${{ github.workspace }}/build_info.json
|
||||
|
||||
- name: Create draft release
|
||||
uses: ncipollo/release-action@v1.10.0
|
||||
uses: ncipollo/release-action@v1.12.0
|
||||
with:
|
||||
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
|
||||
commit: ${{ github.sha }}
|
||||
@ -80,4 +80,4 @@ jobs:
|
||||
body: |
|
||||
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
|
||||
|
||||
Please see the [changelogs](/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
|
||||
Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
|
||||
|
24
.github/workflows/main.yml
vendored
24
.github/workflows/main.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.23, 8.1.10]
|
||||
|
||||
steps:
|
||||
- name: Build and prepare PHP cache
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -31,13 +31,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.23, 8.1.10]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -69,13 +69,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.23, 8.1.10]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -107,7 +107,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.23, 8.1.10]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -115,7 +115,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -147,13 +147,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.18]
|
||||
php: [8.0.23, 8.1.10]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
|
||||
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
@ -198,10 +198,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.20.0
|
||||
uses: shivammathur/setup-php@2.22.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer:3.2
|
||||
tools: php-cs-fixer:3.11
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff --ansi
|
||||
|
2
.github/workflows/support.yml
vendored
2
.github/workflows/support.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
support:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/support-requests@v2
|
||||
- uses: dessant/support-requests@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
support-label: "Support request"
|
||||
|
3
.github/workflows/update-php-versions.php
vendored
3
.github/workflows/update-php-versions.php
vendored
@ -22,7 +22,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const VERSIONS = [
|
||||
"8.0"
|
||||
"8.0",
|
||||
"8.1"
|
||||
];
|
||||
|
||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||
|
5
.idea/codeStyles/Project.xml
generated
5
.idea/codeStyles/Project.xml
generated
@ -62,6 +62,11 @@
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Shell Script">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="neon">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
|
@ -66,10 +66,17 @@ BODY,
|
||||
],
|
||||
'indentation_type' => true,
|
||||
'logical_operators' => true,
|
||||
'native_constant_invocation' => [
|
||||
'scope' => 'namespaced'
|
||||
],
|
||||
'native_function_invocation' => [
|
||||
'scope' => 'namespaced',
|
||||
'include' => ['@all'],
|
||||
],
|
||||
'new_with_braces' => [
|
||||
'named_class' => true,
|
||||
'anonymous_class' => false,
|
||||
],
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
@ -88,6 +95,12 @@ BODY,
|
||||
],
|
||||
'sort_algorithm' => 'alpha'
|
||||
],
|
||||
'phpdoc_align' => [
|
||||
'align' => 'vertical',
|
||||
'tags' => [
|
||||
'param',
|
||||
]
|
||||
],
|
||||
'phpdoc_line_span' => [
|
||||
'property' => 'single',
|
||||
'method' => null,
|
||||
|
@ -18,6 +18,32 @@ Larger contributions like feature additions should be preceded by a [Change Prop
|
||||
## Other things you'll need
|
||||
- [git](https://git-scm.com/)
|
||||
|
||||
## Choosing a target branch
|
||||
PocketMine-MP has three primary branches of development.
|
||||
|
||||
| Type of change | `stable` | `next-minor` | `next-major` |
|
||||
|:---------------|:--------:|:------------:|:------------:|
|
||||
| Bug fixes | ✔️ | ✔️ | ✔️ |
|
||||
| Improvements to API docs | ✔️ | ✔️ | ✔️ |
|
||||
| Cleaning up code | ❌ | ✔️ | ✔️ |
|
||||
| Changing code formatting or style | ❌ | ✔️ | ✔️ |
|
||||
| Addition of new core features | ❌ | 🟡 Only if non-disruptive | ✔️ |
|
||||
| Changing core behaviour (e.g. making something use threads) | ❌ | ✔️ | ✔️ |
|
||||
| Addition of new configuration options | ❌ | 🟡 Only if optional | ✔️ |
|
||||
| Addition of new API classes, methods or constants | ❌ | ✔️ | ✔️ |
|
||||
| Deprecating API classes, methods or constants | ❌ | ✔️ | ✔️ |
|
||||
| Adding optional parameters to an API method | ❌ | ✔️ | ✔️ |
|
||||
| Changing API behaviour | ❌ | 🟡 Only if backwards-compatible | ✔️ |
|
||||
| Removal of API | ❌ | ❌ | ✔️ |
|
||||
| Backwards-incompatible API change (e.g. renaming a method) | ❌ | ❌ | ✔️ |
|
||||
|
||||
### Notes
|
||||
- **Non-disruptive** means that usage should not be significantly altered by the change.
|
||||
- Examples of **non-disruptive** changes include adding new commands, or gameplay features like blocks and items.
|
||||
- Examples of **disruptive** changes include changing the way the server is run, world format changes (since those require downtime for the user to convert their world).
|
||||
- **API** includes all public and protected classes, functions and constants (unless marked as `@internal`).
|
||||
- Private members are not part of the API, **unless in a trait**.
|
||||
|
||||
## Making a pull request
|
||||
The basic procedure to create a pull request is:
|
||||
1. [Fork the repository on GitHub](https://github.com/pmmp/PocketMine-MP/fork). This gives you your own copy of the repository to make changes to.
|
||||
|
@ -7,10 +7,11 @@ GitHub is public and anyone can see the issues you post on the issue tracker, in
|
||||
|
||||
**WARNING: You may put live servers at risk by reporting a vulnerability on the GitHub issue tracker.**
|
||||
|
||||
**Contact us** by sending an email to [**team@pmmp.io**](mailto:team@pmmp.io?subject=Security%20Vulnerability%20in%20PocketMine-MP). Include the following information:
|
||||
**Contact us** by sending an email to [**security@pmmp.io**](mailto:security@pmmp.io). Include the following information:
|
||||
|
||||
- Version of PocketMine-MP
|
||||
- Detailed description of the vulnerability (e.g. how to exploit it, what the effects are)
|
||||
- Your GitHub username, if you wish to be credited for reporting the problem in the security advisory
|
||||
|
||||
Please note that we can't guarantee a reply to every email.
|
||||
|
||||
|
@ -48,6 +48,8 @@ use function sort;
|
||||
use function strrpos;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
use const SORT_STRING;
|
||||
use const STDERR;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
@ -74,7 +76,7 @@ function generateBlockPaletteReport(array $states) : BlockPaletteReport{
|
||||
foreach($states as $stateData){
|
||||
$name = $stateData->getName();
|
||||
$result->seenTypes[$name] = $name;
|
||||
foreach($stateData->getStates() as $k => $v){
|
||||
foreach(Utils::stringifyKeys($stateData->getStates()) as $k => $v){
|
||||
$result->seenStateValues[$k][$v->getValue()] = $v->getValue();
|
||||
asort($result->seenStateValues[$k]);
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 5){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number>");
|
||||
if(count($argv) !== 6){
|
||||
fwrite(STDERR, "required args: <git hash> <tag name> <github repo (owner/name)> <build number> <github actions run ID>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -40,4 +40,5 @@ echo json_encode([
|
||||
"details_url" => "https://github.com/$argv[3]/releases/tag/$argv[2]",
|
||||
"download_url" => "https://github.com/$argv[3]/releases/download/$argv[2]/PocketMine-MP.phar",
|
||||
"source_url" => "https://github.com/$argv[3]/tree/$argv[2]",
|
||||
"build_log_url" => "https://github.com/$argv[3]/actions/runs/$argv[5]",
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";
|
||||
|
@ -36,6 +36,8 @@ use function file_get_contents;
|
||||
use function fopen;
|
||||
use function fwrite;
|
||||
use function strtoupper;
|
||||
use const SORT_STRING;
|
||||
use const STDERR;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\build\generate_known_translation_apis;
|
||||
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\utils\Utils;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function dirname;
|
||||
|
@ -29,7 +29,9 @@ use function count;
|
||||
use function dirname;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function implode;
|
||||
use function is_dir;
|
||||
use function ksort;
|
||||
use function mb_strtoupper;
|
||||
use function preg_match;
|
||||
@ -37,9 +39,11 @@ use function sprintf;
|
||||
use function str_replace;
|
||||
use function substr;
|
||||
use const SORT_STRING;
|
||||
use const STDERR;
|
||||
|
||||
if(count($argv) !== 2){
|
||||
die("Provide a path to process");
|
||||
fwrite(STDERR, "Provide a path to process\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,30 +84,24 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
/** @var string $file */
|
||||
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
|
||||
if(substr($file, -4) !== ".php"){
|
||||
continue;
|
||||
}
|
||||
function processFile(string $file) : void{
|
||||
$contents = file_get_contents($file);
|
||||
if($contents === false){
|
||||
throw new \RuntimeException("Failed to get contents of $file");
|
||||
}
|
||||
|
||||
if(preg_match("/(*ANYCRLF)^namespace (.+);$/m", $contents, $matches) !== 1 || preg_match('/(*ANYCRLF)^((final|abstract)\s+)?class /m', $contents) !== 1){
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
$shortClassName = basename($file, ".php");
|
||||
$className = $matches[1] . "\\" . $shortClassName;
|
||||
if(!class_exists($className)){
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
$reflect = new \ReflectionClass($className);
|
||||
$docComment = $reflect->getDocComment();
|
||||
if($docComment === false || preg_match("/(*ANYCRLF)^\s*\*\s*@generate-registry-docblock$/m", $docComment) !== 1){
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
echo "Found registry in $file\n";
|
||||
|
||||
@ -117,3 +115,18 @@ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1],
|
||||
echo "No changes made to file $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(is_dir($argv[1])){
|
||||
/** @var string $file */
|
||||
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($argv[1], \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $file){
|
||||
if(substr($file, -4) !== ".php"){
|
||||
continue;
|
||||
}
|
||||
|
||||
processFile($file);
|
||||
}
|
||||
}else{
|
||||
processFile($argv[1]);
|
||||
}
|
||||
|
@ -24,13 +24,17 @@ 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\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
use pocketmine\block\utils\MushroomBlockType;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\SuspiciousStewType;
|
||||
use function array_key_first;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
@ -40,9 +44,11 @@ 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';
|
||||
|
||||
@ -57,9 +63,9 @@ function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array
|
||||
$bits = getBitsRequired($memberNames);
|
||||
$lines = [];
|
||||
|
||||
$functionName = "write$virtualTypeName";
|
||||
$lines[] = "public static function $functionName(RuntimeDataWriter \$w, \\$nativeTypeName \$value) : void{";
|
||||
$lines[] = "\t\$w->writeInt($bits, match(\$value){";
|
||||
$functionName = lcfirst($virtualTypeName);
|
||||
$lines[] = "public function $functionName(\\$nativeTypeName \$value) : void{";
|
||||
$lines[] = "\t\$this->int($bits, match(\$value){";
|
||||
|
||||
foreach($memberNames as $key => $memberName){
|
||||
$lines[] = "\t\t$memberName => $key,";
|
||||
@ -82,9 +88,9 @@ function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array
|
||||
$bits = getBitsRequired($memberNames);
|
||||
$lines = [];
|
||||
|
||||
$functionName = "read$virtualTypeName";
|
||||
$lines[] = "public static function $functionName(RuntimeDataReader \$r) : \\$nativeTypeName{";
|
||||
$lines[] = "\treturn match(\$r->readInt($bits)){";
|
||||
$functionName = lcfirst($virtualTypeName);
|
||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
||||
$lines[] = "\t\$value = match(\$this->readInt($bits)){";
|
||||
|
||||
foreach($memberNames as $key => $memberName){
|
||||
$lines[] = "\t\t$key => $memberName,";
|
||||
@ -154,17 +160,29 @@ function buildEnumReaderFunc(array $enumMembers, string &$functionName) : array{
|
||||
|
||||
$enumsUsed = [
|
||||
BellAttachmentType::getAll(),
|
||||
CopperOxidation::getAll(),
|
||||
CoralType::getAll(),
|
||||
DirtType::getAll(),
|
||||
DyeColor::getAll(),
|
||||
FroglightType::getAll(),
|
||||
LeverFacing::getAll(),
|
||||
MushroomBlockType::getAll(),
|
||||
SkullType::getAll(),
|
||||
SlabType::getAll(),
|
||||
SuspiciousStewType::getAll(),
|
||||
PotionType::getAll()
|
||||
];
|
||||
|
||||
$readerFuncs = [];
|
||||
$writerFuncs = [];
|
||||
$readerFuncs = [
|
||||
"" => [
|
||||
"abstract protected function readInt(int \$bits) : int;"
|
||||
]
|
||||
];
|
||||
$writerFuncs = [
|
||||
"" => [
|
||||
"abstract public function int(int \$bits, int \$value) : void;"
|
||||
]
|
||||
];
|
||||
$functionName = "";
|
||||
|
||||
foreach($enumsUsed as $enumMembers){
|
||||
@ -218,14 +236,14 @@ namespace pocketmine\data\runtime;
|
||||
|
||||
HEADER;
|
||||
|
||||
echo "final class $className{\n\n";
|
||||
echo "trait $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, "RuntimeEnumSerializer");
|
||||
printFunctions($readerFuncs, "RuntimeEnumDeserializer");
|
||||
printFunctions($writerFuncs, "RuntimeEnumSerializerTrait");
|
||||
printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait");
|
||||
|
||||
echo "Done. Don't forget to run CS fixup after generating code.\n";
|
||||
|
Submodule build/php updated: 11103498ca...6b605ed7c4
@ -40,12 +40,13 @@ use function rtrim;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function unlink;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_EOL;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* @param string[] $strings
|
||||
* @param string[] $strings
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
|
53
changelogs/4.10.md
Normal file
53
changelogs/4.10.md
Normal file
@ -0,0 +1,53 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.40**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.10.0
|
||||
Released 26th October 2022.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.40.
|
||||
- Removed support for older versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed incorrect command descriptions showing in `/help` when multiple commands use the same name. Previously, the most recently registered command would show, even though it wouldn't actually be invoked.
|
||||
- Fixed splash potions affecting players in spectator mode.
|
||||
- Fixed `World->addParticle()` sending particles to players who couldn't possibly see them when a list of targets was used.
|
||||
- Fixed `World->addSound()` sending sounds to players who couldn't possibly hear them when a list of targets was used.
|
||||
|
||||
## Documentation
|
||||
- Improved type information available for various API methods in `World`.
|
||||
|
||||
# 4.10.1
|
||||
Released 7th November 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed spawning in the void if spawn terrain in a world is solid at the default spawn position.
|
||||
- Fixed totems of undying activating when the player has 1 HP remaining.
|
||||
- Fixed durable items such as tools becoming unbreakable when in stacks larger than 1. Now, the durability correctly resets when the tool breaks.
|
||||
- TPS below 12 now correctly shows as red in `/status`. Previously, it showed as orange due to a condition ordering bug.
|
||||
- Improved handling of missing arguments in user-defined `pocketmine.yml` command aliases. Previously, missing arguments would be filled with an empty string, which caused a variety of unexpected behaviour.
|
||||
|
||||
## Internals
|
||||
- Added validation for the array given to `BaseInventory->setContents()` to ensure that it contains only `Item` instances.
|
||||
- Silenced `PlayerAuthInputPacket` spam when the session is in the "spawn response" state.
|
||||
- Updated to PHPStan 1.9.
|
||||
|
||||
# 4.10.2
|
||||
Released 25th November 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed crashes on macOS and Linux when using console colours without the `TERM` environment variable set.
|
||||
- Fixed crashdumps not being generated when error messages contained invalid UTF-8 characters.
|
||||
|
||||
## Documentation
|
||||
- Clarified documentation of caching behaviour for `Internet::getIP()`.
|
||||
- Added and improved documentation for many `Inventory` methods.
|
||||
- Rewritten documentation for `PlayerCreationEvent` with warnings and more detail.
|
||||
|
||||
## Internals
|
||||
- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions.
|
92
changelogs/4.11-beta.md
Normal file
92
changelogs/4.11-beta.md
Normal file
@ -0,0 +1,92 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.40**
|
||||
|
||||
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.11.0-BETA1
|
||||
Released 7th November 2022.
|
||||
|
||||
## General
|
||||
- Packet receive timings have now been split into two subcategories - Decode and Handle.
|
||||
- Console command entry can now be disabled via the `console.enable-input` setting in `pocketmine.yml`.
|
||||
- Best suited for headless servers (e.g. in a Docker container) where the console will never be used anyway.
|
||||
- Disabling the console reader slightly reduces memory usage, because console reading currently requires an additional subprocess.
|
||||
- Console command output now appears on the terminal only, and is not written to the log file.
|
||||
- The output from console commands now appears with a `Command output |` prefix, instead of as a log message.
|
||||
- Introduced validation for the `--data` and `--plugins` command line options.
|
||||
- Encrypted resource packs are now supported, by means of adding a `.key` file alongside the pack in the `resource_packs` folder.
|
||||
- e.g. `MyEncryptedPack.zip` -> `MyEncryptedPack.zip.key`
|
||||
|
||||
## Gameplay
|
||||
- Fixed supporting blocks of dead bush to be in line with vanilla.
|
||||
- Sugarcane can now be grown using bonemeal on any part of the sugarcane. Previously, it only worked when used on the bottom block.
|
||||
- Fixed modifier values for Instant Damage and Regeneration effects.
|
||||
|
||||
## API
|
||||
### General
|
||||
- Plugins are now always disabled before their dependencies, to ensure that they are able to shutdown properly (e.g. a core plugin depending on a database plugin may want to save data to a DB during `onDisable()`).
|
||||
- [`webmozart/path-util`](https://packagist.org/packages/webmozart/path-util) has been deprecated, and will be dropped in favour of [`symfony/filesystem`](https://packagist.org/packages/symfony/filesystem) in PM5.
|
||||
- To prepare for this change, simply replace any usage of `Webmozart\PathUtil\Path` with `Symfony\Component\Filesystem\Path`, which is available as a dependency in this release.
|
||||
|
||||
### `pocketmine`
|
||||
- The following API methods are now deprecated:
|
||||
- `Server->getPlayerByPrefix()`
|
||||
|
||||
### `pocketmine\entity`
|
||||
- `EntitySpawnEvent` and `ItemSpawnEvent` are now fired on the first tick after the entity is added to the world. Previously, these events were called directly from the entity constructor, making it impossible to get properties like velocity which are often set after the entity is created.
|
||||
- The following API methods are now deprecated:
|
||||
- `Living->hasLineOfSight()`
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new API methods have been added:
|
||||
- `public Armor->clearCustomColor() : $this`
|
||||
|
||||
### `pocketmine\inventory\transaction`
|
||||
- Introduced a `TransactionBuilder` class. This makes it less of a hassle to build an `InventoryTransaction` server-side, since the regular `Inventory` API methods can be used, rather than having to manually create `SlotChangeAction`s.
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following new API methods have been added:
|
||||
- `public Player->sendToastNotification(string $title, string $body) : void` - makes a grey box appear at the top of the player's screen containing the specified message
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following new API methods have been added:
|
||||
- `public static TextFormat::addBase(string $baseFormat, string $string) : string` - used for coloured log messages, changes the base formatting of a string by inserting the given formatting codes after every RESET code
|
||||
|
||||
## Internals
|
||||
- Improved performance of `ContainerTrait` dropping items on block destroy. (24e72ec109c1442b09558df89b6833cf2f2e0ec7)
|
||||
- Avoid repeated calls to `Position->getWorld()` (use local variables). (2940547026db40ce76deb46e992870de3ead79ad)
|
||||
- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. (e90abecf38d9c57635fa0497514bba7e546a2469)
|
||||
- Console polling is now done on the main thread (no longer a performance concern).
|
||||
- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie.
|
||||
- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged.
|
||||
- `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused.
|
||||
|
||||
# 4.11.0-BETA2
|
||||
Released 13th November 2022.
|
||||
|
||||
## Configuration
|
||||
- The `chunk-ticking.per-tick` setting is now deprecated, and will be removed in a future release.
|
||||
- The functionality of this setting has been removed, since it caused more problems than it solved.
|
||||
- Setting it to zero will still disable chunk ticking (for now), but this should now be done by setting `chunk-ticking.tick-radius` to `0` instead.
|
||||
|
||||
## Gameplay
|
||||
- Improved chunk random ticking:
|
||||
- Removed the limit on chunks ticked per tick, and its associated config option is no longer respected.
|
||||
- This change significantly improves crop and plant growth with large numbers of players, but may cause higher CPU usage.
|
||||
- This limit was causing a linear decrease in chunk ticking speed with larger numbers of players, leading to worsened gameplay experience.
|
||||
- Every chunk within the configured tick radius of a player will be ticked. Previously, chunks were randomly selected from the radius.
|
||||
- Implemented Darkness effect.
|
||||
|
||||
## API
|
||||
### `pocketmine\world`
|
||||
- The following new API methods have been added:
|
||||
- `public World->getChunkTickRadius() : int` - returns the world's simulation radius
|
||||
- `public World->setChunkTickRadius(int $radius) : void` - sets the world's simulation radius
|
||||
|
||||
## Internals
|
||||
- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions.
|
106
changelogs/4.11.md
Normal file
106
changelogs/4.11.md
Normal file
@ -0,0 +1,106 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.40**
|
||||
|
||||
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.11.0
|
||||
Released 25th November 2022.
|
||||
|
||||
## General
|
||||
- Packet receive timings have now been split into two subcategories - Decode and Handle.
|
||||
- Console command entry can now be disabled via the `console.enable-input` setting in `pocketmine.yml`.
|
||||
- Best suited for headless servers (e.g. in a Docker container) where the console will never be used anyway.
|
||||
- Disabling the console reader slightly reduces memory usage, because console reading currently requires an additional subprocess.
|
||||
- Console command output now appears on the terminal only, and is not written to the log file.
|
||||
- The output from console commands now appears with a `Command output |` prefix, instead of as a log message.
|
||||
- User-defined `pocketmine.yml` custom commands now use a generic description which makes clear the command is config-defined.
|
||||
- Introduced validation for the `--data` and `--plugins` command line options.
|
||||
- Encrypted resource packs are now supported, by means of adding a `.key` file alongside the pack in the `resource_packs` folder.
|
||||
- e.g. `MyEncryptedPack.zip` -> `MyEncryptedPack.zip.key`
|
||||
- The file must contain the raw key bytes, and must not end with a newline.
|
||||
|
||||
## Configuration
|
||||
- The `chunk-ticking.per-tick` setting is now deprecated, and will be removed in a future release.
|
||||
- The functionality of this setting has been removed, since it caused more problems than it solved.
|
||||
- Setting it to zero will still disable chunk ticking (for now), but this should now be done by setting `chunk-ticking.tick-radius` to `0` instead.
|
||||
|
||||
## Gameplay
|
||||
- Fixed supporting blocks of dead bush to be in line with vanilla.
|
||||
- Sugarcane can now be grown using bonemeal on any part of the sugarcane. Previously, it only worked when used on the bottom block.
|
||||
- Fixed missing sounds when adding, rotating, or removing items in item frames.
|
||||
- Fixed modifier values for Instant Damage and Regeneration effects.
|
||||
- Implemented Darkness effect.
|
||||
- Improved chunk random ticking:
|
||||
- Removed the limit on chunks ticked per tick, and its associated config option is no longer respected.
|
||||
- This change significantly improves crop and plant growth with large numbers of players.
|
||||
- This limit was causing a linear decrease in chunk ticking speed with larger numbers of players, leading to worsened gameplay experience.
|
||||
- **Warning: This change will result in increased CPU usage if players are spread over a very large area.**
|
||||
- Every chunk within the configured tick radius of a player will be ticked. Previously, chunks were randomly selected from the radius.
|
||||
|
||||
## API
|
||||
### General
|
||||
- Plugins are now always disabled before their dependencies, to ensure that they are able to shutdown properly (e.g. a core plugin depending on a database plugin may want to save data to a DB during `onDisable()`).
|
||||
- [`webmozart/path-util`](https://packagist.org/packages/webmozart/path-util) has been deprecated, and will be dropped in favour of [`symfony/filesystem`](https://packagist.org/packages/symfony/filesystem) in PM5.
|
||||
- To prepare for this change, simply replace any usage of `Webmozart\PathUtil\Path` with `Symfony\Component\Filesystem\Path`, which is available as a dependency in this release.
|
||||
|
||||
### `pocketmine`
|
||||
- The following API methods are now deprecated:
|
||||
- `Server->getPlayerByPrefix()`
|
||||
|
||||
### `pocketmine\entity`
|
||||
- `EntitySpawnEvent` and `ItemSpawnEvent` are now fired on the first tick after the entity is added to the world. Previously, these events were called directly from the entity constructor, making it impossible to get properties like velocity which are often set after the entity is created.
|
||||
- The following API methods are now deprecated:
|
||||
- `Living->hasLineOfSight()`
|
||||
|
||||
### `pocketmine\event\block`
|
||||
- The following new classes have been added:
|
||||
- `BlockDeathEvent` - event called when coral or coral blocks die due to lack of water
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new API methods have been added:
|
||||
- `public Armor->clearCustomColor() : $this`
|
||||
|
||||
### `pocketmine\inventory\transaction`
|
||||
- Introduced a `TransactionBuilder` class. This makes it less of a hassle to build an `InventoryTransaction` server-side, since the regular `Inventory` API methods can be used, rather than having to manually create `SlotChangeAction`s.
|
||||
|
||||
### `pocketmine\lang`
|
||||
- The following new API methods have been added:
|
||||
- `public Language->getAll() : array<string, string>`
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following new API methods have been added:
|
||||
- `public Player->sendToastNotification(string $title, string $body) : void` - makes a grey box appear at the top of the player's screen containing the specified message
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following new API methods have been added:
|
||||
- `public static TextFormat::addBase(string $baseFormat, string $string) : string` - used for coloured log messages, changes the base formatting of a string by inserting the given formatting codes after every RESET code
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following new API methods have been added:
|
||||
- `public World->getChunkTickRadius() : int` - returns the world's simulation radius
|
||||
- `public World->setChunkTickRadius(int $radius) : void` - sets the world's simulation radius
|
||||
|
||||
### `pocketmine\world\sound`
|
||||
- The following new classes have been added:
|
||||
- `ItemFrameAddItemSound`
|
||||
- `ItemFrameRemoveItemSound`
|
||||
- `ItemFrameRotateItemSound`
|
||||
|
||||
## Internals
|
||||
- Improved performance of `ContainerTrait` dropping items on block destroy. ([link](https://github.com/pmmp/PocketMine-MP/commits/24e72ec109c1442b09558df89b6833cf2f2e0ec7))
|
||||
- Avoid repeated calls to `Position->getWorld()` (use local variables). ([link](https://github.com/pmmp/PocketMine-MP/commit/2940547026db40ce76deb46e992870de3ead79ad))
|
||||
- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. ([link](https://github.com/pmmp/PocketMine-MP/commit/e90abecf38d9c57635fa0497514bba7e546a2469))
|
||||
- Inventories are now mapped on a per-slot basis. This means that more than one inventory can be mapped to the same window ID, which is necessary for correctly handling "UI" inventories like crafting tables.
|
||||
- `InventoryManager->getWindow(int $windowId) : ?Inventory` is replaced by `locateWindowAndSlot` (see below).
|
||||
- Added `InventoryManager->locateWindowAndSlot(int $windowId, int $netSlotId) : array{Inventory, int}` - accepts a window ID and absolute slot ID, and returns the associated inventory and the slot relative to the inventory's own start (for use with `getItem()` etc.).
|
||||
- Slot offset mapping for "UI" inventories is now handled in `InventoryManager->createComplexSlotMapping()` instead of in `TypeConverter`.
|
||||
- Console polling is now done on the main thread (no longer a performance concern). ([link](https://github.com/pmmp/PocketMine-MP/commit/b3f03d7ae645de67a54b7300c09b94eeca16298e))
|
||||
- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. ([link](https://github.com/pmmp/PocketMine-MP/commit/2585160ca2c4df5758b8b980331307402ff9f0fb))
|
||||
- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. ([link](https://github.com/pmmp/PocketMine-MP/commit/83e5b0adb6fa0dddec377182bb1c7945ac8f7820))
|
||||
- `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused. ([link](https://github.com/pmmp/PocketMine-MP/commit/3feaa18f6c10c3a99c0deca75f57ec2d74b92ab4))
|
||||
- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions.
|
32
changelogs/4.12.md
Normal file
32
changelogs/4.12.md
Normal file
@ -0,0 +1,32 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.50**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.12.0
|
||||
Released 30th November 2022.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.50.
|
||||
- Removed support for older versions.
|
||||
|
||||
# 4.12.1
|
||||
Released 4th December 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed items glitching when dragging a stack of items across the crafting grid (desync issues).
|
||||
|
||||
# 4.12.2
|
||||
Released 15th December 2022.
|
||||
|
||||
## Fixes
|
||||
- Folder used for plugins (optionally specified by `--plugins`) is no longer required to be writable.
|
||||
- Fixed broken writable check for server data folder (`is_writable()` broken on NFS and similar filesystems).
|
||||
- `Filesystem::createLockFile()` exceptions now include more information about why the lock file could not be created.
|
||||
- Fixed client-side item predictions not being rolled back when cancelling events such as `PlayerItemUseEvent`.
|
||||
|
||||
## Dependencies
|
||||
- Updated BedrockProtocol to [17.1.0](https://github.com/pmmp/BedrockProtocol/releases/tag/17.1.0+bedrock-1.19.50). This adds some missing `LevelSoundEvent` constants and fixes the values for `ContainerUIIds`.
|
42
changelogs/4.6.md
Normal file
42
changelogs/4.6.md
Normal file
@ -0,0 +1,42 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.10**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.6.0
|
||||
Released 13th July 2022.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.10.
|
||||
- Removed support for older versions.
|
||||
|
||||
# 4.6.1
|
||||
Released 22nd July 2022.
|
||||
|
||||
## Tools
|
||||
- `build/generate-registry-annotations.php` now supports processing single files (useful for PhpStorm file watchers).
|
||||
|
||||
## API
|
||||
- Updated documentation for `AsyncTask`.
|
||||
|
||||
## Fixes
|
||||
- Fixed incorrect items being displayed in item frames.
|
||||
- Fixed books not showing in lecterns.
|
||||
- Fixed incorrect damage interval of Wither status effect.
|
||||
- Fixed incorrect fire ticks when being set on fire by lava (8 seconds in Bedrock instead of 15).
|
||||
- `Entity->attack()` now cancels damage from `FIRE` and `FIRE_TICK` damage causes if the entity is fireproof.
|
||||
- Fixed inventory windows getting force-closed when the client attempts to use an enchanting table or anvil.
|
||||
|
||||
# 4.6.2
|
||||
Released 6th August 2022.
|
||||
|
||||
## Core
|
||||
- Improved server-side performance of `PlayerAuthInputPacket` handler.
|
||||
- Improved client-side performance of `FloatingTextParticle` by using an invisible falling block entity. This offered a roughly 5x performance improvement over using tiny invisible players in local testing.
|
||||
|
||||
## Fixes
|
||||
- Fixed assert failures and debug spam on debug Minecraft clients related to abilities in `AddPlayerPacket`.
|
||||
- Fixed crash in `ReversePriorityQueue` on PHP 8.1 by adding `#[ReturnTypeWillChange]` attribute.
|
46
changelogs/4.7.md
Normal file
46
changelogs/4.7.md
Normal file
@ -0,0 +1,46 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.20**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.7.0
|
||||
Released 9th August 2022.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.20.
|
||||
- Removed support for older versions.
|
||||
|
||||
# 4.7.1
|
||||
Released 14th August 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when loading items from disk which have negative meta values.
|
||||
- Fixed Turtle Master potions not giving any effects.
|
||||
- Unimplemented items are no longer craftable.
|
||||
- Fixed incorrect items appearing in item frames (due to an obsolete workaround for 1.19.10).
|
||||
|
||||
# 4.7.2
|
||||
Released 16th August 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed crash when processing player skins with invalid geometry data.
|
||||
- Fixed spectator players being able to pick blocks using mousewheel click.
|
||||
- Improved supporting requirements for sugarcane.
|
||||
|
||||
# 4.7.3
|
||||
Released 22nd August 2022.
|
||||
|
||||
## General
|
||||
- Added complete translations for Spanish and Vietnamese.
|
||||
- All continuous integration (static analysis, unit tests, integration tests) are now performed on PHP 8.1 as well as 8.0.
|
||||
- InventoryTransaction now verifies that stack sizes of items after the transaction don't exceed the maximum stack size of the item type or the containing inventory.
|
||||
|
||||
## Fixes
|
||||
- Fixed Normal generator crash on PHP 8.1.
|
||||
- Fixed a race condition during async worker shutdown that could lead to tasks executing in the wrong order. This (very rarely) led to a crash in `PopulationTask` due to its preceding `GeneratorRegisterTask` not being executed.
|
||||
- Fixed `/give` accepting negative amounts or amounts larger than 32767 (vanilla max).
|
||||
- Fixed placement conditions for vines (no longer able to be placed on the side of cacti).
|
||||
- Fixed incorrect documentation of `SignText::__construct()`.
|
23
changelogs/4.8.md
Normal file
23
changelogs/4.8.md
Normal file
@ -0,0 +1,23 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.21**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.8.0
|
||||
Released 24th August 2022.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.21.
|
||||
- Removed support for older versions.
|
||||
|
||||
# 4.8.1
|
||||
Released 26th August 2022.
|
||||
|
||||
## General
|
||||
- Crashdumps now include JIT mode information for use by the Crash Archive.
|
||||
|
||||
## Fixes
|
||||
- Fixed uninitialized offset error in `DyeColorIdMap` when given invalid dye color IDs.
|
36
changelogs/4.9.md
Normal file
36
changelogs/4.9.md
Normal file
@ -0,0 +1,36 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.30**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.9.0
|
||||
Released 20th September 2022.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.30.
|
||||
- Removed support for older versions.
|
||||
|
||||
# 4.9.1
|
||||
Released 11th October 2022.
|
||||
|
||||
## Documentation
|
||||
- Added and improved documentation for many API methods in `Player` and `Block`.
|
||||
- Added missing `@internal` tag for `TaskHandler->setNextRun()`, `TaskHandler->remove()` and `TaskHandler->run()`.
|
||||
|
||||
## Fixes
|
||||
- Flight state is now locked by the server in spectator mode. This prevents any attempt by the client to toggle flight mode.
|
||||
- Fixed entity health exceeding its max health after the expiry of Health Boost effect.
|
||||
- Fixed burp sound not being played when a player eats food.
|
||||
- Fixed placement conditions for mushrooms - they can now only be placed when the light level at the target is <= 12, or on podzol or mycelium.
|
||||
- Fixed sign text appearing to change colour and/or glow when using dye on a sign - since this feature is not yet implemented, no change should occur.
|
||||
- Fixed players drowning when sprint-swimming on the surface of water.
|
||||
|
||||
## Internals
|
||||
- Added more detailed debug logging during the player login sequence.
|
||||
- Silenced debug spam during `PreSpawnPacketHandler`, considerably reducing debug noise when players join.
|
||||
- Fixed an edge case in `InventoryManager->removeWindow()`. This bug didn't have any effect on stable versions, but caused a `next-minor` development version to crash.
|
||||
- `Item`s returned by event getters are now cloned if modifying the result will have no useful side effects.
|
||||
- Updated `pocketmine/bedrock-data` to [`1.11.1`](https://github.com/pmmp/BedrockData/tree/1.11.1%2Bbedrock-1.19.30), which reduces bandwidth consumption during logins by not sending useless biome generation data.
|
@ -305,3 +305,435 @@ Again, it's acknowledged this is rather more cumbersome than it should be, but t
|
||||
- The following classes have been added:
|
||||
- `pocketmine\world\format\io\GlobalBlockStateHandlers`
|
||||
- `pocketmine\world\format\io\GlobalItemDataHandlers`
|
||||
|
||||
# 5.0.0-ALPHA2
|
||||
Released 14th July 2022.
|
||||
|
||||
## Core
|
||||
- Reduced memory usage of the server on startup.
|
||||
- Fixed error spam when loading item frames without items in them.
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Added the following new blocks:
|
||||
- Cakes with Candle & Dyed Candle
|
||||
- Candle & Dyed Candle
|
||||
- Cartography Table (not currently usable due to maps not being implemented)
|
||||
- Copper block (random oxidation not yet implemented)
|
||||
- Cut Copper block, stairs and slabs (random oxidation not yet implemented)
|
||||
- Crying Obsidian
|
||||
- Gilded Blackstone
|
||||
- Glow Item Frame
|
||||
- Hanging Roots
|
||||
- Lightning Rod
|
||||
- Netherite Block
|
||||
- Smithing Table
|
||||
- Tinted Glass
|
||||
- Warped Wart Block
|
||||
- Wither Rose
|
||||
|
||||
### Items
|
||||
- Added the following new items:
|
||||
- Honey Bottle
|
||||
- Netherite Axe
|
||||
- Netherite Boots
|
||||
- Netherite Chestplate
|
||||
- Netherite Helmet
|
||||
- Netherite Ingot
|
||||
- Netherite Leggings
|
||||
- Netherite Pickaxe
|
||||
- Netherite Scrap
|
||||
- Netherite Shovel
|
||||
- Netherite Sword
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- Dependency between `BlockFactory` and `VanillaBlocks` has been inverted.
|
||||
- Now, blocks are defined in `VanillaBlocks`, and automatically registered in `BlockFactory`.
|
||||
- Manual registration in `BlockFactory` is still required for custom blocks.
|
||||
- `BlockFactory` now has only one purpose, which is to map internal blockstate IDs to `Block` objects when reading blocks from chunks.
|
||||
- The following new API methods have been added:
|
||||
- `public Block->isFireProofAsItem()`
|
||||
- `public Block->onProjectileHit()`
|
||||
- `public ItemFrame->isGlowing()`
|
||||
- `public ItemFrame->setGlowing()`
|
||||
- The following new classes have been added:
|
||||
- `BaseCake`
|
||||
- `CakeWithCandle`
|
||||
- `CakeWithDyedCandle`
|
||||
- `Candle`
|
||||
- `CartographyTable`
|
||||
- `CopperSlab`
|
||||
- `CopperStairs`
|
||||
- `Copper`
|
||||
- `DyedCandle`
|
||||
- `GildedBlackstone`
|
||||
- `HangingRoots`
|
||||
- `LightningRod`
|
||||
- `SmithingTable`
|
||||
- `WitherRose`
|
||||
- `utils\CandleTrait`
|
||||
- `utils\CopperOxidation`
|
||||
- `utils\CopperTrait`
|
||||
|
||||
### `pocketmine\crafting`
|
||||
- JSON models have been updated to reflect updated crafting data format.
|
||||
- The following enum classes have new members:
|
||||
- `ShapelessRecipeType` has new members `CARTOGRAPHY` and `SMITHING`
|
||||
|
||||
### `pocketmine\data`
|
||||
- `LegacyToStringBidirectionalIdMap` has been reduced to `LegacyToStringIdMap`.
|
||||
- Since we never map from string ID to legacy ID, bidirectional mapping is no longer necessary.
|
||||
- This affects the following subclasses:
|
||||
- `LegacyBiomeIdToStringIdMap`
|
||||
- `LegacyBlockIdToStringIdMap`
|
||||
- `LegacyEntityIdToStringIdMap`
|
||||
- `LegacyItemIdToStringIdMap`
|
||||
- The following internal API methods have been added:
|
||||
- `public LegacyToStringIdMap->add(string $string, int $legacy) : void` - adds a mapping from a custom legacy ID to custom string ID, needed for upgrading old saved data
|
||||
- `public LegacyBlockStateMapper->addMapping(string $stringId, int $intId, int $meta, BlockStateData $stateData) : void` - adds a mapping from legacy block data to a modern blockstate, needed for upgrading old saved data
|
||||
- `public BlockStateData->getState(string $name) : ?Tag`
|
||||
- The following internal API methods have signature changes:
|
||||
- `BlockStateData->__construct()` now accepts `array<string, Tag`> for `$states` instead of `CompoundTag`
|
||||
- `BlockStateData->getStates()` now returns `array<string, Tag>` instead of `CompoundTag` (allows reducing memory usage)
|
||||
- The following classes have been added:
|
||||
- `UnsupportedItemTypeException`
|
||||
|
||||
### `pocketmine\item`
|
||||
- `ItemFactory` has been removed.
|
||||
- Vanilla item registration is now done via `VanillaItems`.
|
||||
- The procedure for registering a custom item is the same as in ALPHA1, minus the `ItemFactory` step.
|
||||
- The following API methods have been added:
|
||||
- `public ArmorTypeInfo->getToughness() : int`
|
||||
- `public ArmorTypeInfo->isFireProof() : bool`
|
||||
- `public Item->isFireProof() : bool`
|
||||
- The following API methods have signature changes:
|
||||
- `ArmorTypeInfo->__construct()` now accepts optional parameters `int $toughness` and `bool $fireProof`
|
||||
- The following classes have been added:
|
||||
- `HoneyBottle`
|
||||
- The following enums have new members:
|
||||
- `ToolTier` has new member `NETHERITE`
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following API methods have signature changes:
|
||||
- `SubChunk->__construct()` parameter `$blocks` has been renamed to `$blockLayers`.
|
||||
- The following classes have been added:
|
||||
- `CopperWaxApplySound`
|
||||
- `CopperWaxRemoveSound`
|
||||
|
||||
# 5.0.0-ALPHA3
|
||||
Released 14th August 2022.
|
||||
|
||||
## Core
|
||||
- Support for Bedrock 1.19.20.
|
||||
- Dropped support for Bedrock versions older than 1.19.20.
|
||||
- Improved performance of dropping block inventory contents when the block is destroyed.
|
||||
|
||||
## Fixes
|
||||
- Fixed errors when loading air itemstacks from PM4 worlds. These weren't supposed to exist (vanilla doesn't save them), but they were present in older PM worlds due to a bug in older versions.
|
||||
- Fixed server crash when discovering unknown blocks in non-leveldb worlds during conversion.
|
||||
- Fixed crimson / warped planks being usable as furnace fuel.
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Added the following new blocks:
|
||||
- Cauldron
|
||||
- Chorus Flower
|
||||
- Chorus Plant
|
||||
- Froglight (pearlescent, verdant, ochre)
|
||||
- Mangrove Roots
|
||||
- Muddy Mangrove Roots
|
||||
- Rooted Dirt
|
||||
- Spore Blossom
|
||||
- Fixed lava setting entities on fire for an incorrect duration (Java vs Bedrock inconsistency).
|
||||
|
||||
### Items
|
||||
- Glass bottles can now be filled with water by clicking on a water source block.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
#### Highlights
|
||||
- Introduced "type tags" concept, which allows marking certain blocks as having certain behaviours.
|
||||
- The idea for this system was borrowed from the Minecraft Java tags system.
|
||||
- It's still in very early concept stage, but is currently used for deciding which types of blocks plants can be placed on without needing to enumerate every single ID in every class, eliminating a bunch of boilerplate code and improving consistency.
|
||||
- All `Block` descendents now accept `BlockTypeInfo` in the constructor, instead of `BlockBreakInfo`.
|
||||
- This allows for future additions without needing to change dozens of overridden constructors.
|
||||
- Dynamic type and state property serialization now each use a single, unified method (`describeType` and `describeState` respectively) which accept `RuntimeDataReader|RuntimeDataWriter`, instead of separate decode/encode methods.
|
||||
- This simplifies implementing new blocks and avoids duplication of information.
|
||||
- `&$returnedItems` reference parameter is now used in some places to enable actions to return items to players without caring about whether they are in creative or anything else.
|
||||
- This eliminates boilerplate code of deciding whether to set the player's held item or not, as well as automatically dropping any overflow items that don't fit into the inventory.
|
||||
- This is currently used when filling/emptying cauldrons using buckets or glass bottles.
|
||||
- `BlockTypeIds` now exposes `newId()` static method to ease addition of custom blocks.
|
||||
|
||||
#### Changes
|
||||
- The following API methods have signature changes:
|
||||
- `Block->onInteract()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Block->onBreak()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Block->readStateFromWorld()` now returns `Block`.
|
||||
- This allows blocks to replace themselves with a different block entirely based on world conditions.
|
||||
- The following new classes have been added:
|
||||
- `BlockTypeInfo`
|
||||
- `BlockTypeTags`
|
||||
- The following new API methods have been added:
|
||||
- `protected Block->describeState(RuntimeDataReader|RuntimeDataWriter $w) : void` - describes to a runtime data reader/writer how to read/write the block's state properties
|
||||
- `protected Block->describeType(RuntimeDataReader|RuntimeDataWriter $w) : void` - describes to a runtime data reader/writer how to read/write the block's dynamic type properties
|
||||
- `public Block->getTypeTags() : array<string>`
|
||||
- `public Block->hasTypeTag(string $tag) : bool`
|
||||
- `public Spawnable->getRenderUpdateBugWorkaroundStateProperties(Block $block) : array<string, Tag>` - allows spawnable tiles to spoof block state properties to work around client-side rendering bugs without actually changing the block server-side
|
||||
- `public static BlockBreakInfo::axe(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockBreakInfo::pickaxe(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockBreakInfo::shovel(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockBreakInfo::tier(float $hardness, int $toolType, ToolTier $toolTier, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockTypeIds::newId() : int` - returns a new dynamic block type ID for use by custom blocks
|
||||
|
||||
### `pocketmine\data`
|
||||
- The following classes have been renamed:
|
||||
- `LegacyBlockStateMapper` -> `BlockIdMetaUpgrader`
|
||||
- The following API methods have been added:
|
||||
- `public BlockDataUpgrader->getIdMetaUpgrader() : BlockIdMetaUpgrader`
|
||||
- `public BlockIdMetaUpgrader->addIdMetaToStateMapping(string $stringId, int $meta, BlockStateData $stateData) : void`
|
||||
- `public BlockIdMetaUpgrader->addIntIdToStringIdMapping(int $intId, string $stringId) : void`
|
||||
- The following API methods have been removed:
|
||||
- `BlockIdMetaUpgrader->addMapping()` - use `addIdMetaToStateMapping()` (and `addIntIdToStringIdMapping()` if necessary) instead
|
||||
- `LegacyToStringMap` no longer throws exceptions when adding the same mapping twice if the addition would have no effect.
|
||||
|
||||
### `pocketmine\item`
|
||||
#### Highlights
|
||||
- `&$returnedItems` reference parameter is now used in some places to enable actions to return items to players without caring about whether they are in creative or anything else.
|
||||
- This eliminates boilerplate code of deciding whether to set the player's held item or not, as well as automatically dropping any overflow items that don't fit into the inventory.
|
||||
- This is used for things like filling/emptying buckets and bottles, and equipping armor.
|
||||
|
||||
#### Changes
|
||||
- The following new API methods have been added:
|
||||
- `public Armor->clearCustomColor() : $this` - clears the custom color of an armor item
|
||||
- `public static ItemTypeIds::newId() : int` - returns a new dynamic item type ID for use by custom items
|
||||
- The following API methods have signature changes:
|
||||
- `Item->onAttackEntity()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onClickAir()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onDestroyBlock()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onInteractBlock()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onReleaseUsing()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
|
||||
# 5.0.0-ALPHA4
|
||||
Released 24th September 2022.
|
||||
|
||||
## Core
|
||||
- Now targeting Minecraft: Bedrock 1.19.30.
|
||||
- All tests and static analysis are now being run on PHP 8.1 as well as PHP 8.0.
|
||||
- Silenced warning about Xdebug when it's loaded but disabled by `xdebug.mode` configuration.
|
||||
- A new `console.enable-input` option has been added to `pocketmine.yml`, which allows disabling the console reader in environments where it's not needed (e.g. a Docker container). This can be useful to save processor and memory resources.
|
||||
- Console reader polling is now done on the main thread. Since the console reader communication is now done via sockets, there's no longer any reason for it to have its own thread.
|
||||
- Crashdumps now include JIT mode information for use by the Crash Archive.
|
||||
- Improved handling of "UI" inventories in network `InventoryManager`. Their contents are now synced correctly.
|
||||
- Fixed cartography table recipes pretending to be smithing table recipes in `CraftingDataPacket`.
|
||||
- Fixed incorrect key being used for saving entity type IDs in save data.
|
||||
|
||||
## API
|
||||
### General
|
||||
- Plugin dependents are now always disabled before their dependencies on shutdown, to ensure that the dependents can finish what they are doing correctly.
|
||||
|
||||
### `pocketmine\block`
|
||||
- The following new API methods have been added:
|
||||
- `public SignText->isGlowing() : bool`
|
||||
- `public SignText->getBaseColor() : pocketmine\color\Color`
|
||||
- The following API methods have signature changes:
|
||||
- `SignText::fromBlob()` now accepts two new optional parameters: `?Color $baseColor` and `bool $glowing`
|
||||
- `SignText::__construct()` now accepts two new optional parameters: `?Color $baseColor` and `bool $glowing`
|
||||
- The following API methods have been removed:
|
||||
- `TreeType::fromMagicNumber()`
|
||||
- `TreeType->getMagicNumber()`
|
||||
|
||||
### `pocketmine\command`
|
||||
- Command permissions are now always checked by the server when running a command.
|
||||
- This only affects commands implemented by extending `Command`. Plugins using `PluginBase->onCommand()` are not affected by this change, since they already had permissions checked by the server anyway.
|
||||
- Previously, direct inheritors of `Command` were responsible for checking permissions, which required developers to duplicate the same code in every command, and opened lots of potential for security vulnerabilities.
|
||||
- If you want to do something on permission denied (e.g. sending a special message, or audit logging), you can do so by overriding `Command->testPermission()`, instead of baking the code directly into `Command->execute()`.
|
||||
- If you don't want to use permissions at all, just create a permission with a default of `true` (or belonging to `pocketmine.group.user`) and assign that.
|
||||
|
||||
### `pocketmine\data`
|
||||
#### Highlights
|
||||
- Introduced an experimental, mostly unified item (de)serializer registrar, `ItemSerializerDeserializerRegistrar`.
|
||||
- This class includes helper methods to register symmetric serializer and deserializer callbacks into an `ItemSerializer` and `ItemDeserializer`.
|
||||
- This halves the amount of code needed to register new items in the vast majority of cases.
|
||||
- This is currently used to register all currently implemented items.
|
||||
|
||||
#### Other changes
|
||||
- The following classes have been renamed:
|
||||
- `BlockObjectToBlockStateSerializer` -> `BlockObjectToStateSerializer`
|
||||
- `BlockStateToBlockObjectDeserializer` -> `BlockStateToObjectDeserializer`
|
||||
- The following classes have been removed:
|
||||
- `CachingBlockStateDeserializer`
|
||||
- `CachingBlockStateSerializer`
|
||||
- `DelegatingBlockStateDeserializer`
|
||||
- `DelegatingBlockStateSerializer`
|
||||
- The following new API methods have been added:
|
||||
- `public BlockStateToObjectDeserializer->mapSimple(string $stringId, \Closure() : Block $getBlock) : void` - for symmetry with the serializer
|
||||
|
||||
### `pocketmine\event`
|
||||
- `BlockFormEvent` now includes information about the block which caused the event.
|
||||
- Added `public BlockFormEvent->getCausingBlock() : Block`
|
||||
|
||||
### `pocketmine\inventory`
|
||||
- Introduced a new `TransactionBuilder` class, which considerably simplifies the process of constructing an `InventoryTransaction` from generic `setItem()` calls.
|
||||
- This is currently used to build server-side transactions for evicting the contents of the crafting grid when closing the main inventory.
|
||||
- This is planned for use with the new Minecraft Bedrock item stack request system.
|
||||
- Improved PHPStan type information available for `Inventory` and `BaseInventory`.
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following API methods have been changed:
|
||||
- `Item->encodeType(RuntimeDataWriter $w) : void` -> `Item->describeType(RuntimeDataReader|RuntimeDataWriter $w) : void`
|
||||
- The following new classes have been added:
|
||||
- `SuspiciousStew`
|
||||
- `SuspiciousStewType`
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following new API methods have been added:
|
||||
- `public static Utils::getOpcacheJitMode() : int`
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following new classes have been added:
|
||||
- `sound\DyeUseSound`
|
||||
- `sound\InkSacUseSound`
|
||||
- The following API methods have changed signatures:
|
||||
- `GlobalBlockStateHandlers::getSerializer()` now returns `BlockObjectToStateSerializer` directly instead of `BlockStateSerializer`.
|
||||
- `GlobalBlockStateHandlers::getDeserializer()` now returns `BlockStateToObjectDeserializer` directly instead of `BlockStateDeserializer`.
|
||||
|
||||
## Gameplay
|
||||
### General
|
||||
- Spectator players are no longer able to acquire blocks by block picking that they don't already have in their inventories. The behaviour is now the same as survival mode.
|
||||
|
||||
### Blocks
|
||||
- Coral and coral fans now behave correctly when placed out of water (they no longer immediately die).
|
||||
- Added support for dyeing sign text and making it glow.
|
||||
- Fixed dead bush being able to be placed on some invalid blocks (e.g. stone).
|
||||
- TNT can now be ignited by fire charges.
|
||||
- Vines can now only be placed on the side of full-cube blocks.
|
||||
- Fixed sugarcane not being able to be placed on some blocks.
|
||||
|
||||
### Items
|
||||
- Added the following new items:
|
||||
- Fire Charge
|
||||
- Suspicious Stew
|
||||
|
||||
### Effects
|
||||
- Updated damage modifier amounts for Instant Damage to be more in line with vanilla.
|
||||
- Updated duration modifier amounts for Regeneration to be more like vanilla.
|
||||
|
||||
# 5.0.0-ALPHA5
|
||||
Released 13th November 2022.
|
||||
|
||||
**This release includes changes from [4.11.0-BETA2](https://github.com/pmmp/PocketMine-MP/releases/4.11.0-BETA2) and earlier, which may not be listed here.**
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock 1.19.40.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- `FallableTrait` now includes a default implementation of `tickFalling()`.
|
||||
|
||||
### `pocketmine\event`
|
||||
- The following API methods have been removed:
|
||||
- `PlayerCommandPreprocessEvent`
|
||||
- The following API methods have been added:
|
||||
- `public DataPacketSendEvent->setPackets(list<ClientboundPacket> $packets) : void`
|
||||
|
||||
## Gameplay
|
||||
### General
|
||||
- Fixed dyeing leather armour in cauldrons.
|
||||
|
||||
### Blocks
|
||||
- Copper blocks now play the correct scrape sound when using an axe on them.
|
||||
|
||||
## Internals
|
||||
- Moved command timings to `Timings`.
|
||||
|
||||
# 5.0.0-ALPHA6
|
||||
Released 19th December 2022.
|
||||
|
||||
**This release includes changes from the following releases, which may not be mentioned:**
|
||||
- [4.10.2](https://github.com/pmmp/PocketMine-MP/releases/tag/4.10.2)
|
||||
- [4.11.0](https://github.com/pmmp/PocketMine-MP/releases/tag/4.11.0)
|
||||
- [4.12.0](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.0)
|
||||
- [4.12.1](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.1)
|
||||
- [4.12.2](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.2)
|
||||
|
||||
## General
|
||||
- Fixed the Bedrock client asking to upgrade worlds exported from PocketMine-MP to Bedrock (missing level.dat fields).
|
||||
- Added support for 1.19.40 and newer Bedrock worlds.
|
||||
- Commands are now enabled by default in worlds exported from PocketMine-MP to Bedrock.
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Added the following new blocks:
|
||||
- Twisting Vines
|
||||
- Weeping Vines
|
||||
- Anvils are now damaged when they hit the ground after falling.
|
||||
- Added missing sounds for anvils hitting the ground after falling.
|
||||
- Fixed missing sounds when a projectile strikes an amethyst block.
|
||||
- Fixed some blocks being incorrectly able to be placed on top of a candle cake.
|
||||
|
||||
### Items
|
||||
- Added the following new items:
|
||||
- Music Disc (5)
|
||||
- Music Disc (Otherside)
|
||||
- Music Disc (Pigstep)
|
||||
- Implemented Swift Sneak enchantment.
|
||||
- Armour durability is now only reduced when the wearer receives a type of damage that the armour can protect against.
|
||||
|
||||
## API
|
||||
### General
|
||||
- Union and mixed native parameter, return and property types are now used where appropriate.
|
||||
|
||||
### `pocketmine\block`
|
||||
- The following new API methods have been added:
|
||||
- `public Furnace->getType() : utils\FurnaceType`
|
||||
- The following interfaces have new requirements:
|
||||
- `utils\Fallable` now requires `onHitGround()` to be implemented (although filled by default implementation in `FallableTrait`).
|
||||
- `utils\Fallable` now requires `getLandSound()` to be implemented (although filled by default implementation in `FallableTrait`).
|
||||
- The following new API constants have been added:
|
||||
- `public BlockTypeTags::FIRE` - used by fire and soul fire
|
||||
|
||||
### `pocketmine\crafting`
|
||||
- The `$type` parameter of `ShapelessRecipe->__construct()` is now mandatory.
|
||||
|
||||
### `pocketmine\entity`
|
||||
- The following new API methods have been added:
|
||||
- `public Living->getDisplayName() : string`
|
||||
- The following API methods have changed signatures:
|
||||
- `EntityFactory->register()` no longer accepts a `$legacyMcpeSaveId` parameter (now handled by internal conversions instead).
|
||||
|
||||
### `pocketmine\event`
|
||||
- The following classes have been renamed:
|
||||
- `entity\ExplosionPrimeEvent` -> `entity\EntityPreExplodeEvent`
|
||||
- The following new classes have been added:
|
||||
- `world\WorldParticleEvent` - called when a particle is spawned in a world
|
||||
- `world\WorldSoundEvent` - called when a sound is played in a world
|
||||
- The following API methods have changed signatures:
|
||||
- `entity\EntityPreExplodeEvent->__construct()` has the `$force` parameter renamed to `$radius`
|
||||
- `entity\EntityPreExplodeEvent->getForce() : float` -> `entity\EntityPreExplodeEvent->getRadius() : float`
|
||||
- `entity\EntityPreExplodeEvent->setForce(float $force) : void` -> `entity\EntityPreExplodeEvent->setRadius(float $radius) : void`
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new API methods have been added:
|
||||
- `public Item->keepOnDeath() : bool` - returns whether this item will be retained when a player carrying it dies
|
||||
- `public Item->onInteractEntity(Player $player, Entity $entity, Vector3 $clickPos) : bool` - called when a player interacts with an entity with this item in their hand
|
||||
- `public Item->setKeepOnDeath(bool $keepOnDeath) : void` - sets whether this item will be retained when a player carrying it dies
|
||||
- `public StringToItemParser->lookupAliases(Item $item) : list<string>` - returns a list of all registered aliases for the given item
|
||||
- `public StringToItemParser->lookupBlockAliases(Block $block) : list<string>` - returns a list of all registered aliases for the given block
|
||||
|
||||
### `pocketmine\resourcepacks`
|
||||
- The following new API methods have been added:
|
||||
- `public ResourcePackManager->setPackEncryptionKey(string $id, ?string $key) : void` - sets the encryption key to be used for the resource pack identified by the given UUID
|
||||
- `public ResourcePackManager->setResourceStack(list<ResourcePack> $resourceStack) : void` - sets the resource stack to be used by the server
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following API methods have changed signatures:
|
||||
- `Explosion->__construct()` has the `$size` parameter renamed to `$radius`
|
||||
- The following public properties have been renamed:
|
||||
- `Explosion->size` -> `Explosion->radius`
|
||||
|
||||
## Internals
|
||||
- `EntityLegacyIds` has been removed. Legacy entity IDs found during world loading are now converted via `LegacyEntityIdToStringIdMap`.
|
||||
- All usages of NBT keys now use class constants instead of hardcoded strings (except for an occasional overlooked one).
|
||||
- All members of `BlockTypeTags` now have a `pocketmine:` prefix on the value. This does not affect constant usages.
|
||||
|
@ -37,13 +37,13 @@
|
||||
"pocketmine/bedrock-block-upgrade-schema": "dev-master@dev",
|
||||
"pocketmine/bedrock-data": "dev-modern-world-support@dev",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "dev-master",
|
||||
"pocketmine/bedrock-protocol": "~10.0.0+bedrock-1.19.0",
|
||||
"pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.8.0",
|
||||
"pocketmine/locale-data": "~2.11.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.4.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
@ -52,10 +52,10 @@
|
||||
"pocketmine/raklib-ipc": "^0.1.0",
|
||||
"pocketmine/snooze": "^0.3.0",
|
||||
"ramsey/uuid": "^4.1",
|
||||
"webmozart/path-util": "^2.3"
|
||||
"symfony/filesystem": "^5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.8.0",
|
||||
"phpstan/phpstan": "1.9.4",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
971
composer.lock
generated
971
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
includes:
|
||||
- tests/phpstan/analyse-for-current-php-version.neon.php
|
||||
- tests/phpstan/configs/actual-problems.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/impossible-generics.neon
|
||||
- tests/phpstan/configs/php-bugs.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
|
@ -119,8 +119,6 @@ chunk-sending:
|
||||
spawn-radius: 4
|
||||
|
||||
chunk-ticking:
|
||||
#Max amount of chunks processed each tick
|
||||
per-tick: 40
|
||||
#Radius of chunks around a player to tick
|
||||
tick-radius: 3
|
||||
#Number of blocks inside ticking areas' subchunks that get ticked every tick. Higher values will accelerate events
|
||||
@ -168,6 +166,9 @@ timings:
|
||||
host: timings.pmmp.io
|
||||
|
||||
console:
|
||||
#Whether to accept commands via the console. If disabled, anything typed on the console will be ignored.
|
||||
#Useful to save resources on headless servers where the console is never used (e.g. hosted server, Docker, etc.)
|
||||
enable-input: true
|
||||
#Choose whether to enable server stats reporting on the console title.
|
||||
#NOTE: The title ticker will be disabled regardless if console colours are not enabled.
|
||||
title-tick: true
|
||||
|
@ -10,3 +10,4 @@ resource_stack:
|
||||
# - natural.zip
|
||||
# - vanilla.zip
|
||||
#If you want to force clients to use vanilla resources, you must place a vanilla resource pack in your resources folder and add it to the stack here.
|
||||
#To specify a resource encryption key, put the key in the <resource>.key file alongside the resource pack. Example: vanilla.zip.key
|
||||
|
@ -30,7 +30,7 @@ use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\Process;
|
||||
use pocketmine\utils\Utils;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function arsort;
|
||||
use function count;
|
||||
use function fclose;
|
||||
@ -285,10 +285,8 @@ class MemoryManager{
|
||||
|
||||
/**
|
||||
* Static memory dumper accessible from any thread.
|
||||
*
|
||||
* @param mixed $startingObject
|
||||
*/
|
||||
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
|
||||
public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
|
||||
$hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
|
||||
ini_set('memory_limit', '-1');
|
||||
gc_disable();
|
||||
@ -398,7 +396,7 @@ class MemoryManager{
|
||||
|
||||
do{
|
||||
$continue = false;
|
||||
foreach($objects as $hash => $object){
|
||||
foreach(Utils::stringifyKeys($objects) as $hash => $object){
|
||||
if(!is_object($object)){
|
||||
continue;
|
||||
}
|
||||
@ -479,13 +477,15 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $from
|
||||
* @param object[] $objects reference parameter
|
||||
* @param object[] $objects reference parameter
|
||||
* @param int[] $refCounts reference parameter
|
||||
*
|
||||
* @return mixed
|
||||
* @phpstan-param array<string, object> $objects
|
||||
* @phpstan-param array<string, int> $refCounts
|
||||
* @phpstan-param-out array<string, object> $objects
|
||||
* @phpstan-param-out array<string, int> $refCounts
|
||||
*/
|
||||
private static function continueDump($from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize){
|
||||
private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{
|
||||
if($maxNesting <= 0){
|
||||
return "(error) NESTING LIMIT REACHED";
|
||||
}
|
||||
|
@ -34,15 +34,19 @@ namespace pocketmine {
|
||||
use pocketmine\utils\Timezone;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function function_exists;
|
||||
use function getcwd;
|
||||
use function is_dir;
|
||||
use function mkdir;
|
||||
use function phpversion;
|
||||
use function preg_match;
|
||||
use function preg_quote;
|
||||
use function realpath;
|
||||
use function version_compare;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
|
||||
@ -160,7 +164,7 @@ namespace pocketmine {
|
||||
if(PHP_DEBUG !== 0){
|
||||
$logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
|
||||
}
|
||||
if(extension_loaded("xdebug")){
|
||||
if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){
|
||||
$logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
|
||||
}
|
||||
if(((int) ini_get('zend.assertions')) !== -1){
|
||||
@ -176,10 +180,10 @@ namespace pocketmine {
|
||||
|
||||
|
||||
--------------------------------------- ! WARNING ! ---------------------------------------
|
||||
You're using PHP 8.0 with JIT enabled. This provides significant performance improvements.
|
||||
You're using PHP with JIT enabled. This provides significant performance improvements.
|
||||
HOWEVER, it is EXPERIMENTAL, and has already been seen to cause weird and unexpected bugs.
|
||||
Proceed with caution.
|
||||
If you want to report any bugs, make sure to mention that you are using PHP 8.0 with JIT.
|
||||
If you want to report any bugs, make sure to mention that you have enabled PHP JIT.
|
||||
To turn off JIT, change `opcache.jit` to `0` in your php.ini file.
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
@ -200,6 +204,22 @@ JIT_WARNING
|
||||
ini_set('assert.exception', '1');
|
||||
}
|
||||
|
||||
function getopt_string(string $opt) : ?string{
|
||||
$opts = getopt("", ["$opt:"]);
|
||||
if(isset($opts[$opt])){
|
||||
if(is_string($opts[$opt])){
|
||||
return $opts[$opt];
|
||||
}
|
||||
if(is_array($opts[$opt])){
|
||||
critical_error("Cannot specify --$opt multiple times");
|
||||
}else{
|
||||
critical_error("Missing value for --$opt");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
@ -251,27 +271,42 @@ JIT_WARNING
|
||||
|
||||
ErrorToExceptionHandler::set();
|
||||
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
|
||||
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
|
||||
$dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR;
|
||||
$pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR;
|
||||
$dataPath = getopt_string("data") ?? $cwd;
|
||||
$pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
|
||||
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
|
||||
|
||||
if(!file_exists($dataPath)){
|
||||
mkdir($dataPath, 0777, true);
|
||||
if(!@mkdir($dataPath, 0777, true) && !is_dir($dataPath)){
|
||||
critical_error("Unable to create/access data directory at $dataPath. Check that the target location is accessible by the current user.");
|
||||
exit(1);
|
||||
}
|
||||
//this has to be done after we're sure the data path exists
|
||||
$dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
|
||||
|
||||
$lockFilePath = Path::join($dataPath, 'server.lock');
|
||||
if(($pid = Filesystem::createLockFile($lockFilePath)) !== null){
|
||||
try{
|
||||
$pid = Filesystem::createLockFile($lockFilePath);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
critical_error($e->getMessage());
|
||||
critical_error("Please ensure that there is enough space on the disk and that the current user has read/write permissions to the selected data directory $dataPath.");
|
||||
exit(1);
|
||||
}
|
||||
if($pid !== null){
|
||||
critical_error("Another " . VersionInfo::NAME . " instance (PID $pid) is already using this folder (" . realpath($dataPath) . ").");
|
||||
critical_error("Please stop the other server first before running a new one.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(!@mkdir($pluginPath, 0777, true) && !is_dir($pluginPath)){
|
||||
critical_error("Unable to create plugin directory at $pluginPath. Check that the target location is accessible by the current user.");
|
||||
exit(1);
|
||||
}
|
||||
$pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
|
||||
|
||||
//Logger has a dependency on timezone
|
||||
Timezone::init();
|
||||
|
||||
$opts = getopt("", ["no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
if(isset($opts["enable-ansi"])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts["disable-ansi"])){
|
||||
|
@ -31,7 +31,7 @@ use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\SimpleCommandMap;
|
||||
use pocketmine\console\ConsoleCommandSender;
|
||||
use pocketmine\console\ConsoleReaderThread;
|
||||
use pocketmine\console\ConsoleReaderChildProcessDaemon;
|
||||
use pocketmine\crafting\CraftingManager;
|
||||
use pocketmine\crafting\CraftingManagerFromDataHelper;
|
||||
use pocketmine\crash\CrashDump;
|
||||
@ -88,12 +88,12 @@ use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\stats\SendUsageTask;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\updater\UpdateChecker;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\BroadcastLoggerForwarder;
|
||||
use pocketmine\utils\Config;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Internet;
|
||||
@ -115,7 +115,7 @@ use pocketmine\world\World;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use pocketmine\world\WorldManager;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_sum;
|
||||
use function base64_encode;
|
||||
use function cli_set_process_title;
|
||||
@ -132,6 +132,7 @@ use function get_class;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function is_resource;
|
||||
use function is_string;
|
||||
@ -165,7 +166,6 @@ use function zlib_encode;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_EOL;
|
||||
use const PHP_INT_MAX;
|
||||
use const PTHREADS_INHERIT_NONE;
|
||||
use const ZLIB_ENCODING_GZIP;
|
||||
|
||||
/**
|
||||
@ -225,7 +225,8 @@ class Server{
|
||||
|
||||
private MemoryManager $memoryManager;
|
||||
|
||||
private ConsoleReaderThread $console;
|
||||
private ?ConsoleReaderChildProcessDaemon $console = null;
|
||||
private ?ConsoleCommandSender $consoleSender = null;
|
||||
|
||||
private SimpleCommandMap $commandMap;
|
||||
|
||||
@ -469,10 +470,7 @@ class Server{
|
||||
return $this->configGroup->getPropertyBool("player.save-player-data", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OfflinePlayer|Player
|
||||
*/
|
||||
public function getOfflinePlayer(string $name){
|
||||
public function getOfflinePlayer(string $name) : Player|OfflinePlayer|null{
|
||||
$name = strtolower($name);
|
||||
$result = $this->getPlayerExact($name);
|
||||
|
||||
@ -559,7 +557,7 @@ class Server{
|
||||
$ev->call();
|
||||
$class = $ev->getPlayerClass();
|
||||
|
||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
|
||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
|
||||
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
||||
$spawn = $playerPos->asVector3();
|
||||
}else{
|
||||
@ -570,6 +568,7 @@ class Server{
|
||||
$playerPos = null;
|
||||
$spawn = $world->getSpawnLocation();
|
||||
}
|
||||
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
|
||||
$playerPromiseResolver = new PromiseResolver();
|
||||
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
||||
function() use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
|
||||
@ -607,6 +606,10 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method's results are unpredictable. The string "Steve" will return the player named "SteveJobs",
|
||||
* until another player named "SteveJ" joins the server, at which point it will return that player instead. Prefer
|
||||
* filtering the results of {@link Server::getOnlinePlayers()} yourself.
|
||||
*
|
||||
* Returns an online player whose name begins with or equals the given string (case insensitive).
|
||||
* The closest match will be returned, or null if there are no online matches.
|
||||
*
|
||||
@ -1043,22 +1046,14 @@ class Server{
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET)));
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3)))));
|
||||
|
||||
//TODO: move console parts to a separate component
|
||||
$consoleSender = new ConsoleCommandSender($this, $this->language);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender);
|
||||
$forwarder = new BroadcastLoggerForwarder($this, $this->logger, $this->language);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder);
|
||||
|
||||
$consoleNotifier = new SleeperNotifier();
|
||||
$commandBuffer = new \Threaded();
|
||||
$this->console = new ConsoleReaderThread($commandBuffer, $consoleNotifier);
|
||||
$this->tickSleeper->addNotifier($consoleNotifier, function() use ($commandBuffer, $consoleSender) : void{
|
||||
Timings::$serverCommand->startTiming();
|
||||
while(($line = $commandBuffer->shift()) !== null){
|
||||
$this->dispatchCommand($consoleSender, (string) $line);
|
||||
}
|
||||
Timings::$serverCommand->stopTiming();
|
||||
});
|
||||
$this->console->start(PTHREADS_INHERIT_NONE);
|
||||
//TODO: move console parts to a separate component
|
||||
if($this->configGroup->getPropertyBool("console.enable-input", true)){
|
||||
$this->console = new ConsoleReaderChildProcessDaemon($this->logger);
|
||||
}
|
||||
|
||||
$this->tickProcessor();
|
||||
$this->forceShutdown();
|
||||
@ -1270,7 +1265,7 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommandSender[]|null $recipients
|
||||
* @param CommandSender[]|null $recipients
|
||||
*/
|
||||
public function broadcastMessage(Translatable|string $message, ?array $recipients = null) : int{
|
||||
$recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||
@ -1323,9 +1318,9 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
|
||||
* @param int $stay Duration in ticks to stay on screen for
|
||||
* @param int $fadeOut Duration in ticks for fade-out.
|
||||
* @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
|
||||
* @param int $stay Duration in ticks to stay on screen for
|
||||
* @param int $fadeOut Duration in ticks for fade-out.
|
||||
* @param Player[]|null $recipients
|
||||
*/
|
||||
public function broadcastTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{
|
||||
@ -1365,6 +1360,7 @@ class Server{
|
||||
return false;
|
||||
}
|
||||
$recipients = $ev->getTargets();
|
||||
$packets = $ev->getPackets();
|
||||
|
||||
/** @var PacketBroadcaster[] $broadcasters */
|
||||
$broadcasters = [];
|
||||
@ -1511,7 +1507,7 @@ class Server{
|
||||
$this->configGroup->save();
|
||||
}
|
||||
|
||||
if(isset($this->console)){
|
||||
if($this->console !== null){
|
||||
$this->getLogger()->debug("Closing console");
|
||||
$this->console->quit();
|
||||
}
|
||||
@ -1539,7 +1535,7 @@ class Server{
|
||||
* @param mixed[][]|null $trace
|
||||
* @phpstan-param list<array<string, mixed>>|null $trace
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null) : void{
|
||||
public function exceptionHandler(\Throwable $e, ?array $trace = null) : void{
|
||||
while(@ob_end_flush()){}
|
||||
global $lastError;
|
||||
|
||||
@ -1650,12 +1646,14 @@ class Server{
|
||||
], 10, [], $postUrlError);
|
||||
|
||||
if($reply !== null && is_object($data = json_decode($reply->getBody()))){
|
||||
if(isset($data->crashId) && isset($data->crashUrl)){
|
||||
if(isset($data->crashId) && is_int($data->crashId) && isset($data->crashUrl) && is_string($data->crashUrl)){
|
||||
$reportId = $data->crashId;
|
||||
$reportUrl = $data->crashUrl;
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
|
||||
}elseif(isset($data->error)){
|
||||
}elseif(isset($data->error) && is_string($data->error)){
|
||||
$this->logger->emergency("Automatic crash report submission failed: $data->error");
|
||||
}else{
|
||||
$this->logger->emergency("Invalid JSON response received from crash archive: " . $reply->getBody());
|
||||
}
|
||||
}else{
|
||||
$this->logger->emergency("Failed to communicate with crash archive: $postUrlError");
|
||||
@ -1716,7 +1714,7 @@ class Server{
|
||||
$session = $player->getNetworkSession();
|
||||
$position = $player->getPosition();
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_player_logIn(
|
||||
TextFormat::AQUA . $player->getName() . TextFormat::WHITE,
|
||||
TextFormat::AQUA . $player->getName() . TextFormat::RESET,
|
||||
$session->getIp(),
|
||||
(string) $session->getPort(),
|
||||
(string) $player->getId(),
|
||||
@ -1853,6 +1851,15 @@ class Server{
|
||||
|
||||
$this->getMemoryManager()->check();
|
||||
|
||||
if($this->console !== null){
|
||||
Timings::$serverCommand->startTiming();
|
||||
while(($line = $this->console->readLine()) !== null){
|
||||
$this->consoleSender ??= new ConsoleCommandSender($this, $this->language);
|
||||
$this->dispatchCommand($this->consoleSender, $line);
|
||||
}
|
||||
Timings::$serverCommand->stopTiming();
|
||||
}
|
||||
|
||||
Timings::$serverTick->stopTiming();
|
||||
|
||||
$now = microtime(true);
|
||||
|
@ -43,12 +43,7 @@ final class ServerConfigGroup{
|
||||
private Config $serverProperties
|
||||
){}
|
||||
|
||||
/**
|
||||
* @param mixed $defaultValue
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getProperty(string $variable, $defaultValue = null){
|
||||
public function getProperty(string $variable, mixed $defaultValue = null) : mixed{
|
||||
if(!array_key_exists($variable, $this->propertyCache)){
|
||||
$v = getopt("", ["$variable::"]);
|
||||
if(isset($v[$variable])){
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.0.0-ALPHA1";
|
||||
public const BASE_VERSION = "5.0.0-ALPHA6";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "alpha";
|
||||
|
||||
|
@ -30,12 +30,17 @@ use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\AnvilFallSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function lcg_value;
|
||||
use function round;
|
||||
|
||||
class Anvil extends Transparent implements Fallable{
|
||||
use FallableTrait;
|
||||
@ -49,22 +54,14 @@ class Anvil extends Transparent implements Fallable{
|
||||
|
||||
public function getRequiredTypeDataBits() : int{ return 2; }
|
||||
|
||||
protected function decodeType(RuntimeDataReader $r) : void{
|
||||
$this->setDamage($r->readBoundedInt(2, self::UNDAMAGED, self::VERY_DAMAGED));
|
||||
}
|
||||
|
||||
protected function encodeType(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(2, $this->getDamage());
|
||||
protected function describeType(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(2, self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
|
||||
}
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 2; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->setFacing($r->readHorizontalFacing());
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalFacing($this->getFacing());
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
}
|
||||
|
||||
public function getDamage() : int{ return $this->damage; }
|
||||
@ -89,7 +86,7 @@ class Anvil extends Transparent implements Fallable{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
$player->setCurrentWindow(new AnvilInventory($this->position));
|
||||
}
|
||||
@ -104,7 +101,18 @@ class Anvil extends Transparent implements Fallable{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function tickFalling() : ?Block{
|
||||
return null;
|
||||
public function onHitGround(FallingBlock $blockEntity) : bool{
|
||||
if(lcg_value() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){
|
||||
if($this->damage !== self::VERY_DAMAGED){
|
||||
$this->damage = $this->damage + 1;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLandSound() : ?Sound{
|
||||
return new AnvilFallSound();
|
||||
}
|
||||
}
|
||||
|
@ -58,16 +58,10 @@ class Bamboo extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->setLeafSize($r->readBoundedInt(2, self::NO_LEAVES, self::LARGE_LEAVES));
|
||||
$this->setThick($r->readBool());
|
||||
$this->setReady($r->readBool());
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(2, $this->getLeafSize());
|
||||
$w->writeBool($this->isThick());
|
||||
$w->writeBool($this->isReady());
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(2, self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
|
||||
$w->bool($this->thick);
|
||||
$w->bool($this->ready);
|
||||
}
|
||||
|
||||
public function isThick() : bool{ return $this->thick; }
|
||||
@ -130,14 +124,11 @@ class Bamboo extends Transparent{
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
//TODO: tags would be better for this
|
||||
return
|
||||
$block instanceof Dirt ||
|
||||
$block instanceof Grass ||
|
||||
$block instanceof Gravel ||
|
||||
$block instanceof Sand ||
|
||||
$block instanceof Mycelium ||
|
||||
$block instanceof Podzol;
|
||||
$block->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$block->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
private function seekToTop() : Bamboo{
|
||||
@ -149,7 +140,7 @@ class Bamboo extends Transparent{
|
||||
return $top;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$top = $this->seekToTop();
|
||||
if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){
|
||||
@ -166,9 +157,10 @@ class Bamboo extends Transparent{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$below = $this->position->getWorld()->getBlock($this->position->down());
|
||||
$world = $this->position->getWorld();
|
||||
$below = $world->getBlock($this->position->down());
|
||||
if(!$this->canBeSupportedBy($below) && !$below->isSameType($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$world->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +206,7 @@ class Bamboo extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
$tx = new BlockTransaction($this->position->getWorld());
|
||||
$tx = new BlockTransaction($world);
|
||||
foreach($newBlocks as $idx => $newBlock){
|
||||
$tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock);
|
||||
}
|
||||
|
@ -39,12 +39,8 @@ final class BambooSapling extends Flowable{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 1; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->setReady($r->readBool());
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeBool($this->isReady());
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->bool($this->ready);
|
||||
}
|
||||
|
||||
public function isReady() : bool{ return $this->ready; }
|
||||
@ -56,14 +52,11 @@ final class BambooSapling extends Flowable{
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
//TODO: tags would be better for this
|
||||
return
|
||||
$block instanceof Dirt ||
|
||||
$block instanceof Grass ||
|
||||
$block instanceof Gravel ||
|
||||
$block instanceof Sand ||
|
||||
$block instanceof Mycelium ||
|
||||
$block instanceof Podzol;
|
||||
$block->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$block->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -73,7 +66,7 @@ final class BambooSapling extends Flowable{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer || $item instanceof ItemBamboo){
|
||||
if($this->grow($player)){
|
||||
$item->pop();
|
||||
@ -84,8 +77,9 @@ final class BambooSapling extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->position->getWorld()->getBlock($this->position->down()))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
||||
$world->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,9 @@ class Barrel extends Opaque{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->setFacing($r->readFacing());
|
||||
$this->setOpen($r->readBool());
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeFacing($this->getFacing());
|
||||
$w->writeBool($this->isOpen());
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->facing($this->facing);
|
||||
$w->bool($this->open);
|
||||
}
|
||||
|
||||
public function isOpen() : bool{
|
||||
@ -81,7 +76,7 @@ class Barrel extends Opaque{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
$barrel = $this->position->getWorld()->getTile($this->position);
|
||||
if($barrel instanceof TileBarrel){
|
||||
|
@ -48,18 +48,20 @@ abstract class BaseBanner extends Transparent{
|
||||
*/
|
||||
protected array $patterns = [];
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::BLACK();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileBanner){
|
||||
$this->color = $tile->getBaseColor();
|
||||
$this->setPatterns($tile->getPatterns());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
@ -87,7 +89,7 @@ abstract class BaseBanner extends Transparent{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BannerPatternLayer[] $patterns
|
||||
* @param BannerPatternLayer[] $patterns
|
||||
*
|
||||
* @phpstan-param list<BannerPatternLayer> $patterns
|
||||
* @return $this
|
||||
|
89
src/block/BaseCake.php
Normal file
89
src/block/BaseCake.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\FoodSource;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
abstract class BaseCake extends Transparent implements FoodSource{
|
||||
|
||||
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{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
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{
|
||||
if($player !== null){
|
||||
return $player->consumeObject($this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFoodRestore() : int{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public function getSaturationRestore() : float{
|
||||
return 0.4;
|
||||
}
|
||||
|
||||
public function requiresHunger() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EffectInstance[]
|
||||
*/
|
||||
public function getAdditionalEffects() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
abstract public function getResidue() : Block;
|
||||
|
||||
public function onConsume(Living $consumer) : void{
|
||||
$this->position->getWorld()->setBlock($this->position, $this->getResidue());
|
||||
}
|
||||
}
|
@ -26,31 +26,30 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\BlockDeathEvent;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class BaseCoral extends Transparent{
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
$this->coralType = CoralType::TUBE();
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->dead){
|
||||
$world = $this->position->getWorld();
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
||||
}
|
||||
}
|
||||
|
||||
$hasWater = false;
|
||||
foreach($this->position->sides() as $vector3){
|
||||
if($world->getBlock($vector3) instanceof Water){
|
||||
$hasWater = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: check water inside the block itself (not supported on the API yet)
|
||||
if(!$hasWater){
|
||||
$world->setBlock($this->position, $this->setDead(true));
|
||||
public function onScheduledUpdate() : void{
|
||||
if(!$this->dead && !$this->isCoveredWithWater()){
|
||||
$ev = new BlockDeathEvent($this, $this->setDead(true));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,6 +64,21 @@ abstract class BaseCoral extends Transparent{
|
||||
|
||||
public function isSolid() : bool{ return false; }
|
||||
|
||||
protected function isCoveredWithWater() : bool{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$hasWater = false;
|
||||
foreach($this->position->sides() as $vector3){
|
||||
if($world->getBlock($vector3) instanceof Water){
|
||||
$hasWater = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: check water inside the block itself (not supported on the API yet)
|
||||
return $hasWater;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{ return []; }
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
|
@ -162,6 +162,7 @@ abstract class BaseRail extends Flowable{
|
||||
$thisConnections = $this->getConnectedDirections();
|
||||
$changed = false;
|
||||
|
||||
$world = $this->position->getWorld();
|
||||
do{
|
||||
$possible = $this->getPossibleConnectionDirections($thisConnections);
|
||||
$continue = false;
|
||||
@ -189,7 +190,7 @@ abstract class BaseRail extends Flowable{
|
||||
if(isset($otherPossible[$otherSide])){
|
||||
$otherConnections[] = $otherSide;
|
||||
$other->setConnections($otherConnections);
|
||||
$this->position->getWorld()->setBlock($other->position, $other);
|
||||
$world->setBlock($other->position, $other);
|
||||
|
||||
$changed = true;
|
||||
$thisConnections[] = $thisSide;
|
||||
@ -202,7 +203,7 @@ abstract class BaseRail extends Flowable{
|
||||
|
||||
if($changed){
|
||||
$this->setConnections($thisConnections);
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,12 +221,13 @@ abstract class BaseRail extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach($this->getCurrentShapeConnections() as $connection){
|
||||
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$world->useBreakOn($this->position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -24,17 +24,23 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Sign as TileSign;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SignText;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\block\utils\WoodTypeTrait;
|
||||
use pocketmine\color\Color;
|
||||
use pocketmine\event\block\SignChangeEvent;
|
||||
use pocketmine\item\Dye;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\DyeUseSound;
|
||||
use pocketmine\world\sound\InkSacUseSound;
|
||||
use function array_map;
|
||||
use function assert;
|
||||
use function strlen;
|
||||
@ -51,20 +57,22 @@ abstract class BaseSign extends Transparent{
|
||||
/**
|
||||
* @param \Closure() : Item $asItemCallback
|
||||
*/
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo, WoodType $woodType, \Closure $asItemCallback){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, WoodType $woodType, \Closure $asItemCallback){
|
||||
$this->woodType = $woodType;
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
$this->text = new SignText();
|
||||
$this->asItemCallback = $asItemCallback;
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileSign){
|
||||
$this->text = $tile->getText();
|
||||
$this->editorEntityRuntimeId = $tile->getEditorEntityRuntimeId();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
@ -109,6 +117,55 @@ abstract class BaseSign extends Transparent{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
private function doSignChange(SignText $newText, Player $player, Item $item) : bool{
|
||||
$ev = new SignChangeEvent($this, $player, $newText);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->text = $ev->getNewText();
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function changeSignGlowingState(bool $glowing, Player $player, Item $item) : bool{
|
||||
if($this->text->isGlowing() !== $glowing && $this->doSignChange(new SignText($this->text->getLines(), $this->text->getBaseColor(), $glowing), $player, $item)){
|
||||
$this->position->getWorld()->addSound($this->position, new InkSacUseSound());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player === null){
|
||||
return false;
|
||||
}
|
||||
$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
|
||||
ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
|
||||
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
|
||||
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
|
||||
default => null
|
||||
};
|
||||
if($dyeColor !== null){
|
||||
$color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
|
||||
if($color->toARGB() === $this->text->getBaseColor()->toARGB()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)){
|
||||
$this->position->getWorld()->addSound($this->position, new DyeUseSound());
|
||||
return true;
|
||||
}
|
||||
}elseif($item->getTypeId() === ItemTypeIds::INK_SAC){
|
||||
return $this->changeSignGlowingState(false, $player, $item);
|
||||
}elseif($item->getTypeId() === ItemTypeIds::GLOW_INK_SAC){
|
||||
return $this->changeSignGlowingState(true, $player, $item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing information about the sign text.
|
||||
*/
|
||||
|
@ -49,32 +49,28 @@ class Bed extends Transparent{
|
||||
protected bool $occupied = false;
|
||||
protected bool $head = false;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::RED();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->facing = $r->readHorizontalFacing();
|
||||
$this->occupied = $r->readBool();
|
||||
$this->head = $r->readBool();
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->bool($this->occupied);
|
||||
$w->bool($this->head);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalFacing($this->facing);
|
||||
$w->writeBool($this->occupied);
|
||||
$w->writeBool($this->head);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
//read extra state information from the tile - this is an ugly hack
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileBed){
|
||||
$this->color = $tile->getColor();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
@ -130,7 +126,7 @@ class Bed extends Transparent{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
$other = $this->getOtherHalf();
|
||||
$playerPos = $player->getPosition();
|
||||
|
@ -31,12 +31,8 @@ class Bedrock extends Opaque{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 1; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->burnsForever = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeBool($this->burnsForever);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->bool($this->burnsForever);
|
||||
}
|
||||
|
||||
public function burnsForever() : bool{
|
||||
|
@ -29,8 +29,6 @@ use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\data\runtime\RuntimeEnumDeserializer;
|
||||
use pocketmine\data\runtime\RuntimeEnumSerializer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -44,21 +42,16 @@ final class Bell extends Transparent{
|
||||
|
||||
private BellAttachmentType $attachmentType;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->attachmentType = BellAttachmentType::FLOOR();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->attachmentType = RuntimeEnumDeserializer::readBellAttachmentType($r);
|
||||
$this->facing = $r->readHorizontalFacing();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
RuntimeEnumSerializer::writeBellAttachmentType($w, $this->attachmentType);
|
||||
$w->writeHorizontalFacing($this->facing);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->bellAttachmentType($this->attachmentType);
|
||||
$w->horizontalFacing($this->facing);
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
@ -139,7 +132,7 @@ final class Bell extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
$faceHit = Facing::opposite($player->getHorizontalFacing());
|
||||
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
|
||||
@ -160,10 +153,11 @@ final class Bell extends Transparent{
|
||||
}
|
||||
|
||||
public function ring(int $faceHit) : void{
|
||||
$this->position->getWorld()->addSound($this->position, new BellRingSound());
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
$world = $this->position->getWorld();
|
||||
$world->addSound($this->position, new BellRingSound());
|
||||
$tile = $world->getTile($this->position);
|
||||
if($tile instanceof TileBell){
|
||||
$this->position->getWorld()->broadcastPacketToViewers($this->position, $tile->createFakeUpdatePacket($faceHit));
|
||||
$world->broadcastPacketToViewers($this->position, $tile->createFakeUpdatePacket($faceHit));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemBlock;
|
||||
@ -46,6 +47,7 @@ use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\World;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class Block{
|
||||
@ -54,19 +56,19 @@ class Block{
|
||||
|
||||
protected BlockIdentifier $idInfo;
|
||||
protected string $fallbackName;
|
||||
protected BlockBreakInfo $breakInfo;
|
||||
protected BlockTypeInfo $typeInfo;
|
||||
protected Position $position;
|
||||
|
||||
/** @var AxisAlignedBB[]|null */
|
||||
protected ?array $collisionBoxes = null;
|
||||
|
||||
/**
|
||||
* @param string $name English name of the block type (TODO: implement translations)
|
||||
* @param string $name English name of the block type (TODO: implement translations)
|
||||
*/
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->idInfo = $idInfo;
|
||||
$this->fallbackName = $name;
|
||||
$this->breakInfo = $breakInfo;
|
||||
$this->typeInfo = $typeInfo;
|
||||
$this->position = new Position(0, 0, 0, null);
|
||||
}
|
||||
|
||||
@ -74,21 +76,38 @@ class Block{
|
||||
$this->position = clone $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing information about how to identify and store this block type, such as type ID and
|
||||
* tile type (if any).
|
||||
*/
|
||||
public function getIdInfo() : BlockIdentifier{
|
||||
return $this->idInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printable English name of the block.
|
||||
*/
|
||||
public function getName() : string{
|
||||
return $this->fallbackName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Returns the full blockstate ID of this block. This is a compact way of representing a blockstate used to store
|
||||
* blocks in chunks at runtime.
|
||||
*
|
||||
* This ID can be used to later obtain a copy of this block using {@link BlockFactory::fromStateId()}.
|
||||
*/
|
||||
public function getStateId() : int{
|
||||
return ($this->getTypeId() << self::INTERNAL_STATE_DATA_BITS) | $this->computeStateData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block as an item.
|
||||
* State information such as facing, powered/unpowered, open/closed, etc., is discarded.
|
||||
* Type information such as colour, wood type, etc. is preserved.
|
||||
*/
|
||||
public function asItem() : Item{
|
||||
return new ItemBlock($this);
|
||||
}
|
||||
@ -105,10 +124,10 @@ class Block{
|
||||
$givenBits = $typeBits;
|
||||
$reader = new RuntimeDataReader($givenBits, $data);
|
||||
|
||||
$this->decodeType($reader);
|
||||
$this->describeType($reader);
|
||||
$readBits = $reader->getOffset();
|
||||
if($typeBits !== $readBits){
|
||||
throw new \LogicException("Exactly $typeBits bits of type data were provided, but $readBits were read");
|
||||
throw new \LogicException(get_class($this) . ": Exactly $typeBits bits of type data were provided, but $readBits were read");
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,21 +141,13 @@ class Block{
|
||||
$reader = new RuntimeDataReader($givenBits, $data);
|
||||
$this->decodeTypeData($reader->readInt($typeBits));
|
||||
|
||||
$this->decodeState($reader);
|
||||
$this->describeState($reader);
|
||||
$readBits = $reader->getOffset() - $typeBits;
|
||||
if($stateBits !== $readBits){
|
||||
throw new \LogicException("Exactly $stateBits bits of state data were provided, but $readBits were read");
|
||||
throw new \LogicException(get_class($this) . ": Exactly $stateBits bits of state data were provided, but $readBits were read");
|
||||
}
|
||||
}
|
||||
|
||||
protected function decodeType(RuntimeDataReader $r) : void{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -145,10 +156,10 @@ class Block{
|
||||
$requiredBits = $typeBits;
|
||||
$writer = new RuntimeDataWriter($requiredBits);
|
||||
|
||||
$this->encodeType($writer);
|
||||
$this->describeType($writer);
|
||||
$writtenBits = $writer->getOffset();
|
||||
if($typeBits !== $writtenBits){
|
||||
throw new \LogicException("Exactly $typeBits bits of type data were expected, but $writtenBits were written");
|
||||
throw new \LogicException(get_class($this) . ": Exactly $typeBits bits of type data were expected, but $writtenBits were written");
|
||||
}
|
||||
|
||||
return $writer->getValue();
|
||||
@ -162,22 +173,22 @@ class Block{
|
||||
$stateBits = $this->getRequiredStateDataBits();
|
||||
$requiredBits = $typeBits + $stateBits;
|
||||
$writer = new RuntimeDataWriter($requiredBits);
|
||||
$writer->writeInt($typeBits, $this->computeTypeData());
|
||||
$writer->int($typeBits, $this->computeTypeData());
|
||||
|
||||
$this->encodeState($writer);
|
||||
$this->describeState($writer);
|
||||
$writtenBits = $writer->getOffset() - $typeBits;
|
||||
if($stateBits !== $writtenBits){
|
||||
throw new \LogicException("Exactly $stateBits bits of state data were expected, but $writtenBits were written");
|
||||
throw new \LogicException(get_class($this) . ": Exactly $stateBits bits of state data were expected, but $writtenBits were written");
|
||||
}
|
||||
|
||||
return $writer->getValue();
|
||||
}
|
||||
|
||||
protected function encodeType(RuntimeDataWriter $w) : void{
|
||||
protected function describeType(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
@ -187,16 +198,28 @@ class Block{
|
||||
*
|
||||
* 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.
|
||||
* data from a block entity).
|
||||
*/
|
||||
public function readStateFromWorld() : void{
|
||||
public function readStateFromWorld() : Block{
|
||||
$this->collisionBoxes = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes information about the block into the world. This writes the blockstate ID into the chunk, and creates
|
||||
* and/or removes tiles as necessary.
|
||||
*
|
||||
* Note: Do not call this directly. Pass the block to {@link World::setBlock()} instead.
|
||||
*/
|
||||
public function writeStateToWorld() : void{
|
||||
$this->position->getWorld()->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getStateId());
|
||||
$world = $this->position->getWorld();
|
||||
$world->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getStateId());
|
||||
|
||||
$tileType = $this->idInfo->getTileClass();
|
||||
$oldTile = $this->position->getWorld()->getTile($this->position);
|
||||
$oldTile = $world->getTile($this->position);
|
||||
if($oldTile !== null){
|
||||
if($tileType === null || !($oldTile instanceof $tileType)){
|
||||
$oldTile->close();
|
||||
@ -210,13 +233,13 @@ class Block{
|
||||
* @var Tile $tile
|
||||
* @see Tile::__construct()
|
||||
*/
|
||||
$tile = new $tileType($this->position->getWorld(), $this->position->asVector3());
|
||||
$this->position->getWorld()->addTile($tile);
|
||||
$tile = new $tileType($world, $this->position->asVector3());
|
||||
$world->addTile($tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a type ID that identifies this type of block. This does not include information like facing, colour,
|
||||
* Returns a type ID that identifies this type of block. This does not include information like facing, open/closed,
|
||||
* powered/unpowered, etc.
|
||||
*/
|
||||
public function getTypeId() : int{
|
||||
@ -237,6 +260,25 @@ class Block{
|
||||
return $this->getStateId() === $other->getStateId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTypeTags() : array{
|
||||
return $this->typeInfo->getTypeTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this block type has the given type tag. Type tags are used as a dynamic way to tag blocks as
|
||||
* having certain properties, allowing type checks which are more dynamic than hardcoding a bunch of IDs or a bunch
|
||||
* of instanceof checks.
|
||||
*
|
||||
* For example, grass blocks, dirt, farmland, podzol and mycelium are all dirt-like blocks, and support the
|
||||
* placement of blocks like flowers, so they have a common tag which allows them to be identified as such.
|
||||
*/
|
||||
public function hasTypeTag(string $tag) : bool{
|
||||
return $this->typeInfo->hasTypeTag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* AKA: Block->isPlaceable
|
||||
*/
|
||||
@ -244,22 +286,36 @@ class Block{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this block can be replaced by another block placed in the same position.
|
||||
*/
|
||||
public function canBeReplaced() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this block can replace the given block in the given placement conditions.
|
||||
* This is used to allow slabs of the same type to combine into double slabs.
|
||||
*/
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
return $blockReplace->canBeReplaced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Places the Block, using block space and block target, and side. Returns if the block has been placed.
|
||||
* Generates a block transaction to set all blocks affected by placing this block. Usually this is just the block
|
||||
* itself, but may be multiple blocks in some cases (such as doors).
|
||||
*
|
||||
* @return bool whether the placement should go ahead
|
||||
*/
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$tx->addBlock($blockReplace->position, $this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately after the block has been placed in the world. Since placement uses a block transaction, some
|
||||
* things may not be possible until after the transaction has been executed.
|
||||
*/
|
||||
public function onPostPlace() : void{
|
||||
|
||||
}
|
||||
@ -268,17 +324,20 @@ class Block{
|
||||
* Returns an object containing information about the destruction requirements of this block.
|
||||
*/
|
||||
public function getBreakInfo() : BlockBreakInfo{
|
||||
return $this->breakInfo;
|
||||
return $this->typeInfo->getBreakInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actions needed so the block is broken with the Item
|
||||
*
|
||||
* @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if full)
|
||||
*/
|
||||
public function onBreak(Item $item, ?Player $player = null) : bool{
|
||||
if(($t = $this->position->getWorld()->getTile($this->position)) !== null){
|
||||
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(($t = $world->getTile($this->position)) !== null){
|
||||
$t->onBlockDestroyed();
|
||||
}
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
|
||||
$world->setBlock($this->position, VanillaBlocks::AIR());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -298,7 +357,7 @@ class Block{
|
||||
|
||||
/**
|
||||
* Called when this block is randomly updated due to chunk ticking.
|
||||
* WARNING: This will not be called if ticksRandomly() does not return true!
|
||||
* WARNING: This will not be called if {@link Block::ticksRandomly()} does not return true!
|
||||
*/
|
||||
public function onRandomTick() : void{
|
||||
|
||||
@ -313,14 +372,15 @@ class Block{
|
||||
|
||||
/**
|
||||
* Do actions when interacted by Item. Returns if it has done anything
|
||||
*
|
||||
* @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
|
||||
*/
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this block is attacked (left-clicked). This is called when a player left-clicks the block to try and
|
||||
* start to break it in survival mode.
|
||||
* Called when this block is attacked (left-clicked) by a player attempting to start breaking it in survival.
|
||||
*
|
||||
* @return bool if an action took place, prevents starting to break the block if true.
|
||||
*/
|
||||
@ -328,11 +388,19 @@ class Block{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a multiplier applied to the velocity of entities moving on top of this block. A higher value will make
|
||||
* the block more slippery (like ice).
|
||||
*
|
||||
* @return float 0.0-1.0
|
||||
*/
|
||||
public function getFrictionFactor() : float{
|
||||
return 0.6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of light emitted by this block.
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getLightLevel() : int{
|
||||
@ -375,10 +443,6 @@ class Block{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether entities can climb up this block.
|
||||
*/
|
||||
@ -386,10 +450,6 @@ class Block{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addVelocityToEntity(Entity $entity) : ?Vector3{
|
||||
return null;
|
||||
}
|
||||
|
||||
final public function getPosition() : Position{
|
||||
return $this->position;
|
||||
}
|
||||
@ -407,7 +467,7 @@ class Block{
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getDrops(Item $item) : array{
|
||||
if($this->breakInfo->isToolCompatible($item)){
|
||||
if($this->getBreakInfo()->isToolCompatible($item)){
|
||||
if($this->isAffectedBySilkTouch() && $item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
return $this->getSilkTouchDrops($item);
|
||||
}
|
||||
@ -449,7 +509,7 @@ class Block{
|
||||
* Returns how much XP will be dropped by breaking this block with the given item.
|
||||
*/
|
||||
public function getXpDropForTool(Item $item) : int{
|
||||
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH()) || !$this->breakInfo->isToolCompatible($item)){
|
||||
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH()) || !$this->getBreakInfo()->isToolCompatible($item)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -472,6 +532,7 @@ class Block{
|
||||
|
||||
/**
|
||||
* Returns the item that players will equip when middle-clicking on this block.
|
||||
* If addUserData is true, additional data may be added, such as banner patterns, chest contents, etc.
|
||||
*/
|
||||
public function getPickedItem(bool $addUserData = false) : Item{
|
||||
$item = $this->asItem();
|
||||
@ -502,6 +563,10 @@ class Block{
|
||||
return 64;
|
||||
}
|
||||
|
||||
public function isFireProofAsItem() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chance that the block will catch fire from nearby fire sources. Higher values lead to faster catching
|
||||
* fire.
|
||||
@ -595,7 +660,7 @@ class Block{
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for collision against an AxisAlignedBB
|
||||
* Returns whether any of the block's collision boxes intersect with the given AxisAlignedBB.
|
||||
*/
|
||||
public function collidesWithBB(AxisAlignedBB $bb) : bool{
|
||||
foreach($this->getCollisionBoxes() as $bb2){
|
||||
@ -607,10 +672,21 @@ class Block{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the block has actions to be executed when an entity enters its cell (full cube space).
|
||||
*
|
||||
* @see Block::onEntityInside()
|
||||
*/
|
||||
public function hasEntityCollision() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an entity's bounding box clips inside this block's cell. Note that the entity may not be intersecting
|
||||
* with the collision box or bounding box.
|
||||
*
|
||||
* WARNING: This will not be called if {@link Block::hasEntityCollision()} returns false.
|
||||
*
|
||||
* @return bool Whether the block is still the same after the intersection. If it changed (e.g. due to an explosive
|
||||
* being ignited), this should return false.
|
||||
*/
|
||||
@ -618,6 +694,19 @@ class Block{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a direction vector describing which way an entity intersecting this block should be pushed.
|
||||
* This is used by liquids to push entities in liquid currents.
|
||||
*
|
||||
* The returned vector is summed with vectors from every other block the entity is intersecting, and normalized to
|
||||
* produce a final direction vector.
|
||||
*
|
||||
* WARNING: This will not be called if {@link Block::hasEntityCollision()} does not return true!
|
||||
*/
|
||||
public function addVelocityToEntity(Entity $entity) : ?Vector3{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an entity lands on this block (usually due to falling).
|
||||
* @return float|null The new vertical velocity of the entity, or null if unchanged.
|
||||
@ -627,6 +716,20 @@ class Block{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a projectile collides with one of this block's collision boxes.
|
||||
*/
|
||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of collision bounding boxes for this block.
|
||||
* These are used for:
|
||||
* - entity movement collision checks (to ensure entities can't clip through blocks)
|
||||
* - projectile flight paths
|
||||
* - block placement (to ensure the player can't place blocks inside itself or another entity)
|
||||
* - anti-cheat checks in plugins
|
||||
*
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
final public function getCollisionBoxes() : array{
|
||||
@ -657,6 +760,10 @@ class Block{
|
||||
return [AxisAlignedBB::one()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of support that the block can provide on the given face. This is used to determine whether
|
||||
* blocks placed on the given face can be supported by this block.
|
||||
*/
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::FULL();
|
||||
}
|
||||
@ -667,6 +774,10 @@ class Block{
|
||||
return count($bb) === 1 && $bb[0]->getAverageEdgeLength() >= 1 && $bb[0]->isCube();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a ray trace along the line between the two positions using the block's collision boxes.
|
||||
* Returns the intersection point closest to pos1, or null if no intersection occurred.
|
||||
*/
|
||||
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{
|
||||
$bbs = $this->getCollisionBoxes();
|
||||
if(count($bbs) === 0){
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ToolTier;
|
||||
use function get_class;
|
||||
|
||||
class BlockBreakInfo{
|
||||
@ -52,6 +53,22 @@ class BlockBreakInfo{
|
||||
$this->blastResistance = $blastResistance ?? $hardness * 5;
|
||||
}
|
||||
|
||||
public static function tier(float $hardness, int $toolType, ToolTier $toolTier, ?float $blastResistance = null) : self{
|
||||
return new self($hardness, $toolType, $toolTier->getHarvestLevel(), $blastResistance);
|
||||
}
|
||||
|
||||
public static function pickaxe(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : self{
|
||||
return new self($hardness, BlockToolType::PICKAXE, $toolTier?->getHarvestLevel() ?? 0, $blastResistance);
|
||||
}
|
||||
|
||||
public static function shovel(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : self{
|
||||
return new self($hardness, BlockToolType::SHOVEL, $toolTier?->getHarvestLevel() ?? 0, $blastResistance);
|
||||
}
|
||||
|
||||
public static function axe(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : self{
|
||||
return new self($hardness, BlockToolType::AXE, $toolTier?->getHarvestLevel() ?? 0, $blastResistance);
|
||||
}
|
||||
|
||||
public static function instant(int $toolType = BlockToolType::NONE, int $toolHarvestLevel = 0) : self{
|
||||
return new self(0.0, $toolType, $toolHarvestLevel, 0.0);
|
||||
}
|
||||
|
@ -25,36 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\BlockBreakInfo as BreakInfo;
|
||||
use pocketmine\block\BlockIdentifier as BID;
|
||||
use pocketmine\block\BlockToolType as ToolType;
|
||||
use pocketmine\block\BlockTypeIds as Ids;
|
||||
use pocketmine\block\tile\Banner as TileBanner;
|
||||
use pocketmine\block\tile\Barrel as TileBarrel;
|
||||
use pocketmine\block\tile\Beacon as TileBeacon;
|
||||
use pocketmine\block\tile\Bed as TileBed;
|
||||
use pocketmine\block\tile\Bell as TileBell;
|
||||
use pocketmine\block\tile\BlastFurnace as TileBlastFurnace;
|
||||
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\tile\Comparator as TileComparator;
|
||||
use pocketmine\block\tile\DaylightSensor as TileDaylightSensor;
|
||||
use pocketmine\block\tile\EnchantTable as TileEnchantingTable;
|
||||
use pocketmine\block\tile\EnderChest as TileEnderChest;
|
||||
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
|
||||
use pocketmine\block\tile\Hopper as TileHopper;
|
||||
use pocketmine\block\tile\ItemFrame as TileItemFrame;
|
||||
use pocketmine\block\tile\Jukebox as TileJukebox;
|
||||
use pocketmine\block\tile\Lectern as TileLectern;
|
||||
use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
|
||||
use pocketmine\block\tile\NormalFurnace as TileNormalFurnace;
|
||||
use pocketmine\block\tile\Note as TileNote;
|
||||
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
|
||||
use pocketmine\block\tile\Skull as TileSkull;
|
||||
use pocketmine\block\tile\Smoker as TileSmoker;
|
||||
use pocketmine\block\utils\TreeType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\data\runtime\InvalidSerializedRuntimeDataException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ToolTier;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\world\light\LightUpdate;
|
||||
@ -102,741 +73,9 @@ class BlockFactory{
|
||||
public array $blastResistance = [];
|
||||
|
||||
public function __construct(){
|
||||
$railBreakInfo = new BlockBreakInfo(0.7);
|
||||
$this->register(new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL), "Activator Rail", $railBreakInfo));
|
||||
$this->register(new Air(new BID(Ids::AIR), "Air", BreakInfo::indestructible(-1.0)));
|
||||
$this->register(new Anvil(new BID(Ids::ANVIL), "Anvil", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 6000.0)));
|
||||
$this->register(new Bamboo(new BID(Ids::BAMBOO), "Bamboo", new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{
|
||||
public function getBreakTime(Item $item) : float{
|
||||
if($item->getBlockToolType() === ToolType::SWORD){
|
||||
return 0.0;
|
||||
}
|
||||
return parent::getBreakTime($item);
|
||||
}
|
||||
}));
|
||||
$this->register(new BambooSapling(new BID(Ids::BAMBOO_SAPLING), "Bamboo Sapling", BreakInfo::instant()));
|
||||
|
||||
$bannerBreakInfo = new BreakInfo(1.0, ToolType::AXE);
|
||||
$this->register(new FloorBanner(new BID(Ids::BANNER, TileBanner::class), "Banner", $bannerBreakInfo));
|
||||
$this->register(new WallBanner(new BID(Ids::WALL_BANNER, TileBanner::class), "Wall Banner", $bannerBreakInfo));
|
||||
$this->register(new Barrel(new BID(Ids::BARREL, TileBarrel::class), "Barrel", new BreakInfo(2.5, ToolType::AXE)));
|
||||
$this->register(new Transparent(new BID(Ids::BARRIER), "Barrier", BreakInfo::indestructible()));
|
||||
$this->register(new Beacon(new BID(Ids::BEACON, TileBeacon::class), "Beacon", new BreakInfo(3.0)));
|
||||
$this->register(new Bed(new BID(Ids::BED, TileBed::class), "Bed Block", new BreakInfo(0.2)));
|
||||
$this->register(new Bedrock(new BID(Ids::BEDROCK), "Bedrock", BreakInfo::indestructible()));
|
||||
|
||||
$this->register(new Beetroot(new BID(Ids::BEETROOTS), "Beetroot Block", BreakInfo::instant()));
|
||||
$this->register(new Bell(new BID(Ids::BELL, TileBell::class), "Bell", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new BlueIce(new BID(Ids::BLUE_ICE), "Blue Ice", new BreakInfo(2.8, ToolType::PICKAXE)));
|
||||
$this->register(new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Bookshelf(new BID(Ids::BOOKSHELF), "Bookshelf", new BreakInfo(1.5, ToolType::AXE)));
|
||||
$this->register(new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
|
||||
$bricksBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Stair(new BID(Ids::BRICK_STAIRS), "Brick Stairs", $bricksBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::BRICKS), "Bricks", $bricksBreakInfo));
|
||||
|
||||
$this->register(new BrownMushroom(new BID(Ids::BROWN_MUSHROOM), "Brown Mushroom", BreakInfo::instant()));
|
||||
$this->register(new Cactus(new BID(Ids::CACTUS), "Cactus", new BreakInfo(0.4)));
|
||||
$this->register(new Cake(new BID(Ids::CAKE), "Cake", new BreakInfo(0.5)));
|
||||
$this->register(new Carrot(new BID(Ids::CARROTS), "Carrot Block", BreakInfo::instant()));
|
||||
|
||||
$chestBreakInfo = new BreakInfo(2.5, ToolType::AXE);
|
||||
$this->register(new Chest(new BID(Ids::CHEST, TileChest::class), "Chest", $chestBreakInfo));
|
||||
$this->register(new Clay(new BID(Ids::CLAY), "Clay Block", new BreakInfo(0.6, ToolType::SHOVEL)));
|
||||
$this->register(new Coal(new BID(Ids::COAL), "Coal Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
|
||||
|
||||
$cobblestoneBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register($cobblestone = new Opaque(new BID(Ids::COBBLESTONE), "Cobblestone", $cobblestoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::MOSSY_COBBLESTONE), "Mossy Cobblestone", $cobblestoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::COBBLESTONE_STAIRS), "Cobblestone Stairs", $cobblestoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::MOSSY_COBBLESTONE_STAIRS), "Mossy Cobblestone Stairs", $cobblestoneBreakInfo));
|
||||
|
||||
$this->register(new Cobweb(new BID(Ids::COBWEB), "Cobweb", new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)));
|
||||
$this->register(new CocoaBlock(new BID(Ids::COCOA_POD), "Cocoa Block", new BreakInfo(0.2, ToolType::AXE, 0, 15.0)));
|
||||
$this->register(new CoralBlock(new BID(Ids::CORAL_BLOCK), "Coral Block", new BreakInfo(7.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new CraftingTable(new BID(Ids::CRAFTING_TABLE), "Crafting Table", new BreakInfo(2.5, ToolType::AXE)));
|
||||
$this->register(new DaylightSensor(new BID(Ids::DAYLIGHT_SENSOR, TileDaylightSensor::class), "Daylight Sensor", new BreakInfo(0.2, ToolType::AXE)));
|
||||
$this->register(new DeadBush(new BID(Ids::DEAD_BUSH), "Dead Bush", BreakInfo::instant(ToolType::SHEARS, 1)));
|
||||
$this->register(new DetectorRail(new BID(Ids::DETECTOR_RAIL), "Detector Rail", $railBreakInfo));
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::DIAMOND), "Diamond Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
|
||||
$this->register(new Dirt(new BID(Ids::DIRT), "Dirt", new BreakInfo(0.5, ToolType::SHOVEL)));
|
||||
$this->register(new DoublePlant(new BID(Ids::SUNFLOWER), "Sunflower", BreakInfo::instant()));
|
||||
$this->register(new DoublePlant(new BID(Ids::LILAC), "Lilac", BreakInfo::instant()));
|
||||
$this->register(new DoublePlant(new BID(Ids::ROSE_BUSH), "Rose Bush", BreakInfo::instant()));
|
||||
$this->register(new DoublePlant(new BID(Ids::PEONY), "Peony", BreakInfo::instant()));
|
||||
$this->register(new DoubleTallGrass(new BID(Ids::DOUBLE_TALLGRASS), "Double Tallgrass", BreakInfo::instant(ToolType::SHEARS, 1)));
|
||||
$this->register(new DoubleTallGrass(new BID(Ids::LARGE_FERN), "Large Fern", BreakInfo::instant(ToolType::SHEARS, 1)));
|
||||
$this->register(new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new DriedKelp(new BID(Ids::DRIED_KELP), "Dried Kelp Block", new BreakInfo(0.5, ToolType::NONE, 0, 12.5)));
|
||||
$this->register(new Opaque(new BID(Ids::EMERALD), "Emerald Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
|
||||
$this->register(new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, TileEnchantingTable::class), "Enchanting Table", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 6000.0)));
|
||||
$this->register(new EndPortalFrame(new BID(Ids::END_PORTAL_FRAME), "End Portal Frame", BreakInfo::indestructible()));
|
||||
$this->register(new EndRod(new BID(Ids::END_ROD), "End Rod", BreakInfo::instant()));
|
||||
$this->register(new Opaque(new BID(Ids::END_STONE), "End Stone", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 45.0)));
|
||||
|
||||
$endBrickBreakInfo = new BreakInfo(0.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 4.0);
|
||||
$this->register(new Opaque(new BID(Ids::END_STONE_BRICKS), "End Stone Bricks", $endBrickBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::END_STONE_BRICK_STAIRS), "End Stone Brick Stairs", $endBrickBreakInfo));
|
||||
|
||||
$this->register(new EnderChest(new BID(Ids::ENDER_CHEST, TileEnderChest::class), "Ender Chest", new BreakInfo(22.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 3000.0)));
|
||||
$this->register(new Farmland(new BID(Ids::FARMLAND), "Farmland", new BreakInfo(0.6, ToolType::SHOVEL)));
|
||||
$this->register(new Fire(new BID(Ids::FIRE), "Fire Block", BreakInfo::instant()));
|
||||
$this->register(new FletchingTable(new BID(Ids::FLETCHING_TABLE), "Fletching Table", new BreakInfo(2.5, ToolType::AXE, 0, 2.5)));
|
||||
$this->register(new Flower(new BID(Ids::DANDELION), "Dandelion", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::POPPY), "Poppy", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::ALLIUM), "Allium", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::AZURE_BLUET), "Azure Bluet", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::BLUE_ORCHID), "Blue Orchid", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::CORNFLOWER), "Cornflower", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::LILY_OF_THE_VALLEY), "Lily of the Valley", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::ORANGE_TULIP), "Orange Tulip", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::OXEYE_DAISY), "Oxeye Daisy", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::PINK_TULIP), "Pink Tulip", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::RED_TULIP), "Red Tulip", BreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::WHITE_TULIP), "White Tulip", BreakInfo::instant()));
|
||||
$this->register(new FlowerPot(new BID(Ids::FLOWER_POT, TileFlowerPot::class), "Flower Pot", BreakInfo::instant()));
|
||||
$this->register(new FrostedIce(new BID(Ids::FROSTED_ICE), "Frosted Ice", new BreakInfo(2.5, ToolType::PICKAXE)));
|
||||
$this->register(new Furnace(new BID(Ids::FURNACE, TileNormalFurnace::class), "Furnace", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Furnace(new BID(Ids::BLAST_FURNACE, TileBlastFurnace::class), "Blast Furnace", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Furnace(new BID(Ids::SMOKER, TileSmoker::class), "Smoker", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
|
||||
$glassBreakInfo = new BreakInfo(0.3);
|
||||
$this->register(new Glass(new BID(Ids::GLASS), "Glass", $glassBreakInfo));
|
||||
$this->register(new GlassPane(new BID(Ids::GLASS_PANE), "Glass Pane", $glassBreakInfo));
|
||||
$this->register(new GlowingObsidian(new BID(Ids::GLOWING_OBSIDIAN), "Glowing Obsidian", new BreakInfo(10.0, ToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 50.0)));
|
||||
$this->register(new Glowstone(new BID(Ids::GLOWSTONE), "Glowstone", new BreakInfo(0.3, ToolType::PICKAXE)));
|
||||
$this->register(new Opaque(new BID(Ids::GOLD), "Gold Block", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
|
||||
|
||||
$grassBreakInfo = new BreakInfo(0.6, ToolType::SHOVEL);
|
||||
$this->register(new Grass(new BID(Ids::GRASS), "Grass", $grassBreakInfo));
|
||||
$this->register(new GrassPath(new BID(Ids::GRASS_PATH), "Grass Path", $grassBreakInfo));
|
||||
$this->register(new Gravel(new BID(Ids::GRAVEL), "Gravel", new BreakInfo(0.6, ToolType::SHOVEL)));
|
||||
|
||||
$hardenedClayBreakInfo = new BreakInfo(1.25, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 21.0);
|
||||
$this->register(new HardenedClay(new BID(Ids::HARDENED_CLAY), "Hardened Clay", $hardenedClayBreakInfo));
|
||||
|
||||
$hardenedGlassBreakInfo = new BreakInfo(10.0);
|
||||
$this->register(new HardenedGlass(new BID(Ids::HARDENED_GLASS), "Hardened Glass", $hardenedGlassBreakInfo));
|
||||
$this->register(new HardenedGlassPane(new BID(Ids::HARDENED_GLASS_PANE), "Hardened Glass Pane", $hardenedGlassBreakInfo));
|
||||
$this->register(new HayBale(new BID(Ids::HAY_BALE), "Hay Bale", new BreakInfo(0.5)));
|
||||
$this->register(new Hopper(new BID(Ids::HOPPER, TileHopper::class), "Hopper", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 15.0)));
|
||||
$this->register(new Ice(new BID(Ids::ICE), "Ice", new BreakInfo(0.5, ToolType::PICKAXE)));
|
||||
|
||||
$updateBlockBreakInfo = new BreakInfo(1.0);
|
||||
$this->register(new Opaque(new BID(Ids::INFO_UPDATE), "update!", $updateBlockBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::INFO_UPDATE2), "ate!upd", $updateBlockBreakInfo));
|
||||
$this->register(new Transparent(new BID(Ids::INVISIBLE_BEDROCK), "Invisible Bedrock", BreakInfo::indestructible()));
|
||||
|
||||
$ironBreakInfo = new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Opaque(new BID(Ids::IRON), "Iron Block", $ironBreakInfo));
|
||||
$this->register(new Thin(new BID(Ids::IRON_BARS), "Iron Bars", $ironBreakInfo));
|
||||
$ironDoorBreakInfo = new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 25.0);
|
||||
$this->register(new Door(new BID(Ids::IRON_DOOR), "Iron Door", $ironDoorBreakInfo));
|
||||
$this->register(new Trapdoor(new BID(Ids::IRON_TRAPDOOR), "Iron Trapdoor", $ironDoorBreakInfo));
|
||||
$this->register(new ItemFrame(new BID(Ids::ITEM_FRAME, TileItemFrame::class), "Item Frame", new BreakInfo(0.25)));
|
||||
$this->register(new Jukebox(new BID(Ids::JUKEBOX, TileJukebox::class), "Jukebox", new BreakInfo(0.8, ToolType::AXE))); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not
|
||||
$this->register(new Ladder(new BID(Ids::LADDER), "Ladder", new BreakInfo(0.4, ToolType::AXE)));
|
||||
|
||||
$lanternBreakInfo = new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
|
||||
$this->register(new Lantern(new BID(Ids::LANTERN), "Lantern", $lanternBreakInfo, 15));
|
||||
$this->register(new Lantern(new BID(Ids::SOUL_LANTERN), "Soul Lantern", $lanternBreakInfo, 10));
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::LAPIS_LAZULI), "Lapis Lazuli Block", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
|
||||
$this->register(new Lava(new BID(Ids::LAVA), "Lava", BreakInfo::indestructible(500.0)));
|
||||
$this->register(new Lectern(new BID(Ids::LECTERN, TileLectern::class), "Lectern", new BreakInfo(2.0, ToolType::AXE)));
|
||||
$this->register(new Lever(new BID(Ids::LEVER), "Lever", new BreakInfo(0.5)));
|
||||
$this->register(new Loom(new BID(Ids::LOOM), "Loom", new BreakInfo(2.5, ToolType::AXE)));
|
||||
$this->register(new Magma(new BID(Ids::MAGMA), "Magma Block", new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Melon(new BID(Ids::MELON), "Melon Block", new BreakInfo(1.0, ToolType::AXE)));
|
||||
$this->register(new MelonStem(new BID(Ids::MELON_STEM), "Melon Stem", BreakInfo::instant()));
|
||||
$this->register(new MonsterSpawner(new BID(Ids::MONSTER_SPAWNER, TileMonsterSpawner::class), "Monster Spawner", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Mycelium(new BID(Ids::MYCELIUM), "Mycelium", new BreakInfo(0.6, ToolType::SHOVEL)));
|
||||
|
||||
$netherBrickBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Opaque(new BID(Ids::NETHER_BRICKS), "Nether Bricks", $netherBrickBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::RED_NETHER_BRICKS), "Red Nether Bricks", $netherBrickBreakInfo));
|
||||
$this->register(new Fence(new BID(Ids::NETHER_BRICK_FENCE), "Nether Brick Fence", $netherBrickBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::NETHER_BRICK_STAIRS), "Nether Brick Stairs", $netherBrickBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::RED_NETHER_BRICK_STAIRS), "Red Nether Brick Stairs", $netherBrickBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CHISELED_NETHER_BRICKS), "Chiseled Nether Bricks", $netherBrickBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CRACKED_NETHER_BRICKS), "Cracked Nether Bricks", $netherBrickBreakInfo));
|
||||
|
||||
$this->register(new NetherPortal(new BID(Ids::NETHER_PORTAL), "Nether Portal", BreakInfo::indestructible(0.0)));
|
||||
$this->register(new NetherReactor(new BID(Ids::NETHER_REACTOR_CORE), "Nether Reactor Core", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Opaque(new BID(Ids::NETHER_WART_BLOCK), "Nether Wart Block", new BreakInfo(1.0, ToolType::HOE)));
|
||||
$this->register(new NetherWartPlant(new BID(Ids::NETHER_WART), "Nether Wart", BreakInfo::instant()));
|
||||
$this->register(new Netherrack(new BID(Ids::NETHERRACK), "Netherrack", new BreakInfo(0.4, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Note(new BID(Ids::NOTE_BLOCK, TileNote::class), "Note Block", new BreakInfo(0.8, ToolType::AXE)));
|
||||
$this->register(new Opaque(new BID(Ids::OBSIDIAN), "Obsidian", new BreakInfo(35.0 /* 50 in PC */, ToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 6000.0)));
|
||||
$this->register(new PackedIce(new BID(Ids::PACKED_ICE), "Packed Ice", new BreakInfo(0.5, ToolType::PICKAXE)));
|
||||
$this->register(new Podzol(new BID(Ids::PODZOL), "Podzol", new BreakInfo(0.5, ToolType::SHOVEL)));
|
||||
$this->register(new Potato(new BID(Ids::POTATOES), "Potato Block", BreakInfo::instant()));
|
||||
$this->register(new PoweredRail(new BID(Ids::POWERED_RAIL), "Powered Rail", $railBreakInfo));
|
||||
|
||||
$prismarineBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Opaque(new BID(Ids::PRISMARINE), "Prismarine", $prismarineBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::DARK_PRISMARINE), "Dark Prismarine", $prismarineBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::PRISMARINE_BRICKS), "Prismarine Bricks", $prismarineBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::PRISMARINE_BRICKS_STAIRS), "Prismarine Bricks Stairs", $prismarineBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::DARK_PRISMARINE_STAIRS), "Dark Prismarine Stairs", $prismarineBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::PRISMARINE_STAIRS), "Prismarine Stairs", $prismarineBreakInfo));
|
||||
|
||||
$pumpkinBreakInfo = new BreakInfo(1.0, ToolType::AXE);
|
||||
$this->register(new Pumpkin(new BID(Ids::PUMPKIN), "Pumpkin", $pumpkinBreakInfo));
|
||||
$this->register(new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN), "Carved Pumpkin", $pumpkinBreakInfo));
|
||||
$this->register(new LitPumpkin(new BID(Ids::LIT_PUMPKIN), "Jack o'Lantern", $pumpkinBreakInfo));
|
||||
|
||||
$this->register(new PumpkinStem(new BID(Ids::PUMPKIN_STEM), "Pumpkin Stem", BreakInfo::instant()));
|
||||
|
||||
$purpurBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Opaque(new BID(Ids::PURPUR), "Purpur Block", $purpurBreakInfo));
|
||||
$this->register(new SimplePillar(new BID(Ids::PURPUR_PILLAR), "Purpur Pillar", $purpurBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::PURPUR_STAIRS), "Purpur Stairs", $purpurBreakInfo));
|
||||
|
||||
$quartzBreakInfo = new BreakInfo(0.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
|
||||
$this->register(new Opaque(new BID(Ids::QUARTZ), "Quartz Block", $quartzBreakInfo));
|
||||
$this->register(new SimplePillar(new BID(Ids::CHISELED_QUARTZ), "Chiseled Quartz Block", $quartzBreakInfo));
|
||||
$this->register(new SimplePillar(new BID(Ids::QUARTZ_PILLAR), "Quartz Pillar", $quartzBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::SMOOTH_QUARTZ), "Smooth Quartz Block", $quartzBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::QUARTZ_BRICKS), "Quartz Bricks", $quartzBreakInfo));
|
||||
|
||||
$this->register(new Stair(new BID(Ids::QUARTZ_STAIRS), "Quartz Stairs", $quartzBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::SMOOTH_QUARTZ_STAIRS), "Smooth Quartz Stairs", $quartzBreakInfo));
|
||||
|
||||
$this->register(new Rail(new BID(Ids::RAIL), "Rail", $railBreakInfo));
|
||||
$this->register(new RedMushroom(new BID(Ids::RED_MUSHROOM), "Red Mushroom", BreakInfo::instant()));
|
||||
$this->register(new Redstone(new BID(Ids::REDSTONE), "Redstone Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
|
||||
$this->register(new RedstoneComparator(new BID(Ids::REDSTONE_COMPARATOR, TileComparator::class), "Redstone Comparator", BreakInfo::instant()));
|
||||
$this->register(new RedstoneLamp(new BID(Ids::REDSTONE_LAMP), "Redstone Lamp", new BreakInfo(0.3)));
|
||||
$this->register(new RedstoneRepeater(new BID(Ids::REDSTONE_REPEATER), "Redstone Repeater", BreakInfo::instant()));
|
||||
$this->register(new RedstoneTorch(new BID(Ids::REDSTONE_TORCH), "Redstone Torch", BreakInfo::instant()));
|
||||
$this->register(new RedstoneWire(new BID(Ids::REDSTONE_WIRE), "Redstone", BreakInfo::instant()));
|
||||
$this->register(new Reserved6(new BID(Ids::RESERVED6), "reserved6", BreakInfo::instant()));
|
||||
|
||||
$sandBreakInfo = new BreakInfo(0.5, ToolType::SHOVEL);
|
||||
$this->register(new Sand(new BID(Ids::SAND), "Sand", $sandBreakInfo));
|
||||
$this->register(new Sand(new BID(Ids::RED_SAND), "Red Sand", $sandBreakInfo));
|
||||
|
||||
$this->register(new SeaLantern(new BID(Ids::SEA_LANTERN), "Sea Lantern", new BreakInfo(0.3)));
|
||||
$this->register(new SeaPickle(new BID(Ids::SEA_PICKLE), "Sea Pickle", BreakInfo::instant()));
|
||||
$this->register(new Skull(new BID(Ids::MOB_HEAD, TileSkull::class), "Mob Head", new BreakInfo(1.0)));
|
||||
$this->register(new Slime(new BID(Ids::SLIME), "Slime Block", BreakInfo::instant()));
|
||||
$this->register(new Snow(new BID(Ids::SNOW), "Snow Block", new BreakInfo(0.2, ToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new SnowLayer(new BID(Ids::SNOW_LAYER), "Snow Layer", new BreakInfo(0.1, ToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new SoulSand(new BID(Ids::SOUL_SAND), "Soul Sand", new BreakInfo(0.5, ToolType::SHOVEL)));
|
||||
$this->register(new Sponge(new BID(Ids::SPONGE), "Sponge", new BreakInfo(0.6, ToolType::HOE)));
|
||||
$shulkerBoxBreakInfo = new BreakInfo(2, ToolType::PICKAXE);
|
||||
$this->register(new ShulkerBox(new BID(Ids::SHULKER_BOX, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo));
|
||||
|
||||
$stoneBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(
|
||||
$stone = new class(new BID(Ids::STONE), "Stone", $stoneBreakInfo) extends Opaque{
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [VanillaBlocks::COBBLESTONE()->asItem()];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
);
|
||||
$this->register(new Opaque(new BID(Ids::ANDESITE), "Andesite", $stoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::DIORITE), "Diorite", $stoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::GRANITE), "Granite", $stoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::POLISHED_ANDESITE), "Polished Andesite", $stoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::POLISHED_DIORITE), "Polished Diorite", $stoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::POLISHED_GRANITE), "Polished Granite", $stoneBreakInfo));
|
||||
|
||||
$this->register($stoneBrick = new Opaque(new BID(Ids::STONE_BRICKS), "Stone Bricks", $stoneBreakInfo));
|
||||
$this->register($mossyStoneBrick = new Opaque(new BID(Ids::MOSSY_STONE_BRICKS), "Mossy Stone Bricks", $stoneBreakInfo));
|
||||
$this->register($crackedStoneBrick = new Opaque(new BID(Ids::CRACKED_STONE_BRICKS), "Cracked Stone Bricks", $stoneBreakInfo));
|
||||
$this->register($chiseledStoneBrick = new Opaque(new BID(Ids::CHISELED_STONE_BRICKS), "Chiseled Stone Bricks", $stoneBreakInfo));
|
||||
|
||||
$infestedStoneBreakInfo = new BreakInfo(0.75, ToolType::PICKAXE);
|
||||
$this->register(new InfestedStone(new BID(Ids::INFESTED_STONE), "Infested Stone", $infestedStoneBreakInfo, $stone));
|
||||
$this->register(new InfestedStone(new BID(Ids::INFESTED_STONE_BRICK), "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick));
|
||||
$this->register(new InfestedStone(new BID(Ids::INFESTED_COBBLESTONE), "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone));
|
||||
$this->register(new InfestedStone(new BID(Ids::INFESTED_MOSSY_STONE_BRICK), "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick));
|
||||
$this->register(new InfestedStone(new BID(Ids::INFESTED_CRACKED_STONE_BRICK), "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick));
|
||||
$this->register(new InfestedStone(new BID(Ids::INFESTED_CHISELED_STONE_BRICK), "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick));
|
||||
|
||||
$this->register(new Stair(new BID(Ids::STONE_STAIRS), "Stone Stairs", $stoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::SMOOTH_STONE), "Smooth Stone", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::ANDESITE_STAIRS), "Andesite Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::DIORITE_STAIRS), "Diorite Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::GRANITE_STAIRS), "Granite Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::POLISHED_ANDESITE_STAIRS), "Polished Andesite Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::POLISHED_DIORITE_STAIRS), "Polished Diorite Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::POLISHED_GRANITE_STAIRS), "Polished Granite Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::STONE_BRICK_STAIRS), "Stone Brick Stairs", $stoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS), "Mossy Stone Brick Stairs", $stoneBreakInfo));
|
||||
$this->register(new StoneButton(new BID(Ids::STONE_BUTTON), "Stone Button", new BreakInfo(0.5, ToolType::PICKAXE)));
|
||||
$this->register(new Stonecutter(new BID(Ids::STONECUTTER), "Stonecutter", new BreakInfo(3.5, ToolType::PICKAXE)));
|
||||
$this->register(new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE), "Stone Pressure Plate", new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
|
||||
//TODO: in the future this won't be the same for all the types
|
||||
$stoneSlabBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
foreach([
|
||||
new Slab(new BID(Ids::BRICK_SLAB), "Brick", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::COBBLESTONE_SLAB), "Cobblestone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::FAKE_WOODEN_SLAB), "Fake Wooden", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::NETHER_BRICK_SLAB), "Nether Brick", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::QUARTZ_SLAB), "Quartz", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::SANDSTONE_SLAB), "Sandstone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::SMOOTH_STONE_SLAB), "Smooth Stone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::STONE_BRICK_SLAB), "Stone Brick", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::DARK_PRISMARINE_SLAB), "Dark Prismarine", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::MOSSY_COBBLESTONE_SLAB), "Mossy Cobblestone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::PRISMARINE_SLAB), "Prismarine", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::PRISMARINE_BRICKS_SLAB), "Prismarine Bricks", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::PURPUR_SLAB), "Purpur", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::RED_NETHER_BRICK_SLAB), "Red Nether Brick", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::RED_SANDSTONE_SLAB), "Red Sandstone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::SMOOTH_SANDSTONE_SLAB), "Smooth Sandstone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::ANDESITE_SLAB), "Andesite", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::DIORITE_SLAB), "Diorite", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::END_STONE_BRICK_SLAB), "End Stone Brick", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::GRANITE_SLAB), "Granite", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::POLISHED_ANDESITE_SLAB), "Polished Andesite", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::POLISHED_DIORITE_SLAB), "Polished Diorite", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::POLISHED_GRANITE_SLAB), "Polished Granite", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::SMOOTH_RED_SANDSTONE_SLAB), "Smooth Red Sandstone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::CUT_RED_SANDSTONE_SLAB), "Cut Red Sandstone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::CUT_SANDSTONE_SLAB), "Cut Sandstone", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::MOSSY_STONE_BRICK_SLAB), "Mossy Stone Brick", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::SMOOTH_QUARTZ_SLAB), "Smooth Quartz", $stoneSlabBreakInfo),
|
||||
new Slab(new BID(Ids::STONE_SLAB), "Stone", $stoneSlabBreakInfo),
|
||||
] as $slabType){
|
||||
$this->register($slabType);
|
||||
foreach(VanillaBlocks::getAll() as $block){
|
||||
$this->register($block);
|
||||
}
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::LEGACY_STONECUTTER), "Legacy Stonecutter", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Sugarcane(new BID(Ids::SUGARCANE), "Sugarcane", BreakInfo::instant()));
|
||||
$this->register(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH), "Sweet Berry Bush", BreakInfo::instant()));
|
||||
$this->register(new TNT(new BID(Ids::TNT), "TNT", BreakInfo::instant()));
|
||||
$this->register(new TallGrass(new BID(Ids::FERN), "Fern", BreakInfo::instant(ToolType::SHEARS, 1)));
|
||||
$this->register(new TallGrass(new BID(Ids::TALL_GRASS), "Tall Grass", BreakInfo::instant(ToolType::SHEARS, 1)));
|
||||
|
||||
$this->register(new Torch(new BID(Ids::BLUE_TORCH), "Blue Torch", BreakInfo::instant()));
|
||||
$this->register(new Torch(new BID(Ids::PURPLE_TORCH), "Purple Torch", BreakInfo::instant()));
|
||||
$this->register(new Torch(new BID(Ids::RED_TORCH), "Red Torch", BreakInfo::instant()));
|
||||
$this->register(new Torch(new BID(Ids::GREEN_TORCH), "Green Torch", BreakInfo::instant()));
|
||||
$this->register(new Torch(new BID(Ids::TORCH), "Torch", BreakInfo::instant()));
|
||||
|
||||
$this->register(new TrappedChest(new BID(Ids::TRAPPED_CHEST, TileChest::class), "Trapped Chest", $chestBreakInfo));
|
||||
$this->register(new Tripwire(new BID(Ids::TRIPWIRE), "Tripwire", BreakInfo::instant()));
|
||||
$this->register(new TripwireHook(new BID(Ids::TRIPWIRE_HOOK), "Tripwire Hook", BreakInfo::instant()));
|
||||
$this->register(new UnderwaterTorch(new BID(Ids::UNDERWATER_TORCH), "Underwater Torch", BreakInfo::instant()));
|
||||
$this->register(new Vine(new BID(Ids::VINES), "Vines", new BreakInfo(0.2, ToolType::AXE)));
|
||||
$this->register(new Water(new BID(Ids::WATER), "Water", BreakInfo::indestructible(500.0)));
|
||||
$this->register(new WaterLily(new BID(Ids::LILY_PAD), "Lily Pad", BreakInfo::instant()));
|
||||
|
||||
$weightedPressurePlateBreakInfo = new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
|
||||
$this->register(new WeightedPressurePlateHeavy(new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY), "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo));
|
||||
$this->register(new WeightedPressurePlateLight(new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT), "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo));
|
||||
$this->register(new Wheat(new BID(Ids::WHEAT), "Wheat Block", BreakInfo::instant()));
|
||||
|
||||
$planksBreakInfo = new BreakInfo(2.0, ToolType::AXE, 0, 15.0);
|
||||
$leavesBreakInfo = new class(0.2, ToolType::HOE) extends BreakInfo{
|
||||
public function getBreakTime(Item $item) : float{
|
||||
if($item->getBlockToolType() === ToolType::SHEARS){
|
||||
return 0.0;
|
||||
}
|
||||
return parent::getBreakTime($item);
|
||||
}
|
||||
};
|
||||
$signBreakInfo = new BreakInfo(1.0, ToolType::AXE);
|
||||
$logBreakInfo = new BreakInfo(2.0, ToolType::AXE);
|
||||
$woodenDoorBreakInfo = new BreakInfo(3.0, ToolType::AXE, 0, 15.0);
|
||||
$woodenButtonBreakInfo = new BreakInfo(0.5, ToolType::AXE);
|
||||
$woodenPressurePlateBreakInfo = new BreakInfo(0.5, ToolType::AXE);
|
||||
|
||||
foreach(TreeType::getAll() as $treeType){
|
||||
$name = $treeType->getDisplayName();
|
||||
$this->register(new Sapling(BlockLegacyIdHelper::getSaplingIdentifier($treeType), $name . " Sapling", BreakInfo::instant(), $treeType));
|
||||
$this->register(new Leaves(BlockLegacyIdHelper::getLeavesIdentifier($treeType), $name . " Leaves", $leavesBreakInfo, $treeType));
|
||||
}
|
||||
|
||||
foreach(WoodType::getAll() as $woodType){
|
||||
$name = $woodType->getDisplayName();
|
||||
|
||||
$this->register(new Wood(BlockLegacyIdHelper::getLogIdentifier($woodType), $name . " " . ($woodType->getStandardLogSuffix() ?? "Log"), $logBreakInfo, $woodType));
|
||||
$this->register(new Wood(BlockLegacyIdHelper::getAllSidedLogIdentifier($woodType), $name . " " . ($woodType->getAllSidedLogSuffix() ?? "Wood"), $logBreakInfo, $woodType));
|
||||
|
||||
$this->register(new Planks(BlockLegacyIdHelper::getWoodenPlanksIdentifier($woodType), $name . " Planks", $planksBreakInfo, $woodType));
|
||||
$this->register(new WoodenFence(BlockLegacyIdHelper::getWoodenFenceIdentifier($woodType), $name . " Fence", $planksBreakInfo, $woodType));
|
||||
$this->register(new WoodenSlab(BlockLegacyIdHelper::getWoodenSlabIdentifier($woodType), $name, $planksBreakInfo, $woodType));
|
||||
|
||||
$this->register(new FenceGate(BlockLegacyIdHelper::getWoodenFenceGateIdentifier($woodType), $name . " Fence Gate", $planksBreakInfo, $woodType));
|
||||
$this->register(new WoodenStairs(BlockLegacyIdHelper::getWoodenStairsIdentifier($woodType), $name . " Stairs", $planksBreakInfo, $woodType));
|
||||
$this->register(new WoodenDoor(BlockLegacyIdHelper::getWoodenDoorIdentifier($woodType), $name . " Door", $woodenDoorBreakInfo, $woodType));
|
||||
|
||||
$this->register(new WoodenButton(BlockLegacyIdHelper::getWoodenButtonIdentifier($woodType), $name . " Button", $woodenButtonBreakInfo, $woodType));
|
||||
$this->register(new WoodenPressurePlate(BlockLegacyIdHelper::getWoodenPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType));
|
||||
$this->register(new WoodenTrapdoor(BlockLegacyIdHelper::getWoodenTrapdoorIdentifier($woodType), $name . " Trapdoor", $woodenDoorBreakInfo, $woodType));
|
||||
|
||||
[$floorSignId, $wallSignId, $signAsItem] = BlockLegacyIdHelper::getWoodenSignInfo($woodType);
|
||||
$this->register(new FloorSign($floorSignId, $name . " Sign", $signBreakInfo, $woodType, $signAsItem));
|
||||
$this->register(new WallSign($wallSignId, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem));
|
||||
}
|
||||
|
||||
$sandstoneBreakInfo = new BreakInfo(0.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
|
||||
$this->register(new Stair(new BID(Ids::RED_SANDSTONE_STAIRS), "Red Sandstone Stairs", $sandstoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::SMOOTH_RED_SANDSTONE_STAIRS), "Smooth Red Sandstone Stairs", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::RED_SANDSTONE), "Red Sandstone", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CHISELED_RED_SANDSTONE), "Chiseled Red Sandstone", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CUT_RED_SANDSTONE), "Cut Red Sandstone", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::SMOOTH_RED_SANDSTONE), "Smooth Red Sandstone", $sandstoneBreakInfo));
|
||||
|
||||
$this->register(new Stair(new BID(Ids::SANDSTONE_STAIRS), "Sandstone Stairs", $sandstoneBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::SMOOTH_SANDSTONE_STAIRS), "Smooth Sandstone Stairs", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::SANDSTONE), "Sandstone", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CHISELED_SANDSTONE), "Chiseled Sandstone", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CUT_SANDSTONE), "Cut Sandstone", $sandstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::SMOOTH_SANDSTONE), "Smooth Sandstone", $sandstoneBreakInfo));
|
||||
|
||||
$this->register(new GlazedTerracotta(new BID(Ids::GLAZED_TERRACOTTA), "Glazed Terracotta", new BreakInfo(1.4, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new DyedShulkerBox(new BID(Ids::DYED_SHULKER_BOX, TileShulkerBox::class), "Dyed Shulker Box", $shulkerBoxBreakInfo));
|
||||
$this->register(new StainedGlass(new BID(Ids::STAINED_GLASS), "Stained Glass", $glassBreakInfo));
|
||||
$this->register(new StainedGlassPane(new BID(Ids::STAINED_GLASS_PANE), "Stained Glass Pane", $glassBreakInfo));
|
||||
$this->register(new StainedHardenedClay(new BID(Ids::STAINED_CLAY), "Stained Clay", $hardenedClayBreakInfo));
|
||||
$this->register(new StainedHardenedGlass(new BID(Ids::STAINED_HARDENED_GLASS), "Stained Hardened Glass", $hardenedGlassBreakInfo));
|
||||
$this->register(new StainedHardenedGlassPane(new BID(Ids::STAINED_HARDENED_GLASS_PANE), "Stained Hardened Glass Pane", $hardenedGlassBreakInfo));
|
||||
$this->register(new Carpet(new BID(Ids::CARPET), "Carpet", new BreakInfo(0.1)));
|
||||
$this->register(new Concrete(new BID(Ids::CONCRETE), "Concrete", new BreakInfo(1.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new ConcretePowder(new BID(Ids::CONCRETE_POWDER), "Concrete Powder", new BreakInfo(0.5, ToolType::SHOVEL)));
|
||||
$this->register(new Wool(new BID(Ids::WOOL), "Wool", new class(0.8, ToolType::SHEARS) extends BreakInfo{
|
||||
public function getBreakTime(Item $item) : float{
|
||||
$time = parent::getBreakTime($item);
|
||||
if($item->getBlockToolType() === ToolType::SHEARS){
|
||||
$time *= 3; //shears break compatible blocks 15x faster, but wool 5x
|
||||
}
|
||||
|
||||
return $time;
|
||||
}
|
||||
}));
|
||||
|
||||
//TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap
|
||||
$wallBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Wall(new BID(Ids::COBBLESTONE_WALL), "Cobblestone Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::ANDESITE_WALL), "Andesite Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::BRICK_WALL), "Brick Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::DIORITE_WALL), "Diorite Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::END_STONE_BRICK_WALL), "End Stone Brick Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::GRANITE_WALL), "Granite Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::MOSSY_STONE_BRICK_WALL), "Mossy Stone Brick Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::MOSSY_COBBLESTONE_WALL), "Mossy Cobblestone Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::NETHER_BRICK_WALL), "Nether Brick Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::PRISMARINE_WALL), "Prismarine Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::RED_NETHER_BRICK_WALL), "Red Nether Brick Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::RED_SANDSTONE_WALL), "Red Sandstone Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::SANDSTONE_WALL), "Sandstone Wall", $wallBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::STONE_BRICK_WALL), "Stone Brick Wall", $wallBreakInfo));
|
||||
|
||||
$this->registerElements();
|
||||
|
||||
$chemistryTableBreakInfo = new BreakInfo(2.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
|
||||
$this->register(new ChemistryTable(new BID(Ids::COMPOUND_CREATOR), "Compound Creator", $chemistryTableBreakInfo));
|
||||
$this->register(new ChemistryTable(new BID(Ids::ELEMENT_CONSTRUCTOR), "Element Constructor", $chemistryTableBreakInfo));
|
||||
$this->register(new ChemistryTable(new BID(Ids::LAB_TABLE), "Lab Table", $chemistryTableBreakInfo));
|
||||
$this->register(new ChemistryTable(new BID(Ids::MATERIAL_REDUCER), "Material Reducer", $chemistryTableBreakInfo));
|
||||
|
||||
$this->register(new ChemicalHeat(new BID(Ids::CHEMICAL_HEAT), "Heat Block", $chemistryTableBreakInfo));
|
||||
|
||||
$this->registerMushroomBlocks();
|
||||
|
||||
$this->register(new Coral(
|
||||
new BID(Ids::CORAL),
|
||||
"Coral",
|
||||
BreakInfo::instant(),
|
||||
));
|
||||
$this->register(new FloorCoralFan(
|
||||
new BID(Ids::CORAL_FAN),
|
||||
"Coral Fan",
|
||||
BreakInfo::instant(),
|
||||
));
|
||||
$this->register(new WallCoralFan(
|
||||
new BID(Ids::WALL_CORAL_FAN),
|
||||
"Wall Coral Fan",
|
||||
BreakInfo::instant(),
|
||||
));
|
||||
|
||||
$this->registerBlocksR13();
|
||||
$this->registerBlocksR14();
|
||||
$this->registerBlocksR16();
|
||||
$this->registerBlocksR17();
|
||||
$this->registerMudBlocks();
|
||||
|
||||
$this->registerOres();
|
||||
}
|
||||
|
||||
private function registerMushroomBlocks() : void{
|
||||
$mushroomBlockBreakInfo = new BreakInfo(0.2, ToolType::AXE);
|
||||
|
||||
$this->register(new BrownMushroomBlock(new BID(Ids::BROWN_MUSHROOM_BLOCK), "Brown Mushroom Block", $mushroomBlockBreakInfo));
|
||||
$this->register(new RedMushroomBlock(new BID(Ids::RED_MUSHROOM_BLOCK), "Red Mushroom Block", $mushroomBlockBreakInfo));
|
||||
|
||||
//finally, the stems
|
||||
$this->register(new MushroomStem(new BID(Ids::MUSHROOM_STEM), "Mushroom Stem", $mushroomBlockBreakInfo));
|
||||
$this->register(new MushroomStem(new BID(Ids::ALL_SIDED_MUSHROOM_STEM), "All Sided Mushroom Stem", $mushroomBlockBreakInfo));
|
||||
}
|
||||
|
||||
private function registerElements() : void{
|
||||
$instaBreak = BreakInfo::instant();
|
||||
$this->register(new Opaque(new BID(Ids::ELEMENT_ZERO), "???", $instaBreak));
|
||||
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_HYDROGEN), "Hydrogen", $instaBreak, "h", 1, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_HELIUM), "Helium", $instaBreak, "he", 2, 7));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_LITHIUM), "Lithium", $instaBreak, "li", 3, 0));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BERYLLIUM), "Beryllium", $instaBreak, "be", 4, 1));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BORON), "Boron", $instaBreak, "b", 5, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CARBON), "Carbon", $instaBreak, "c", 6, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NITROGEN), "Nitrogen", $instaBreak, "n", 7, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_OXYGEN), "Oxygen", $instaBreak, "o", 8, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_FLUORINE), "Fluorine", $instaBreak, "f", 9, 6));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NEON), "Neon", $instaBreak, "ne", 10, 7));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SODIUM), "Sodium", $instaBreak, "na", 11, 0));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MAGNESIUM), "Magnesium", $instaBreak, "mg", 12, 1));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ALUMINUM), "Aluminum", $instaBreak, "al", 13, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SILICON), "Silicon", $instaBreak, "si", 14, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PHOSPHORUS), "Phosphorus", $instaBreak, "p", 15, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SULFUR), "Sulfur", $instaBreak, "s", 16, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CHLORINE), "Chlorine", $instaBreak, "cl", 17, 6));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ARGON), "Argon", $instaBreak, "ar", 18, 7));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_POTASSIUM), "Potassium", $instaBreak, "k", 19, 0));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CALCIUM), "Calcium", $instaBreak, "ca", 20, 1));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SCANDIUM), "Scandium", $instaBreak, "sc", 21, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TITANIUM), "Titanium", $instaBreak, "ti", 22, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_VANADIUM), "Vanadium", $instaBreak, "v", 23, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CHROMIUM), "Chromium", $instaBreak, "cr", 24, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MANGANESE), "Manganese", $instaBreak, "mn", 25, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_IRON), "Iron", $instaBreak, "fe", 26, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_COBALT), "Cobalt", $instaBreak, "co", 27, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NICKEL), "Nickel", $instaBreak, "ni", 28, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_COPPER), "Copper", $instaBreak, "cu", 29, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ZINC), "Zinc", $instaBreak, "zn", 30, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_GALLIUM), "Gallium", $instaBreak, "ga", 31, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_GERMANIUM), "Germanium", $instaBreak, "ge", 32, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ARSENIC), "Arsenic", $instaBreak, "as", 33, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SELENIUM), "Selenium", $instaBreak, "se", 34, 5));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BROMINE), "Bromine", $instaBreak, "br", 35, 6));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_KRYPTON), "Krypton", $instaBreak, "kr", 36, 7));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RUBIDIUM), "Rubidium", $instaBreak, "rb", 37, 0));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_STRONTIUM), "Strontium", $instaBreak, "sr", 38, 1));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_YTTRIUM), "Yttrium", $instaBreak, "y", 39, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ZIRCONIUM), "Zirconium", $instaBreak, "zr", 40, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NIOBIUM), "Niobium", $instaBreak, "nb", 41, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MOLYBDENUM), "Molybdenum", $instaBreak, "mo", 42, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TECHNETIUM), "Technetium", $instaBreak, "tc", 43, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RUTHENIUM), "Ruthenium", $instaBreak, "ru", 44, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RHODIUM), "Rhodium", $instaBreak, "rh", 45, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PALLADIUM), "Palladium", $instaBreak, "pd", 46, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SILVER), "Silver", $instaBreak, "ag", 47, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CADMIUM), "Cadmium", $instaBreak, "cd", 48, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_INDIUM), "Indium", $instaBreak, "in", 49, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TIN), "Tin", $instaBreak, "sn", 50, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ANTIMONY), "Antimony", $instaBreak, "sb", 51, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TELLURIUM), "Tellurium", $instaBreak, "te", 52, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_IODINE), "Iodine", $instaBreak, "i", 53, 6));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_XENON), "Xenon", $instaBreak, "xe", 54, 7));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CESIUM), "Cesium", $instaBreak, "cs", 55, 0));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BARIUM), "Barium", $instaBreak, "ba", 56, 1));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_LANTHANUM), "Lanthanum", $instaBreak, "la", 57, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CERIUM), "Cerium", $instaBreak, "ce", 58, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PRASEODYMIUM), "Praseodymium", $instaBreak, "pr", 59, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NEODYMIUM), "Neodymium", $instaBreak, "nd", 60, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PROMETHIUM), "Promethium", $instaBreak, "pm", 61, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SAMARIUM), "Samarium", $instaBreak, "sm", 62, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_EUROPIUM), "Europium", $instaBreak, "eu", 63, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_GADOLINIUM), "Gadolinium", $instaBreak, "gd", 64, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TERBIUM), "Terbium", $instaBreak, "tb", 65, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_DYSPROSIUM), "Dysprosium", $instaBreak, "dy", 66, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_HOLMIUM), "Holmium", $instaBreak, "ho", 67, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ERBIUM), "Erbium", $instaBreak, "er", 68, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_THULIUM), "Thulium", $instaBreak, "tm", 69, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_YTTERBIUM), "Ytterbium", $instaBreak, "yb", 70, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_LUTETIUM), "Lutetium", $instaBreak, "lu", 71, 8));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_HAFNIUM), "Hafnium", $instaBreak, "hf", 72, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TANTALUM), "Tantalum", $instaBreak, "ta", 73, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TUNGSTEN), "Tungsten", $instaBreak, "w", 74, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RHENIUM), "Rhenium", $instaBreak, "re", 75, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_OSMIUM), "Osmium", $instaBreak, "os", 76, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_IRIDIUM), "Iridium", $instaBreak, "ir", 77, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PLATINUM), "Platinum", $instaBreak, "pt", 78, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_GOLD), "Gold", $instaBreak, "au", 79, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MERCURY), "Mercury", $instaBreak, "hg", 80, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_THALLIUM), "Thallium", $instaBreak, "tl", 81, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_LEAD), "Lead", $instaBreak, "pb", 82, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BISMUTH), "Bismuth", $instaBreak, "bi", 83, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_POLONIUM), "Polonium", $instaBreak, "po", 84, 4));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ASTATINE), "Astatine", $instaBreak, "at", 85, 6));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RADON), "Radon", $instaBreak, "rn", 86, 7));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_FRANCIUM), "Francium", $instaBreak, "fr", 87, 0));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RADIUM), "Radium", $instaBreak, "ra", 88, 1));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ACTINIUM), "Actinium", $instaBreak, "ac", 89, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_THORIUM), "Thorium", $instaBreak, "th", 90, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PROTACTINIUM), "Protactinium", $instaBreak, "pa", 91, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_URANIUM), "Uranium", $instaBreak, "u", 92, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NEPTUNIUM), "Neptunium", $instaBreak, "np", 93, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_PLUTONIUM), "Plutonium", $instaBreak, "pu", 94, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_AMERICIUM), "Americium", $instaBreak, "am", 95, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CURIUM), "Curium", $instaBreak, "cm", 96, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BERKELIUM), "Berkelium", $instaBreak, "bk", 97, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_CALIFORNIUM), "Californium", $instaBreak, "cf", 98, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_EINSTEINIUM), "Einsteinium", $instaBreak, "es", 99, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_FERMIUM), "Fermium", $instaBreak, "fm", 100, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MENDELEVIUM), "Mendelevium", $instaBreak, "md", 101, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NOBELIUM), "Nobelium", $instaBreak, "no", 102, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_LAWRENCIUM), "Lawrencium", $instaBreak, "lr", 103, 9));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_RUTHERFORDIUM), "Rutherfordium", $instaBreak, "rf", 104, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_DUBNIUM), "Dubnium", $instaBreak, "db", 105, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_SEABORGIUM), "Seaborgium", $instaBreak, "sg", 106, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_BOHRIUM), "Bohrium", $instaBreak, "bh", 107, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_HASSIUM), "Hassium", $instaBreak, "hs", 108, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MEITNERIUM), "Meitnerium", $instaBreak, "mt", 109, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_DARMSTADTIUM), "Darmstadtium", $instaBreak, "ds", 110, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_ROENTGENIUM), "Roentgenium", $instaBreak, "rg", 111, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_COPERNICIUM), "Copernicium", $instaBreak, "cn", 112, 2));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_NIHONIUM), "Nihonium", $instaBreak, "nh", 113, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_FLEROVIUM), "Flerovium", $instaBreak, "fl", 114, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_MOSCOVIUM), "Moscovium", $instaBreak, "mc", 115, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_LIVERMORIUM), "Livermorium", $instaBreak, "lv", 116, 3));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_TENNESSINE), "Tennessine", $instaBreak, "ts", 117, 6));
|
||||
$this->register(new Element(new BID(Ids::ELEMENT_OGANESSON), "Oganesson", $instaBreak, "og", 118, 7));
|
||||
}
|
||||
|
||||
private function registerOres() : void{
|
||||
$stoneOreBreakInfo = fn(ToolTier $toolTier) => new BreakInfo(3.0, ToolType::PICKAXE, $toolTier->getHarvestLevel());
|
||||
$this->register(new CoalOre(new BID(Ids::COAL_ORE), "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD())));
|
||||
$this->register(new CopperOre(new BID(Ids::COPPER_ORE), "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE())));
|
||||
$this->register(new DiamondOre(new BID(Ids::DIAMOND_ORE), "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON())));
|
||||
$this->register(new EmeraldOre(new BID(Ids::EMERALD_ORE), "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON())));
|
||||
$this->register(new GoldOre(new BID(Ids::GOLD_ORE), "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON())));
|
||||
$this->register(new IronOre(new BID(Ids::IRON_ORE), "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE())));
|
||||
$this->register(new LapisOre(new BID(Ids::LAPIS_LAZULI_ORE), "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE())));
|
||||
$this->register(new RedstoneOre(new BID(Ids::REDSTONE_ORE), "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON())));
|
||||
|
||||
$deepslateOreBreakInfo = fn(ToolTier $toolTier) => new BreakInfo(4.5, ToolType::PICKAXE, $toolTier->getHarvestLevel());
|
||||
$this->register(new CoalOre(new BID(Ids::DEEPSLATE_COAL_ORE), "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD())));
|
||||
$this->register(new CopperOre(new BID(Ids::DEEPSLATE_COPPER_ORE), "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE())));
|
||||
$this->register(new DiamondOre(new BID(Ids::DEEPSLATE_DIAMOND_ORE), "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
|
||||
$this->register(new EmeraldOre(new BID(Ids::DEEPSLATE_EMERALD_ORE), "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
|
||||
$this->register(new GoldOre(new BID(Ids::DEEPSLATE_GOLD_ORE), "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
|
||||
$this->register(new IronOre(new BID(Ids::DEEPSLATE_IRON_ORE), "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE())));
|
||||
$this->register(new LapisOre(new BID(Ids::DEEPSLATE_LAPIS_LAZULI_ORE), "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE())));
|
||||
$this->register(new RedstoneOre(new BID(Ids::DEEPSLATE_REDSTONE_ORE), "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
|
||||
|
||||
$netherrackOreBreakInfo = new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
|
||||
$this->register(new NetherQuartzOre(new BID(Ids::NETHER_QUARTZ_ORE), "Nether Quartz Ore", $netherrackOreBreakInfo));
|
||||
$this->register(new NetherGoldOre(new BID(Ids::NETHER_GOLD_ORE), "Nether Gold Ore", $netherrackOreBreakInfo));
|
||||
}
|
||||
|
||||
private function registerBlocksR13() : void{
|
||||
$this->register(new Light(new BID(Ids::LIGHT), "Light Block", BreakInfo::indestructible()));
|
||||
}
|
||||
|
||||
private function registerBlocksR14() : void{
|
||||
$this->register(new Opaque(new BID(Ids::HONEYCOMB), "Honeycomb Block", new BreakInfo(0.6)));
|
||||
}
|
||||
|
||||
private function registerBlocksR16() : void{
|
||||
//for some reason, slabs have weird hardness like the legacy ones
|
||||
$slabBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new BreakInfo(30, ToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 3600.0)));
|
||||
|
||||
$basaltBreakInfo = new BreakInfo(1.25, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 21.0);
|
||||
$this->register(new SimplePillar(new BID(Ids::BASALT), "Basalt", $basaltBreakInfo));
|
||||
$this->register(new SimplePillar(new BID(Ids::POLISHED_BASALT), "Polished Basalt", $basaltBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::SMOOTH_BASALT), "Smooth Basalt", $basaltBreakInfo));
|
||||
|
||||
$blackstoneBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register(new Opaque(new BID(Ids::BLACKSTONE), "Blackstone", $blackstoneBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::BLACKSTONE_SLAB), "Blackstone", $slabBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::BLACKSTONE_STAIRS), "Blackstone Stairs", $blackstoneBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::BLACKSTONE_WALL), "Blackstone Wall", $blackstoneBreakInfo));
|
||||
|
||||
//TODO: polished blackstone ought to have 2.0 hardness (as per java) but it's 1.5 in Bedrock (probably parity bug)
|
||||
$prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : "");
|
||||
$this->register(new Opaque(new BID(Ids::POLISHED_BLACKSTONE), $prefix(""), $blackstoneBreakInfo));
|
||||
$this->register(new StoneButton(new BID(Ids::POLISHED_BLACKSTONE_BUTTON), $prefix("Button"), new BreakInfo(0.5, ToolType::PICKAXE))); //same as regular stone button
|
||||
$this->register(new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel()))); //same as regular stone pressure plate
|
||||
$this->register(new Slab(new BID(Ids::POLISHED_BLACKSTONE_SLAB), $prefix(""), $slabBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::POLISHED_BLACKSTONE_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::POLISHED_BLACKSTONE_WALL), $prefix("Wall"), $blackstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CHISELED_POLISHED_BLACKSTONE), "Chiseled Polished Blackstone", $blackstoneBreakInfo));
|
||||
|
||||
$prefix = fn(string $thing) => "Polished Blackstone Brick" . ($thing !== "" ? " $thing" : "");
|
||||
$this->register(new Opaque(new BID(Ids::POLISHED_BLACKSTONE_BRICKS), "Polished Blackstone Bricks", $blackstoneBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::POLISHED_BLACKSTONE_BRICK_SLAB), "Polished Blackstone Brick", $slabBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::POLISHED_BLACKSTONE_BRICK_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::POLISHED_BLACKSTONE_BRICK_WALL), $prefix("Wall"), $blackstoneBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS), "Cracked Polished Blackstone Bricks", $blackstoneBreakInfo));
|
||||
|
||||
$this->register(new Torch(new BID(Ids::SOUL_TORCH), "Soul Torch", BreakInfo::instant()));
|
||||
$this->register(new SoulFire(new BID(Ids::SOUL_FIRE), "Soul Fire", BreakInfo::instant()));
|
||||
|
||||
//TODO: soul soul ought to have 0.5 hardness (as per java) but it's 1.0 in Bedrock (probably parity bug)
|
||||
$this->register(new Opaque(new BID(Ids::SOUL_SOIL), "Soul Soil", new BreakInfo(1.0, ToolType::SHOVEL)));
|
||||
|
||||
$this->register(new class(new BID(Ids::SHROOMLIGHT), "Shroomlight", new BreakInfo(1.0, ToolType::HOE)) extends Opaque{
|
||||
public function getLightLevel() : int{ return 15; }
|
||||
});
|
||||
}
|
||||
|
||||
private function registerBlocksR17() : void{
|
||||
//in java this can be acquired using any tool - seems to be a parity issue in bedrock
|
||||
$this->register(new Opaque(new BID(Ids::AMETHYST), "Amethyst", new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::CALCITE), "Calcite", new BreakInfo(0.75, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Opaque(new BID(Ids::TUFF), "Tuff", new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new BreakInfo(5, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel(), 30.0)));
|
||||
$this->register(new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new BreakInfo(5, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
|
||||
$this->register(new Opaque(new BID(Ids::RAW_IRON), "Raw Iron Block", new BreakInfo(5, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel(), 30.0)));
|
||||
|
||||
$deepslateBreakInfo = new BreakInfo(3, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 18.0);
|
||||
$this->register(new SimplePillar(new BID(Ids::DEEPSLATE), "Deepslate", $deepslateBreakInfo));
|
||||
|
||||
//TODO: parity issue here - in Java this has a hardness of 3.0, but in bedrock it's 3.5
|
||||
$this->register(new Opaque(new BID(Ids::CHISELED_DEEPSLATE), "Chiseled Deepslate", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 18.0)));
|
||||
|
||||
$deepslateBrickBreakInfo = new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 18.0);
|
||||
$this->register(new Opaque(new BID(Ids::DEEPSLATE_BRICKS), "Deepslate Bricks", $deepslateBrickBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::DEEPSLATE_BRICK_SLAB), "Deepslate Brick", $deepslateBrickBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::DEEPSLATE_BRICK_STAIRS), "Deepslate Brick Stairs", $deepslateBrickBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::DEEPSLATE_BRICK_WALL), "Deepslate Brick Wall", $deepslateBrickBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CRACKED_DEEPSLATE_BRICKS), "Cracked Deepslate Bricks", $deepslateBrickBreakInfo));
|
||||
|
||||
$deepslateTilesBreakInfo = new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 18.0);
|
||||
$this->register(new Opaque(new BID(Ids::DEEPSLATE_TILES), "Deepslate Tiles", $deepslateTilesBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::DEEPSLATE_TILE_SLAB), "Deepslate Tile", $deepslateTilesBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::DEEPSLATE_TILE_STAIRS), "Deepslate Tile Stairs", $deepslateTilesBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::DEEPSLATE_TILE_WALL), "Deepslate Tile Wall", $deepslateTilesBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::CRACKED_DEEPSLATE_TILES), "Cracked Deepslate Tiles", $deepslateTilesBreakInfo));
|
||||
|
||||
$cobbledDeepslateBreakInfo = new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 18.0);
|
||||
$this->register(new Opaque(new BID(Ids::COBBLED_DEEPSLATE), "Cobbled Deepslate", $cobbledDeepslateBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::COBBLED_DEEPSLATE_SLAB), "Cobbled Deepslate", $cobbledDeepslateBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::COBBLED_DEEPSLATE_STAIRS), "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::COBBLED_DEEPSLATE_WALL), "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo));
|
||||
|
||||
$polishedDeepslateBreakInfo = new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 18.0);
|
||||
$this->register(new Opaque(new BID(Ids::POLISHED_DEEPSLATE), "Polished Deepslate", $polishedDeepslateBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::POLISHED_DEEPSLATE_SLAB), "Polished Deepslate", $polishedDeepslateBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::POLISHED_DEEPSLATE_STAIRS), "Polished Deepslate Stairs", $polishedDeepslateBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::POLISHED_DEEPSLATE_WALL), "Polished Deepslate Wall", $polishedDeepslateBreakInfo));
|
||||
}
|
||||
|
||||
private function registerMudBlocks() : void{
|
||||
$mudBricksBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
|
||||
$this->register(new Opaque(new BID(Ids::MUD_BRICKS), "Mud Bricks", $mudBricksBreakInfo));
|
||||
$this->register(new Slab(new BID(Ids::MUD_BRICK_SLAB), "Mud Brick", $mudBricksBreakInfo));
|
||||
$this->register(new Stair(new BID(Ids::MUD_BRICK_STAIRS), "Mud Brick Stairs", $mudBricksBreakInfo));
|
||||
$this->register(new Wall(new BID(Ids::MUD_BRICK_WALL), "Mud Brick Wall", $mudBricksBreakInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -846,7 +85,7 @@ class BlockFactory{
|
||||
* NOTE: If you are registering a new block type, you will need to add it to the creative inventory yourself - it
|
||||
* will not automatically appear there.
|
||||
*
|
||||
* @param bool $override Whether to override existing registrations
|
||||
* @param bool $override Whether to override existing registrations
|
||||
*
|
||||
* @throws \InvalidArgumentException if something attempted to override an already-registered block without specifying the
|
||||
* $override parameter.
|
||||
@ -918,7 +157,7 @@ class BlockFactory{
|
||||
}else{
|
||||
$typeId = $stateId >> Block::INTERNAL_STATE_DATA_BITS;
|
||||
$stateData = $stateId & Block::INTERNAL_STATE_DATA_MASK;
|
||||
$block = new UnknownBlock(new BID($typeId), BreakInfo::instant(), $stateData);
|
||||
$block = new UnknownBlock(new BID($typeId), new BlockTypeInfo(BreakInfo::instant()), $stateData);
|
||||
}
|
||||
|
||||
return $block;
|
||||
|
@ -658,7 +658,7 @@ final class BlockTypeIds{
|
||||
public const MANGROVE_WALL_SIGN = 10631;
|
||||
public const CRIMSON_WALL_SIGN = 10632;
|
||||
public const WARPED_WALL_SIGN = 10633;
|
||||
|
||||
public const TINTED_GLASS = 10634;
|
||||
public const HONEYCOMB = 10635;
|
||||
public const DEEPSLATE_COAL_ORE = 10636;
|
||||
public const DEEPSLATE_DIAMOND_ORE = 10637;
|
||||
@ -676,6 +676,45 @@ final class BlockTypeIds{
|
||||
public const MUD_BRICK_STAIRS = 10649;
|
||||
public const MUD_BRICK_WALL = 10650;
|
||||
public const PACKED_MUD = 10651;
|
||||
public const WARPED_WART_BLOCK = 10652;
|
||||
public const CRYING_OBSIDIAN = 10653;
|
||||
public const GILDED_BLACKSTONE = 10654;
|
||||
public const LIGHTNING_ROD = 10655;
|
||||
public const COPPER = 10656;
|
||||
public const CUT_COPPER = 10657;
|
||||
public const CUT_COPPER_SLAB = 10658;
|
||||
public const CUT_COPPER_STAIRS = 10659;
|
||||
public const CANDLE = 10660;
|
||||
public const DYED_CANDLE = 10661;
|
||||
public const CAKE_WITH_CANDLE = 10662;
|
||||
public const CAKE_WITH_DYED_CANDLE = 10663;
|
||||
public const WITHER_ROSE = 10664;
|
||||
public const HANGING_ROOTS = 10665;
|
||||
public const CARTOGRAPHY_TABLE = 10666;
|
||||
public const SMITHING_TABLE = 10667;
|
||||
public const NETHERITE = 10668;
|
||||
public const SPORE_BLOSSOM = 10669;
|
||||
public const CAULDRON = 10670;
|
||||
public const WATER_CAULDRON = 10671;
|
||||
public const LAVA_CAULDRON = 10672;
|
||||
public const POTION_CAULDRON = 10673;
|
||||
public const POWDER_SNOW_CAULDRON = 10674;
|
||||
public const CHORUS_FLOWER = 10675;
|
||||
public const CHORUS_PLANT = 10676;
|
||||
public const MANGROVE_ROOTS = 10677;
|
||||
public const MUDDY_MANGROVE_ROOTS = 10678;
|
||||
public const FROGLIGHT = 10679;
|
||||
public const TWISTING_VINES = 10680;
|
||||
public const WEEPING_VINES = 10681;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10652;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10682;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
/**
|
||||
* Returns a new runtime block type ID, e.g. for use by a custom block.
|
||||
*/
|
||||
public static function newId() : int{
|
||||
return self::$nextDynamicId++;
|
||||
}
|
||||
}
|
||||
|
52
src/block/BlockTypeInfo.php
Normal file
52
src/block/BlockTypeInfo.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?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 function array_fill_keys;
|
||||
use function array_keys;
|
||||
|
||||
final class BlockTypeInfo{
|
||||
/**
|
||||
* @var true[]
|
||||
* @phpstan-var array<string, true>
|
||||
*/
|
||||
private array $typeTags;
|
||||
|
||||
/**
|
||||
* @param string[] $typeTags
|
||||
*/
|
||||
public function __construct(
|
||||
private BlockBreakInfo $breakInfo,
|
||||
array $typeTags = []
|
||||
){
|
||||
$this->typeTags = array_fill_keys($typeTags, true);
|
||||
}
|
||||
|
||||
public function getBreakInfo() : BlockBreakInfo{ return $this->breakInfo; }
|
||||
|
||||
/** @return string[] */
|
||||
public function getTypeTags() : array{ return array_keys($this->typeTags); }
|
||||
|
||||
public function hasTypeTag(string $tag) : bool{ return isset($this->typeTags[$tag]); }
|
||||
}
|
34
src/block/BlockTypeTags.php
Normal file
34
src/block/BlockTypeTags.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
final class BlockTypeTags{
|
||||
private const PREFIX = "pocketmine:";
|
||||
|
||||
public const DIRT = self::PREFIX . "dirt";
|
||||
public const MUD = self::PREFIX . "mud";
|
||||
public const SAND = self::PREFIX . "sand";
|
||||
public const POTTABLE_PLANTS = self::PREFIX . "pottable";
|
||||
public const FIRE = self::PREFIX . "fire";
|
||||
}
|
@ -46,29 +46,8 @@ class BrewingStand extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 3; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$result = [];
|
||||
foreach([
|
||||
BrewingStandSlot::EAST(),
|
||||
BrewingStandSlot::NORTHWEST(),
|
||||
BrewingStandSlot::SOUTHWEST(),
|
||||
] as $member){
|
||||
if($r->readBool()){
|
||||
$result[$member->id()] = $member;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setSlots($result);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
foreach([
|
||||
BrewingStandSlot::EAST(),
|
||||
BrewingStandSlot::NORTHWEST(),
|
||||
BrewingStandSlot::SOUTHWEST(),
|
||||
] as $member){
|
||||
$w->writeBool(isset($this->slots[$member->id()]));
|
||||
}
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->brewingStandSlots($this->slots);
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
@ -118,7 +97,7 @@ class BrewingStand extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
$stand = $this->position->getWorld()->getTile($this->position);
|
||||
if($stand instanceof TileBrewingStand && $stand->canOpenWith($item->getCustomName())){
|
||||
@ -130,10 +109,11 @@ class BrewingStand extends Transparent{
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
$brewing = $this->position->getWorld()->getTile($this->position);
|
||||
$world = $this->position->getWorld();
|
||||
$brewing = $world->getTile($this->position);
|
||||
if($brewing instanceof TileBrewingStand){
|
||||
if($brewing->onUpdate()){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
|
||||
$world->scheduleDelayedBlockUpdate($this->position, 1);
|
||||
}
|
||||
|
||||
$changed = false;
|
||||
@ -146,7 +126,7 @@ class BrewingStand extends Transparent{
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,14 +41,9 @@ abstract class Button extends Flowable{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->facing = $r->readFacing();
|
||||
$this->pressed = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeFacing($this->facing);
|
||||
$w->writeBool($this->pressed);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->facing($this->facing);
|
||||
$w->bool($this->pressed);
|
||||
}
|
||||
|
||||
public function isPressed() : bool{ return $this->pressed; }
|
||||
@ -69,12 +64,13 @@ abstract class Button extends Flowable{
|
||||
|
||||
abstract protected function getActivationTime() : int;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if(!$this->pressed){
|
||||
$this->pressed = true;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, $this->getActivationTime());
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOnSound());
|
||||
$world = $this->position->getWorld();
|
||||
$world->setBlock($this->position, $this);
|
||||
$world->scheduleDelayedBlockUpdate($this->position, $this->getActivationTime());
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOnSound());
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -83,8 +79,9 @@ abstract class Button extends Flowable{
|
||||
public function onScheduledUpdate() : void{
|
||||
if($this->pressed){
|
||||
$this->pressed = false;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound());
|
||||
$world = $this->position->getWorld();
|
||||
$world->setBlock($this->position, $this);
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,12 +44,8 @@ class Cactus extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->age = $r->readBoundedInt(4, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(4, $this->age);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
@ -71,7 +67,7 @@ class Cactus extends Transparent{
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
static $shrinkSize = 1 / 16;
|
||||
$shrinkSize = 1 / 16;
|
||||
return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)];
|
||||
}
|
||||
|
||||
@ -85,15 +81,19 @@ class Cactus extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->isSameType($this) || $block->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getTypeId() !== BlockTypeIds::SAND && $down->getTypeId() !== BlockTypeIds::RED_SAND && !$down->isSameType($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
$b = $this->getSide($side);
|
||||
if($b->isSolid()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
$world->useBreakOn($this->position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -106,35 +106,35 @@ class Cactus extends Transparent{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
|
||||
$world = $this->position->getWorld();
|
||||
if($this->age === self::MAX_AGE){
|
||||
for($y = 1; $y < 3; ++$y){
|
||||
if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){
|
||||
if(!$world->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){
|
||||
break;
|
||||
}
|
||||
$b = $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z);
|
||||
$b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z);
|
||||
if($b->getTypeId() === BlockTypeIds::AIR){
|
||||
$ev = new BlockGrowEvent($b, VanillaBlocks::CACTUS());
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
break;
|
||||
}
|
||||
$this->position->getWorld()->setBlock($b->position, $ev->getNewState());
|
||||
$world->setBlock($b->position, $ev->getNewState());
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->age = 0;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$world->setBlock($this->position, $this);
|
||||
}else{
|
||||
++$this->age;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$world->setBlock($this->position, $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::SAND || $down->getTypeId() === BlockTypeIds::RED_SAND || $down->isSameType($this)){
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
if($this->getSide($side)->isSolid()){
|
||||
return false;
|
||||
|
@ -23,32 +23,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\FoodSource;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemBlock;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Cake extends Transparent implements FoodSource{
|
||||
class Cake extends BaseCake{
|
||||
public const MAX_BITES = 6;
|
||||
|
||||
protected int $bites = 0;
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 3; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->bites = $r->readBoundedInt(3, 0, self::MAX_BITES);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(3, $this->bites);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_BITES, $this->bites);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,10 +55,6 @@ class Cake extends Transparent implements FoodSource{
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function getBites() : int{ return $this->bites; }
|
||||
|
||||
/** @return $this */
|
||||
@ -78,49 +66,27 @@ class Cake extends Transparent implements FoodSource{
|
||||
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::AIR){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof ItemBlock){
|
||||
$block = $item->getBlock();
|
||||
$resultBlock = null;
|
||||
if($block->getTypeId() === BlockTypeIds::CANDLE){
|
||||
$resultBlock = VanillaBlocks::CAKE_WITH_CANDLE();
|
||||
}elseif($block instanceof DyedCandle){
|
||||
$resultBlock = VanillaBlocks::CAKE_WITH_DYED_CANDLE()->setColor($block->getColor());
|
||||
}
|
||||
|
||||
if($resultBlock !== null){
|
||||
$this->position->getWorld()->setBlock($this->position, $resultBlock);
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){ //Replace with common break method
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
|
||||
}
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
return $player->consumeObject($this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFoodRestore() : int{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public function getSaturationRestore() : float{
|
||||
return 0.4;
|
||||
}
|
||||
|
||||
public function requiresHunger() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Block
|
||||
*/
|
||||
public function getResidue(){
|
||||
public function getResidue() : Block{
|
||||
$clone = clone $this;
|
||||
$clone->bites++;
|
||||
if($clone->bites > self::MAX_BITES){
|
||||
@ -128,15 +94,4 @@ class Cake extends Transparent implements FoodSource{
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EffectInstance[]
|
||||
*/
|
||||
public function getAdditionalEffects() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function onConsume(Living $consumer) : void{
|
||||
$this->position->getWorld()->setBlock($this->position, $this->getResidue());
|
||||
}
|
||||
}
|
||||
|
78
src/block/CakeWithCandle.php
Normal file
78
src/block/CakeWithCandle.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?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\CandleTrait;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class CakeWithCandle extends BaseCake{
|
||||
use CandleTrait {
|
||||
onInteract as onInteractCandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
->contract(1 / 16, 0, 1 / 16)
|
||||
->trim(Facing::UP, 0.5) //TODO: not sure if the candle affects height
|
||||
];
|
||||
}
|
||||
|
||||
public function getCandle() : Candle{
|
||||
return VanillaBlocks::CANDLE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($this->onInteractCandle($item, $face, $clickVector, $player, $returnedItems)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [$this->getCandle()->asItem()];
|
||||
}
|
||||
|
||||
public function getPickedItem(bool $addUserData = false) : Item{
|
||||
return VanillaBlocks::CAKE()->getPickedItem($addUserData);
|
||||
}
|
||||
|
||||
public function getResidue() : Block{
|
||||
return VanillaBlocks::CAKE()->setBites(1);
|
||||
}
|
||||
|
||||
public function onConsume(Living $consumer) : void{
|
||||
parent::onConsume($consumer);
|
||||
$this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), $this->getCandle()->asItem());
|
||||
}
|
||||
}
|
40
src/block/CakeWithDyedCandle.php
Normal file
40
src/block/CakeWithDyedCandle.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?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\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
class CakeWithDyedCandle extends CakeWithCandle{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function getCandle() : Candle{
|
||||
return VanillaBlocks::DYED_CANDLE()->setColor($this->color);
|
||||
}
|
||||
}
|
131
src/block/Candle.php
Normal file
131
src/block/Candle.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?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\CandleTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
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\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Candle extends Transparent{
|
||||
use CandleTrait {
|
||||
describeState as encodeLitState;
|
||||
getLightLevel as getBaseLightLevel;
|
||||
}
|
||||
|
||||
public const MIN_COUNT = 1;
|
||||
public const MAX_COUNT = 4;
|
||||
|
||||
private int $count = self::MIN_COUNT;
|
||||
|
||||
public function getRequiredStateDataBits() : int{
|
||||
return 3;
|
||||
}
|
||||
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$this->encodeLitState($w);
|
||||
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
}
|
||||
|
||||
public function getCount() : int{ return $this->count; }
|
||||
|
||||
/** @return $this */
|
||||
public function setCount(int $count) : self{
|
||||
if($count < self::MIN_COUNT || $count > self::MAX_COUNT){
|
||||
throw new \InvalidArgumentException("Count must be in range " . self::MIN_COUNT . " ... " . self::MAX_COUNT);
|
||||
}
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->getBaseLightLevel() * $this->count;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [
|
||||
(match($this->count){
|
||||
1 => AxisAlignedBB::one()
|
||||
->squash(Axis::X, 7 / 16)
|
||||
->squash(Axis::Z, 7 / 16),
|
||||
2 => AxisAlignedBB::one()
|
||||
->squash(Axis::X, 5 / 16)
|
||||
->trim(Facing::NORTH, 7 / 16) //0.3 thick on the Z axis
|
||||
->trim(Facing::SOUTH, 6 / 16),
|
||||
3 => AxisAlignedBB::one()
|
||||
->trim(Facing::WEST, 5 / 16)
|
||||
->trim(Facing::EAST, 6 / 16)
|
||||
->trim(Facing::NORTH, 6 / 16)
|
||||
->trim(Facing::SOUTH, 5 / 16),
|
||||
4 => AxisAlignedBB::one()
|
||||
->squash(Axis::X, 5 / 16)
|
||||
->trim(Facing::NORTH, 5 / 16)
|
||||
->trim(Facing::SOUTH, 6 / 16),
|
||||
default => throw new AssumptionFailedError("Unreachable")
|
||||
})->trim(Facing::UP, 10 / 16)
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
||||
return $block instanceof Candle && $block->isSameType($this) ? $block : null;
|
||||
}
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
$candle = $this->getCandleIfCompatibleType($blockReplace);
|
||||
return $candle !== null ? $candle->count < self::MAX_COUNT : parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $blockReplace->getSide(Facing::DOWN);
|
||||
if(!$down->getSupportType(Facing::UP)->hasCenterSupport()){
|
||||
return false;
|
||||
}
|
||||
$existing = $this->getCandleIfCompatibleType($blockReplace);
|
||||
if($existing !== null){
|
||||
if($existing->count >= self::MAX_COUNT){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->count = $existing->count + 1;
|
||||
$this->lit = $existing->lit;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [$this->asItem()->setCount($this->count)];
|
||||
}
|
||||
}
|
@ -35,9 +35,9 @@ use pocketmine\world\BlockTransaction;
|
||||
class Carpet extends Flowable{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function isSolid() : bool{
|
||||
|
44
src/block/CartographyTable.php
Normal file
44
src/block/CartographyTable.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\CartographyTableInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
final class CartographyTable extends Opaque{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
$player->setCurrentWindow(new CartographyTableInventory($this->position));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
return 300;
|
||||
}
|
||||
}
|
104
src/block/Cauldron.php
Normal file
104
src/block/Cauldron.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?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\Cauldron as TileCauldron;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\item\Potion;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\SplashPotion;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function assert;
|
||||
|
||||
final class Cauldron extends Transparent{
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
assert($tile instanceof TileCauldron);
|
||||
|
||||
//empty cauldrons don't use this information
|
||||
$tile->setCustomWaterColor(null);
|
||||
$tile->setPotionItem(null);
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$result = [
|
||||
AxisAlignedBB::one()->trim(Facing::UP, 11 / 16) //bottom of the cauldron
|
||||
];
|
||||
|
||||
foreach(Facing::HORIZONTAL as $f){ //add the frame parts around the bowl
|
||||
$result[] = AxisAlignedBB::one()->trim($f, 14 / 16);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] &$returnedItems
|
||||
*/
|
||||
private function fill(int $amount, FillableCauldron $result, Item $usedItem, Item $returnedItem, array &$returnedItems) : void{
|
||||
$this->position->getWorld()->setBlock($this->position, $result->setFillLevel($amount));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), $result->getFillSound());
|
||||
|
||||
$usedItem->pop();
|
||||
$returnedItems[] = $returnedItem;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item->getTypeId() === ItemTypeIds::WATER_BUCKET){
|
||||
$this->fill(FillableCauldron::MAX_FILL_LEVEL, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::BUCKET(), $returnedItems);
|
||||
}elseif($item->getTypeId() === ItemTypeIds::LAVA_BUCKET){
|
||||
$this->fill(FillableCauldron::MAX_FILL_LEVEL, VanillaBlocks::LAVA_CAULDRON(), $item, VanillaItems::BUCKET(), $returnedItems);
|
||||
}elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){
|
||||
//TODO: powder snow cauldron
|
||||
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
|
||||
if($item->getType()->equals(PotionType::WATER())){
|
||||
$this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
||||
}else{
|
||||
$this->fill(PotionCauldron::POTION_FILL_AMOUNT, VanillaBlocks::POTION_CAULDRON()->setPotionItem($item), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if($world->getBlock($this->position->up())->getTypeId() === BlockTypeIds::WATER){
|
||||
$cauldron = VanillaBlocks::WATER_CAULDRON()->setFillLevel(FillableCauldron::MAX_FILL_LEVEL);
|
||||
$world->setBlock($this->position, $cauldron);
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), $cauldron->getFillSound());
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ final class ChemistryTable extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
//TODO
|
||||
return false;
|
||||
}
|
||||
|
@ -51,13 +51,13 @@ class Chest extends Transparent{
|
||||
}
|
||||
|
||||
public function onPostPlace() : void{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
$world = $this->position->getWorld();
|
||||
$tile = $world->getTile($this->position);
|
||||
if($tile instanceof TileChest){
|
||||
foreach([false, true] as $clockwise){
|
||||
$side = Facing::rotateY($this->facing, $clockwise);
|
||||
$c = $this->getSide($side);
|
||||
if($c instanceof Chest && $c->isSameType($this) && $c->facing === $this->facing){
|
||||
$world = $this->position->getWorld();
|
||||
$pair = $world->getTile($c->position);
|
||||
if($pair instanceof TileChest && !$pair->isPaired()){
|
||||
[$left, $right] = $clockwise ? [$c, $this] : [$this, $c];
|
||||
@ -74,7 +74,7 @@ class Chest extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
|
||||
$chest = $this->position->getWorld()->getTile($this->position);
|
||||
|
236
src/block/ChorusFlower.php
Normal file
236
src/block/ChorusFlower.php
Normal file
@ -0,0 +1,236 @@
|
||||
<?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\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\ChorusFlowerDieSound;
|
||||
use pocketmine\world\sound\ChorusFlowerGrowSound;
|
||||
use pocketmine\world\World;
|
||||
use function array_rand;
|
||||
use function mt_rand;
|
||||
|
||||
final class ChorusFlower extends Flowable{
|
||||
public const MIN_AGE = 0;
|
||||
public const MAX_AGE = 5;
|
||||
|
||||
private const MAX_STEM_HEIGHT = 5;
|
||||
|
||||
private int $age = self::MIN_AGE;
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 3; }
|
||||
|
||||
protected function describeState(RuntimeDataWriter|RuntimeDataReader $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{
|
||||
return [AxisAlignedBB::one()];
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Position $position) : bool{
|
||||
$world = $position->getWorld();
|
||||
$down = $world->getBlock($position->down());
|
||||
|
||||
if($down->getTypeId() === BlockTypeIds::END_STONE || $down->getTypeId() === BlockTypeIds::CHORUS_PLANT){
|
||||
return true;
|
||||
}
|
||||
|
||||
$plantAdjacent = false;
|
||||
foreach($position->sidesAroundAxis(Axis::Y) as $sidePosition){
|
||||
$block = $world->getBlock($sidePosition);
|
||||
|
||||
if($block->getTypeId() === BlockTypeIds::CHORUS_PLANT){
|
||||
if($plantAdjacent){ //at most one plant may be horizontally adjacent
|
||||
return false;
|
||||
}
|
||||
$plantAdjacent = true;
|
||||
}elseif($block->getTypeId() !== BlockTypeIds::AIR){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
|
||||
|
||||
/**
|
||||
* @phpstan-return array{int, bool}
|
||||
*/
|
||||
private function scanStem() : array{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$stemHeight = 0;
|
||||
$endStoneBelow = false;
|
||||
for($yOffset = 0; $yOffset < self::MAX_STEM_HEIGHT; $yOffset++, $stemHeight++){
|
||||
$down = $world->getBlock($this->position->down($yOffset + 1));
|
||||
|
||||
if($down->getTypeId() !== BlockTypeIds::CHORUS_PLANT){
|
||||
if($down->getTypeId() === BlockTypeIds::END_STONE){
|
||||
$endStoneBelow = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [$stemHeight, $endStoneBelow];
|
||||
}
|
||||
|
||||
private function allHorizontalBlocksEmpty(World $world, Vector3 $position, ?int $except) : bool{
|
||||
foreach($position->sidesAroundAxis(Axis::Y) as $facing => $sidePosition){
|
||||
if($facing === $except){
|
||||
continue;
|
||||
}
|
||||
if($world->getBlock($sidePosition)->getTypeId() !== BlockTypeIds::AIR){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canGrowUpwards(int $stemHeight, bool $endStoneBelow) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$up = $this->position->up();
|
||||
if(
|
||||
//the space above must be empty and writable
|
||||
!$world->isInWorld($up->x, $up->y, $up->z) ||
|
||||
$world->getBlock($up)->getTypeId() !== BlockTypeIds::AIR ||
|
||||
(
|
||||
//the space above that must be empty, but doesn't need to be writable
|
||||
$world->isInWorld($up->x, $up->y + 1, $up->z) &&
|
||||
$world->getBlock($up->up())->getTypeId() !== BlockTypeIds::AIR
|
||||
)
|
||||
){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR){
|
||||
if($stemHeight >= self::MAX_STEM_HEIGHT){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($stemHeight > 1 && $stemHeight > mt_rand(0, $endStoneBelow ? 4 : 3)){ //chance decreases for each added block of chorus plant
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->allHorizontalBlocksEmpty($world, $up, null);
|
||||
}
|
||||
|
||||
private function grow(int $facing, int $ageChange, ?BlockTransaction $tx) : BlockTransaction{
|
||||
if($tx === null){
|
||||
$tx = new BlockTransaction($this->position->getWorld());
|
||||
}
|
||||
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge($this->getAge() + $ageChange));
|
||||
|
||||
return $tx;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
if($this->age >= self::MAX_AGE){
|
||||
return;
|
||||
}
|
||||
|
||||
$tx = null;
|
||||
|
||||
[$stemHeight, $endStoneBelow] = $this->scanStem();
|
||||
if($this->canGrowUpwards($stemHeight, $endStoneBelow)){
|
||||
$tx = $this->grow(Facing::UP, 0, $tx);
|
||||
}else{
|
||||
$facingVisited = [];
|
||||
for($attempts = 0, $maxAttempts = mt_rand(0, $endStoneBelow ? 4 : 3); $attempts < $maxAttempts; $attempts++){
|
||||
$facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)];
|
||||
if(isset($facingVisited[$facing])){
|
||||
continue;
|
||||
}
|
||||
$facingVisited[$facing] = true;
|
||||
|
||||
$sidePosition = $this->position->getSide($facing);
|
||||
if(
|
||||
$world->getBlock($sidePosition)->getTypeId() === BlockTypeIds::AIR &&
|
||||
$world->getBlock($sidePosition->down())->getTypeId() === BlockTypeIds::AIR &&
|
||||
$this->allHorizontalBlocksEmpty($world, $sidePosition, Facing::opposite($facing))
|
||||
){
|
||||
$tx = $this->grow($facing, 1, $tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($tx !== null){
|
||||
$tx->addBlock($this->position, VanillaBlocks::CHORUS_PLANT());
|
||||
$ev = new StructureGrowEvent($this, $tx, null);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled() && $tx->apply()){
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ChorusFlowerGrowSound());
|
||||
}
|
||||
}else{
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ChorusFlowerDieSound());
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setAge(self::MAX_AGE));
|
||||
}
|
||||
}
|
||||
}
|
102
src/block/ChorusPlant.php
Normal file
102
src/block/ChorusPlant.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
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\world\BlockTransaction;
|
||||
use pocketmine\world\Position;
|
||||
use function mt_rand;
|
||||
|
||||
final class ChorusPlant extends Flowable{
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$bb = AxisAlignedBB::one();
|
||||
foreach($this->getAllSides() as $facing => $block){
|
||||
$id = $block->getTypeId();
|
||||
if($id !== BlockTypeIds::END_STONE && $id !== BlockTypeIds::CHORUS_FLOWER && !$block->isSameType($this)){
|
||||
$bb->trim($facing, 2 / 16);
|
||||
}
|
||||
}
|
||||
|
||||
return [$bb];
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->isSameType($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
|
||||
}
|
||||
|
||||
private function canStay(Position $position) : bool{
|
||||
$world = $position->getWorld();
|
||||
|
||||
$down = $world->getBlock($position->down());
|
||||
$verticalAir = $down->getTypeId() === BlockTypeIds::AIR || $world->getBlock($position->up())->getTypeId() === BlockTypeIds::AIR;
|
||||
|
||||
foreach($position->sidesAroundAxis(Axis::Y) as $sidePosition){
|
||||
$block = $world->getBlock($sidePosition);
|
||||
|
||||
if($block->getTypeId() === BlockTypeIds::CHORUS_PLANT){
|
||||
if(!$verticalAir){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->canBeSupportedBy($block->getSide(Facing::DOWN))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($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{
|
||||
if(mt_rand(0, 1) === 1){
|
||||
return [VanillaItems::CHORUS_FRUIT()];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@ -49,14 +49,9 @@ class CocoaBlock extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->facing = $r->readHorizontalFacing();
|
||||
$this->age = $r->readBoundedInt(2, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalFacing($this->facing);
|
||||
$w->writeInt(2, $this->age);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
@ -101,7 +96,7 @@ class CocoaBlock extends Transparent{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer && $this->grow()){
|
||||
$item->pop();
|
||||
|
||||
|
@ -29,8 +29,8 @@ use pocketmine\block\utils\DyeColor;
|
||||
class Concrete extends Opaque{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -36,14 +36,14 @@ class ConcretePowder extends Opaque implements Fallable{
|
||||
onNearbyBlockChange as protected startFalling;
|
||||
}
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(($block = $this->checkAdjacentWater()) !== null){
|
||||
$ev = new BlockFormEvent($this, $block);
|
||||
if(($water = $this->getAdjacentWater()) !== null){
|
||||
$ev = new BlockFormEvent($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
@ -54,16 +54,20 @@ class ConcretePowder extends Opaque implements Fallable{
|
||||
}
|
||||
|
||||
public function tickFalling() : ?Block{
|
||||
return $this->checkAdjacentWater();
|
||||
if($this->getAdjacentWater() === null){
|
||||
return null;
|
||||
}
|
||||
return VanillaBlocks::CONCRETE()->setColor($this->color);
|
||||
}
|
||||
|
||||
private function checkAdjacentWater() : ?Block{
|
||||
private function getAdjacentWater() : ?Water{
|
||||
foreach(Facing::ALL as $i){
|
||||
if($i === Facing::DOWN){
|
||||
continue;
|
||||
}
|
||||
if($this->getSide($i) instanceof Water){
|
||||
return VanillaBlocks::CONCRETE()->setColor($this->color);
|
||||
$block = $this->getSide($i);
|
||||
if($block instanceof Water){
|
||||
return $block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block;
|
||||
namespace pocketmine\block;
|
||||
|
||||
interface DelegatingBlockStateSerializer extends BlockStateSerializer{
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
public function getRealSerializer() : BlockStateSerializer;
|
||||
class Copper extends Opaque{
|
||||
use CopperTrait;
|
||||
}
|
@ -21,9 +21,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block;
|
||||
namespace pocketmine\block;
|
||||
|
||||
interface DelegatingBlockStateDeserializer extends BlockStateDeserializer{
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
public function getRealDeserializer() : BlockStateDeserializer;
|
||||
class CopperSlab extends Slab{
|
||||
use CopperTrait;
|
||||
}
|
36
src/block/CopperStairs.php
Normal file
36
src/block/CopperStairs.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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\CopperOxidation;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperStairs extends Stair{
|
||||
use CopperTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->oxidation = CopperOxidation::NONE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
@ -25,15 +25,16 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\event\block\BlockDeathEvent;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
final class CoralBlock extends Opaque{
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->coralType = CoralType::TUBE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
@ -54,7 +55,11 @@ final class CoralBlock extends Opaque{
|
||||
}
|
||||
}
|
||||
if(!$hasWater){
|
||||
$world->setBlock($this->position, $this->setDead(true));
|
||||
$ev = new BlockDeathEvent($this, $this->setDead(true));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$world->setBlock($this->position, $ev->getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ use pocketmine\player\Player;
|
||||
|
||||
class CraftingTable extends Opaque{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
$player->setCurrentWindow(new CraftingTableInventory($this->position));
|
||||
}
|
||||
|
@ -41,12 +41,8 @@ abstract class Crops extends Flowable{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 3; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->age = $r->readBoundedInt(3, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(3, $this->age);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
@ -68,7 +64,7 @@ abstract class Crops extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
|
||||
$block = clone $this;
|
||||
$block->age += mt_rand(2, 5);
|
||||
|
@ -44,14 +44,9 @@ class DaylightSensor extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 5; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->signalStrength = $r->readBoundedInt(4, 0, 15);
|
||||
$this->inverted = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(4, $this->signalStrength);
|
||||
$w->writeBool($this->inverted);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(4, 0, 15, $this->signalStrength);
|
||||
$w->bool($this->inverted);
|
||||
}
|
||||
|
||||
public function isInverted() : bool{
|
||||
@ -81,7 +76,7 @@ class DaylightSensor extends Transparent{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
$this->inverted = !$this->inverted;
|
||||
$this->signalStrength = $this->recalculateSignalStrength();
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
@ -89,21 +84,23 @@ class DaylightSensor extends Transparent{
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
$world = $this->position->getWorld();
|
||||
$signalStrength = $this->recalculateSignalStrength();
|
||||
if($this->signalStrength !== $signalStrength){
|
||||
$this->signalStrength = $signalStrength;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 20);
|
||||
$world->scheduleDelayedBlockUpdate($this->position, 20);
|
||||
}
|
||||
|
||||
private function recalculateSignalStrength() : int{
|
||||
$lightLevel = $this->position->getWorld()->getRealBlockSkyLightAt($this->position->x, $this->position->y, $this->position->z);
|
||||
$world = $this->position->getWorld();
|
||||
$lightLevel = $world->getRealBlockSkyLightAt($this->position->x, $this->position->y, $this->position->z);
|
||||
if($this->inverted){
|
||||
return 15 - $lightLevel;
|
||||
}
|
||||
|
||||
$sunAngle = $this->position->getWorld()->getSunAnglePercentage();
|
||||
$sunAngle = $world->getSunAnglePercentage();
|
||||
return max(0, (int) round($lightLevel * cos(($sunAngle + ((($sunAngle < 0.5 ? 0 : 1) - $sunAngle) / 5)) * 2 * M_PI)));
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ use function mt_rand;
|
||||
class DeadBush extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ class DeadBush extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
@ -64,4 +64,15 @@ class DeadBush extends Flowable{
|
||||
public function getFlammability() : int{
|
||||
return 100;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
$blockId = $block->getTypeId();
|
||||
return $blockId === BlockTypeIds::SAND
|
||||
|| $blockId === BlockTypeIds::RED_SAND
|
||||
|| $blockId === BlockTypeIds::PODZOL
|
||||
|| $blockId === BlockTypeIds::MYCELIUM
|
||||
|| $blockId === BlockTypeIds::DIRT
|
||||
|| $blockId === BlockTypeIds::HARDENED_CLAY
|
||||
|| $blockId === BlockTypeIds::STAINED_CLAY;
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,9 @@ class DetectorRail extends StraightOnlyRail{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
parent::decodeState($r);
|
||||
$this->activated = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
parent::encodeState($w);
|
||||
$w->writeBool($this->activated);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
parent::describeState($w);
|
||||
$w->bool($this->activated);
|
||||
}
|
||||
|
||||
public function isActivated() : bool{ return $this->activated; }
|
||||
|
@ -23,44 +23,70 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Hoe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\Potion;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\SplashPotion;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\ItemUseOnBlockSound;
|
||||
use pocketmine\world\sound\WaterSplashSound;
|
||||
|
||||
class Dirt extends Opaque{
|
||||
protected bool $coarse = false;
|
||||
protected DirtType $dirtType;
|
||||
|
||||
public function getRequiredTypeDataBits() : int{ return 1; }
|
||||
|
||||
protected function decodeType(RuntimeDataReader $r) : void{
|
||||
$this->coarse = $r->readBool();
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->dirtType = DirtType::NORMAL();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function encodeType(RuntimeDataWriter $w) : void{
|
||||
$w->writeBool($this->coarse);
|
||||
public function getRequiredTypeDataBits() : int{ return 2; }
|
||||
|
||||
protected function describeType(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->dirtType($this->dirtType);
|
||||
}
|
||||
|
||||
public function isCoarse() : bool{ return $this->coarse; }
|
||||
public function getDirtType() : DirtType{ return $this->dirtType; }
|
||||
|
||||
/** @return $this */
|
||||
public function setCoarse(bool $coarse) : self{
|
||||
$this->coarse = $coarse;
|
||||
public function setDirtType(DirtType $dirtType) : self{
|
||||
$this->dirtType = $dirtType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if($face === Facing::UP && $item instanceof Hoe){
|
||||
$item->applyDamage(1);
|
||||
|
||||
$newBlock = $this->coarse ? VanillaBlocks::DIRT() : VanillaBlocks::FARMLAND();
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
|
||||
$this->position->getWorld()->setBlock($this->position, $newBlock);
|
||||
$newBlock = $this->dirtType->equals(DirtType::NORMAL()) ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
|
||||
$center = $this->position->add(0.5, 0.5, 0.5);
|
||||
$world->addSound($center, new ItemUseOnBlockSound($newBlock));
|
||||
$world->setBlock($this->position, $newBlock);
|
||||
if($this->dirtType->equals(DirtType::ROOTED())){
|
||||
$world->dropItem($center, VanillaBlocks::HANGING_ROOTS()->asItem());
|
||||
}
|
||||
|
||||
return true;
|
||||
}elseif($this->dirtType->equals(DirtType::ROOTED()) && $item instanceof Fertilizer){
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
||||
return true;
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
$world->setBlock($down->position, VanillaBlocks::HANGING_ROOTS());
|
||||
//TODO: bonemeal particles, growth sounds
|
||||
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType()->equals(PotionType::WATER())){
|
||||
$item->pop();
|
||||
$world->setBlock($this->position, VanillaBlocks::MUD());
|
||||
$world->addSound($this->position, new WaterSplashSound(0.5));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -44,21 +44,14 @@ class Door extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 5; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->facing = $r->readHorizontalFacing();
|
||||
$this->top = $r->readBool();
|
||||
$this->hingeRight = $r->readBool();
|
||||
$this->open = $r->readBool();
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->bool($this->top);
|
||||
$w->bool($this->hingeRight);
|
||||
$w->bool($this->open);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalFacing($this->facing);
|
||||
$w->writeBool($this->top);
|
||||
$w->writeBool($this->hingeRight);
|
||||
$w->writeBool($this->open);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
|
||||
//copy door properties from other half
|
||||
@ -71,6 +64,8 @@ class Door extends Transparent{
|
||||
$this->hingeRight = $other->hingeRight;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isTop() : bool{ return $this->top; }
|
||||
@ -148,17 +143,18 @@ class Door extends Transparent{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
$this->open = !$this->open;
|
||||
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
$world = $this->position->getWorld();
|
||||
if($other instanceof Door && $other->isSameType($this)){
|
||||
$other->open = $this->open;
|
||||
$this->position->getWorld()->setBlock($other->position, $other);
|
||||
$world->setBlock($other->position, $other);
|
||||
}
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$this->position->getWorld()->addSound($this->position, new DoorSound());
|
||||
$world->setBlock($this->position, $this);
|
||||
$world->addSound($this->position, new DoorSound());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -36,12 +36,8 @@ class DoublePlant extends Flowable{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 1; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->top = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeBool($this->top);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->bool($this->top);
|
||||
}
|
||||
|
||||
public function isTop() : bool{ return $this->top; }
|
||||
@ -53,8 +49,8 @@ class DoublePlant extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$id = $blockReplace->getSide(Facing::DOWN)->getTypeId();
|
||||
if(($id === BlockTypeIds::GRASS || $id === BlockTypeIds::DIRT) && $blockReplace->getSide(Facing::UP)->canBeReplaced()){
|
||||
$down = $blockReplace->getSide(Facing::DOWN);
|
||||
if($down->hasTypeTag(BlockTypeTags::DIRT) && $blockReplace->getSide(Facing::UP)->canBeReplaced()){
|
||||
$top = clone $this;
|
||||
$top->top = true;
|
||||
$tx->addBlock($blockReplace->position, $this)->addBlock($blockReplace->position->getSide(Facing::UP), $top);
|
||||
@ -78,7 +74,8 @@ class DoublePlant extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->isValidHalfPlant() || (!$this->top && $this->getSide(Facing::DOWN)->isTransparent())){
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$this->isValidHalfPlant() || (!$this->top && !$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
@ -44,11 +44,7 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function tickFalling() : ?Block{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
$this->teleport();
|
||||
return true;
|
||||
}
|
||||
@ -62,8 +58,9 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function teleport() : void{
|
||||
$world = $this->position->getWorld();
|
||||
for($tries = 0; $tries < 16; ++$tries){
|
||||
$block = $this->position->getWorld()->getBlockAt(
|
||||
$block = $world->getBlockAt(
|
||||
$this->position->x + mt_rand(-16, 16),
|
||||
max(World::Y_MIN, min(World::Y_MAX - 1, $this->position->y + mt_rand(-8, 8))),
|
||||
$this->position->z + mt_rand(-16, 16)
|
||||
@ -76,9 +73,9 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
$blockPos = $ev->getTo();
|
||||
$this->position->getWorld()->addParticle($this->position, new DragonEggTeleportParticle($this->position->x - $blockPos->x, $this->position->y - $blockPos->y, $this->position->z - $blockPos->z));
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
|
||||
$this->position->getWorld()->setBlock($blockPos, $this);
|
||||
$world->addParticle($this->position, new DragonEggTeleportParticle($this->position->x - $blockPos->x, $this->position->y - $blockPos->y, $this->position->z - $blockPos->z));
|
||||
$world->setBlock($this->position, VanillaBlocks::AIR());
|
||||
$world->setBlock($blockPos, $this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
42
src/block/DyedCandle.php
Normal file
42
src/block/DyedCandle.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?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\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
class DyedCandle extends Candle{
|
||||
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{
|
||||
$result = parent::getCandleIfCompatibleType($block);
|
||||
//different coloured candles can't be combined in the same block
|
||||
return $result instanceof DyedCandle && $result->color->equals($this->color) ? $result : null;
|
||||
}
|
||||
}
|
@ -29,8 +29,8 @@ use pocketmine\block\utils\DyeColor;
|
||||
final class DyedShulkerBox extends ShulkerBox{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -27,12 +27,12 @@ class Element extends Opaque{
|
||||
public function __construct(
|
||||
BlockIdentifier $idInfo,
|
||||
string $name,
|
||||
BlockBreakInfo $breakInfo,
|
||||
BlockTypeInfo $typeInfo,
|
||||
private string $symbol,
|
||||
private int $atomicWeight,
|
||||
private int $group
|
||||
){
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function getAtomicWeight() : int{
|
||||
|
@ -44,7 +44,7 @@ class EnchantingTable extends Transparent{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
//TODO lock
|
||||
|
||||
|
@ -38,14 +38,9 @@ class EndPortalFrame extends Opaque{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 3; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->facing = $r->readHorizontalFacing();
|
||||
$this->eye = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalFacing($this->facing);
|
||||
$w->writeBool($this->eye);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->bool($this->eye);
|
||||
}
|
||||
|
||||
public function hasEye() : bool{ return $this->eye; }
|
||||
|
@ -54,7 +54,7 @@ class EnderChest extends Transparent{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player instanceof Player){
|
||||
$enderChest = $this->position->getWorld()->getTile($this->position);
|
||||
if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){
|
||||
|
@ -40,12 +40,8 @@ class Farmland extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 3; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->wetness = $r->readBoundedInt(3, 0, self::MAX_WETNESS);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(3, $this->wetness);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_WETNESS, $this->wetness);
|
||||
}
|
||||
|
||||
public function getWetness() : int{ return $this->wetness; }
|
||||
@ -77,16 +73,17 @@ class Farmland extends Transparent{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canHydrate()){
|
||||
if($this->wetness > 0){
|
||||
$this->wetness--;
|
||||
$this->position->getWorld()->setBlock($this->position, $this, false);
|
||||
$world->setBlock($this->position, $this, false);
|
||||
}else{
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
|
||||
$world->setBlock($this->position, VanillaBlocks::DIRT());
|
||||
}
|
||||
}elseif($this->wetness < self::MAX_WETNESS){
|
||||
$this->wetness = self::MAX_WETNESS;
|
||||
$this->position->getWorld()->setBlock($this->position, $this, false);
|
||||
$world->setBlock($this->position, $this, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ class Fence extends Transparent{
|
||||
return 0.25;
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
@ -48,6 +48,8 @@ class Fence extends Transparent{
|
||||
unset($this->connections[$facing]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,16 +45,10 @@ class FenceGate extends Transparent{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->facing = $r->readHorizontalFacing();
|
||||
$this->open = $r->readBool();
|
||||
$this->inWall = $r->readBool();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalFacing($this->facing);
|
||||
$w->writeBool($this->open);
|
||||
$w->writeBool($this->inWall);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->bool($this->open);
|
||||
$w->bool($this->inWall);
|
||||
}
|
||||
|
||||
public function isOpen() : bool{ return $this->open; }
|
||||
@ -109,7 +103,7 @@ class FenceGate extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
$this->open = !$this->open;
|
||||
if($this->open && $player !== null){
|
||||
$playerFacing = $player->getHorizontalFacing();
|
||||
@ -118,13 +112,14 @@ class FenceGate extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
$this->position->getWorld()->addSound($this->position, new DoorSound());
|
||||
$world = $this->position->getWorld();
|
||||
$world->setBlock($this->position, $this);
|
||||
$world->addSound($this->position, new DoorSound());
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
return 300;
|
||||
return $this->woodType->isFlammable() ? 300 : 0;
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
|
132
src/block/FillableCauldron.php
Normal file
132
src/block/FillableCauldron.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?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\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function min;
|
||||
|
||||
abstract class FillableCauldron extends Transparent{
|
||||
public const MIN_FILL_LEVEL = 1;
|
||||
public const MAX_FILL_LEVEL = 6;
|
||||
|
||||
private int $fillLevel = self::MIN_FILL_LEVEL;
|
||||
|
||||
public function getRequiredStateDataBits() : int{
|
||||
return 3;
|
||||
}
|
||||
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(3, self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
|
||||
}
|
||||
|
||||
public function getFillLevel() : int{ return $this->fillLevel; }
|
||||
|
||||
/** @return $this */
|
||||
public function setFillLevel(int $fillLevel) : self{
|
||||
if($fillLevel < self::MIN_FILL_LEVEL || $fillLevel > self::MAX_FILL_LEVEL){
|
||||
throw new \InvalidArgumentException("Fill level must be in range " . self::MIN_FILL_LEVEL . " ... " . self::MAX_FILL_LEVEL);
|
||||
}
|
||||
$this->fillLevel = $fillLevel;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$result = [
|
||||
AxisAlignedBB::one()->trim(Facing::UP, 11 / 16) //bottom of the cauldron
|
||||
];
|
||||
|
||||
foreach(Facing::HORIZONTAL as $f){ //add the frame parts around the bowl
|
||||
$result[] = AxisAlignedBB::one()->trim($f, 14 / 16);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
|
||||
}
|
||||
|
||||
protected function withFillLevel(int $fillLevel) : Block{
|
||||
return $fillLevel === 0 ? VanillaBlocks::CAULDRON() : $this->setFillLevel(min(self::MAX_FILL_LEVEL, $fillLevel));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] &$returnedItems
|
||||
*/
|
||||
protected function addFillLevels(int $amount, Item $usedItem, Item $returnedItem, array &$returnedItems) : void{
|
||||
if($this->fillLevel >= self::MAX_FILL_LEVEL){
|
||||
return;
|
||||
}
|
||||
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->fillLevel + $amount));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), $this->getFillSound());
|
||||
|
||||
$usedItem->pop();
|
||||
$returnedItems[] = $returnedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] &$returnedItems
|
||||
*/
|
||||
protected function removeFillLevels(int $amount, Item $usedItem, Item $returnedItem, array &$returnedItems) : void{
|
||||
if($this->fillLevel < $amount){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->fillLevel - $amount));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), $this->getEmptySound());
|
||||
|
||||
$usedItem->pop();
|
||||
$returnedItems[] = $returnedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sound played when adding levels to the cauldron liquid.
|
||||
*/
|
||||
abstract public function getFillSound() : Sound;
|
||||
|
||||
/**
|
||||
* Returns the sound played when removing levels from the cauldron liquid.
|
||||
*/
|
||||
abstract public function getEmptySound() : Sound;
|
||||
|
||||
/**
|
||||
* @param Item[] &$returnedItems
|
||||
*/
|
||||
protected function mix(Item $usedItem, Item $returnedItem, array &$returnedItems) : void{
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::CAULDRON());
|
||||
//TODO: sounds and particles
|
||||
|
||||
$usedItem->pop();
|
||||
$returnedItems[] = $returnedItem;
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
return VanillaBlocks::CAULDRON()->asItem();
|
||||
}
|
||||
}
|
@ -42,12 +42,8 @@ class Fire extends BaseFire{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->age = $r->readBoundedInt(4, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeInt(4, $this->age);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
@ -66,13 +62,14 @@ class Fire extends BaseFire{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(SoulFire::canBeSupportedBy($down)){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::SOUL_FIRE());
|
||||
$world->setBlock($this->position, VanillaBlocks::SOUL_FIRE());
|
||||
}elseif($down->isTransparent() && !$this->hasAdjacentFlammableBlocks()){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
|
||||
$world->setBlock($this->position, VanillaBlocks::AIR());
|
||||
}else{
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
$world->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,11 +102,12 @@ class Fire extends BaseFire{
|
||||
}
|
||||
}
|
||||
|
||||
$world = $this->position->getWorld();
|
||||
if($result !== null){
|
||||
$this->position->getWorld()->setBlock($this->position, $result);
|
||||
$world->setBlock($this->position, $result);
|
||||
}
|
||||
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
$world->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
|
||||
if($canSpread){
|
||||
$this->burnBlocksAround();
|
||||
@ -150,7 +148,8 @@ class Fire extends BaseFire{
|
||||
if(!$ev->isCancelled()){
|
||||
$block->onIncinerate();
|
||||
|
||||
if($this->position->getWorld()->getBlock($block->getPosition())->isSameState($block)){
|
||||
$world = $this->position->getWorld();
|
||||
if($world->getBlock($block->getPosition())->isSameState($block)){
|
||||
$spreadedFire = false;
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
@ -158,7 +157,7 @@ class Fire extends BaseFire{
|
||||
$spreadedFire = $this->spreadBlock($block, $fire);
|
||||
}
|
||||
if(!$spreadedFire){
|
||||
$this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR());
|
||||
$world->setBlock($block->position, VanillaBlocks::AIR());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,12 +40,8 @@ final class FloorCoralFan extends BaseCoral{
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return parent::getRequiredStateDataBits() + 1; }
|
||||
|
||||
protected function decodeState(RuntimeDataReader $r) : void{
|
||||
$this->axis = $r->readHorizontalAxis();
|
||||
}
|
||||
|
||||
protected function encodeState(RuntimeDataWriter $w) : void{
|
||||
$w->writeHorizontalAxis($this->axis);
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->horizontalAxis($this->axis);
|
||||
}
|
||||
|
||||
public function getAxis() : int{ return $this->axis; }
|
||||
@ -74,6 +70,9 @@ final class FloorCoralFan extends BaseCoral{
|
||||
$this->axis = Axis::Z;
|
||||
}
|
||||
}
|
||||
|
||||
$this->dead = !$this->isCoveredWithWater();
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,10 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
|
||||
/**
|
||||
* "Flowable" blocks are destroyed if water flows into the same space as the block. These blocks usually don't have any
|
||||
* collision boxes, and can't provide support for other blocks.
|
||||
*/
|
||||
abstract class Flowable extends Transparent{
|
||||
|
||||
public function canBeFlowedInto() : bool{
|
||||
|
@ -33,7 +33,7 @@ class Flower extends Flowable{
|
||||
|
||||
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::GRASS || $down->getTypeId() === BlockTypeIds::DIRT || $down->getTypeId() === BlockTypeIds::FARMLAND){
|
||||
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
@ -41,7 +41,8 @@ class Flower extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user