Compare commits

..

221 Commits

Author SHA1 Message Date
15645759e9 Release 4.9.1 2022-10-11 22:47:54 +01:00
7df2719fce Update Composer dependencies 2022-10-11 21:57:22 +01:00
10b8dcfdd1 Bump phpstan/phpstan from 1.8.6 to 1.8.8 (#5324)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.8.6 to 1.8.8.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.9.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.8.6...1.8.8)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-07 11:43:39 +01:00
c1fbac412e event: ensure that modifications to items expected to be readonly have no effect
this isn't a very glorious fix, but it's the best I have for now.
2022-10-07 11:33:14 +01:00
cd4bb91676 Living: alter eye height so the player doesn't drown on the surface of water when swimming
fixes #4989
2022-09-29 00:15:07 +01:00
2be527060f Sign: Fixed desync of colour and glowing state when using dye on signs
fixes #4932
2022-09-28 23:34:08 +01:00
6f68c6d8a0 Melon: extend Solid instead of Transparent, fixes #5050 2022-09-28 23:07:53 +01:00
ac16378410 Silence pre-spawn PlayerAuthInputPacket debug spam 2022-09-28 21:58:23 +01:00
1f9dfa77bf PreSpawnPacketHandler: emit a separate debug message for sending creative data 2022-09-28 21:58:23 +01:00
fc56c041f3 Correct knockback from explosions (#5161) 2022-09-28 21:09:07 +01:00
22486dd75e Mushroom: check the light for placement, unless placed on mycelium or podzol (#5054)
The previous behaviour was inconsistent with vanilla.
2022-09-28 18:41:23 +01:00
37ec1193ea Update PHPStan baselines 2022-09-28 18:34:01 +01:00
def2f8c145 InventoryManager: ensure the windowID is valid before attempting to remove any window
this is currently a harmless bug, since remove() isn't currently doing any heavy lifting.
2022-09-28 01:01:42 +01:00
ed7c95549d PreSpawnPacketHandler: add a bunch of debug messages
this is useful for observing timings during first spawn, so that performance issues can be more easily spotted.
2022-09-27 21:08:31 +01:00
4650a3bb22 CONTRIBUTING: added a table of what types of changes are accepted by what branches 2022-09-27 18:13:59 +01:00
5e5661de75 Play burp sound when consuming a FoodSource (#5158) 2022-09-27 17:21:55 +01:00
e2f1b10165 Bump phpunit/phpunit from 9.5.24 to 9.5.25 (#5308)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.24 to 9.5.25.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.24...9.5.25)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-27 17:18:58 +01:00
455f9fa92e Bump tests/plugins/DevTools from bd0fa04 to 95921c6 (#5307)
Bumps [tests/plugins/DevTools](https://github.com/pmmp/DevTools) from `bd0fa04` to `95921c6`.
- [Release notes](https://github.com/pmmp/DevTools/releases)
- [Commits](bd0fa048da...95921c6d87)

---
updated-dependencies:
- dependency-name: tests/plugins/DevTools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-27 17:18:47 +01:00
dec188d4ad TaskHandler: mark some public methods as @internal (#5310)
closes #5309
2022-09-27 17:17:29 +01:00
2db3498891 Updated DevTools submodule to pmmp/DevTools@bd0fa048da 2022-09-24 21:26:42 +01:00
f448b2e685 Block: Improve documentation for a whole bunch of methods 2022-09-24 18:06:46 +01:00
6a0c54f850 Block: Relocate and document addVelocityToEntity()
maybe we should consider merging this with onEntityInside(), since they are both called for the same reasons? ...
2022-09-24 17:32:02 +01:00
77a18d0aea Block: add documentation for getFrictionFactor()
has no one ever questioned the fact that a higher _friction_ factor _reduces_ the block's friction???
2022-09-24 17:05:38 +01:00
140a809c40 Block: improve documentation of hasEntityCollision() and onEntityInside() 2022-09-24 17:04:42 +01:00
cb7c136035 Added documentation for some base Block classes 2022-09-24 16:54:21 +01:00
3f7d8a3777 Bump phpstan/phpstan-strict-rules from 1.4.3 to 1.4.4 (#5300)
Bumps [phpstan/phpstan-strict-rules](https://github.com/phpstan/phpstan-strict-rules) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/phpstan/phpstan-strict-rules/releases)
- [Commits](https://github.com/phpstan/phpstan-strict-rules/compare/1.4.3...1.4.4)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-strict-rules
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-24 13:45:39 +01:00
Ali
3c55db531d HealthBoostEffect: Ensure that current health is within limits after reducing max health on removal(#5303) 2022-09-24 13:45:12 +01:00
93d4475111 Bump phpstan/phpstan from 1.8.5 to 1.8.6 (#5299)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.8.5 to 1.8.6.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.8.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.8.5...1.8.6)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-23 11:42:50 +01:00
7804172846 Player: added API documentation for some functions 2022-09-21 14:46:04 +01:00
481bda8cd5 Bump build/php from cf79c01 to 50062b5 (#5293)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `cf79c01` to `50062b5`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](cf79c01722...50062b5861)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-21 12:43:06 +01:00
d1c75da14b Player: lock flight state in spectator mode
players should not be able to stop flying in spectator mode
2022-09-20 21:53:51 +01:00
66e70e5b0e 4.9.1 is next 2022-09-20 20:09:38 +01:00
785dc71256 Release 4.9.0 2022-09-20 20:09:38 +01:00
d459afaa54 fix CS 2022-09-20 20:00:40 +01:00
db586233da Changes for 1.19.30 support 2022-09-20 19:50:27 +01:00
23e98a30f5 ItemEntity: don't ignore parent's savable state 2022-09-20 14:50:35 +01:00
5bc7ca6569 ItemEntity: disable saving if the contained item is air or has a zero count 2022-09-20 14:45:10 +01:00
f39d2a9be3 bootstrap: update JIT warning 2022-09-20 14:43:05 +01:00
47d98af6ac Bump ramsey/uuid from 4.5.0 to 4.5.1 (#5292)
Bumps [ramsey/uuid](https://github.com/ramsey/uuid) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/ramsey/uuid/releases)
- [Changelog](https://github.com/ramsey/uuid/blob/4.x/CHANGELOG.md)
- [Commits](https://github.com/ramsey/uuid/compare/4.5.0...4.5.1)

---
updated-dependencies:
- dependency-name: ramsey/uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-16 12:53:46 +01:00
82ba7903c8 Fixed wrong key being used for entity type ID in save data (#5288)
closes #5260
2022-09-15 13:40:48 +01:00
112974758e Updated PHPStan to 1.8.5 2022-09-15 12:13:50 +01:00
313850eec4 Updated composer dependencies 2022-09-15 12:08:36 +01:00
a82923acb4 Updated Actions PHP versions 2022-09-15 12:03:36 +01:00
67887afd6b Updated php-cs-fixer version for Actions 2022-09-15 12:03:04 +01:00
3d03bb1301 Fix CS 2022-09-15 12:01:26 +01:00
c063198b89 Fixed incorrect array key type in BrewingStandTest 2022-09-02 20:01:52 +01:00
f3ca6de1eb shut 2022-09-02 20:00:52 +01:00
88eafdd614 Improve type info for RegistryTrait::getAll() and its users 2022-09-02 19:57:22 +01:00
6dd5fec4ea ExperienceManager: remove superfluous doc comment 2022-09-02 19:38:23 +01:00
6866c86d39 BaseInventory: fix CS 2022-09-02 19:36:45 +01:00
a735a69870 BaseInventory: improve type info available to setContents() and internalSetContents() 2022-09-02 19:36:08 +01:00
a0ea74c08f Inventory: Improve quality of type info of arrays 2022-09-02 19:34:12 +01:00
ca4b8a5827 World: remove local static recursion guard variable, closes #3125 2022-09-02 19:24:09 +01:00
f88c4d9a8c Remove more unnecessary local static variable usages
these are never mutated. Local constants would be better, if we had those.
2022-09-02 19:19:04 +01:00
66cd156d80 Utils: use static property for core count cache, instead of local static variable 2022-09-02 19:18:01 +01:00
222049927a Language: fixed bogus callable reference in array_map
for some reason phpstan only reports this under checkImplicitMixed.
2022-09-02 19:15:05 +01:00
d72e947d15 BlockFactory: avoid unnecessary local static variable usage
phpstan treats these as always mixed, because it can't be sure what their types will be.
2022-09-02 19:14:35 +01:00
770cca2efa Server: harden response handling for crash report submission
this eliminates some checkImplicitMixed errors in phpstan.
2022-09-02 19:13:54 +01:00
033dac3d16 Server: be explicit about the player promise resolver type
since there's no way for phpstan to infer the type of this, it becomes implicit mixed, which can conceal bugs.
2022-09-02 19:13:16 +01:00
1ee02d7e02 Do not install pocketmine/locale-data 2.8.9 (it's incorrectly versioned)
the changes made in 2.8.9 should have been released as a new minor version, not a patch.
2022-09-02 18:43:39 +01:00
85678aa356 phpstan 1.8.3 2022-09-02 18:28:33 +01:00
1d253bc8c2 Utils: remove 32-bit specific code from javaStringHash()
this was necessary in the days of 32-bit, but for 64-bit, the 0xffffffff mask is sufficient and produces the exact same result.
2022-09-02 18:23:49 +01:00
973a56ab28 Update composer dependencies 2022-09-02 18:02:16 +01:00
9e0b4621be Fixed languages-not-found bug (#5272)
* Fixed languages-not-found bug

* Update Language.php

Co-authored-by: Dylan T <odigiman@gmail.com>
2022-09-02 03:51:31 +01:00
b7a15b6e01 Bump phpunit/phpunit from 9.5.23 to 9.5.24 (#5266)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.23 to 9.5.24.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.23...9.5.24)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 02:11:18 +01:00
456439566b Language: treat empty translation files the same as missing ones 2022-08-31 01:53:15 +01:00
fb25e05416 InventoryManager: fixed current window getting removed in race conditions with close window ACK
this could be observed by pressing E and immediately clicking a chest, which, if timed correctly, would lead to the chest lid closing, but the inventory being opened anyway.
2022-08-27 17:26:43 +01:00
78b5be8dd0 4.8.2 is next 2022-08-26 19:16:39 +01:00
0a92e91a30 Release 4.8.1 2022-08-26 19:16:39 +01:00
b3a13a2f21 in future, do not allow Copilot to write changelogs ... 2022-08-26 19:13:13 +01:00
08b9495bce DyeColorIdMap: fixed uninitialized offset error on invalid dye colours 2022-08-26 18:58:00 +01:00
5779622235 Bump phpunit/phpunit from 9.5.22 to 9.5.23 (#5252)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.22 to 9.5.23.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.22...9.5.23)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-25 19:29:25 +01:00
7f175b47e6 Fix CS 2022-08-25 18:19:22 +01:00
0e73ffe555 CrashDump: Added JIT mode to data
this is necessary for identifying JIT-specific bugs, which, unfortunately, are very common.
2022-08-25 17:39:40 +01:00
1ffd38b37b Utils: fixed currentTrace() when xdebug is loaded, but not in develop mode
this is really dumb... why does it register the functions at all if they aren't usable ???
2022-08-25 16:56:26 +01:00
bd13f39156 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2022-08-24 20:04:15 +01:00
0c446c276c 4.8.1 is next 2022-08-24 20:03:57 +01:00
0284e65f60 Release 4.8.0 2022-08-24 20:03:56 +01:00
b0d787b3d3 Update BedrockProtocol for 1.19.21 2022-08-24 19:54:41 +01:00
65e3ed43d5 Bump build/php from e90ff50 to cf79c01 (#5248)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `e90ff50` to `cf79c01`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](e90ff50310...cf79c01722)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-24 18:47:04 +01:00
75eba9c9ed 4.7.4 is next 2022-08-22 19:28:47 +01:00
b5a049d1fe Release 4.7.3 2022-08-22 19:28:43 +01:00
bd9fcffe62 Bump build/php from f292501 to e90ff50 (#5242)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `f292501` to `e90ff50`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](f292501a70...e90ff50310)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:42:17 +01:00
feffbc2c5b Bump phpunit/phpunit from 9.5.21 to 9.5.22 (#5243)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.21 to 9.5.22.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.21...9.5.22)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:33:16 +01:00
53b51c99b4 Bump pocketmine/locale-data from 2.8.6 to 2.8.7 (#5244)
Bumps [pocketmine/locale-data](https://github.com/pmmp/Language) from 2.8.6 to 2.8.7.
- [Release notes](https://github.com/pmmp/Language/releases)
- [Commits](https://github.com/pmmp/Language/compare/2.8.6...2.8.7)

---
updated-dependencies:
- dependency-name: pocketmine/locale-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:32:50 +01:00
5cb77c8365 GiveCommand: fix CS 2022-08-22 00:55:17 +01:00
bf8befc40b Remove dead comment on GiveCommand (#5241) 2022-08-22 00:49:22 +01:00
f75ca312cc Worker: Unstack tasks in a synchronized block
this prevents any tasks still left in the queue on shutdown getting pulled out by the worker when we're attempting to shut it down.
This led to various race conditions, most notably weird cases where PopulationTask would inexplicably find its expected generator state had not been correctly set up.
2022-08-21 21:57:11 +01:00
d144832928 GiveCommand: limit max amount in line with vanilla 2022-08-21 21:19:16 +01:00
709a869045 Vines can now only be placed on full cube blocks (#5053)
fixes #2673
2022-08-21 21:04:24 +01:00
ac056044ce Updated PHPStan baseline 2022-08-21 20:46:38 +01:00
fc8434308b SignText: changed misleading documentation
this looks like a leftover from the days when sign text was handled by the tile directly
2022-08-21 20:45:23 +01:00
5426b41447 InventoryTransaction: prevent client-authoritative item overstacking
this cheat is often used to carry more items in the inventory, wear multiple pieces of armour in one slot, and more.
2022-08-21 20:35:23 +01:00
af2babec23 GiveCommand: do not accept negative amounts 2022-08-21 20:28:39 +01:00
717ab1989a Update setup-php-action to pmmp/setup-php-action@82a44d659b 2022-08-21 18:14:07 +01:00
83db186b6a Updated setup-php-action to pmmp/setup-php-action@e128aee02f 2022-08-20 18:53:08 +01:00
6a4e5aba8b Update setup-php-action to pmmp/setup-php-action@330b4c2940 2022-08-20 18:03:30 +01:00
c13170a00b Avoid implicit integer cast in Normal::pickBiome()
this throws deprecation warnings on PHP 8.1.
2022-08-20 17:16:38 +01:00
98778052bb actions: start building on 8.1 2022-08-20 16:32:36 +01:00
e86e8254a8 Workaround PHPStan "feature" phpstan/phpstan#7701 2022-08-20 16:29:26 +01:00
1b852ac290 bootstrap: do not complain about xdebug if mode is 'off'
if xdebug.mode=off, the performance impact is the same as if xdebug wasn't loaded.
2022-08-19 16:45:40 +01:00
10b799fadb Bump shivammathur/setup-php from 2.21.1 to 2.21.2 (#5238)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.21.1 to 2.21.2.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.21.1...2.21.2)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 15:51:44 +01:00
bc5008334a Bump pocketmine/locale-data from 2.8.3 to 2.8.6 (#5239)
Bumps [pocketmine/locale-data](https://github.com/pmmp/Language) from 2.8.3 to 2.8.6.
- [Release notes](https://github.com/pmmp/Language/releases)
- [Commits](https://github.com/pmmp/Language/compare/2.8.3...2.8.6)

---
updated-dependencies:
- dependency-name: pocketmine/locale-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 15:51:04 +01:00
575dd47db7 4.7.3 is next 2022-08-16 17:51:26 +01:00
e4a5defabb Release 4.7.2 2022-08-16 17:51:26 +01:00
c9626c610b Skin: Correctly handle errors produced by commented JSON decoder 2022-08-16 17:35:23 +01:00
8fb7fff6b9 Update SECURITY.md 2022-08-16 17:22:22 +01:00
5c8d8ff61f Update SECURITY.md 2022-08-16 17:04:25 +01:00
99b55f7427 actions: use newer php-cs-fixer 2022-08-15 17:26:42 +01:00
dce8bd6d21 CS: Standardize new with braces 2022-08-15 17:16:23 +01:00
8fa81242d6 Sugarcane: fixed support conditions (#5052) 2022-08-15 17:08:26 +01:00
2f4a9469b6 Player: spectator shouldn't able to pick blocks they don't have (#5111)
Jury is out on whether they should be able to pick blocks at all, or be considered to have infinite resources, but this solution has been used in a few other places already anyway, so it can be cleaned up another time.
2022-08-15 16:48:37 +01:00
c5b2488fc1 oh come on... 2022-08-14 20:02:07 +01:00
d62df585f2 4.7.2 is next 2022-08-14 19:56:00 +01:00
19d7c2b552 Release 4.7.1 2022-08-14 19:55:56 +01:00
036e06e889 Revert "Workaround items in blockentity NBT not being processed correctly in 1.19.10"
This reverts commit 2b61c025c2.
2022-08-14 17:25:55 +01:00
9343a0b800 Added build log link to Discord release embed 2022-08-14 17:20:01 +01:00
14b4644b03 Added build_log_url to build_info.json 2022-08-14 17:20:01 +01:00
464b65b25c Bump docker/build-push-action from 3.1.0 to 3.1.1 (#5213)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-12 21:11:11 +01:00
15586ed80e Fix CS 2022-08-12 21:09:15 +01:00
0f8ad8ecf7 Update permission doc output format 2022-08-12 20:47:38 +01:00
82b9afef77 Allow generating RST permission summaries, to be used on doc.pmmp.io 2022-08-12 18:00:52 +01:00
2fc84f6c67 ItemFactory: treat durables with negative meta as unknown items
fixes #5117
2022-08-12 17:24:43 +01:00
566f5935a3 CraftingManagerFromDataHelper: do not register recipes with unknown outputs
fixes #5093

we don't need to check the inputs, since unknown input items shouldn't be obtainable anyway.
2022-08-12 17:19:47 +01:00
44e4dabf6e Fixed Turtle Master potions giving no effects 2022-08-12 17:05:08 +01:00
8acc535218 ffs 2022-08-09 19:27:54 +01:00
e9a1cb7ce5 4.7.1 is next 2022-08-09 19:24:02 +01:00
a21419d120 Release 4.7.0 2022-08-09 19:24:01 +01:00
c2b599166c Added new shiny webhook for Discord release notifications 2022-08-09 19:21:36 +01:00
df7a1fcba6 Changes for 1.19.20 2022-08-09 19:06:05 +01:00
d77a95e4af actions/draft-release: bake the full changelog blob URL into the release notes, to ensure it works properly in emails and embeds 2022-08-06 15:49:04 +01:00
5c72807b16 Bump shivammathur/setup-php from 2.21.0 to 2.21.1 (#5199)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.21.0 to 2.21.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.21.0...2.21.1)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-06 15:39:36 +01:00
5c6927e16c 4.6.3 is next 2022-08-06 15:35:47 +01:00
9abbb85a93 Release 4.6.2 2022-08-06 15:35:47 +01:00
554182b2cb Update composer dependencies 2022-08-06 15:27:11 +01:00
d669a6f0c7 ReversePriorityQueue: add ReturnTypeWillChange attribute
it's doubtful any plugin dev is extending this, but nonetheless, we can't change it in a patch.
2022-07-27 03:51:06 +01:00
5d9f783037 InGamePacketHandler: do not update player rotation if it didn't change
setRotation() does an alarmingly large amount of work...
2022-07-24 21:07:35 +01:00
01ca14c314 InGamePacketHandler: avoid processing movement if position is unchanged since last tick 2022-07-24 21:00:12 +01:00
608c6ed6db Improved suboptimal code in Player::handleMovement() 2022-07-24 20:51:28 +01:00
c26631d06d InGamePacketHandler: avoid useless object allocations when forceMoveSync=false (99.9% of the time) 2022-07-24 20:44:27 +01:00
b75bc61a64 InGamePacketHandler: don't bother checking for flag changes if the flag fields are identical
we don't need to check this on a bit by bit level if the integers are the same.

this saves 2-3 microseconds per packet on my machine, which doesn't sound like much, but it adds up when there are lots of players.
2022-07-24 20:35:49 +01:00
3724479be3 InGamePacketHandler: improve performance of input flag resolving 2022-07-24 20:33:35 +01:00
eb916fe43d Use a falling block entity to improve client side performance of FloatingTextParticle (#4714)
Performance tests show that this has a considerable client-side performance advantage over using players. In my local tests, using 1000 floating texts in a 10x10x10 area, I observed an FPS increase from 1.5 to 8.0.
2022-07-24 18:22:21 +01:00
5e3b3a0700 Fix assert spam on debug clients 2022-07-24 17:51:02 +01:00
e10a624444 4.6.2 is next 2022-07-22 19:35:10 +01:00
b20e04539d Release 4.6.1 2022-07-22 19:34:57 +01:00
4852f8029a AsyncTask: update documentation 2022-07-21 23:26:46 +01:00
c4f85e526b Bump shivammathur/setup-php from 2.20.0 to 2.21.0 (#5181)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.20.0 to 2.21.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.20.0...2.21.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-20 15:19:21 +01:00
6cee428287 Bump docker/build-push-action from 3.0.0 to 3.1.0 (#5182)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-20 15:18:38 +01:00
bcba064d69 Bump build/php from 1110349 to f292501 (#5180)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `1110349` to `f292501`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](11103498ca...f292501a70)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-20 15:16:34 +01:00
86647683bc fix CS again 2022-07-19 20:35:34 +01:00
64f0e58e60 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2022-07-19 20:17:15 +01:00
62f21516d1 build/generate-registry-annotations.php: allow processing a single file
this is useful for automatically invoking the script via a PhpStorm file watcher.
2022-07-19 20:17:07 +01:00
c553f7cf06 build/generate-registry-annotations.php: write to stderr on error 2022-07-19 20:15:44 +01:00
fec89b7803 Lava burns entities for only 8 seconds in Bedrock (#5173) 2022-07-17 20:50:15 +01:00
2b61c025c2 Workaround items in blockentity NBT not being processed correctly in 1.19.10
closes #5154

this hack sends only the bare essential data to create the tiles in LevelChunkPacket,
and then separately sending the full tile data using BlockActorDataPacket afterwards.

This is necessary because the client doesn't handle items correctly in NBT when chunks are sent without using the SubChunkRequest system.
In 4.6 this is observed with incorrect items shown in item frames; in 5.0 it's seen with items simply not showing up at all (difference due to modernization of the serialization format in 5.0).
2022-07-14 21:54:01 +01:00
c7133bc2e6 InGamePacketHandler: don't kick the player out of inventory windows on actor events
this is sent when the player crafts something using an anvil.
2022-07-14 20:36:11 +01:00
baf75089f5 Entity: cancel fire damage for fireproof entities 2022-07-14 19:53:25 +01:00
Ali
705df7d508 EffectManager: remove redundant check (#5153) 2022-07-14 17:56:18 +01:00
75d7adfb2d WitherEffect: fixed incorrect damage interval 2022-07-14 16:05:35 +01:00
9d535e2917 4.6.1 is next 2022-07-13 01:28:42 +01:00
3ccd288afd Release 4.6.0 2022-07-13 01:28:37 +01:00
06655bee78 Updated to 1.19.10 2022-07-13 00:59:49 +01:00
0ad2985247 Update documentation for Item::__construct() 2022-07-06 23:54:29 +01:00
269b6ed16a FallableTrait: fixed logic for block replacement
closes #5126

I don't know why it wasn't done this way to begin with. FallingBlock always used canBeReplaced()...
2022-07-06 16:16:49 +01:00
f031c3c602 Updated NBT dependency 2022-07-06 15:19:19 +01:00
f3e09dd7d5 Bump shivammathur/setup-php from 2.19.1 to 2.20.0 (#5135)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.19.1 to 2.20.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.19.1...2.20.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-06 13:28:46 +01:00
68e704bf97 Bump shivammathur/setup-php from 2.19.0 to 2.19.1 (#5098)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.19.0 to 2.19.1.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.19.0...2.19.1)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-04 15:15:17 +01:00
9898577135 Bump phpstan/phpstan from 1.7.15 to 1.8.0 (#5120)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.7.15 to 1.8.0.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.8.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.7.15...1.8.0)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-30 16:09:03 +01:00
784d602600 Fixed ItemBreakParticle using untranslated internal ID/meta for network data
this caused it to display particles for incorrect items. It may also have been possibly responsible for client crashes.
2022-06-29 14:01:39 +01:00
15c99cfe77 4.5.3 is next 2022-06-29 02:18:50 +01:00
d5fa0a2fc5 Release 4.5.2 2022-06-29 02:18:50 +01:00
0da9260994 Updated composer dependencies 2022-06-29 02:13:32 +01:00
df2d1fd4f9 of course there were two bugs on one line ... 2022-06-24 01:40:26 +01:00
9f65fb5f90 Fixed top-side skulls with no-drop flag set being treated as unknown blocks 2022-06-24 01:31:11 +01:00
caa4b78a3f Update composer dependencies 2022-06-21 20:21:02 +01:00
14352a05bc reword support bot message 2022-06-11 15:54:44 +01:00
bb5b52d998 Player: fix terrain getting redrawn when moving in noclip mode 2022-06-09 13:48:29 +01:00
5e22b70b6d this is a joke ... 2022-06-08 14:56:25 +01:00
02513818a9 4.5.2 is next 2022-06-08 02:50:34 +01:00
d641812c52 Release 4.5.1 2022-06-08 02:50:33 +01:00
a851496293 Updated BedrockProtocol 2022-06-08 02:46:01 +01:00
01a8bce2dd Fix whitespace error in support.yml workflow 2022-06-07 19:54:51 +01:00
becbd562d6 FormattedCommandAlias: fixed incorrect arguments array being passed to the target 2022-06-07 19:47:45 +01:00
82edb20e0c 4.5.1 is next 2022-06-07 17:57:39 +01:00
64a8c462f9 Release 4.5.0 2022-06-07 17:57:39 +01:00
4ec97d0f7a InGamePacketHandler: added missing break
I'm getting sloppy ...
2022-06-07 17:52:59 +01:00
016a80bb70 1.19.0 changes 2022-06-07 17:47:13 +01:00
ce66a400a7 Updated composer dependencies 2022-06-07 17:44:06 +01:00
50776875bb 4.4.3 is next 2022-06-07 15:54:55 +01:00
bcb0e2ff1f Release 4.4.2 2022-06-07 15:54:55 +01:00
1584768c80 PaintingMotive: fixed botched painting fix from 0ea3861d43
I knew I should have used a singleton for this ...
2022-06-07 15:48:20 +01:00
5fd685e07d TypeConverter: fix crash on arbitrary out-of-bounds item IDs
I don't know why I didn't consider this fix necessary when the item meta bug was originally discovered.
2022-06-06 19:29:44 +01:00
6ecfbd1bde FishingRod: make class less useless 2022-06-05 20:20:16 +01:00
b661097c51 changelog: fix mistake
[ci skip]
2022-06-05 17:59:36 +01:00
0771295899 4.4.2 is next 2022-06-05 16:15:38 +01:00
702816458c Release 4.4.1 2022-06-05 16:15:34 +01:00
e040c2b281 InventoryManager: fixed windows not opening when the server removes windows
closes #5094
2022-06-05 16:03:24 +01:00
e12e4e8fb8 StatusCommand: fixed output of global memory limit (#5090)
Fix incorrect "Maximum memory (manager)" output
2022-06-04 17:41:55 +01:00
d15a90899e build fix 2022-06-04 17:38:45 +01:00
237c2866e0 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2022-06-04 17:35:18 +01:00
38d6284671 Use PHP-CS-Fixer to enforce file header presence 2022-06-04 17:34:49 +01:00
7355798e77 Exit with error code if the server failed to start
this ensures that external tools such as start.cmd actually see an error if there is one
2022-06-03 18:34:54 +01:00
4b662d65b3 PluginManager: check graylist before doing any loadability checks
fixes #5087
2022-06-02 16:29:22 +01:00
c87a3b054c composer.json: fix make-devtools command 2022-06-01 20:04:47 +01:00
8b86e43d51 Update support.yml 2022-06-01 15:56:47 +01:00
eade2d2af0 4.4.1 is next 2022-06-01 15:37:48 +01:00
f2299a562f Release 4.4.0 2022-06-01 15:37:48 +01:00
3fcf6372e0 Merge branch 'stable' into next-minor 2022-06-01 15:32:37 +01:00
533cb77c50 Updated dependencies 2022-06-01 15:29:39 +01:00
681a9bb0e1 Bump build/php from 8138c6a to 1110349 (#5075)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `8138c6a` to `1110349`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](8138c6a4a4...11103498ca)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-31 17:18:47 +01:00
6c080dae55 Bump shivammathur/setup-php from 2.18.1 to 2.19.0 (#5076)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.18.1 to 2.19.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.18.1...2.19.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-31 17:18:27 +01:00
670fb4de74 Bump phpstan/phpstan from 1.7.1 to 1.7.4 (#5074)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.7.1 to 1.7.4.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.7.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.7.1...1.7.4)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-30 16:09:30 +01:00
6d7318af43 Liquid: fixed merge regression 2022-05-26 18:48:45 +01:00
97c0d72e28 ItemFactory: use import aliases to reduce code width 2022-05-26 15:55:33 +01:00
4ccae2d1de BlockFactory: use import aliases to reduce code width 2022-05-26 15:50:29 +01:00
b36c6ea13b StringToItemParser: Use import aliases to reduce code width 2022-05-26 15:40:18 +01:00
39b8daeeec Living: fixed a usage of hardcoded numeric ID 2022-05-25 22:23:14 +01:00
c492352d50 changelog: fix typo
[ci skip]
2022-05-25 17:09:29 +01:00
8f1452acd1 4.4.0-BETA2 is next 2022-05-25 16:44:32 +01:00
227f28a6d2 Use VanillaItems::AIR() instead of ItemFactory 2022-05-24 15:47:27 +01:00
1205 changed files with 4768 additions and 3824 deletions

View File

@ -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.1.1
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.1.1
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.1.1
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.1.1
with:
push: true
context: ./pocketmine-mp

View 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);
}

View 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.21.2
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 }}

View File

@ -18,7 +18,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.18.1
uses: shivammathur/setup-php@2.21.2
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
@ -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.

View File

@ -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"
@ -195,10 +195,10 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.18.1
uses: shivammathur/setup-php@2.21.2
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

View File

@ -13,7 +13,14 @@ jobs:
github-token: ${{ github.token }}
support-label: "Support request"
issue-comment: >
Thanks, but this issue tracker is not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
Hi, we only accept **bug reports** on this issue tracker, but this issue looks like a support request.
Instead of creating an issue, try the following:
- Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io)
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)

View File

@ -22,7 +22,8 @@
declare(strict_types=1);
const VERSIONS = [
"8.0"
"8.0",
"8.1"
];
$workflowFile = file_get_contents(__DIR__ . '/main.yml');

View File

@ -42,12 +42,38 @@ return (new PhpCsFixer\Config)
'import_functions' => true,
'import_classes' => null,
],
'header_comment' => [
'comment_type' => 'comment',
'header' => <<<BODY
____ _ _ __ __ _ __ __ ____
| _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
| |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
| __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
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/
BODY,
'location' => 'after_open'
],
'indentation_type' => true,
'logical_operators' => true,
'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,

View File

@ -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.

View File

@ -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.

View File

@ -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";

View File

@ -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;
@ -39,7 +41,8 @@ use function substr;
use const SORT_STRING;
if(count($argv) !== 2){
die("Provide a path to process");
fwrite(STDERR, "Provide a path to process\n");
exit(1);
}
/**
@ -80,30 +83,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 +114,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]);
}

View File

@ -19,7 +19,7 @@ Released 25th May 2022.
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to generate (e.g. due to invalid generator settings)
- Enabling the server whitelist while the server is running (e.g. using `/whitelist on`) will now kick any non-whitelisted players currently on the server (**PR [#4774](https://github.com/pmmp/PocketMine-MP/pull/4774)**).
- Help for commands (`/help <name of command>`) now displays a list of aliases of that command.
- A CRITICIAL log message is now generated if a plugin disables itself when enabling, in case the plugin doesn't emit any error of its own.
- A CRITICAL log message is now generated if a plugin disables itself when enabling, in case the plugin doesn't emit any error of its own.
- The `/give` command now shows the alias used to find the given item in the success message, instead of the item ID/meta.
## Fixes

97
changelogs/4.4.md Normal file
View File

@ -0,0 +1,97 @@
**For Minecraft: Bedrock Edition 1.18.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.4.0
Released 1st June 2022.
## General
- The server will now shut itself down if any of the following errors occur during startup:
- Any plugin fails to load or enable (plugins loaded by other plugins post-startup are **not** affected by this change) (**PR [#4951](https://github.com/pmmp/PocketMine-MP/pull/4951)**)
- The motivation for this change is to prevent situations where plugins failing to load could result in adverse outcomes, such as a world protection plugin leaving a lobby unprotected from griefing.
- If you encounter this problem, remove the offending plugin(s) or prevent it from loading using `plugin_list.yml`.
- See **PR [#4951](https://github.com/pmmp/PocketMine-MP/pull/4951)** for more detail on this change.
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to load (worlds loaded by plugins are **not** affected by this change)
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to generate (e.g. due to invalid generator settings)
- Enabling the server whitelist while the server is running (e.g. using `/whitelist on`) will now kick any non-whitelisted players currently on the server (**PR [#4774](https://github.com/pmmp/PocketMine-MP/pull/4774)**).
- Help for commands (`/help <name of command>`) now displays a list of aliases of that command.
- A CRITICAL log message is now generated if a plugin disables itself when enabling, in case the plugin doesn't emit any error of its own.
- The `/give` command now shows the alias used to find the given item in the success message, instead of the item ID/meta.
## Fixes
- Block placement has been fixed in many places where it previously didn't work correctly (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**):
- torches on top of slabs, upside-down stairs
- torches on the back face of stairs
- flower pots on top of fences
- the list goes on and on ...
- Fixed backslash escapes not getting properly removed from commands in some cases.
- Fixed aliases defined in the `aliases` section of `pocketmine.yml` not being treated as quote-aware.
## Gameplay
- Plants in flower pots can now be removed by right-clicking on the flower pot.
- Leaves now have a 2% chance of dropping sticks when destroyed by hand (**PR [#5019](https://github.com/pmmp/PocketMine-MP/pull/5019)**).
- Food exhaustion now matches Bedrock 1.18.30 (**PR [#5034](https://github.com/pmmp/PocketMine-MP/pull/5034)**).
- Implemented Stonecutter block (**PR [#4732](https://github.com/pmmp/PocketMine-MP/pull/4732)**).
## API
### Block
- Added `Block->getSupportType(Facing) : SupportType` (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**).
- This is used to determine the kind of support a block face can provide to a block (e.g. a torch) placed on it.
- Added `utils\SupportType` enum (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**).
- `tile\Spawnable->isDirty()` and `tile\Spawnable->setDirty()` are now `@deprecated`.
### Command
- Added `CommandStringHelper::parseQuoteAware()`. This static method contains the code used by `SimpleCommandMap` used to parse quoted command arguments.
### Entity
- Added `Human::emote()` (**PR [#4610](https://github.com/pmmp/PocketMine-MP/pull/4610)**)
### Event
- `PlayerCommandPreprocessEvent` is now `@deprecated`, since its functionality is entirely replaced by other, general-purpose events.
- Use `CommandEvent` to intercept commands.
- Use `PlayerChatEvent` to intercept chat messages.
- To convert a chat message into a command, pass it directly to `Server->dispatchCommand()` with the player as sender.
- Added `PlayerPostChunkSendEvent` (**PR [#4937](https://github.com/pmmp/PocketMine-MP/pull/4937)**).
- Added `PlayerDeathEvent->setKeepXp()` (**PR [#4015](https://github.com/pmmp/PocketMine-MP/pull/4015)**).
- `InventoryCloseEvent` is now called **after** the target window has been removed. This fixes various feedback loops caused by trying to open new windows to a player while there was one still active.
- As a side effect, this now means that `Player->getCurrentWindow()` will return `null` during `InventoryCloseEvent`. Use `InventoryCloseEvent->getInventory()` instead.
### Item
- `StringToItemParser` now recognizes `cod`, `raw_cod` and `cooked_cod` aliases.
### Plugin
- `DisablePluginException` may now be thrown from `Plugin::onEnable()` to make the server gracefully disable the plugin (without crashing) (**PR [#4780](https://github.com/pmmp/PocketMine-MP/pull/4780)**).
- `PluginManager->registerEvent()` now returns the `RegisteredListener` created for the handler, to permit unregistering it later.
## Internals
- Private property declarations now use typed properties (PHP 7.4) and promoted constructor properties (PHP 8.0) wherever possible.
- Protected and public properties remain unchanged, since they can't be changed without breaking subclasses.
- Promoted constructor properties are only used when it's consistently possible to promote most or all properties in a class.
- Simplified and improved legibility of `FormattedCommandAlias`.
- Added unit tests for the quote-aware command parser used by `SimpleCommandMap`.
- Various hardcoded values in `block` package classes have been moved to private constants to improve readability.
- Added various constants used in the `LevelDB` world provider.
# 4.4.1
Released 5th June 2022.
## General
- The server process will now exit with an error code if plugins, worlds or network interfaces failed to start.
## Fixes
- Fixed graylisted plugins preventing the server from starting.
- Fixed `composer make-devtools` command.
- Fixed the `Maximum memory (manager)` units being incorrectly displayed in `/status`.
- Fixed `Player->removeCurrentWindow()` breaking inventory windows.
# 4.4.2
Released 7th June 2022.
## Fixes
- Fixed a crash when arbitrary item IDs appeared in network items in some cases.
- Fixed saved paintings being deleted when loaded from disk (regression from 4.3.4).
- Fixed max stack size of fishing rods.

28
changelogs/4.5.md Normal file
View File

@ -0,0 +1,28 @@
**For Minecraft: Bedrock Edition 1.19.0**
### 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.5.0
Released 7th June 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.19.0.
- Removed support for older versions.
# 4.5.1
Released 8th June 2022.
## Fixes
- Fixed commands defined in `pocketmine.yml` `aliases` not passing the correct arguments.
- Updated BedrockProtocol to fix command argument types displayed on client-side command suggestions.
# 4.5.2
Released 29th June 2022.
## Fixes
- Fixed terrain getting redrawn when flying in spectator mode (or when using `Player->setHasBlockCollision(false)`).
- Fixed skulls with the `noDrops` flag set being treated as unknown blocks.

42
changelogs/4.6.md Normal file
View 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
View 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
View 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
View 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.

View File

@ -34,14 +34,14 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.7.0+bedrock-1.18.30",
"pocketmine/bedrock-protocol": "~9.0.0+bedrock-1.18.30",
"pocketmine/bedrock-data": "~1.11.0+bedrock-1.19.30",
"pocketmine/bedrock-protocol": "~13.0.0+bedrock-1.19.30",
"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.8.0 <2.8.9",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0",
@ -53,7 +53,7 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "1.7.1",
"phpstan/phpstan": "1.8.8",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"
@ -79,7 +79,7 @@
"sort-packages": true
},
"scripts": {
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make ./ --relative tests/plugins/DevTools --out plugins/DevTools.phar",
"make-server": [
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
"@php -dphar.readonly=0 build/server-phar.php"

610
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
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

View File

@ -37,6 +37,7 @@ namespace pocketmine {
use Webmozart\PathUtil\Path;
use function defined;
use function extension_loaded;
use function function_exists;
use function getcwd;
use function phpversion;
use function preg_match;
@ -160,7 +161,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 +177,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.
-------------------------------------------------------------------------------------------

View File

@ -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;
@ -570,6 +571,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{
@ -861,7 +863,7 @@ class Server{
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error3()));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4("settings.enable-dev-builds")));
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error5("https://github.com/pmmp/PocketMine-MP/releases")));
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
@ -976,7 +978,7 @@ class Server{
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(file_get_contents($graylistFile)));
}catch(\InvalidArgumentException $e){
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool("plugins.legacy-data-dir", true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist);
@ -1007,28 +1009,28 @@ class Server{
$this->pluginManager->loadPlugins($this->pluginPath, $loadErrorCount);
if($loadErrorCount > 0){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someLoadErrors()));
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
if(!$this->startupPrepareWorlds()){
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
if(!$this->startupPrepareNetworkInterfaces()){
$this->forceShutdown();
$this->forceShutdownExit();
return;
}
@ -1456,6 +1458,11 @@ class Server{
}
}
private function forceShutdownExit() : void{
$this->forceShutdown();
Process::kill(Process::pid(), true);
}
public function forceShutdown() : void{
if($this->hasStopped){
return;
@ -1645,12 +1652,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");

View File

@ -31,9 +31,9 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.4.0-BETA1";
public const BASE_VERSION = "4.9.1";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "beta";
public const BUILD_CHANNEL = "stable";
private function __construct(){
//NOOP

View File

@ -78,25 +78,47 @@ class Block{
$this->position = clone $this->position;
}
/**
* Returns an object containing information about how to identify and store this block type, such as its legacy
* numeric ID(s), tile type (if any), and legacy variant metadata.
*/
public function getIdInfo() : BlockIdentifier{
return $this->idInfo;
}
/**
* Returns the printable English name of the block.
*/
public function getName() : string{
return $this->fallbackName;
}
/**
* @deprecated
*
* Returns the legacy numeric Minecraft block ID.
*/
public function getId() : int{
return $this->idInfo->getBlockId();
}
/**
* @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::get()}.
*/
public function getFullId() : int{
return ($this->getId() << self::INTERNAL_METADATA_BITS) | $this->getMeta();
}
/**
* 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 ItemFactory::getInstance()->get(
$this->idInfo->getItemId(),
@ -104,6 +126,12 @@ class Block{
);
}
/**
* @deprecated
*
* Returns the legacy Minecraft block meta value. This is a mixed-purpose value, which is used to store different
* things for different blocks.
*/
public function getMeta() : int{
$stateMeta = $this->writeStateToMeta();
assert(($stateMeta & ~$this->getStateBitmask()) === 0);
@ -116,6 +144,7 @@ class Block{
/**
* Returns a bitmask used to extract state bits from block metadata.
* This is used to remove unwanted information from the legacy meta value when getting the block as an item.
*/
public function getStateBitmask() : int{
return 0;
@ -143,6 +172,12 @@ class Block{
$this->collisionBoxes = null;
}
/**
* 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->getFullId());
@ -167,7 +202,7 @@ class Block{
}
/**
* 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{
@ -198,22 +233,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{
}
@ -252,7 +301,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{
@ -273,8 +322,7 @@ class Block{
}
/**
* 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.
*/
@ -282,11 +330,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{
@ -329,10 +385,6 @@ class Block{
return false;
}
public function hasEntityCollision() : bool{
return false;
}
/**
* Returns whether entities can climb up this block.
*/
@ -340,10 +392,6 @@ class Block{
return false;
}
public function addVelocityToEntity(Entity $entity) : ?Vector3{
return null;
}
final public function getPosition() : Position{
return $this->position;
}
@ -426,6 +474,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();
@ -549,7 +598,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){
@ -561,10 +610,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.
*/
@ -572,6 +632,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.
@ -581,6 +654,13 @@ class Block{
}
/**
* 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{
@ -611,6 +691,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();
}
@ -621,6 +705,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){

View File

@ -23,10 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\BlockBreakInfo as BreakInfo;
use pocketmine\block\BlockIdentifier as BID;
use pocketmine\block\BlockIdentifierFlattened as BIDFlattened;
use pocketmine\block\BlockLegacyIds as Ids;
use pocketmine\block\BlockLegacyMetadata as Meta;
use pocketmine\block\BlockToolType as ToolType;
use pocketmine\block\tile\Banner as TileBanner;
use pocketmine\block\tile\Barrel as TileBarrel;
use pocketmine\block\tile\Beacon as TileBeacon;
@ -112,184 +114,184 @@ class BlockFactory{
$this->blocksDirectSkyLight = \SplFixedArray::fromArray(array_fill(0, 1024 << Block::INTERNAL_METADATA_BITS, false));
$this->blastResistance = \SplFixedArray::fromArray(array_fill(0, 1024 << Block::INTERNAL_METADATA_BITS, 0.0));
$railBreakInfo = new BlockBreakInfo(0.7);
$railBreakInfo = new BreakInfo(0.7);
$this->registerAllMeta(new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL, 0), "Activator Rail", $railBreakInfo));
$this->registerAllMeta(new Air(new BID(Ids::AIR, 0), "Air", BlockBreakInfo::indestructible(-1.0)));
$this->registerAllMeta(new Anvil(new BID(Ids::ANVIL, 0), "Anvil", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 6000.0)));
$this->registerAllMeta(new Bamboo(new BID(Ids::BAMBOO, 0), "Bamboo", new class(2.0 /* 1.0 in PC */, BlockToolType::AXE) extends BlockBreakInfo{
$this->registerAllMeta(new Air(new BID(Ids::AIR, 0), "Air", BreakInfo::indestructible(-1.0)));
$this->registerAllMeta(new Anvil(new BID(Ids::ANVIL, 0), "Anvil", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 6000.0)));
$this->registerAllMeta(new Bamboo(new BID(Ids::BAMBOO, 0), "Bamboo", new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{
public function getBreakTime(Item $item) : float{
if($item->getBlockToolType() === BlockToolType::SWORD){
if($item->getBlockToolType() === ToolType::SWORD){
return 0.0;
}
return parent::getBreakTime($item);
}
}));
$this->registerAllMeta(new BambooSapling(new BID(Ids::BAMBOO_SAPLING, 0), "Bamboo Sapling", BlockBreakInfo::instant()));
$this->registerAllMeta(new BambooSapling(new BID(Ids::BAMBOO_SAPLING, 0), "Bamboo Sapling", BreakInfo::instant()));
$bannerBreakInfo = new BlockBreakInfo(1.0, BlockToolType::AXE);
$bannerBreakInfo = new BreakInfo(1.0, ToolType::AXE);
$this->registerAllMeta(new FloorBanner(new BID(Ids::STANDING_BANNER, 0, ItemIds::BANNER, TileBanner::class), "Banner", $bannerBreakInfo));
$this->registerAllMeta(new WallBanner(new BID(Ids::WALL_BANNER, 0, ItemIds::BANNER, TileBanner::class), "Wall Banner", $bannerBreakInfo));
$this->registerAllMeta(new Barrel(new BID(Ids::BARREL, 0, null, TileBarrel::class), "Barrel", new BlockBreakInfo(2.5, BlockToolType::AXE)));
$this->registerAllMeta(new Transparent(new BID(Ids::BARRIER, 0), "Barrier", BlockBreakInfo::indestructible()));
$this->registerAllMeta(new Beacon(new BID(Ids::BEACON, 0, null, TileBeacon::class), "Beacon", new BlockBreakInfo(3.0)));
$this->registerAllMeta(new Bed(new BID(Ids::BED_BLOCK, 0, ItemIds::BED, TileBed::class), "Bed Block", new BlockBreakInfo(0.2)));
$this->registerAllMeta(new Bedrock(new BID(Ids::BEDROCK, 0), "Bedrock", BlockBreakInfo::indestructible()));
$this->registerAllMeta(new Barrel(new BID(Ids::BARREL, 0, null, TileBarrel::class), "Barrel", new BreakInfo(2.5, ToolType::AXE)));
$this->registerAllMeta(new Transparent(new BID(Ids::BARRIER, 0), "Barrier", BreakInfo::indestructible()));
$this->registerAllMeta(new Beacon(new BID(Ids::BEACON, 0, null, TileBeacon::class), "Beacon", new BreakInfo(3.0)));
$this->registerAllMeta(new Bed(new BID(Ids::BED_BLOCK, 0, ItemIds::BED, TileBed::class), "Bed Block", new BreakInfo(0.2)));
$this->registerAllMeta(new Bedrock(new BID(Ids::BEDROCK, 0), "Bedrock", BreakInfo::indestructible()));
$this->registerAllMeta(new Beetroot(new BID(Ids::BEETROOT_BLOCK, 0), "Beetroot Block", BlockBreakInfo::instant()));
$this->registerAllMeta(new Bell(new BID(Ids::BELL, 0, null, TileBell::class), "Bell", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new BlueIce(new BID(Ids::BLUE_ICE, 0), "Blue Ice", new BlockBreakInfo(2.8, BlockToolType::PICKAXE)));
$this->registerAllMeta(new BoneBlock(new BID(Ids::BONE_BLOCK, 0), "Bone Block", new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Bookshelf(new BID(Ids::BOOKSHELF, 0), "Bookshelf", new BlockBreakInfo(1.5, BlockToolType::AXE)));
$this->registerAllMeta(new BrewingStand(new BID(Ids::BREWING_STAND_BLOCK, 0, ItemIds::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Beetroot(new BID(Ids::BEETROOT_BLOCK, 0), "Beetroot Block", BreakInfo::instant()));
$this->registerAllMeta(new Bell(new BID(Ids::BELL, 0, null, TileBell::class), "Bell", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new BlueIce(new BID(Ids::BLUE_ICE, 0), "Blue Ice", new BreakInfo(2.8, ToolType::PICKAXE)));
$this->registerAllMeta(new BoneBlock(new BID(Ids::BONE_BLOCK, 0), "Bone Block", new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Bookshelf(new BID(Ids::BOOKSHELF, 0), "Bookshelf", new BreakInfo(1.5, ToolType::AXE)));
$this->registerAllMeta(new BrewingStand(new BID(Ids::BREWING_STAND_BLOCK, 0, ItemIds::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$bricksBreakInfo = new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$bricksBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta(new Stair(new BID(Ids::BRICK_STAIRS, 0), "Brick Stairs", $bricksBreakInfo));
$this->registerAllMeta(new Opaque(new BID(Ids::BRICK_BLOCK, 0), "Bricks", $bricksBreakInfo));
$this->registerAllMeta(new BrownMushroom(new BID(Ids::BROWN_MUSHROOM, 0), "Brown Mushroom", BlockBreakInfo::instant()));
$this->registerAllMeta(new Cactus(new BID(Ids::CACTUS, 0), "Cactus", new BlockBreakInfo(0.4)));
$this->registerAllMeta(new Cake(new BID(Ids::CAKE_BLOCK, 0, ItemIds::CAKE), "Cake", new BlockBreakInfo(0.5)));
$this->registerAllMeta(new Carrot(new BID(Ids::CARROTS, 0), "Carrot Block", BlockBreakInfo::instant()));
$this->registerAllMeta(new BrownMushroom(new BID(Ids::BROWN_MUSHROOM, 0), "Brown Mushroom", BreakInfo::instant()));
$this->registerAllMeta(new Cactus(new BID(Ids::CACTUS, 0), "Cactus", new BreakInfo(0.4)));
$this->registerAllMeta(new Cake(new BID(Ids::CAKE_BLOCK, 0, ItemIds::CAKE), "Cake", new BreakInfo(0.5)));
$this->registerAllMeta(new Carrot(new BID(Ids::CARROTS, 0), "Carrot Block", BreakInfo::instant()));
$chestBreakInfo = new BlockBreakInfo(2.5, BlockToolType::AXE);
$chestBreakInfo = new BreakInfo(2.5, ToolType::AXE);
$this->registerAllMeta(new Chest(new BID(Ids::CHEST, 0, null, TileChest::class), "Chest", $chestBreakInfo));
$this->registerAllMeta(new Clay(new BID(Ids::CLAY_BLOCK, 0), "Clay Block", new BlockBreakInfo(0.6, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Coal(new BID(Ids::COAL_BLOCK, 0), "Coal Block", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new CoalOre(new BID(Ids::COAL_ORE, 0), "Coal Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Clay(new BID(Ids::CLAY_BLOCK, 0), "Clay Block", new BreakInfo(0.6, ToolType::SHOVEL)));
$this->registerAllMeta(new Coal(new BID(Ids::COAL_BLOCK, 0), "Coal Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new CoalOre(new BID(Ids::COAL_ORE, 0), "Coal Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$cobblestoneBreakInfo = new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$cobblestoneBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta($cobblestone = new Opaque(new BID(Ids::COBBLESTONE, 0), "Cobblestone", $cobblestoneBreakInfo));
$this->registerAllMeta(new Opaque(new BID(Ids::MOSSY_COBBLESTONE, 0), "Mossy Cobblestone", $cobblestoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::COBBLESTONE_STAIRS, 0), "Cobblestone Stairs", $cobblestoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::MOSSY_COBBLESTONE_STAIRS, 0), "Mossy Cobblestone Stairs", $cobblestoneBreakInfo));
$this->registerAllMeta(new Cobweb(new BID(Ids::COBWEB, 0), "Cobweb", new BlockBreakInfo(4.0, BlockToolType::SWORD | BlockToolType::SHEARS, 1)));
$this->registerAllMeta(new CocoaBlock(new BID(Ids::COCOA, 0), "Cocoa Block", new BlockBreakInfo(0.2, BlockToolType::AXE, 0, 15.0)));
$this->registerAllMeta(new CoralBlock(new BID(Ids::CORAL_BLOCK, 0), "Coral Block", new BlockBreakInfo(7.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new CraftingTable(new BID(Ids::CRAFTING_TABLE, 0), "Crafting Table", new BlockBreakInfo(2.5, BlockToolType::AXE)));
$this->registerAllMeta(new DaylightSensor(new BIDFlattened(Ids::DAYLIGHT_DETECTOR, [Ids::DAYLIGHT_DETECTOR_INVERTED], 0, null, TileDaylightSensor::class), "Daylight Sensor", new BlockBreakInfo(0.2, BlockToolType::AXE)));
$this->registerAllMeta(new DeadBush(new BID(Ids::DEADBUSH, 0), "Dead Bush", BlockBreakInfo::instant(BlockToolType::SHEARS, 1)));
$this->registerAllMeta(new Cobweb(new BID(Ids::COBWEB, 0), "Cobweb", new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)));
$this->registerAllMeta(new CocoaBlock(new BID(Ids::COCOA, 0), "Cocoa Block", new BreakInfo(0.2, ToolType::AXE, 0, 15.0)));
$this->registerAllMeta(new CoralBlock(new BID(Ids::CORAL_BLOCK, 0), "Coral Block", new BreakInfo(7.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new CraftingTable(new BID(Ids::CRAFTING_TABLE, 0), "Crafting Table", new BreakInfo(2.5, ToolType::AXE)));
$this->registerAllMeta(new DaylightSensor(new BIDFlattened(Ids::DAYLIGHT_DETECTOR, [Ids::DAYLIGHT_DETECTOR_INVERTED], 0, null, TileDaylightSensor::class), "Daylight Sensor", new BreakInfo(0.2, ToolType::AXE)));
$this->registerAllMeta(new DeadBush(new BID(Ids::DEADBUSH, 0), "Dead Bush", BreakInfo::instant(ToolType::SHEARS, 1)));
$this->registerAllMeta(new DetectorRail(new BID(Ids::DETECTOR_RAIL, 0), "Detector Rail", $railBreakInfo));
$this->registerAllMeta(new Opaque(new BID(Ids::DIAMOND_BLOCK, 0), "Diamond Block", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new DiamondOre(new BID(Ids::DIAMOND_ORE, 0), "Diamond Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new Dirt(new BID(Ids::DIRT, 0), "Dirt", new BlockBreakInfo(0.5, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Opaque(new BID(Ids::DIAMOND_BLOCK, 0), "Diamond Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new DiamondOre(new BID(Ids::DIAMOND_ORE, 0), "Diamond Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new Dirt(new BID(Ids::DIRT, 0), "Dirt", new BreakInfo(0.5, ToolType::SHOVEL)));
$this->registerAllMeta(
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_SUNFLOWER), "Sunflower", BlockBreakInfo::instant()),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_LILAC), "Lilac", BlockBreakInfo::instant()),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_ROSE_BUSH), "Rose Bush", BlockBreakInfo::instant()),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_PEONY), "Peony", BlockBreakInfo::instant()),
new DoubleTallGrass(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_TALLGRASS), "Double Tallgrass", BlockBreakInfo::instant(BlockToolType::SHEARS, 1)),
new DoubleTallGrass(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_LARGE_FERN), "Large Fern", BlockBreakInfo::instant(BlockToolType::SHEARS, 1)),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_SUNFLOWER), "Sunflower", BreakInfo::instant()),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_LILAC), "Lilac", BreakInfo::instant()),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_ROSE_BUSH), "Rose Bush", BreakInfo::instant()),
new DoublePlant(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_PEONY), "Peony", BreakInfo::instant()),
new DoubleTallGrass(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_TALLGRASS), "Double Tallgrass", BreakInfo::instant(ToolType::SHEARS, 1)),
new DoubleTallGrass(new BID(Ids::DOUBLE_PLANT, Meta::DOUBLE_PLANT_LARGE_FERN), "Large Fern", BreakInfo::instant(ToolType::SHEARS, 1)),
);
$this->registerAllMeta(new DragonEgg(new BID(Ids::DRAGON_EGG, 0), "Dragon Egg", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new DriedKelp(new BID(Ids::DRIED_KELP_BLOCK, 0), "Dried Kelp Block", new BlockBreakInfo(0.5, BlockToolType::NONE, 0, 12.5)));
$this->registerAllMeta(new Opaque(new BID(Ids::EMERALD_BLOCK, 0), "Emerald Block", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new EmeraldOre(new BID(Ids::EMERALD_ORE, 0), "Emerald Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, 0, null, TileEnchantingTable::class), "Enchanting Table", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 6000.0)));
$this->registerAllMeta(new EndPortalFrame(new BID(Ids::END_PORTAL_FRAME, 0), "End Portal Frame", BlockBreakInfo::indestructible()));
$this->registerAllMeta(new EndRod(new BID(Ids::END_ROD, 0), "End Rod", BlockBreakInfo::instant()));
$this->registerAllMeta(new Opaque(new BID(Ids::END_STONE, 0), "End Stone", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 45.0)));
$this->registerAllMeta(new DragonEgg(new BID(Ids::DRAGON_EGG, 0), "Dragon Egg", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new DriedKelp(new BID(Ids::DRIED_KELP_BLOCK, 0), "Dried Kelp Block", new BreakInfo(0.5, ToolType::NONE, 0, 12.5)));
$this->registerAllMeta(new Opaque(new BID(Ids::EMERALD_BLOCK, 0), "Emerald Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new EmeraldOre(new BID(Ids::EMERALD_ORE, 0), "Emerald Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, 0, null, TileEnchantingTable::class), "Enchanting Table", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 6000.0)));
$this->registerAllMeta(new EndPortalFrame(new BID(Ids::END_PORTAL_FRAME, 0), "End Portal Frame", BreakInfo::indestructible()));
$this->registerAllMeta(new EndRod(new BID(Ids::END_ROD, 0), "End Rod", BreakInfo::instant()));
$this->registerAllMeta(new Opaque(new BID(Ids::END_STONE, 0), "End Stone", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 45.0)));
$endBrickBreakInfo = new BlockBreakInfo(0.8, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 4.0);
$endBrickBreakInfo = new BreakInfo(0.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 4.0);
$this->registerAllMeta(new Opaque(new BID(Ids::END_BRICKS, 0), "End Stone Bricks", $endBrickBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::END_BRICK_STAIRS, 0), "End Stone Brick Stairs", $endBrickBreakInfo));
$this->registerAllMeta(new EnderChest(new BID(Ids::ENDER_CHEST, 0, null, TileEnderChest::class), "Ender Chest", new BlockBreakInfo(22.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 3000.0)));
$this->registerAllMeta(new Farmland(new BID(Ids::FARMLAND, 0), "Farmland", new BlockBreakInfo(0.6, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Fire(new BID(Ids::FIRE, 0), "Fire Block", BlockBreakInfo::instant()));
$this->registerAllMeta(new FletchingTable(new BID(Ids::FLETCHING_TABLE, 0), "Fletching Table", new BlockBreakInfo(2.5, BlockToolType::AXE, 0, 2.5)));
$this->registerAllMeta(new Flower(new BID(Ids::DANDELION, 0), "Dandelion", BlockBreakInfo::instant()));
$this->registerAllMeta(new EnderChest(new BID(Ids::ENDER_CHEST, 0, null, TileEnderChest::class), "Ender Chest", new BreakInfo(22.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 3000.0)));
$this->registerAllMeta(new Farmland(new BID(Ids::FARMLAND, 0), "Farmland", new BreakInfo(0.6, ToolType::SHOVEL)));
$this->registerAllMeta(new Fire(new BID(Ids::FIRE, 0), "Fire Block", BreakInfo::instant()));
$this->registerAllMeta(new FletchingTable(new BID(Ids::FLETCHING_TABLE, 0), "Fletching Table", new BreakInfo(2.5, ToolType::AXE, 0, 2.5)));
$this->registerAllMeta(new Flower(new BID(Ids::DANDELION, 0), "Dandelion", BreakInfo::instant()));
$this->registerAllMeta(
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_POPPY), "Poppy", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_ALLIUM), "Allium", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_AZURE_BLUET), "Azure Bluet", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_BLUE_ORCHID), "Blue Orchid", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_CORNFLOWER), "Cornflower", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_LILY_OF_THE_VALLEY), "Lily of the Valley", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_ORANGE_TULIP), "Orange Tulip", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_OXEYE_DAISY), "Oxeye Daisy", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_PINK_TULIP), "Pink Tulip", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_RED_TULIP), "Red Tulip", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_WHITE_TULIP), "White Tulip", BlockBreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_POPPY), "Poppy", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_ALLIUM), "Allium", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_AZURE_BLUET), "Azure Bluet", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_BLUE_ORCHID), "Blue Orchid", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_CORNFLOWER), "Cornflower", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_LILY_OF_THE_VALLEY), "Lily of the Valley", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_ORANGE_TULIP), "Orange Tulip", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_OXEYE_DAISY), "Oxeye Daisy", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_PINK_TULIP), "Pink Tulip", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_RED_TULIP), "Red Tulip", BreakInfo::instant()),
new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_WHITE_TULIP), "White Tulip", BreakInfo::instant()),
);
$this->registerAllMeta(new FlowerPot(new BID(Ids::FLOWER_POT_BLOCK, 0, ItemIds::FLOWER_POT, TileFlowerPot::class), "Flower Pot", BlockBreakInfo::instant()));
$this->registerAllMeta(new FrostedIce(new BID(Ids::FROSTED_ICE, 0), "Frosted Ice", new BlockBreakInfo(2.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new Furnace(new BIDFlattened(Ids::FURNACE, [Ids::LIT_FURNACE], 0, null, TileNormalFurnace::class), "Furnace", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Furnace(new BIDFlattened(Ids::BLAST_FURNACE, [Ids::LIT_BLAST_FURNACE], 0, null, TileBlastFurnace::class), "Blast Furnace", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Furnace(new BIDFlattened(Ids::SMOKER, [Ids::LIT_SMOKER], 0, null, TileSmoker::class), "Smoker", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new FlowerPot(new BID(Ids::FLOWER_POT_BLOCK, 0, ItemIds::FLOWER_POT, TileFlowerPot::class), "Flower Pot", BreakInfo::instant()));
$this->registerAllMeta(new FrostedIce(new BID(Ids::FROSTED_ICE, 0), "Frosted Ice", new BreakInfo(2.5, ToolType::PICKAXE)));
$this->registerAllMeta(new Furnace(new BIDFlattened(Ids::FURNACE, [Ids::LIT_FURNACE], 0, null, TileNormalFurnace::class), "Furnace", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Furnace(new BIDFlattened(Ids::BLAST_FURNACE, [Ids::LIT_BLAST_FURNACE], 0, null, TileBlastFurnace::class), "Blast Furnace", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Furnace(new BIDFlattened(Ids::SMOKER, [Ids::LIT_SMOKER], 0, null, TileSmoker::class), "Smoker", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$glassBreakInfo = new BlockBreakInfo(0.3);
$glassBreakInfo = new BreakInfo(0.3);
$this->registerAllMeta(new Glass(new BID(Ids::GLASS, 0), "Glass", $glassBreakInfo));
$this->registerAllMeta(new GlassPane(new BID(Ids::GLASS_PANE, 0), "Glass Pane", $glassBreakInfo));
$this->registerAllMeta(new GlowingObsidian(new BID(Ids::GLOWINGOBSIDIAN, 0), "Glowing Obsidian", new BlockBreakInfo(10.0, BlockToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 50.0)));
$this->registerAllMeta(new Glowstone(new BID(Ids::GLOWSTONE, 0), "Glowstone", new BlockBreakInfo(0.3, BlockToolType::PICKAXE)));
$this->registerAllMeta(new Opaque(new BID(Ids::GOLD_BLOCK, 0), "Gold Block", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new Opaque(new BID(Ids::GOLD_ORE, 0), "Gold Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new GlowingObsidian(new BID(Ids::GLOWINGOBSIDIAN, 0), "Glowing Obsidian", new BreakInfo(10.0, ToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 50.0)));
$this->registerAllMeta(new Glowstone(new BID(Ids::GLOWSTONE, 0), "Glowstone", new BreakInfo(0.3, ToolType::PICKAXE)));
$this->registerAllMeta(new Opaque(new BID(Ids::GOLD_BLOCK, 0), "Gold Block", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new Opaque(new BID(Ids::GOLD_ORE, 0), "Gold Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$grassBreakInfo = new BlockBreakInfo(0.6, BlockToolType::SHOVEL);
$grassBreakInfo = new BreakInfo(0.6, ToolType::SHOVEL);
$this->registerAllMeta(new Grass(new BID(Ids::GRASS, 0), "Grass", $grassBreakInfo));
$this->registerAllMeta(new GrassPath(new BID(Ids::GRASS_PATH, 0), "Grass Path", $grassBreakInfo));
$this->registerAllMeta(new Gravel(new BID(Ids::GRAVEL, 0), "Gravel", new BlockBreakInfo(0.6, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Gravel(new BID(Ids::GRAVEL, 0), "Gravel", new BreakInfo(0.6, ToolType::SHOVEL)));
$hardenedClayBreakInfo = new BlockBreakInfo(1.25, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 21.0);
$hardenedClayBreakInfo = new BreakInfo(1.25, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 21.0);
$this->registerAllMeta(new HardenedClay(new BID(Ids::HARDENED_CLAY, 0), "Hardened Clay", $hardenedClayBreakInfo));
$hardenedGlassBreakInfo = new BlockBreakInfo(10.0);
$hardenedGlassBreakInfo = new BreakInfo(10.0);
$this->registerAllMeta(new HardenedGlass(new BID(Ids::HARD_GLASS, 0), "Hardened Glass", $hardenedGlassBreakInfo));
$this->registerAllMeta(new HardenedGlassPane(new BID(Ids::HARD_GLASS_PANE, 0), "Hardened Glass Pane", $hardenedGlassBreakInfo));
$this->registerAllMeta(new HayBale(new BID(Ids::HAY_BALE, 0), "Hay Bale", new BlockBreakInfo(0.5)));
$this->registerAllMeta(new Hopper(new BID(Ids::HOPPER_BLOCK, 0, ItemIds::HOPPER, TileHopper::class), "Hopper", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 15.0)));
$this->registerAllMeta(new Ice(new BID(Ids::ICE, 0), "Ice", new BlockBreakInfo(0.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new HayBale(new BID(Ids::HAY_BALE, 0), "Hay Bale", new BreakInfo(0.5)));
$this->registerAllMeta(new Hopper(new BID(Ids::HOPPER_BLOCK, 0, ItemIds::HOPPER, TileHopper::class), "Hopper", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 15.0)));
$this->registerAllMeta(new Ice(new BID(Ids::ICE, 0), "Ice", new BreakInfo(0.5, ToolType::PICKAXE)));
$updateBlockBreakInfo = new BlockBreakInfo(1.0);
$updateBlockBreakInfo = new BreakInfo(1.0);
$this->registerAllMeta(new Opaque(new BID(Ids::INFO_UPDATE, 0), "update!", $updateBlockBreakInfo));
$this->registerAllMeta(new Opaque(new BID(Ids::INFO_UPDATE2, 0), "ate!upd", $updateBlockBreakInfo));
$this->registerAllMeta(new Transparent(new BID(Ids::INVISIBLEBEDROCK, 0), "Invisible Bedrock", BlockBreakInfo::indestructible()));
$this->registerAllMeta(new Transparent(new BID(Ids::INVISIBLEBEDROCK, 0), "Invisible Bedrock", BreakInfo::indestructible()));
$ironBreakInfo = new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel(), 30.0);
$ironBreakInfo = new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel(), 30.0);
$this->registerAllMeta(new Opaque(new BID(Ids::IRON_BLOCK, 0), "Iron Block", $ironBreakInfo));
$this->registerAllMeta(new Thin(new BID(Ids::IRON_BARS, 0), "Iron Bars", $ironBreakInfo));
$ironDoorBreakInfo = new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 25.0);
$ironDoorBreakInfo = new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 25.0);
$this->registerAllMeta(new Door(new BID(Ids::IRON_DOOR_BLOCK, 0, ItemIds::IRON_DOOR), "Iron Door", $ironDoorBreakInfo));
$this->registerAllMeta(new Trapdoor(new BID(Ids::IRON_TRAPDOOR, 0), "Iron Trapdoor", $ironDoorBreakInfo));
$this->registerAllMeta(new Opaque(new BID(Ids::IRON_ORE, 0), "Iron Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new ItemFrame(new BID(Ids::FRAME_BLOCK, 0, ItemIds::FRAME, TileItemFrame::class), "Item Frame", new BlockBreakInfo(0.25)));
$this->registerAllMeta(new Jukebox(new BID(Ids::JUKEBOX, 0, ItemIds::JUKEBOX, TileJukebox::class), "Jukebox", new BlockBreakInfo(0.8, BlockToolType::AXE))); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not
$this->registerAllMeta(new Ladder(new BID(Ids::LADDER, 0), "Ladder", new BlockBreakInfo(0.4, BlockToolType::AXE)));
$this->registerAllMeta(new Lantern(new BID(Ids::LANTERN, 0), "Lantern", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Opaque(new BID(Ids::LAPIS_BLOCK, 0), "Lapis Lazuli Block", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new LapisOre(new BID(Ids::LAPIS_ORE, 0), "Lapis Lazuli Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new Lava(new BIDFlattened(Ids::FLOWING_LAVA, [Ids::STILL_LAVA], 0), "Lava", BlockBreakInfo::indestructible(500.0)));
$this->registerAllMeta(new Lectern(new BID(Ids::LECTERN, 0, ItemIds::LECTERN, TileLectern::class), "Lectern", new BlockBreakInfo(2.0, BlockToolType::AXE)));
$this->registerAllMeta(new Lever(new BID(Ids::LEVER, 0), "Lever", new BlockBreakInfo(0.5)));
$this->registerAllMeta(new Loom(new BID(Ids::LOOM, 0), "Loom", new BlockBreakInfo(2.5, BlockToolType::AXE)));
$this->registerAllMeta(new Magma(new BID(Ids::MAGMA, 0), "Magma Block", new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Melon(new BID(Ids::MELON_BLOCK, 0), "Melon Block", new BlockBreakInfo(1.0, BlockToolType::AXE)));
$this->registerAllMeta(new MelonStem(new BID(Ids::MELON_STEM, 0, ItemIds::MELON_SEEDS), "Melon Stem", BlockBreakInfo::instant()));
$this->registerAllMeta(new MonsterSpawner(new BID(Ids::MOB_SPAWNER, 0, null, TileMonsterSpawner::class), "Monster Spawner", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Mycelium(new BID(Ids::MYCELIUM, 0), "Mycelium", new BlockBreakInfo(0.6, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Opaque(new BID(Ids::IRON_ORE, 0), "Iron Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new ItemFrame(new BID(Ids::FRAME_BLOCK, 0, ItemIds::FRAME, TileItemFrame::class), "Item Frame", new BreakInfo(0.25)));
$this->registerAllMeta(new Jukebox(new BID(Ids::JUKEBOX, 0, ItemIds::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->registerAllMeta(new Ladder(new BID(Ids::LADDER, 0), "Ladder", new BreakInfo(0.4, ToolType::AXE)));
$this->registerAllMeta(new Lantern(new BID(Ids::LANTERN, 0), "Lantern", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Opaque(new BID(Ids::LAPIS_BLOCK, 0), "Lapis Lazuli Block", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new LapisOre(new BID(Ids::LAPIS_ORE, 0), "Lapis Lazuli Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel())));
$this->registerAllMeta(new Lava(new BIDFlattened(Ids::FLOWING_LAVA, [Ids::STILL_LAVA], 0), "Lava", BreakInfo::indestructible(500.0)));
$this->registerAllMeta(new Lectern(new BID(Ids::LECTERN, 0, ItemIds::LECTERN, TileLectern::class), "Lectern", new BreakInfo(2.0, ToolType::AXE)));
$this->registerAllMeta(new Lever(new BID(Ids::LEVER, 0), "Lever", new BreakInfo(0.5)));
$this->registerAllMeta(new Loom(new BID(Ids::LOOM, 0), "Loom", new BreakInfo(2.5, ToolType::AXE)));
$this->registerAllMeta(new Magma(new BID(Ids::MAGMA, 0), "Magma Block", new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Melon(new BID(Ids::MELON_BLOCK, 0), "Melon Block", new BreakInfo(1.0, ToolType::AXE)));
$this->registerAllMeta(new MelonStem(new BID(Ids::MELON_STEM, 0, ItemIds::MELON_SEEDS), "Melon Stem", BreakInfo::instant()));
$this->registerAllMeta(new MonsterSpawner(new BID(Ids::MOB_SPAWNER, 0, null, TileMonsterSpawner::class), "Monster Spawner", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Mycelium(new BID(Ids::MYCELIUM, 0), "Mycelium", new BreakInfo(0.6, ToolType::SHOVEL)));
$netherBrickBreakInfo = new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$netherBrickBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta(new Opaque(new BID(Ids::NETHER_BRICK_BLOCK, 0), "Nether Bricks", $netherBrickBreakInfo));
$this->registerAllMeta(new Opaque(new BID(Ids::RED_NETHER_BRICK, 0), "Red Nether Bricks", $netherBrickBreakInfo));
$this->registerAllMeta(new Fence(new BID(Ids::NETHER_BRICK_FENCE, 0), "Nether Brick Fence", $netherBrickBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::NETHER_BRICK_STAIRS, 0), "Nether Brick Stairs", $netherBrickBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::RED_NETHER_BRICK_STAIRS, 0), "Red Nether Brick Stairs", $netherBrickBreakInfo));
$this->registerAllMeta(new NetherPortal(new BID(Ids::PORTAL, 0), "Nether Portal", BlockBreakInfo::indestructible(0.0)));
$this->registerAllMeta(new NetherQuartzOre(new BID(Ids::NETHER_QUARTZ_ORE, 0), "Nether Quartz Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new NetherReactor(new BID(Ids::NETHERREACTOR, 0), "Nether Reactor Core", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Opaque(new BID(Ids::NETHER_WART_BLOCK, 0), "Nether Wart Block", new BlockBreakInfo(1.0, BlockToolType::HOE)));
$this->registerAllMeta(new NetherWartPlant(new BID(Ids::NETHER_WART_PLANT, 0, ItemIds::NETHER_WART), "Nether Wart", BlockBreakInfo::instant()));
$this->registerAllMeta(new Netherrack(new BID(Ids::NETHERRACK, 0), "Netherrack", new BlockBreakInfo(0.4, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Note(new BID(Ids::NOTEBLOCK, 0, null, TileNote::class), "Note Block", new BlockBreakInfo(0.8, BlockToolType::AXE)));
$this->registerAllMeta(new Opaque(new BID(Ids::OBSIDIAN, 0), "Obsidian", new BlockBreakInfo(35.0 /* 50 in PC */, BlockToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 6000.0)));
$this->registerAllMeta(new PackedIce(new BID(Ids::PACKED_ICE, 0), "Packed Ice", new BlockBreakInfo(0.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new Podzol(new BID(Ids::PODZOL, 0), "Podzol", new BlockBreakInfo(0.5, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Potato(new BID(Ids::POTATOES, 0), "Potato Block", BlockBreakInfo::instant()));
$this->registerAllMeta(new NetherPortal(new BID(Ids::PORTAL, 0), "Nether Portal", BreakInfo::indestructible(0.0)));
$this->registerAllMeta(new NetherQuartzOre(new BID(Ids::NETHER_QUARTZ_ORE, 0), "Nether Quartz Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new NetherReactor(new BID(Ids::NETHERREACTOR, 0), "Nether Reactor Core", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Opaque(new BID(Ids::NETHER_WART_BLOCK, 0), "Nether Wart Block", new BreakInfo(1.0, ToolType::HOE)));
$this->registerAllMeta(new NetherWartPlant(new BID(Ids::NETHER_WART_PLANT, 0, ItemIds::NETHER_WART), "Nether Wart", BreakInfo::instant()));
$this->registerAllMeta(new Netherrack(new BID(Ids::NETHERRACK, 0), "Netherrack", new BreakInfo(0.4, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Note(new BID(Ids::NOTEBLOCK, 0, null, TileNote::class), "Note Block", new BreakInfo(0.8, ToolType::AXE)));
$this->registerAllMeta(new Opaque(new BID(Ids::OBSIDIAN, 0), "Obsidian", new BreakInfo(35.0 /* 50 in PC */, ToolType::PICKAXE, ToolTier::DIAMOND()->getHarvestLevel(), 6000.0)));
$this->registerAllMeta(new PackedIce(new BID(Ids::PACKED_ICE, 0), "Packed Ice", new BreakInfo(0.5, ToolType::PICKAXE)));
$this->registerAllMeta(new Podzol(new BID(Ids::PODZOL, 0), "Podzol", new BreakInfo(0.5, ToolType::SHOVEL)));
$this->registerAllMeta(new Potato(new BID(Ids::POTATOES, 0), "Potato Block", BreakInfo::instant()));
$this->registerAllMeta(new PoweredRail(new BID(Ids::GOLDEN_RAIL, 0), "Powered Rail", $railBreakInfo));
$prismarineBreakInfo = new BlockBreakInfo(1.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$prismarineBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta(
new Opaque(new BID(Ids::PRISMARINE, Meta::PRISMARINE_NORMAL), "Prismarine", $prismarineBreakInfo),
new Opaque(new BID(Ids::PRISMARINE, Meta::PRISMARINE_DARK), "Dark Prismarine", $prismarineBreakInfo),
@ -299,21 +301,21 @@ class BlockFactory{
$this->registerAllMeta(new Stair(new BID(Ids::DARK_PRISMARINE_STAIRS, 0), "Dark Prismarine Stairs", $prismarineBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::PRISMARINE_STAIRS, 0), "Prismarine Stairs", $prismarineBreakInfo));
$pumpkinBreakInfo = new BlockBreakInfo(1.0, BlockToolType::AXE);
$pumpkinBreakInfo = new BreakInfo(1.0, ToolType::AXE);
$this->registerAllMeta(new Pumpkin(new BID(Ids::PUMPKIN, 0), "Pumpkin", $pumpkinBreakInfo));
$this->registerAllMeta(new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN, 0), "Carved Pumpkin", $pumpkinBreakInfo));
$this->registerAllMeta(new LitPumpkin(new BID(Ids::JACK_O_LANTERN, 0), "Jack o'Lantern", $pumpkinBreakInfo));
$this->registerAllMeta(new PumpkinStem(new BID(Ids::PUMPKIN_STEM, 0, ItemIds::PUMPKIN_SEEDS), "Pumpkin Stem", BlockBreakInfo::instant()));
$this->registerAllMeta(new PumpkinStem(new BID(Ids::PUMPKIN_STEM, 0, ItemIds::PUMPKIN_SEEDS), "Pumpkin Stem", BreakInfo::instant()));
$purpurBreakInfo = new BlockBreakInfo(1.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$purpurBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta(
new Opaque(new BID(Ids::PURPUR_BLOCK, Meta::PURPUR_NORMAL), "Purpur Block", $purpurBreakInfo),
new SimplePillar(new BID(Ids::PURPUR_BLOCK, Meta::PURPUR_PILLAR), "Purpur Pillar", $purpurBreakInfo)
);
$this->registerAllMeta(new Stair(new BID(Ids::PURPUR_STAIRS, 0), "Purpur Stairs", $purpurBreakInfo));
$quartzBreakInfo = new BlockBreakInfo(0.8, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$quartzBreakInfo = new BreakInfo(0.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$this->registerAllMeta(
new Opaque(new BID(Ids::QUARTZ_BLOCK, Meta::QUARTZ_NORMAL), "Quartz Block", $quartzBreakInfo),
new SimplePillar(new BID(Ids::QUARTZ_BLOCK, Meta::QUARTZ_CHISELED), "Chiseled Quartz Block", $quartzBreakInfo),
@ -324,33 +326,33 @@ class BlockFactory{
$this->registerAllMeta(new Stair(new BID(Ids::SMOOTH_QUARTZ_STAIRS, 0), "Smooth Quartz Stairs", $quartzBreakInfo));
$this->registerAllMeta(new Rail(new BID(Ids::RAIL, 0), "Rail", $railBreakInfo));
$this->registerAllMeta(new RedMushroom(new BID(Ids::RED_MUSHROOM, 0), "Red Mushroom", BlockBreakInfo::instant()));
$this->registerAllMeta(new Redstone(new BID(Ids::REDSTONE_BLOCK, 0), "Redstone Block", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new RedstoneComparator(new BIDFlattened(Ids::UNPOWERED_COMPARATOR, [Ids::POWERED_COMPARATOR], 0, ItemIds::COMPARATOR, TileComparator::class), "Redstone Comparator", BlockBreakInfo::instant()));
$this->registerAllMeta(new RedstoneLamp(new BIDFlattened(Ids::REDSTONE_LAMP, [Ids::LIT_REDSTONE_LAMP], 0), "Redstone Lamp", new BlockBreakInfo(0.3)));
$this->registerAllMeta(new RedstoneOre(new BIDFlattened(Ids::REDSTONE_ORE, [Ids::LIT_REDSTONE_ORE], 0), "Redstone Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new RedstoneRepeater(new BIDFlattened(Ids::UNPOWERED_REPEATER, [Ids::POWERED_REPEATER], 0, ItemIds::REPEATER), "Redstone Repeater", BlockBreakInfo::instant()));
$this->registerAllMeta(new RedstoneTorch(new BIDFlattened(Ids::REDSTONE_TORCH, [Ids::UNLIT_REDSTONE_TORCH], 0), "Redstone Torch", BlockBreakInfo::instant()));
$this->registerAllMeta(new RedstoneWire(new BID(Ids::REDSTONE_WIRE, 0, ItemIds::REDSTONE), "Redstone", BlockBreakInfo::instant()));
$this->registerAllMeta(new Reserved6(new BID(Ids::RESERVED6, 0), "reserved6", BlockBreakInfo::instant()));
$this->registerAllMeta(new RedMushroom(new BID(Ids::RED_MUSHROOM, 0), "Red Mushroom", BreakInfo::instant()));
$this->registerAllMeta(new Redstone(new BID(Ids::REDSTONE_BLOCK, 0), "Redstone Block", new BreakInfo(5.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0)));
$this->registerAllMeta(new RedstoneComparator(new BIDFlattened(Ids::UNPOWERED_COMPARATOR, [Ids::POWERED_COMPARATOR], 0, ItemIds::COMPARATOR, TileComparator::class), "Redstone Comparator", BreakInfo::instant()));
$this->registerAllMeta(new RedstoneLamp(new BIDFlattened(Ids::REDSTONE_LAMP, [Ids::LIT_REDSTONE_LAMP], 0), "Redstone Lamp", new BreakInfo(0.3)));
$this->registerAllMeta(new RedstoneOre(new BIDFlattened(Ids::REDSTONE_ORE, [Ids::LIT_REDSTONE_ORE], 0), "Redstone Ore", new BreakInfo(3.0, ToolType::PICKAXE, ToolTier::IRON()->getHarvestLevel())));
$this->registerAllMeta(new RedstoneRepeater(new BIDFlattened(Ids::UNPOWERED_REPEATER, [Ids::POWERED_REPEATER], 0, ItemIds::REPEATER), "Redstone Repeater", BreakInfo::instant()));
$this->registerAllMeta(new RedstoneTorch(new BIDFlattened(Ids::REDSTONE_TORCH, [Ids::UNLIT_REDSTONE_TORCH], 0), "Redstone Torch", BreakInfo::instant()));
$this->registerAllMeta(new RedstoneWire(new BID(Ids::REDSTONE_WIRE, 0, ItemIds::REDSTONE), "Redstone", BreakInfo::instant()));
$this->registerAllMeta(new Reserved6(new BID(Ids::RESERVED6, 0), "reserved6", BreakInfo::instant()));
$sandBreakInfo = new BlockBreakInfo(0.5, BlockToolType::SHOVEL);
$sandBreakInfo = new BreakInfo(0.5, ToolType::SHOVEL);
$this->registerAllMeta(
new Sand(new BID(Ids::SAND, 0), "Sand", $sandBreakInfo),
new Sand(new BID(Ids::SAND, 1), "Red Sand", $sandBreakInfo)
);
$this->registerAllMeta(new SeaLantern(new BID(Ids::SEALANTERN, 0), "Sea Lantern", new BlockBreakInfo(0.3)));
$this->registerAllMeta(new SeaPickle(new BID(Ids::SEA_PICKLE, 0), "Sea Pickle", BlockBreakInfo::instant()));
$this->registerAllMeta(new Skull(new BID(Ids::MOB_HEAD_BLOCK, 0, ItemIds::SKULL, TileSkull::class), "Mob Head", new BlockBreakInfo(1.0)));
$this->registerAllMeta(new Slime(new BID(Ids::SLIME, 0), "Slime Block", BlockBreakInfo::instant()));
$this->registerAllMeta(new Snow(new BID(Ids::SNOW, 0), "Snow Block", new BlockBreakInfo(0.2, BlockToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new SnowLayer(new BID(Ids::SNOW_LAYER, 0), "Snow Layer", new BlockBreakInfo(0.1, BlockToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new SoulSand(new BID(Ids::SOUL_SAND, 0), "Soul Sand", new BlockBreakInfo(0.5, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Sponge(new BID(Ids::SPONGE, 0), "Sponge", new BlockBreakInfo(0.6, BlockToolType::HOE)));
$shulkerBoxBreakInfo = new BlockBreakInfo(2, BlockToolType::PICKAXE);
$this->registerAllMeta(new SeaLantern(new BID(Ids::SEALANTERN, 0), "Sea Lantern", new BreakInfo(0.3)));
$this->registerAllMeta(new SeaPickle(new BID(Ids::SEA_PICKLE, 0), "Sea Pickle", BreakInfo::instant()));
$this->registerAllMeta(new Skull(new BID(Ids::MOB_HEAD_BLOCK, 0, ItemIds::SKULL, TileSkull::class), "Mob Head", new BreakInfo(1.0)));
$this->registerAllMeta(new Slime(new BID(Ids::SLIME, 0), "Slime Block", BreakInfo::instant()));
$this->registerAllMeta(new Snow(new BID(Ids::SNOW, 0), "Snow Block", new BreakInfo(0.2, ToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new SnowLayer(new BID(Ids::SNOW_LAYER, 0), "Snow Layer", new BreakInfo(0.1, ToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new SoulSand(new BID(Ids::SOUL_SAND, 0), "Soul Sand", new BreakInfo(0.5, ToolType::SHOVEL)));
$this->registerAllMeta(new Sponge(new BID(Ids::SPONGE, 0), "Sponge", new BreakInfo(0.6, ToolType::HOE)));
$shulkerBoxBreakInfo = new BreakInfo(2, ToolType::PICKAXE);
$this->registerAllMeta(new ShulkerBox(new BID(Ids::UNDYED_SHULKER_BOX, 0, null, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo));
$stoneBreakInfo = new BlockBreakInfo(1.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$stoneBreakInfo = new BreakInfo(1.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta(
$stone = new class(new BID(Ids::STONE, Meta::STONE_NORMAL), "Stone", $stoneBreakInfo) extends Opaque{
public function getDropsForCompatibleTool(Item $item) : array{
@ -374,7 +376,7 @@ class BlockFactory{
$crackedStoneBrick = new Opaque(new BID(Ids::STONEBRICK, Meta::STONE_BRICK_CRACKED), "Cracked Stone Bricks", $stoneBreakInfo),
$chiseledStoneBrick = new Opaque(new BID(Ids::STONEBRICK, Meta::STONE_BRICK_CHISELED), "Chiseled Stone Bricks", $stoneBreakInfo)
);
$infestedStoneBreakInfo = new BlockBreakInfo(0.75, BlockToolType::PICKAXE);
$infestedStoneBreakInfo = new BreakInfo(0.75, ToolType::PICKAXE);
$this->registerAllMeta(
new InfestedStone(new BID(Ids::MONSTER_EGG, Meta::INFESTED_STONE), "Infested Stone", $infestedStoneBreakInfo, $stone),
new InfestedStone(new BID(Ids::MONSTER_EGG, Meta::INFESTED_STONE_BRICK), "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick),
@ -393,12 +395,12 @@ class BlockFactory{
$this->registerAllMeta(new Stair(new BID(Ids::POLISHED_GRANITE_STAIRS, 0), "Polished Granite Stairs", $stoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::STONE_BRICK_STAIRS, 0), "Stone Brick Stairs", $stoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS, 0), "Mossy Stone Brick Stairs", $stoneBreakInfo));
$this->registerAllMeta(new StoneButton(new BID(Ids::STONE_BUTTON, 0), "Stone Button", new BlockBreakInfo(0.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new Stonecutter(new BID(Ids::STONECUTTER_BLOCK, 0, ItemIds::STONECUTTER_BLOCK), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE)));
$this->registerAllMeta(new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE, 0), "Stone Pressure Plate", new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new StoneButton(new BID(Ids::STONE_BUTTON, 0), "Stone Button", new BreakInfo(0.5, ToolType::PICKAXE)));
$this->registerAllMeta(new Stonecutter(new BID(Ids::STONECUTTER_BLOCK, 0, ItemIds::STONECUTTER_BLOCK), "Stonecutter", new BreakInfo(3.5, ToolType::PICKAXE)));
$this->registerAllMeta(new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE, 0), "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 BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$stoneSlabBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$getStoneSlabId = static fn(int $stoneSlabId, int $meta) => BlockLegacyIdHelper::getStoneSlabIdentifier($stoneSlabId, $meta);
foreach([
@ -435,50 +437,50 @@ class BlockFactory{
$this->registerSlabWithDoubleHighBitsRemapping($slabType);
}
$this->registerAllMeta(new Opaque(new BID(Ids::STONECUTTER, 0), "Legacy Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Sugarcane(new BID(Ids::REEDS_BLOCK, 0, ItemIds::REEDS), "Sugarcane", BlockBreakInfo::instant()));
$this->registerAllMeta(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
$this->registerAllMeta(new TNT(new BID(Ids::TNT, 0), "TNT", BlockBreakInfo::instant()));
$this->registerAllMeta(new Opaque(new BID(Ids::STONECUTTER, 0), "Legacy Stonecutter", new BreakInfo(3.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new Sugarcane(new BID(Ids::REEDS_BLOCK, 0, ItemIds::REEDS), "Sugarcane", BreakInfo::instant()));
$this->registerAllMeta(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BreakInfo::instant()));
$this->registerAllMeta(new TNT(new BID(Ids::TNT, 0), "TNT", BreakInfo::instant()));
$this->registerAllMeta(
new TallGrass(new BID(Ids::TALLGRASS, Meta::TALLGRASS_FERN), "Fern", BlockBreakInfo::instant(BlockToolType::SHEARS, 1)),
new TallGrass(new BID(Ids::TALLGRASS, Meta::TALLGRASS_NORMAL), "Tall Grass", BlockBreakInfo::instant(BlockToolType::SHEARS, 1))
new TallGrass(new BID(Ids::TALLGRASS, Meta::TALLGRASS_FERN), "Fern", BreakInfo::instant(ToolType::SHEARS, 1)),
new TallGrass(new BID(Ids::TALLGRASS, Meta::TALLGRASS_NORMAL), "Tall Grass", BreakInfo::instant(ToolType::SHEARS, 1))
);
$this->registerAllMeta(
new Torch(new BID(Ids::COLORED_TORCH_BP, 0), "Blue Torch", BlockBreakInfo::instant()),
new Torch(new BID(Ids::COLORED_TORCH_BP, 8), "Purple Torch", BlockBreakInfo::instant())
new Torch(new BID(Ids::COLORED_TORCH_BP, 0), "Blue Torch", BreakInfo::instant()),
new Torch(new BID(Ids::COLORED_TORCH_BP, 8), "Purple Torch", BreakInfo::instant())
);
$this->registerAllMeta(
new Torch(new BID(Ids::COLORED_TORCH_RG, 0), "Red Torch", BlockBreakInfo::instant()),
new Torch(new BID(Ids::COLORED_TORCH_RG, 8), "Green Torch", BlockBreakInfo::instant())
new Torch(new BID(Ids::COLORED_TORCH_RG, 0), "Red Torch", BreakInfo::instant()),
new Torch(new BID(Ids::COLORED_TORCH_RG, 8), "Green Torch", BreakInfo::instant())
);
$this->registerAllMeta(new Torch(new BID(Ids::TORCH, 0), "Torch", BlockBreakInfo::instant()));
$this->registerAllMeta(new Torch(new BID(Ids::TORCH, 0), "Torch", BreakInfo::instant()));
$this->registerAllMeta(new TrappedChest(new BID(Ids::TRAPPED_CHEST, 0, null, TileChest::class), "Trapped Chest", $chestBreakInfo));
$this->registerAllMeta(new Tripwire(new BID(Ids::TRIPWIRE, 0, ItemIds::STRING), "Tripwire", BlockBreakInfo::instant()));
$this->registerAllMeta(new TripwireHook(new BID(Ids::TRIPWIRE_HOOK, 0), "Tripwire Hook", BlockBreakInfo::instant()));
$this->registerAllMeta(new UnderwaterTorch(new BID(Ids::UNDERWATER_TORCH, 0), "Underwater Torch", BlockBreakInfo::instant()));
$this->registerAllMeta(new Vine(new BID(Ids::VINE, 0), "Vines", new BlockBreakInfo(0.2, BlockToolType::AXE)));
$this->registerAllMeta(new Water(new BIDFlattened(Ids::FLOWING_WATER, [Ids::STILL_WATER], 0), "Water", BlockBreakInfo::indestructible(500.0)));
$this->registerAllMeta(new WaterLily(new BID(Ids::LILY_PAD, 0), "Lily Pad", BlockBreakInfo::instant()));
$this->registerAllMeta(new Tripwire(new BID(Ids::TRIPWIRE, 0, ItemIds::STRING), "Tripwire", BreakInfo::instant()));
$this->registerAllMeta(new TripwireHook(new BID(Ids::TRIPWIRE_HOOK, 0), "Tripwire Hook", BreakInfo::instant()));
$this->registerAllMeta(new UnderwaterTorch(new BID(Ids::UNDERWATER_TORCH, 0), "Underwater Torch", BreakInfo::instant()));
$this->registerAllMeta(new Vine(new BID(Ids::VINE, 0), "Vines", new BreakInfo(0.2, ToolType::AXE)));
$this->registerAllMeta(new Water(new BIDFlattened(Ids::FLOWING_WATER, [Ids::STILL_WATER], 0), "Water", BreakInfo::indestructible(500.0)));
$this->registerAllMeta(new WaterLily(new BID(Ids::LILY_PAD, 0), "Lily Pad", BreakInfo::instant()));
$weightedPressurePlateBreakInfo = new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$weightedPressurePlateBreakInfo = new BreakInfo(0.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$this->registerAllMeta(new WeightedPressurePlateHeavy(new BID(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE, 0), "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo));
$this->registerAllMeta(new WeightedPressurePlateLight(new BID(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE, 0), "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo));
$this->registerAllMeta(new Wheat(new BID(Ids::WHEAT_BLOCK, 0), "Wheat Block", BlockBreakInfo::instant()));
$this->registerAllMeta(new Wheat(new BID(Ids::WHEAT_BLOCK, 0), "Wheat Block", BreakInfo::instant()));
$planksBreakInfo = new BlockBreakInfo(2.0, BlockToolType::AXE, 0, 15.0);
$leavesBreakInfo = new class(0.2, BlockToolType::HOE) extends BlockBreakInfo{
$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() === BlockToolType::SHEARS){
if($item->getBlockToolType() === ToolType::SHEARS){
return 0.0;
}
return parent::getBreakTime($item);
}
};
$signBreakInfo = new BlockBreakInfo(1.0, BlockToolType::AXE);
$logBreakInfo = new BlockBreakInfo(2.0, BlockToolType::AXE);
$woodenDoorBreakInfo = new BlockBreakInfo(3.0, BlockToolType::AXE, 0, 15.0);
$woodenButtonBreakInfo = new BlockBreakInfo(0.5, BlockToolType::AXE);
$woodenPressurePlateBreakInfo = new BlockBreakInfo(0.5, BlockToolType::AXE);
$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);
$planks = [];
$saplings = [];
@ -489,7 +491,7 @@ class BlockFactory{
$magicNumber = $treeType->getMagicNumber();
$name = $treeType->getDisplayName();
$planks[] = new Planks(new BID(Ids::PLANKS, $magicNumber), $name . " Planks", $planksBreakInfo);
$saplings[] = new Sapling(new BID(Ids::SAPLING, $magicNumber), $name . " Sapling", BlockBreakInfo::instant(), $treeType);
$saplings[] = new Sapling(new BID(Ids::SAPLING, $magicNumber), $name . " Sapling", BreakInfo::instant(), $treeType);
$fences[] = new WoodenFence(new BID(Ids::FENCE, $magicNumber), $name . " Fence", $planksBreakInfo);
$this->registerSlabWithDoubleHighBitsRemapping(new WoodenSlab(new BIDFlattened(Ids::WOODEN_SLAB, [Ids::DOUBLE_WOODEN_SLAB], $magicNumber), $name, $planksBreakInfo));
@ -521,27 +523,26 @@ class BlockFactory{
$this->registerAllMeta(...$leaves);
$this->registerAllMeta(...$allSidedLogs);
static $sandstoneTypes = [
Meta::SANDSTONE_NORMAL => "",
Meta::SANDSTONE_CHISELED => "Chiseled ",
Meta::SANDSTONE_CUT => "Cut ",
Meta::SANDSTONE_SMOOTH => "Smooth "
];
$sandstoneBreakInfo = new BlockBreakInfo(0.8, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$sandstoneBreakInfo = new BreakInfo(0.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$this->registerAllMeta(new Stair(new BID(Ids::RED_SANDSTONE_STAIRS, 0), "Red Sandstone Stairs", $sandstoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::SMOOTH_RED_SANDSTONE_STAIRS, 0), "Smooth Red Sandstone Stairs", $sandstoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::SANDSTONE_STAIRS, 0), "Sandstone Stairs", $sandstoneBreakInfo));
$this->registerAllMeta(new Stair(new BID(Ids::SMOOTH_SANDSTONE_STAIRS, 0), "Smooth Sandstone Stairs", $sandstoneBreakInfo));
$sandstones = [];
$redSandstones = [];
foreach($sandstoneTypes as $variant => $prefix){
foreach([
Meta::SANDSTONE_NORMAL => "",
Meta::SANDSTONE_CHISELED => "Chiseled ",
Meta::SANDSTONE_CUT => "Cut ",
Meta::SANDSTONE_SMOOTH => "Smooth "
] as $variant => $prefix){
$sandstones[] = new Opaque(new BID(Ids::SANDSTONE, $variant), $prefix . "Sandstone", $sandstoneBreakInfo);
$redSandstones[] = new Opaque(new BID(Ids::RED_SANDSTONE, $variant), $prefix . "Red Sandstone", $sandstoneBreakInfo);
}
$this->registerAllMeta(...$sandstones);
$this->registerAllMeta(...$redSandstones);
$glazedTerracottaBreakInfo = new BlockBreakInfo(1.4, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$glazedTerracottaBreakInfo = new BreakInfo(1.4, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
foreach(DyeColor::getAll() as $color){
$coloredName = function(string $name) use($color) : string{
return $color->getDisplayName() . " " . $name;
@ -554,13 +555,13 @@ class BlockFactory{
$this->registerAllMeta(new StainedHardenedClay(new BID(Ids::STAINED_CLAY, 0), "Stained Clay", $hardenedClayBreakInfo));
$this->registerAllMeta(new StainedHardenedGlass(new BID(Ids::HARD_STAINED_GLASS, 0), "Stained Hardened Glass", $hardenedGlassBreakInfo));
$this->registerAllMeta(new StainedHardenedGlassPane(new BID(Ids::HARD_STAINED_GLASS_PANE, 0), "Stained Hardened Glass Pane", $hardenedGlassBreakInfo));
$this->registerAllMeta(new Carpet(new BID(Ids::CARPET, 0), "Carpet", new BlockBreakInfo(0.1)));
$this->registerAllMeta(new Concrete(new BID(Ids::CONCRETE, 0), "Concrete", new BlockBreakInfo(1.8, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new ConcretePowder(new BID(Ids::CONCRETE_POWDER, 0), "Concrete Powder", new BlockBreakInfo(0.5, BlockToolType::SHOVEL)));
$this->registerAllMeta(new Wool(new BID(Ids::WOOL, 0), "Wool", new class(0.8, BlockToolType::SHEARS) extends BlockBreakInfo{
$this->registerAllMeta(new Carpet(new BID(Ids::CARPET, 0), "Carpet", new BreakInfo(0.1)));
$this->registerAllMeta(new Concrete(new BID(Ids::CONCRETE, 0), "Concrete", new BreakInfo(1.8, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
$this->registerAllMeta(new ConcretePowder(new BID(Ids::CONCRETE_POWDER, 0), "Concrete Powder", new BreakInfo(0.5, ToolType::SHOVEL)));
$this->registerAllMeta(new Wool(new BID(Ids::WOOL, 0), "Wool", new class(0.8, ToolType::SHEARS) extends BreakInfo{
public function getBreakTime(Item $item) : float{
$time = parent::getBreakTime($item);
if($item->getBlockToolType() === BlockToolType::SHEARS){
if($item->getBlockToolType() === ToolType::SHEARS){
$time *= 3; //shears break compatible blocks 15x faster, but wool 5x
}
@ -569,7 +570,7 @@ class BlockFactory{
}));
//TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap
$wallBreakInfo = new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$wallBreakInfo = new BreakInfo(2.0, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
$this->registerAllMeta(
new Wall(new BID(Ids::COBBLESTONE_WALL, Meta::WALL_COBBLESTONE), "Cobblestone Wall", $wallBreakInfo),
new Wall(new BID(Ids::COBBLESTONE_WALL, Meta::WALL_ANDESITE), "Andesite Wall", $wallBreakInfo),
@ -589,7 +590,7 @@ class BlockFactory{
$this->registerElements();
$chemistryTableBreakInfo = new BlockBreakInfo(2.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$chemistryTableBreakInfo = new BreakInfo(2.5, ToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel());
$this->registerAllMeta(
new ChemistryTable(new BID(Ids::CHEMISTRY_TABLE, Meta::CHEMISTRY_COMPOUND_CREATOR), "Compound Creator", $chemistryTableBreakInfo),
new ChemistryTable(new BID(Ids::CHEMISTRY_TABLE, Meta::CHEMISTRY_ELEMENT_CONSTRUCTOR), "Element Constructor", $chemistryTableBreakInfo),
@ -604,17 +605,17 @@ class BlockFactory{
$this->registerAllMeta(new Coral(
new BID(Ids::CORAL, 0),
"Coral",
BlockBreakInfo::instant(),
BreakInfo::instant(),
));
$this->registerAllMeta(new FloorCoralFan(
new BlockIdentifierFlattened(Ids::CORAL_FAN, [Ids::CORAL_FAN_DEAD], 0, ItemIds::CORAL_FAN),
"Coral Fan",
BlockBreakInfo::instant(),
BreakInfo::instant(),
));
$this->registerAllMeta(new WallCoralFan(
new BlockIdentifierFlattened(Ids::CORAL_FAN_HANG, [Ids::CORAL_FAN_HANG2, Ids::CORAL_FAN_HANG3], 0, ItemIds::CORAL_FAN),
"Wall Coral Fan",
BlockBreakInfo::instant(),
BreakInfo::instant(),
));
//region --- auto-generated TODOs for bedrock-1.11.0 ---
@ -756,7 +757,7 @@ class BlockFactory{
//shrooms have to be handled one by one because some metas are variants and others aren't, and they can't be
//separated by a bitmask
$mushroomBlockBreakInfo = new BlockBreakInfo(0.2, BlockToolType::AXE);
$mushroomBlockBreakInfo = new BreakInfo(0.2, ToolType::AXE);
$mushroomBlocks = [
new BrownMushroomBlock(new BID(Ids::BROWN_MUSHROOM_BLOCK, 0), "Brown Mushroom Block", $mushroomBlockBreakInfo),
@ -800,7 +801,7 @@ class BlockFactory{
}
private function registerElements() : void{
$instaBreak = BlockBreakInfo::instant();
$instaBreak = BreakInfo::instant();
$this->registerAllMeta(new Opaque(new BID(Ids::ELEMENT_0, 0), "???", $instaBreak));
$this->registerAllMeta(new Element(new BID(Ids::ELEMENT_1, 0), "Hydrogen", $instaBreak, "h", 1, 5));
@ -1054,7 +1055,7 @@ class BlockFactory{
if($this->fullList[$index] !== null){
$block = clone $this->fullList[$index];
}else{
$block = new UnknownBlock(new BID($id, $meta), BlockBreakInfo::instant());
$block = new UnknownBlock(new BID($id, $meta), BreakInfo::instant());
}
return $block;

View File

@ -72,7 +72,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)];
}

View File

@ -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{

View File

@ -94,7 +94,8 @@ class Lava extends Liquid{
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
$entity->attack($ev);
$ev = new EntityCombustByBlockEvent($this, $entity, 15);
//in java burns entities for 15 seconds - seems to be a parity issue in bedrock
$ev = new EntityCombustByBlockEvent($this, $entity, 8);
$ev->call();
if(!$ev->isCancelled()){
$entity->setOnFire($ev->getDuration());

View File

@ -69,7 +69,6 @@ class Lever extends Flowable{
5 => LeverFacing::UP_AXIS_Z(),
6 => LeverFacing::UP_AXIS_X(),
7 => LeverFacing::DOWN_AXIS_Z(),
default => throw new AssumptionFailedError("0x07 mask should make this impossible"), //phpstan doesn't understand :(
};
$this->activated = ($stateMeta & BlockLegacyMetadata::LEVER_FLAG_POWERED) !== 0;

View File

@ -348,7 +348,7 @@ abstract class Liquid extends Transparent{
$ev = new BlockSpreadEvent($block, $this, $new);
$ev->call();
if(!$ev->isCancelled()){
if($block->getId() > 0){
if($block->getId() !== BlockLegacyIds::AIR){
$this->position->getWorld()->useBreakOn($block->position);
}

View File

@ -27,7 +27,7 @@ use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use function mt_rand;
class Melon extends Transparent{
class Melon extends Opaque{
public function getDropsForCompatibleTool(Item $item) : array{
return [

View File

@ -23,6 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
/**
* Opaque blocks do not allow light to pass through. They are usually collidable full-cube blocks.
* Most blocks in Minecraft fall into this category.
*/
class Opaque extends Block{
public function isSolid() : bool{

View File

@ -43,7 +43,11 @@ class RedMushroom 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->isTransparent()){
$position = $this->getPosition();
$lightLevel = $position->getWorld()->getFullLightAt($position->x, $position->y, $position->z);
$downId = $down->getId();
//TODO: nylium support
if(($lightLevel <= 12 && !$down->isTransparent()) || $downId === BlockLegacyIds::MYCELIUM || $downId === BlockLegacyIds::PODZOL){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -56,7 +56,8 @@ class Skull extends Flowable{
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = $stateMeta === 1 ? Facing::UP : BlockDataSerializer::readHorizontalFacing($stateMeta);
$facingMeta = $stateMeta & 0x7;
$this->facing = $facingMeta === 1 ? Facing::UP : BlockDataSerializer::readHorizontalFacing($facingMeta);
$this->noDrops = ($stateMeta & BlockLegacyMetadata::SKULL_FLAG_NO_DROPS) !== 0;
}

View File

@ -98,7 +98,7 @@ class Sugarcane extends Flowable{
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if($down->isTransparent() && !$down->isSameType($this)){
if(!$this->isValidSupport($down)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
@ -122,9 +122,10 @@ class Sugarcane extends Flowable{
$down = $this->getSide(Facing::DOWN);
if($down->isSameType($this)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($down->getId() === BlockLegacyIds::GRASS || $down->getId() === BlockLegacyIds::DIRT || $down->getId() === BlockLegacyIds::SAND || $down->getId() === BlockLegacyIds::PODZOL){
}elseif($this->isValidSupport($down)){
foreach(Facing::HORIZONTAL as $side){
if($down->getSide($side) instanceof Water){
$sideBlock = $down->getSide($side);
if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}
@ -132,4 +133,15 @@ class Sugarcane extends Flowable{
return false;
}
private function isValidSupport(Block $block) : bool{
$id = $block->getId();
//TODO: rooted dirt, moss block
return $block->isSameType($this)
|| $id === BlockLegacyIds::GRASS
|| $id === BlockLegacyIds::DIRT
|| $id === BlockLegacyIds::PODZOL
|| $id === BlockLegacyIds::MYCELIUM
|| $id === BlockLegacyIds::SAND;
}
}

View File

@ -29,6 +29,9 @@ use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use function count;
/**
* Thin blocks behave like glass panes. They connect to full-cube blocks horizontally adjacent to them if possible.
*/
class Thin extends Transparent{
/** @var bool[] facing => dummy */
protected array $connections = [];

View File

@ -23,6 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block;
/**
* Transparent blocks do not block any light from propagating through them.
*
* Note: This does **not** imply that the block is **visually** transparent. For example, chests allow light to pass
* through, but the player cannot see through them except at the edges.
*/
class Transparent extends Block{
public function isTransparent() : bool{

View File

@ -25,6 +25,9 @@ namespace pocketmine\block;
use pocketmine\item\Item;
/**
* Represents a block which is unrecognized or not implemented.
*/
class UnknownBlock extends Transparent{
public function __construct(BlockIdentifier $idInfo, BlockBreakInfo $breakInfo){

View File

@ -585,6 +585,7 @@ final class VanillaBlocks{
/**
* @return Block[]
* @phpstan-return array<string, Block>
*/
public static function getAll() : array{
//phpstan doesn't support generic traits yet :(

View File

@ -116,7 +116,7 @@ class Vine extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockClicked->isSolid() || Facing::axis($face) === Axis::Y){
if(!$blockClicked->isFullCube() || Facing::axis($face) === Axis::Y){
return false;
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\utils\SignText;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\utils\Binary;
use pocketmine\world\World;
use function array_pad;
use function array_slice;
@ -42,6 +43,13 @@ use function sprintf;
class Sign extends Spawnable{
public const TAG_TEXT_BLOB = "Text";
public const TAG_TEXT_LINE = "Text%d"; //sprintf()able
public const TAG_TEXT_COLOR = "SignTextColor";
public const TAG_GLOWING_TEXT = "IgnoreLighting";
/**
* This tag is set to indicate that MCPE-117835 has been addressed in whatever version this sign was created.
* @see https://bugs.mojang.com/browse/MCPE-117835
*/
public const TAG_LEGACY_BUG_RESOLVE = "TextIgnoreLegacyBugResolved";
/**
* @return string[]
@ -111,5 +119,11 @@ class Sign extends Spawnable{
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()));
//the following are not yet used by the server, but needed to roll back any changes to glowing state or colour
//if the client uses dye on the sign
$nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt(0xff_00_00_00));
$nbt->setByte(self::TAG_GLOWING_TEXT, 0);
$nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1);
}
}

View File

@ -24,9 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\Block;
use pocketmine\block\BlockLegacyIds;
use pocketmine\block\Fire;
use pocketmine\block\Liquid;
use pocketmine\block\VanillaBlocks;
use pocketmine\entity\Location;
use pocketmine\entity\object\FallingBlock;
@ -50,7 +47,7 @@ trait FallableTrait{
public function onNearbyBlockChange() : void{
$pos = $this->getPosition();
$down = $pos->getWorld()->getBlock($pos->getSide(Facing::DOWN));
if($down->getId() === BlockLegacyIds::AIR || $down instanceof Liquid || $down instanceof Fire){
if($down->canBeReplaced()){
$pos->getWorld()->setBlock($pos, VanillaBlocks::AIR());
$block = $this;

View File

@ -39,7 +39,7 @@ class SignText{
private array $lines;
/**
* @param string[]|null $lines index-sensitive; omitting an index will leave it unchanged
* @param string[]|null $lines index-sensitive; keys 0-3 will be used, regardless of array order
*
* @throws \InvalidArgumentException if the array size is greater than 4
* @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array

View File

@ -85,7 +85,7 @@ class FormattedCommandAlias extends Command{
$target->timings->startTiming();
try{
$target->execute($sender, $commandLabel, $args);
$target->execute($sender, $commandLabel, $commandArgs);
}catch(InvalidCommandSyntaxException $e){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
}finally{

View File

@ -75,7 +75,11 @@ class GiveCommand extends VanillaCommand{
if(!isset($args[2])){
$item->setCount($item->getMaxStackSize());
}else{
$item->setCount((int) $args[2]);
$count = $this->getBoundedInt($sender, $args[2], 1, 32767);
if($count === null){
return true;
}
$item->setCount($count);
}
if(isset($args[3])){

View File

@ -103,7 +103,7 @@ class StatusCommand extends VanillaCommand{
$globalLimit = $server->getMemoryManager()->getGlobalMemoryLimit();
if($globalLimit > 0){
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (manager): " . TextFormat::RED . number_format(round($globalLimit, 2), 2) . " MB.");
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (manager): " . TextFormat::RED . number_format(round(($globalLimit / 1024) / 1024, 2), 2) . " MB.");
}
foreach($server->getWorldManager()->getWorlds() as $world){

View File

@ -32,9 +32,7 @@ use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\VersionInfo;
use function count;
use function function_exists;
use function implode;
use function opcache_get_status;
use function sprintf;
use function stripos;
use function strtolower;
@ -71,16 +69,10 @@ class VersionCommand extends VanillaCommand{
));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpVersion(PHP_VERSION));
if(
function_exists('opcache_get_status') &&
($opcacheStatus = opcache_get_status(false)) !== false &&
isset($opcacheStatus["jit"]["on"])
){
$jit = $opcacheStatus["jit"];
if($jit["on"] === true){
$jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitEnabled(
sprintf("CRTO: %s%s%s%s", $jit["opt_flags"] >> 2, $jit["opt_flags"] & 0x03, $jit["kind"], $jit["opt_level"])
);
$jitMode = Utils::getOpcacheJitMode();
if($jitMode !== null){
if($jitMode !== 0){
$jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitEnabled(sprintf("CRTO: %d", $jitMode));
}else{
$jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitDisabled();
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\crafting;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use function array_map;
@ -33,6 +34,23 @@ use function json_decode;
final class CraftingManagerFromDataHelper{
/**
* @param Item[] $items
*/
private static function containsUnknownOutputs(array $items) : bool{
$factory = ItemFactory::getInstance();
foreach($items as $item){
if($item->hasAnyDamageValue()){
throw new \InvalidArgumentException("Recipe outputs must not have wildcard meta values");
}
if(!$factory->isRegistered($item->getId(), $item->getMeta())){
return true;
}
}
return false;
}
public static function make(string $filePath) : CraftingManager{
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true);
if(!is_array($recipes)){
@ -52,9 +70,13 @@ final class CraftingManagerFromDataHelper{
if($recipeType === null){
continue;
}
$output = array_map($itemDeserializerFunc, $recipe["output"]);
if(self::containsUnknownOutputs($output)){
continue;
}
$result->registerShapelessRecipe(new ShapelessRecipe(
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"]),
$output,
$recipeType
));
}
@ -62,10 +84,14 @@ final class CraftingManagerFromDataHelper{
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
continue;
}
$output = array_map($itemDeserializerFunc, $recipe["output"]);
if(self::containsUnknownOutputs($output)){
continue;
}
$result->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"],
array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"])
$output
));
}
foreach($recipes["smelting"] as $recipe){
@ -79,19 +105,30 @@ final class CraftingManagerFromDataHelper{
if($furnaceType === null){
continue;
}
$output = Item::jsonDeserialize($recipe["output"]);
if(self::containsUnknownOutputs([$output])){
continue;
}
$result->getFurnaceRecipeManager($furnaceType)->register(new FurnaceRecipe(
Item::jsonDeserialize($recipe["output"]),
$output,
Item::jsonDeserialize($recipe["input"]))
);
}
foreach($recipes["potion_type"] as $recipe){
$output = Item::jsonDeserialize($recipe["output"]);
if(self::containsUnknownOutputs([$output])){
continue;
}
$result->registerPotionTypeRecipe(new PotionTypeRecipe(
Item::jsonDeserialize($recipe["input"]),
Item::jsonDeserialize($recipe["ingredient"]),
Item::jsonDeserialize($recipe["output"])
$output
));
}
foreach($recipes["potion_container_change"] as $recipe){
if(!ItemFactory::getInstance()->isRegistered($recipe["output_item_id"])){
continue;
}
$result->registerPotionContainerChangeRecipe(new PotionContainerChangeRecipe(
$recipe["input_item_id"],
Item::jsonDeserialize($recipe["ingredient"]),

View File

@ -164,6 +164,8 @@ class CrashDump{
}
$this->data->extensions = $extensions;
$this->data->jit_mode = Utils::getOpcacheJitMode();
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){
ob_start();
phpinfo();

View File

@ -66,6 +66,8 @@ final class CrashDumpData implements \JsonSerializable{
*/
public array $extensions = [];
public ?int $jit_mode = null;
public string $phpinfo = "";
public CrashDumpDataGeneral $general;

View File

@ -74,7 +74,7 @@ final class DyeColorIdMap{
}
public function fromId(int $id) : ?DyeColor{
return $this->idToEnum[$id];
return $this->idToEnum[$id] ?? null;
}
public function fromInvertedId(int $id) : ?DyeColor{

View File

@ -522,6 +522,9 @@ abstract class Entity{
}
public function attack(EntityDamageEvent $source) : void{
if($this->isFireProof() && ($source->getCause() === EntityDamageEvent::CAUSE_FIRE || $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK)){
$source->cancel();
}
$source->call();
if($source->isCancelled()){
return;
@ -1461,8 +1464,9 @@ abstract class Entity{
$this->location->pitch,
$this->location->yaw,
$this->location->yaw, //TODO: head yaw
$this->location->yaw, //TODO: body yaw (wtf mojang?)
array_map(function(Attribute $attr) : NetworkAttribute{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
}, $this->attributeMap->getAll()),
$this->getAllNetworkData(),
[] //TODO: entity links

View File

@ -227,7 +227,7 @@ final class EntityFactory{
*/
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
try{
$saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$saveId = $nbt->getTag("identifier") ?? $nbt->getTag("id");
$func = null;
if($saveId instanceof StringTag){
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
@ -248,7 +248,7 @@ final class EntityFactory{
public function injectSaveId(string $class, CompoundTag $saveData) : void{
if(isset($this->saveNames[$class])){
$saveData->setTag("id", new StringTag($this->saveNames[$class]));
$saveData->setTag("identifier", new StringTag($this->saveNames[$class]));
}else{
throw new \InvalidArgumentException("Entity $class is not registered");
}

View File

@ -237,11 +237,10 @@ class ExperienceManager{
}
public function onPickupXp(int $xpValue) : void{
static $mainHandIndex = -1;
static $offHandIndex = -2;
$mainHandIndex = -1;
$offHandIndex = -2;
//TODO: replace this with a more generic equipment getting/setting interface
/** @var Durable[] $equipment */
$equipment = [];
if(($item = $this->entity->getInventory()->getItemInHand()) instanceof Durable && $item->hasEnchantment(VanillaEnchantments::MENDING())){

View File

@ -48,9 +48,9 @@ use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
@ -58,11 +58,15 @@ use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\UpdateAbilitiesPacketLayer;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\world\sound\TotemUseSound;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use function array_fill;
use function array_filter;
use function array_key_exists;
use function array_merge;
@ -471,7 +475,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
$this->getUniqueId(),
$this->getName(),
$this->getId(), //TODO: actor unique ID
$this->getId(),
"",
$this->location->asVector3(),
@ -482,7 +485,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
GameMode::SURVIVAL,
$this->getAllNetworkData(),
AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->getId()), //TODO
UpdateAbilitiesPacket::create(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
new UpdateAbilitiesPacketLayer(
UpdateAbilitiesPacketLayer::LAYER_BASE,
array_fill(0, UpdateAbilitiesPacketLayer::NUMBER_OF_ABILITIES, false),
0.0,
0.0
)
]),
[], //TODO: entity links
"", //device ID (we intentionally don't send this - secvuln)
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)

View File

@ -56,6 +56,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\player\Player;
use pocketmine\timings\Timings;
use pocketmine\utils\Binary;
use pocketmine\world\sound\BurpSound;
use pocketmine\world\sound\EntityLandSound;
use pocketmine\world\sound\EntityLongFallSound;
use pocketmine\world\sound\EntityShortFallSound;
@ -255,8 +256,7 @@ abstract class Living extends Entity{
$size = $this->getInitialSizeInfo();
if($this->isSwimming() || $this->isGliding()){
$width = $size->getWidth();
//we don't actually know an appropriate eye height for a swimming mob, but 2/3 should be good enough.
$this->setSize((new EntitySizeInfo($width, $width, $width * 2 / 3))->scale($this->getScale()));
$this->setSize((new EntitySizeInfo($width, $width, $width * 0.9))->scale($this->getScale()));
}else{
$this->setSize($size->scale($this->getScale()));
}
@ -320,6 +320,9 @@ abstract class Living extends Entity{
foreach($consumable->getAdditionalEffects() as $effect){
$this->effectManager->add($effect);
}
if($consumable instanceof FoodSource){
$this->broadcastSound(new BurpSound());
}
$consumable->onConsume($this);
}
@ -775,7 +778,7 @@ abstract class Living extends Entity{
$id = $block->getId();
if($transparent === null){
if($id !== 0){
if($id !== BlockLegacyIds::AIR){
break;
}
}else{

View File

@ -28,7 +28,6 @@ use pocketmine\utils\Limits;
use function implode;
use function in_array;
use function json_encode;
use function json_last_error_msg;
use function strlen;
use const JSON_THROW_ON_ERROR;
@ -68,9 +67,10 @@ final class Skin{
}
if($geometryData !== ""){
try{
$decodedGeometry = (new CommentedJsonDecoder())->decode($geometryData);
if($decodedGeometry === false){
throw new InvalidSkinException("Invalid geometry data (" . json_last_error_msg() . ")");
}catch(\RuntimeException $e){
throw new InvalidSkinException("Invalid geometry data: " . $e->getMessage(), 0, $e);
}
/*

View File

@ -38,7 +38,6 @@ class Effect{
* @param Translatable|string $name Translation key used for effect name
* @param Color $color Color of bubbles given by this effect
* @param bool $bad Whether the effect is harmful
* @param int $defaultDuration
* @param bool $hasBubbles Whether the effect has potion bubbles. Some do not (e.g. Instant Damage has its own particles instead of bubbles)
*/
public function __construct(

View File

@ -85,15 +85,12 @@ class EffectManager{
$index = spl_object_id($effectType);
if(isset($this->effects[$index])){
$effect = $this->effects[$index];
$hasExpired = $effect->hasExpired();
$ev = new EntityEffectRemoveEvent($this->entity, $effect);
$ev->call();
if($ev->isCancelled()){
if($hasExpired && !$ev->getEffect()->hasExpired()){ //altered duration of an expired effect to make it not get removed
foreach($this->effectAddHooks as $hook){
$hook($ev->getEffect(), true);
}
}
return;
}

View File

@ -33,5 +33,8 @@ class HealthBoostEffect extends Effect{
public function remove(Living $entity, EffectInstance $instance) : void{
$entity->setMaxHealth($entity->getMaxHealth() - 4 * $instance->getEffectLevel());
if($entity->getHealth() > $entity->getMaxHealth()){
$entity->setHealth($entity->getMaxHealth());
}
}
}

View File

@ -35,7 +35,7 @@ final class StringToEffectParser extends StringToTParser{
use SingletonTrait;
private static function make() : self{
$result = new self;
$result = new self();
$result->register("absorption", fn() => VanillaEffects::ABSORPTION());
$result->register("blindness", fn() => VanillaEffects::BLINDNESS());

View File

@ -101,6 +101,7 @@ final class VanillaEffects{
/**
* @return Effect[]
* @phpstan-return array<string, Effect>
*/
public static function getAll() : array{
//phpstan doesn't support generic traits yet :(

View File

@ -30,7 +30,7 @@ use pocketmine\event\entity\EntityDamageEvent;
class WitherEffect extends Effect{
public function canTick(EffectInstance $instance) : bool{
if(($interval = (50 >> $instance->getAmplifier())) > 0){
if(($interval = (40 >> $instance->getAmplifier())) > 0){
return ($instance->getDuration() % $interval) === 0;
}
return true;

View File

@ -186,6 +186,10 @@ class ItemEntity extends Entity{
return true;
}
public function canSaveWithChunk() : bool{
return !$this->item->isNull() && parent::canSaveWithChunk();
}
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setTag("Item", $this->item->nbtSerialize());

View File

@ -30,6 +30,7 @@ class PaintingMotive{
protected static $motives = [];
public static function init() : void{
self::$initialized = true;
foreach([
new PaintingMotive(1, 1, "Alban"),
new PaintingMotive(1, 1, "Aztec"),
@ -67,10 +68,16 @@ class PaintingMotive{
}
public static function registerMotive(PaintingMotive $motive) : void{
if(!self::$initialized){
self::init();
}
self::$motives[$motive->getName()] = $motive;
}
public static function getMotiveByName(string $name) : ?PaintingMotive{
if(!self::$initialized){
self::init();
}
return self::$motives[$name] ?? null;
}

View File

@ -31,7 +31,7 @@ class HandlerListManager{
private static ?self $globalInstance = null;
public static function global() : self{
return self::$globalInstance ?? (self::$globalInstance = new self);
return self::$globalInstance ?? (self::$globalInstance = new self());
}
/** @var HandlerList[] classname => HandlerList */

View File

@ -72,7 +72,7 @@ class BlockBreakEvent extends BlockEvent implements Cancellable{
* Returns the item used to destroy the block.
*/
public function getItem() : Item{
return $this->item;
return clone $this->item;
}
/**

View File

@ -1,5 +1,24 @@
<?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\event\block;

View File

@ -65,7 +65,7 @@ class BlockPlaceEvent extends BlockEvent implements Cancellable{
* Gets the item in hand
*/
public function getItem() : Item{
return $this->item;
return clone $this->item;
}
public function getBlockReplaced() : Block{

View File

@ -1,5 +1,24 @@
<?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\event\block;

View File

@ -69,7 +69,7 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
}
public function getItem() : Item{
return $this->item;
return clone $this->item;
}
public function getBlock() : Block{

View File

@ -60,6 +60,6 @@ class PlayerItemHeldEvent extends PlayerEvent implements Cancellable{
* Returns the item in the slot that the player is trying to equip.
*/
public function getItem() : Item{
return $this->item;
return clone $this->item;
}
}

View File

@ -47,7 +47,7 @@ class PlayerItemUseEvent extends PlayerEvent implements Cancellable{
* Returns the item used.
*/
public function getItem() : Item{
return $this->item;
return clone $this->item;
}
/**

View File

@ -76,11 +76,13 @@ abstract class BaseInventory implements Inventory{
/**
* @param Item[] $items
* @phpstan-param array<int, Item> $items
*/
abstract protected function internalSetContents(array $items) : void;
/**
* @param Item[] $items
* @phpstan-param array<int, Item> $items
*/
public function setContents(array $items) : void{
if(count($items) > $this->getSize()){
@ -132,6 +134,7 @@ abstract class BaseInventory implements Inventory{
return $slots;
}
public function first(Item $item, bool $exact = false) : int{
$count = $exact ? $item->getCount() : max(1, $item->getCount());
$checkDamage = $exact || !$item->hasAnyDamageValue();

View File

@ -47,12 +47,20 @@ interface Inventory{
public function setItem(int $index, Item $item) : void;
/**
* Returns an array of all the itemstacks in the inventory, indexed by their slot number.
* Empty slots are not included unless includeEmpty is true.
*
* @return Item[]
* @phpstan-return array<int, Item>
*/
public function getContents(bool $includeEmpty = false) : array;
/**
* Sets the contents of the inventory. Non-numeric offsets or offsets larger than the size of the inventory are
* ignored.
*
* @param Item[] $items
* @phpstan-param array<int, Item> $items
*/
public function setContents(array $items) : void;
@ -85,8 +93,10 @@ interface Inventory{
/**
* Will return all the Items that has the same id and metadata (if not null).
* Won't check amount
* The returned array is indexed by slot number.
*
* @return Item[]
* @phpstan-return array<int, Item>
*/
public function all(Item $item) : array;

View File

@ -58,6 +58,7 @@ class SimpleInventory extends BaseInventory{
/**
* @return Item[]
* @phpstan-return array<int, Item>
*/
public function getContents(bool $includeEmpty = false) : array{
$contents = [];

View File

@ -42,6 +42,9 @@ class DropItemAction extends InventoryAction{
if($this->targetItem->isNull()){
throw new TransactionValidationException("Cannot drop an empty itemstack");
}
if($this->targetItem->getCount() > $this->targetItem->getMaxStackSize()){
throw new TransactionValidationException("Target item exceeds item type max stack size");
}
}
public function onPreExecute(Player $source) : bool{

View File

@ -70,6 +70,12 @@ class SlotChangeAction extends InventoryAction{
if(!$this->inventory->getItem($this->inventorySlot)->equalsExact($this->sourceItem)){
throw new TransactionValidationException("Slot does not contain expected original item");
}
if($this->targetItem->getCount() > $this->targetItem->getMaxStackSize()){
throw new TransactionValidationException("Target item exceeds item type max stack size");
}
if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){
throw new TransactionValidationException("Target item exceeds inventory max stack size");
}
}
/**

View File

@ -23,7 +23,15 @@ declare(strict_types=1);
namespace pocketmine\item;
class FishingRod extends Item{
class FishingRod extends Durable{
public function getMaxStackSize() : int{
return 1;
}
public function getMaxDurability() : int{
return 384;
}
//TODO
}

View File

@ -100,8 +100,9 @@ class Item implements \JsonSerializable{
* Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register
* into the index.
*
* NOTE: This should NOT BE USED for creating items to set into an inventory. Use {@link ItemFactory#get} for that
* NOTE: This should NOT BE USED for creating items to set into an inventory. Use VanillaItems for that
* purpose.
* @see VanillaItems
*/
public function __construct(ItemIdentifier $identifier, string $name = "Unknown"){
$this->identifier = $identifier;
@ -677,7 +678,7 @@ class Item implements \JsonSerializable{
*/
public static function nbtDeserialize(CompoundTag $tag) : Item{
if($tag->getTag("id") === null || $tag->getTag("Count") === null){
return ItemFactory::getInstance()->get(0);
return VanillaItems::AIR();
}
$count = Binary::unsignByte($tag->getByte("Count"));

View File

@ -30,7 +30,7 @@ use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\RecordType;
use pocketmine\block\utils\SkullType;
use pocketmine\block\utils\TreeType;
use pocketmine\block\VanillaBlocks;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\data\bedrock\CompoundTypeIds;
use pocketmine\data\bedrock\DyeColorIdMap;
use pocketmine\data\bedrock\EntityLegacyIds;
@ -41,6 +41,8 @@ use pocketmine\entity\Squid;
use pocketmine\entity\Villager;
use pocketmine\entity\Zombie;
use pocketmine\inventory\ArmorInventory;
use pocketmine\item\ItemIdentifier as IID;
use pocketmine\item\ItemIds as Ids;
use pocketmine\math\Vector3;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\CompoundTag;
@ -62,25 +64,25 @@ class ItemFactory{
$this->registerSpawnEggs();
$this->registerTierToolItems();
$this->register(new Apple(new ItemIdentifier(ItemIds::APPLE, 0), "Apple"));
$this->register(new Arrow(new ItemIdentifier(ItemIds::ARROW, 0), "Arrow"));
$this->register(new Apple(new IID(Ids::APPLE, 0), "Apple"));
$this->register(new Arrow(new IID(Ids::ARROW, 0), "Arrow"));
$this->register(new BakedPotato(new ItemIdentifier(ItemIds::BAKED_POTATO, 0), "Baked Potato"));
$this->register(new Bamboo(new ItemIdentifier(ItemIds::BAMBOO, 0), "Bamboo"), true);
$this->register(new Beetroot(new ItemIdentifier(ItemIds::BEETROOT, 0), "Beetroot"));
$this->register(new BeetrootSeeds(new ItemIdentifier(ItemIds::BEETROOT_SEEDS, 0), "Beetroot Seeds"));
$this->register(new BeetrootSoup(new ItemIdentifier(ItemIds::BEETROOT_SOUP, 0), "Beetroot Soup"));
$this->register(new BlazeRod(new ItemIdentifier(ItemIds::BLAZE_ROD, 0), "Blaze Rod"));
$this->register(new Book(new ItemIdentifier(ItemIds::BOOK, 0), "Book"));
$this->register(new Bow(new ItemIdentifier(ItemIds::BOW, 0), "Bow"));
$this->register(new Bowl(new ItemIdentifier(ItemIds::BOWL, 0), "Bowl"));
$this->register(new Bread(new ItemIdentifier(ItemIds::BREAD, 0), "Bread"));
$this->register(new Bucket(new ItemIdentifier(ItemIds::BUCKET, 0), "Bucket"));
$this->register(new Carrot(new ItemIdentifier(ItemIds::CARROT, 0), "Carrot"));
$this->register(new ChorusFruit(new ItemIdentifier(ItemIds::CHORUS_FRUIT, 0), "Chorus Fruit"));
$this->register(new Clock(new ItemIdentifier(ItemIds::CLOCK, 0), "Clock"));
$this->register(new Clownfish(new ItemIdentifier(ItemIds::CLOWNFISH, 0), "Clownfish"));
$this->register(new Coal(new ItemIdentifier(ItemIds::COAL, 0), "Coal"));
$this->register(new BakedPotato(new IID(Ids::BAKED_POTATO, 0), "Baked Potato"));
$this->register(new Bamboo(new IID(Ids::BAMBOO, 0), "Bamboo"), true);
$this->register(new Beetroot(new IID(Ids::BEETROOT, 0), "Beetroot"));
$this->register(new BeetrootSeeds(new IID(Ids::BEETROOT_SEEDS, 0), "Beetroot Seeds"));
$this->register(new BeetrootSoup(new IID(Ids::BEETROOT_SOUP, 0), "Beetroot Soup"));
$this->register(new BlazeRod(new IID(Ids::BLAZE_ROD, 0), "Blaze Rod"));
$this->register(new Book(new IID(Ids::BOOK, 0), "Book"));
$this->register(new Bow(new IID(Ids::BOW, 0), "Bow"));
$this->register(new Bowl(new IID(Ids::BOWL, 0), "Bowl"));
$this->register(new Bread(new IID(Ids::BREAD, 0), "Bread"));
$this->register(new Bucket(new IID(Ids::BUCKET, 0), "Bucket"));
$this->register(new Carrot(new IID(Ids::CARROT, 0), "Carrot"));
$this->register(new ChorusFruit(new IID(Ids::CHORUS_FRUIT, 0), "Chorus Fruit"));
$this->register(new Clock(new IID(Ids::CLOCK, 0), "Clock"));
$this->register(new Clownfish(new IID(Ids::CLOWNFISH, 0), "Clownfish"));
$this->register(new Coal(new IID(Ids::COAL, 0), "Coal"));
foreach([
0 => CoralType::TUBE(),
@ -90,192 +92,192 @@ class ItemFactory{
4 => CoralType::HORN()
] as $meta => $coralType){
$this->register(new ItemBlockWallOrFloor(
new ItemIdentifier(ItemIds::CORAL_FAN, $meta),
VanillaBlocks::CORAL_FAN()->setCoralType($coralType)->setDead(false),
VanillaBlocks::WALL_CORAL_FAN()->setCoralType($coralType)->setDead(false)
new IID(Ids::CORAL_FAN, $meta),
Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(false),
Blocks::WALL_CORAL_FAN()->setCoralType($coralType)->setDead(false)
), true);
$this->register(new ItemBlockWallOrFloor(
new ItemIdentifier(ItemIds::CORAL_FAN_DEAD, $meta),
VanillaBlocks::CORAL_FAN()->setCoralType($coralType)->setDead(true),
VanillaBlocks::WALL_CORAL_FAN()->setCoralType($coralType)->setDead(true)
new IID(Ids::CORAL_FAN_DEAD, $meta),
Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(true),
Blocks::WALL_CORAL_FAN()->setCoralType($coralType)->setDead(true)
), true);
}
$this->register(new Coal(new ItemIdentifier(ItemIds::COAL, 1), "Charcoal"));
$this->register(new CocoaBeans(new ItemIdentifier(ItemIds::DYE, 3), "Cocoa Beans"));
$this->register(new Compass(new ItemIdentifier(ItemIds::COMPASS, 0), "Compass"));
$this->register(new CookedChicken(new ItemIdentifier(ItemIds::COOKED_CHICKEN, 0), "Cooked Chicken"));
$this->register(new CookedFish(new ItemIdentifier(ItemIds::COOKED_FISH, 0), "Cooked Fish"));
$this->register(new CookedMutton(new ItemIdentifier(ItemIds::COOKED_MUTTON, 0), "Cooked Mutton"));
$this->register(new CookedPorkchop(new ItemIdentifier(ItemIds::COOKED_PORKCHOP, 0), "Cooked Porkchop"));
$this->register(new CookedRabbit(new ItemIdentifier(ItemIds::COOKED_RABBIT, 0), "Cooked Rabbit"));
$this->register(new CookedSalmon(new ItemIdentifier(ItemIds::COOKED_SALMON, 0), "Cooked Salmon"));
$this->register(new Cookie(new ItemIdentifier(ItemIds::COOKIE, 0), "Cookie"));
$this->register(new DriedKelp(new ItemIdentifier(ItemIds::DRIED_KELP, 0), "Dried Kelp"));
$this->register(new Egg(new ItemIdentifier(ItemIds::EGG, 0), "Egg"));
$this->register(new EnderPearl(new ItemIdentifier(ItemIds::ENDER_PEARL, 0), "Ender Pearl"));
$this->register(new ExperienceBottle(new ItemIdentifier(ItemIds::EXPERIENCE_BOTTLE, 0), "Bottle o' Enchanting"));
$this->register(new Fertilizer(new ItemIdentifier(ItemIds::DYE, 15), "Bone Meal"));
$this->register(new FishingRod(new ItemIdentifier(ItemIds::FISHING_ROD, 0), "Fishing Rod"));
$this->register(new FlintSteel(new ItemIdentifier(ItemIds::FLINT_STEEL, 0), "Flint and Steel"));
$this->register(new GlassBottle(new ItemIdentifier(ItemIds::GLASS_BOTTLE, 0), "Glass Bottle"));
$this->register(new GoldenApple(new ItemIdentifier(ItemIds::GOLDEN_APPLE, 0), "Golden Apple"));
$this->register(new GoldenAppleEnchanted(new ItemIdentifier(ItemIds::ENCHANTED_GOLDEN_APPLE, 0), "Enchanted Golden Apple"));
$this->register(new GoldenCarrot(new ItemIdentifier(ItemIds::GOLDEN_CARROT, 0), "Golden Carrot"));
$this->register(new Item(new ItemIdentifier(ItemIds::BLAZE_POWDER, 0), "Blaze Powder"));
$this->register(new Item(new ItemIdentifier(ItemIds::BLEACH, 0), "Bleach")); //EDU
$this->register(new Item(new ItemIdentifier(ItemIds::BONE, 0), "Bone"));
$this->register(new Item(new ItemIdentifier(ItemIds::BRICK, 0), "Brick"));
$this->register(new Item(new ItemIdentifier(ItemIds::CHORUS_FRUIT_POPPED, 0), "Popped Chorus Fruit"));
$this->register(new Item(new ItemIdentifier(ItemIds::CLAY_BALL, 0), "Clay"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SALT), "Salt"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SODIUM_OXIDE), "Sodium Oxide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SODIUM_HYDROXIDE), "Sodium Hydroxide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::MAGNESIUM_NITRATE), "Magnesium Nitrate"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::IRON_SULPHIDE), "Iron Sulphide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::LITHIUM_HYDRIDE), "Lithium Hydride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SODIUM_HYDRIDE), "Sodium Hydride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::CALCIUM_BROMIDE), "Calcium Bromide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::MAGNESIUM_OXIDE), "Magnesium Oxide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SODIUM_ACETATE), "Sodium Acetate"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::LUMINOL), "Luminol"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::CHARCOAL), "Charcoal")); //??? maybe bug
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SUGAR), "Sugar")); //??? maybe bug
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::ALUMINIUM_OXIDE), "Aluminium Oxide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::BORON_TRIOXIDE), "Boron Trioxide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SOAP), "Soap"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::POLYETHYLENE), "Polyethylene"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::RUBBISH), "Rubbish"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::MAGNESIUM_SALTS), "Magnesium Salts"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SULPHATE), "Sulphate"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::BARIUM_SULPHATE), "Barium Sulphate"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::POTASSIUM_CHLORIDE), "Potassium Chloride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::MERCURIC_CHLORIDE), "Mercuric Chloride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::CERIUM_CHLORIDE), "Cerium Chloride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::TUNGSTEN_CHLORIDE), "Tungsten Chloride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::CALCIUM_CHLORIDE), "Calcium Chloride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::WATER), "Water")); //???
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::GLUE), "Glue"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::HYPOCHLORITE), "Hypochlorite"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::CRUDE_OIL), "Crude Oil"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::LATEX), "Latex"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::POTASSIUM_IODIDE), "Potassium Iodide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SODIUM_FLUORIDE), "Sodium Fluoride"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::BENZENE), "Benzene"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::INK), "Ink"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::HYDROGEN_PEROXIDE), "Hydrogen Peroxide"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::AMMONIA), "Ammonia"));
$this->register(new Item(new ItemIdentifier(ItemIds::COMPOUND, CompoundTypeIds::SODIUM_HYPOCHLORITE), "Sodium Hypochlorite"));
$this->register(new Item(new ItemIdentifier(ItemIds::DIAMOND, 0), "Diamond"));
$this->register(new Item(new ItemIdentifier(ItemIds::DRAGON_BREATH, 0), "Dragon's Breath"));
$this->register(new Item(new ItemIdentifier(ItemIds::DYE, 0), "Ink Sac"));
$this->register(new Item(new ItemIdentifier(ItemIds::DYE, 4), "Lapis Lazuli"));
$this->register(new Item(new ItemIdentifier(ItemIds::EMERALD, 0), "Emerald"));
$this->register(new Item(new ItemIdentifier(ItemIds::FEATHER, 0), "Feather"));
$this->register(new Item(new ItemIdentifier(ItemIds::FERMENTED_SPIDER_EYE, 0), "Fermented Spider Eye"));
$this->register(new Item(new ItemIdentifier(ItemIds::FLINT, 0), "Flint"));
$this->register(new Item(new ItemIdentifier(ItemIds::GHAST_TEAR, 0), "Ghast Tear"));
$this->register(new Item(new ItemIdentifier(ItemIds::GLISTERING_MELON, 0), "Glistering Melon"));
$this->register(new Item(new ItemIdentifier(ItemIds::GLOWSTONE_DUST, 0), "Glowstone Dust"));
$this->register(new Item(new ItemIdentifier(ItemIds::GOLD_INGOT, 0), "Gold Ingot"));
$this->register(new Item(new ItemIdentifier(ItemIds::GOLD_NUGGET, 0), "Gold Nugget"));
$this->register(new Item(new ItemIdentifier(ItemIds::GUNPOWDER, 0), "Gunpowder"));
$this->register(new Item(new ItemIdentifier(ItemIds::HEART_OF_THE_SEA, 0), "Heart of the Sea"));
$this->register(new Item(new ItemIdentifier(ItemIds::IRON_INGOT, 0), "Iron Ingot"));
$this->register(new Item(new ItemIdentifier(ItemIds::IRON_NUGGET, 0), "Iron Nugget"));
$this->register(new Item(new ItemIdentifier(ItemIds::LEATHER, 0), "Leather"));
$this->register(new Item(new ItemIdentifier(ItemIds::MAGMA_CREAM, 0), "Magma Cream"));
$this->register(new Item(new ItemIdentifier(ItemIds::NAUTILUS_SHELL, 0), "Nautilus Shell"));
$this->register(new Item(new ItemIdentifier(ItemIds::NETHER_BRICK, 0), "Nether Brick"));
$this->register(new Item(new ItemIdentifier(ItemIds::NETHER_QUARTZ, 0), "Nether Quartz"));
$this->register(new Item(new ItemIdentifier(ItemIds::NETHER_STAR, 0), "Nether Star"));
$this->register(new Item(new ItemIdentifier(ItemIds::PAPER, 0), "Paper"));
$this->register(new Item(new ItemIdentifier(ItemIds::PRISMARINE_CRYSTALS, 0), "Prismarine Crystals"));
$this->register(new Item(new ItemIdentifier(ItemIds::PRISMARINE_SHARD, 0), "Prismarine Shard"));
$this->register(new Item(new ItemIdentifier(ItemIds::RABBIT_FOOT, 0), "Rabbit's Foot"));
$this->register(new Item(new ItemIdentifier(ItemIds::RABBIT_HIDE, 0), "Rabbit Hide"));
$this->register(new Item(new ItemIdentifier(ItemIds::SHULKER_SHELL, 0), "Shulker Shell"));
$this->register(new Item(new ItemIdentifier(ItemIds::SLIME_BALL, 0), "Slimeball"));
$this->register(new Item(new ItemIdentifier(ItemIds::SUGAR, 0), "Sugar"));
$this->register(new Item(new ItemIdentifier(ItemIds::TURTLE_SHELL_PIECE, 0), "Scute"));
$this->register(new Item(new ItemIdentifier(ItemIds::WHEAT, 0), "Wheat"));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::ACACIA_DOOR, 0), VanillaBlocks::ACACIA_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::BIRCH_DOOR, 0), VanillaBlocks::BIRCH_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::BREWING_STAND, 0), VanillaBlocks::BREWING_STAND()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::CAKE, 0), VanillaBlocks::CAKE()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::COMPARATOR, 0), VanillaBlocks::REDSTONE_COMPARATOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::DARK_OAK_DOOR, 0), VanillaBlocks::DARK_OAK_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::FLOWER_POT, 0), VanillaBlocks::FLOWER_POT()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::HOPPER, 0), VanillaBlocks::HOPPER()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::IRON_DOOR, 0), VanillaBlocks::IRON_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::ITEM_FRAME, 0), VanillaBlocks::ITEM_FRAME()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::JUNGLE_DOOR, 0), VanillaBlocks::JUNGLE_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::NETHER_WART, 0), VanillaBlocks::NETHER_WART()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::OAK_DOOR, 0), VanillaBlocks::OAK_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::REPEATER, 0), VanillaBlocks::REDSTONE_REPEATER()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::SPRUCE_DOOR, 0), VanillaBlocks::SPRUCE_DOOR()));
$this->register(new ItemBlock(new ItemIdentifier(ItemIds::SUGARCANE, 0), VanillaBlocks::SUGARCANE()));
$this->register(new Coal(new IID(Ids::COAL, 1), "Charcoal"));
$this->register(new CocoaBeans(new IID(Ids::DYE, 3), "Cocoa Beans"));
$this->register(new Compass(new IID(Ids::COMPASS, 0), "Compass"));
$this->register(new CookedChicken(new IID(Ids::COOKED_CHICKEN, 0), "Cooked Chicken"));
$this->register(new CookedFish(new IID(Ids::COOKED_FISH, 0), "Cooked Fish"));
$this->register(new CookedMutton(new IID(Ids::COOKED_MUTTON, 0), "Cooked Mutton"));
$this->register(new CookedPorkchop(new IID(Ids::COOKED_PORKCHOP, 0), "Cooked Porkchop"));
$this->register(new CookedRabbit(new IID(Ids::COOKED_RABBIT, 0), "Cooked Rabbit"));
$this->register(new CookedSalmon(new IID(Ids::COOKED_SALMON, 0), "Cooked Salmon"));
$this->register(new Cookie(new IID(Ids::COOKIE, 0), "Cookie"));
$this->register(new DriedKelp(new IID(Ids::DRIED_KELP, 0), "Dried Kelp"));
$this->register(new Egg(new IID(Ids::EGG, 0), "Egg"));
$this->register(new EnderPearl(new IID(Ids::ENDER_PEARL, 0), "Ender Pearl"));
$this->register(new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE, 0), "Bottle o' Enchanting"));
$this->register(new Fertilizer(new IID(Ids::DYE, 15), "Bone Meal"));
$this->register(new FishingRod(new IID(Ids::FISHING_ROD, 0), "Fishing Rod"));
$this->register(new FlintSteel(new IID(Ids::FLINT_STEEL, 0), "Flint and Steel"));
$this->register(new GlassBottle(new IID(Ids::GLASS_BOTTLE, 0), "Glass Bottle"));
$this->register(new GoldenApple(new IID(Ids::GOLDEN_APPLE, 0), "Golden Apple"));
$this->register(new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE, 0), "Enchanted Golden Apple"));
$this->register(new GoldenCarrot(new IID(Ids::GOLDEN_CARROT, 0), "Golden Carrot"));
$this->register(new Item(new IID(Ids::BLAZE_POWDER, 0), "Blaze Powder"));
$this->register(new Item(new IID(Ids::BLEACH, 0), "Bleach")); //EDU
$this->register(new Item(new IID(Ids::BONE, 0), "Bone"));
$this->register(new Item(new IID(Ids::BRICK, 0), "Brick"));
$this->register(new Item(new IID(Ids::CHORUS_FRUIT_POPPED, 0), "Popped Chorus Fruit"));
$this->register(new Item(new IID(Ids::CLAY_BALL, 0), "Clay"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SALT), "Salt"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SODIUM_OXIDE), "Sodium Oxide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SODIUM_HYDROXIDE), "Sodium Hydroxide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::MAGNESIUM_NITRATE), "Magnesium Nitrate"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::IRON_SULPHIDE), "Iron Sulphide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::LITHIUM_HYDRIDE), "Lithium Hydride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SODIUM_HYDRIDE), "Sodium Hydride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::CALCIUM_BROMIDE), "Calcium Bromide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::MAGNESIUM_OXIDE), "Magnesium Oxide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SODIUM_ACETATE), "Sodium Acetate"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::LUMINOL), "Luminol"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::CHARCOAL), "Charcoal")); //??? maybe bug
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SUGAR), "Sugar")); //??? maybe bug
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::ALUMINIUM_OXIDE), "Aluminium Oxide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::BORON_TRIOXIDE), "Boron Trioxide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SOAP), "Soap"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::POLYETHYLENE), "Polyethylene"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::RUBBISH), "Rubbish"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::MAGNESIUM_SALTS), "Magnesium Salts"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SULPHATE), "Sulphate"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::BARIUM_SULPHATE), "Barium Sulphate"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::POTASSIUM_CHLORIDE), "Potassium Chloride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::MERCURIC_CHLORIDE), "Mercuric Chloride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::CERIUM_CHLORIDE), "Cerium Chloride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::TUNGSTEN_CHLORIDE), "Tungsten Chloride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::CALCIUM_CHLORIDE), "Calcium Chloride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::WATER), "Water")); //???
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::GLUE), "Glue"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::HYPOCHLORITE), "Hypochlorite"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::CRUDE_OIL), "Crude Oil"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::LATEX), "Latex"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::POTASSIUM_IODIDE), "Potassium Iodide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SODIUM_FLUORIDE), "Sodium Fluoride"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::BENZENE), "Benzene"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::INK), "Ink"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::HYDROGEN_PEROXIDE), "Hydrogen Peroxide"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::AMMONIA), "Ammonia"));
$this->register(new Item(new IID(Ids::COMPOUND, CompoundTypeIds::SODIUM_HYPOCHLORITE), "Sodium Hypochlorite"));
$this->register(new Item(new IID(Ids::DIAMOND, 0), "Diamond"));
$this->register(new Item(new IID(Ids::DRAGON_BREATH, 0), "Dragon's Breath"));
$this->register(new Item(new IID(Ids::DYE, 0), "Ink Sac"));
$this->register(new Item(new IID(Ids::DYE, 4), "Lapis Lazuli"));
$this->register(new Item(new IID(Ids::EMERALD, 0), "Emerald"));
$this->register(new Item(new IID(Ids::FEATHER, 0), "Feather"));
$this->register(new Item(new IID(Ids::FERMENTED_SPIDER_EYE, 0), "Fermented Spider Eye"));
$this->register(new Item(new IID(Ids::FLINT, 0), "Flint"));
$this->register(new Item(new IID(Ids::GHAST_TEAR, 0), "Ghast Tear"));
$this->register(new Item(new IID(Ids::GLISTERING_MELON, 0), "Glistering Melon"));
$this->register(new Item(new IID(Ids::GLOWSTONE_DUST, 0), "Glowstone Dust"));
$this->register(new Item(new IID(Ids::GOLD_INGOT, 0), "Gold Ingot"));
$this->register(new Item(new IID(Ids::GOLD_NUGGET, 0), "Gold Nugget"));
$this->register(new Item(new IID(Ids::GUNPOWDER, 0), "Gunpowder"));
$this->register(new Item(new IID(Ids::HEART_OF_THE_SEA, 0), "Heart of the Sea"));
$this->register(new Item(new IID(Ids::IRON_INGOT, 0), "Iron Ingot"));
$this->register(new Item(new IID(Ids::IRON_NUGGET, 0), "Iron Nugget"));
$this->register(new Item(new IID(Ids::LEATHER, 0), "Leather"));
$this->register(new Item(new IID(Ids::MAGMA_CREAM, 0), "Magma Cream"));
$this->register(new Item(new IID(Ids::NAUTILUS_SHELL, 0), "Nautilus Shell"));
$this->register(new Item(new IID(Ids::NETHER_BRICK, 0), "Nether Brick"));
$this->register(new Item(new IID(Ids::NETHER_QUARTZ, 0), "Nether Quartz"));
$this->register(new Item(new IID(Ids::NETHER_STAR, 0), "Nether Star"));
$this->register(new Item(new IID(Ids::PAPER, 0), "Paper"));
$this->register(new Item(new IID(Ids::PRISMARINE_CRYSTALS, 0), "Prismarine Crystals"));
$this->register(new Item(new IID(Ids::PRISMARINE_SHARD, 0), "Prismarine Shard"));
$this->register(new Item(new IID(Ids::RABBIT_FOOT, 0), "Rabbit's Foot"));
$this->register(new Item(new IID(Ids::RABBIT_HIDE, 0), "Rabbit Hide"));
$this->register(new Item(new IID(Ids::SHULKER_SHELL, 0), "Shulker Shell"));
$this->register(new Item(new IID(Ids::SLIME_BALL, 0), "Slimeball"));
$this->register(new Item(new IID(Ids::SUGAR, 0), "Sugar"));
$this->register(new Item(new IID(Ids::TURTLE_SHELL_PIECE, 0), "Scute"));
$this->register(new Item(new IID(Ids::WHEAT, 0), "Wheat"));
$this->register(new ItemBlock(new IID(Ids::ACACIA_DOOR, 0), Blocks::ACACIA_DOOR()));
$this->register(new ItemBlock(new IID(Ids::BIRCH_DOOR, 0), Blocks::BIRCH_DOOR()));
$this->register(new ItemBlock(new IID(Ids::BREWING_STAND, 0), Blocks::BREWING_STAND()));
$this->register(new ItemBlock(new IID(Ids::CAKE, 0), Blocks::CAKE()));
$this->register(new ItemBlock(new IID(Ids::COMPARATOR, 0), Blocks::REDSTONE_COMPARATOR()));
$this->register(new ItemBlock(new IID(Ids::DARK_OAK_DOOR, 0), Blocks::DARK_OAK_DOOR()));
$this->register(new ItemBlock(new IID(Ids::FLOWER_POT, 0), Blocks::FLOWER_POT()));
$this->register(new ItemBlock(new IID(Ids::HOPPER, 0), Blocks::HOPPER()));
$this->register(new ItemBlock(new IID(Ids::IRON_DOOR, 0), Blocks::IRON_DOOR()));
$this->register(new ItemBlock(new IID(Ids::ITEM_FRAME, 0), Blocks::ITEM_FRAME()));
$this->register(new ItemBlock(new IID(Ids::JUNGLE_DOOR, 0), Blocks::JUNGLE_DOOR()));
$this->register(new ItemBlock(new IID(Ids::NETHER_WART, 0), Blocks::NETHER_WART()));
$this->register(new ItemBlock(new IID(Ids::OAK_DOOR, 0), Blocks::OAK_DOOR()));
$this->register(new ItemBlock(new IID(Ids::REPEATER, 0), Blocks::REDSTONE_REPEATER()));
$this->register(new ItemBlock(new IID(Ids::SPRUCE_DOOR, 0), Blocks::SPRUCE_DOOR()));
$this->register(new ItemBlock(new IID(Ids::SUGARCANE, 0), Blocks::SUGARCANE()));
//the meta values for buckets are intentionally hardcoded because block IDs will change in the future
$waterBucket = new LiquidBucket(new ItemIdentifier(ItemIds::BUCKET, 8), "Water Bucket", VanillaBlocks::WATER());
$waterBucket = new LiquidBucket(new IID(Ids::BUCKET, 8), "Water Bucket", Blocks::WATER());
$this->register($waterBucket);
$this->remap(new ItemIdentifier(ItemIds::BUCKET, 9), $waterBucket);
$lavaBucket = new LiquidBucket(new ItemIdentifier(ItemIds::BUCKET, 10), "Lava Bucket", VanillaBlocks::LAVA());
$this->remap(new IID(Ids::BUCKET, 9), $waterBucket);
$lavaBucket = new LiquidBucket(new IID(Ids::BUCKET, 10), "Lava Bucket", Blocks::LAVA());
$this->register($lavaBucket);
$this->remap(new ItemIdentifier(ItemIds::BUCKET, 11), $lavaBucket);
$this->register(new Melon(new ItemIdentifier(ItemIds::MELON, 0), "Melon"));
$this->register(new MelonSeeds(new ItemIdentifier(ItemIds::MELON_SEEDS, 0), "Melon Seeds"));
$this->register(new MilkBucket(new ItemIdentifier(ItemIds::BUCKET, 1), "Milk Bucket"));
$this->register(new Minecart(new ItemIdentifier(ItemIds::MINECART, 0), "Minecart"));
$this->register(new MushroomStew(new ItemIdentifier(ItemIds::MUSHROOM_STEW, 0), "Mushroom Stew"));
$this->register(new PaintingItem(new ItemIdentifier(ItemIds::PAINTING, 0), "Painting"));
$this->register(new PoisonousPotato(new ItemIdentifier(ItemIds::POISONOUS_POTATO, 0), "Poisonous Potato"));
$this->register(new Potato(new ItemIdentifier(ItemIds::POTATO, 0), "Potato"));
$this->register(new Pufferfish(new ItemIdentifier(ItemIds::PUFFERFISH, 0), "Pufferfish"));
$this->register(new PumpkinPie(new ItemIdentifier(ItemIds::PUMPKIN_PIE, 0), "Pumpkin Pie"));
$this->register(new PumpkinSeeds(new ItemIdentifier(ItemIds::PUMPKIN_SEEDS, 0), "Pumpkin Seeds"));
$this->register(new RabbitStew(new ItemIdentifier(ItemIds::RABBIT_STEW, 0), "Rabbit Stew"));
$this->register(new RawBeef(new ItemIdentifier(ItemIds::RAW_BEEF, 0), "Raw Beef"));
$this->register(new RawChicken(new ItemIdentifier(ItemIds::RAW_CHICKEN, 0), "Raw Chicken"));
$this->register(new RawFish(new ItemIdentifier(ItemIds::RAW_FISH, 0), "Raw Fish"));
$this->register(new RawMutton(new ItemIdentifier(ItemIds::RAW_MUTTON, 0), "Raw Mutton"));
$this->register(new RawPorkchop(new ItemIdentifier(ItemIds::RAW_PORKCHOP, 0), "Raw Porkchop"));
$this->register(new RawRabbit(new ItemIdentifier(ItemIds::RAW_RABBIT, 0), "Raw Rabbit"));
$this->register(new RawSalmon(new ItemIdentifier(ItemIds::RAW_SALMON, 0), "Raw Salmon"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_13, 0), RecordType::DISK_13(), "Record 13"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_CAT, 0), RecordType::DISK_CAT(), "Record Cat"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_BLOCKS, 0), RecordType::DISK_BLOCKS(), "Record Blocks"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_CHIRP, 0), RecordType::DISK_CHIRP(), "Record Chirp"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_FAR, 0), RecordType::DISK_FAR(), "Record Far"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_MALL, 0), RecordType::DISK_MALL(), "Record Mall"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_MELLOHI, 0), RecordType::DISK_MELLOHI(), "Record Mellohi"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_STAL, 0), RecordType::DISK_STAL(), "Record Stal"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_STRAD, 0), RecordType::DISK_STRAD(), "Record Strad"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_WARD, 0), RecordType::DISK_WARD(), "Record Ward"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_11, 0), RecordType::DISK_11(), "Record 11"));
$this->register(new Record(new ItemIdentifier(ItemIds::RECORD_WAIT, 0), RecordType::DISK_WAIT(), "Record Wait"));
$this->register(new Redstone(new ItemIdentifier(ItemIds::REDSTONE, 0), "Redstone"));
$this->register(new RottenFlesh(new ItemIdentifier(ItemIds::ROTTEN_FLESH, 0), "Rotten Flesh"));
$this->register(new Shears(new ItemIdentifier(ItemIds::SHEARS, 0), "Shears"));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::SIGN, 0), VanillaBlocks::OAK_SIGN(), VanillaBlocks::OAK_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::SPRUCE_SIGN, 0), VanillaBlocks::SPRUCE_SIGN(), VanillaBlocks::SPRUCE_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::BIRCH_SIGN, 0), VanillaBlocks::BIRCH_SIGN(), VanillaBlocks::BIRCH_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::JUNGLE_SIGN, 0), VanillaBlocks::JUNGLE_SIGN(), VanillaBlocks::JUNGLE_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::ACACIA_SIGN, 0), VanillaBlocks::ACACIA_SIGN(), VanillaBlocks::ACACIA_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::DARKOAK_SIGN, 0), VanillaBlocks::DARK_OAK_SIGN(), VanillaBlocks::DARK_OAK_WALL_SIGN()));
$this->register(new Snowball(new ItemIdentifier(ItemIds::SNOWBALL, 0), "Snowball"));
$this->register(new SpiderEye(new ItemIdentifier(ItemIds::SPIDER_EYE, 0), "Spider Eye"));
$this->register(new Steak(new ItemIdentifier(ItemIds::STEAK, 0), "Steak"));
$this->register(new Stick(new ItemIdentifier(ItemIds::STICK, 0), "Stick"));
$this->register(new StringItem(new ItemIdentifier(ItemIds::STRING, 0), "String"));
$this->register(new SweetBerries(new ItemIdentifier(ItemIds::SWEET_BERRIES, 0), "Sweet Berries"));
$this->register(new Totem(new ItemIdentifier(ItemIds::TOTEM, 0), "Totem of Undying"));
$this->register(new WheatSeeds(new ItemIdentifier(ItemIds::WHEAT_SEEDS, 0), "Wheat Seeds"));
$this->register(new WritableBook(new ItemIdentifier(ItemIds::WRITABLE_BOOK, 0), "Book & Quill"));
$this->register(new WrittenBook(new ItemIdentifier(ItemIds::WRITTEN_BOOK, 0), "Written Book"));
$this->remap(new IID(Ids::BUCKET, 11), $lavaBucket);
$this->register(new Melon(new IID(Ids::MELON, 0), "Melon"));
$this->register(new MelonSeeds(new IID(Ids::MELON_SEEDS, 0), "Melon Seeds"));
$this->register(new MilkBucket(new IID(Ids::BUCKET, 1), "Milk Bucket"));
$this->register(new Minecart(new IID(Ids::MINECART, 0), "Minecart"));
$this->register(new MushroomStew(new IID(Ids::MUSHROOM_STEW, 0), "Mushroom Stew"));
$this->register(new PaintingItem(new IID(Ids::PAINTING, 0), "Painting"));
$this->register(new PoisonousPotato(new IID(Ids::POISONOUS_POTATO, 0), "Poisonous Potato"));
$this->register(new Potato(new IID(Ids::POTATO, 0), "Potato"));
$this->register(new Pufferfish(new IID(Ids::PUFFERFISH, 0), "Pufferfish"));
$this->register(new PumpkinPie(new IID(Ids::PUMPKIN_PIE, 0), "Pumpkin Pie"));
$this->register(new PumpkinSeeds(new IID(Ids::PUMPKIN_SEEDS, 0), "Pumpkin Seeds"));
$this->register(new RabbitStew(new IID(Ids::RABBIT_STEW, 0), "Rabbit Stew"));
$this->register(new RawBeef(new IID(Ids::RAW_BEEF, 0), "Raw Beef"));
$this->register(new RawChicken(new IID(Ids::RAW_CHICKEN, 0), "Raw Chicken"));
$this->register(new RawFish(new IID(Ids::RAW_FISH, 0), "Raw Fish"));
$this->register(new RawMutton(new IID(Ids::RAW_MUTTON, 0), "Raw Mutton"));
$this->register(new RawPorkchop(new IID(Ids::RAW_PORKCHOP, 0), "Raw Porkchop"));
$this->register(new RawRabbit(new IID(Ids::RAW_RABBIT, 0), "Raw Rabbit"));
$this->register(new RawSalmon(new IID(Ids::RAW_SALMON, 0), "Raw Salmon"));
$this->register(new Record(new IID(Ids::RECORD_13, 0), RecordType::DISK_13(), "Record 13"));
$this->register(new Record(new IID(Ids::RECORD_CAT, 0), RecordType::DISK_CAT(), "Record Cat"));
$this->register(new Record(new IID(Ids::RECORD_BLOCKS, 0), RecordType::DISK_BLOCKS(), "Record Blocks"));
$this->register(new Record(new IID(Ids::RECORD_CHIRP, 0), RecordType::DISK_CHIRP(), "Record Chirp"));
$this->register(new Record(new IID(Ids::RECORD_FAR, 0), RecordType::DISK_FAR(), "Record Far"));
$this->register(new Record(new IID(Ids::RECORD_MALL, 0), RecordType::DISK_MALL(), "Record Mall"));
$this->register(new Record(new IID(Ids::RECORD_MELLOHI, 0), RecordType::DISK_MELLOHI(), "Record Mellohi"));
$this->register(new Record(new IID(Ids::RECORD_STAL, 0), RecordType::DISK_STAL(), "Record Stal"));
$this->register(new Record(new IID(Ids::RECORD_STRAD, 0), RecordType::DISK_STRAD(), "Record Strad"));
$this->register(new Record(new IID(Ids::RECORD_WARD, 0), RecordType::DISK_WARD(), "Record Ward"));
$this->register(new Record(new IID(Ids::RECORD_11, 0), RecordType::DISK_11(), "Record 11"));
$this->register(new Record(new IID(Ids::RECORD_WAIT, 0), RecordType::DISK_WAIT(), "Record Wait"));
$this->register(new Redstone(new IID(Ids::REDSTONE, 0), "Redstone"));
$this->register(new RottenFlesh(new IID(Ids::ROTTEN_FLESH, 0), "Rotten Flesh"));
$this->register(new Shears(new IID(Ids::SHEARS, 0), "Shears"));
$this->register(new ItemBlockWallOrFloor(new IID(Ids::SIGN, 0), Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new IID(Ids::SPRUCE_SIGN, 0), Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new IID(Ids::BIRCH_SIGN, 0), Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new IID(Ids::JUNGLE_SIGN, 0), Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new IID(Ids::ACACIA_SIGN, 0), Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
$this->register(new ItemBlockWallOrFloor(new IID(Ids::DARKOAK_SIGN, 0), Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN()));
$this->register(new Snowball(new IID(Ids::SNOWBALL, 0), "Snowball"));
$this->register(new SpiderEye(new IID(Ids::SPIDER_EYE, 0), "Spider Eye"));
$this->register(new Steak(new IID(Ids::STEAK, 0), "Steak"));
$this->register(new Stick(new IID(Ids::STICK, 0), "Stick"));
$this->register(new StringItem(new IID(Ids::STRING, 0), "String"));
$this->register(new SweetBerries(new IID(Ids::SWEET_BERRIES, 0), "Sweet Berries"));
$this->register(new Totem(new IID(Ids::TOTEM, 0), "Totem of Undying"));
$this->register(new WheatSeeds(new IID(Ids::WHEAT_SEEDS, 0), "Wheat Seeds"));
$this->register(new WritableBook(new IID(Ids::WRITABLE_BOOK, 0), "Book & Quill"));
$this->register(new WrittenBook(new IID(Ids::WRITTEN_BOOK, 0), "Written Book"));
foreach(SkullType::getAll() as $skullType){
$this->register(new Skull(new ItemIdentifier(ItemIds::SKULL, $skullType->getMagicNumber()), $skullType->getDisplayName(), $skullType));
$this->register(new Skull(new IID(Ids::SKULL, $skullType->getMagicNumber()), $skullType->getDisplayName(), $skullType));
}
$dyeMap = [
@ -288,23 +290,23 @@ class ItemFactory{
foreach(DyeColor::getAll() as $color){
//TODO: use colour object directly
//TODO: add interface to dye-colour objects
$this->register(new Dye(new ItemIdentifier(ItemIds::DYE, $dyeMap[$color->id()] ?? $colorIdMap->toInvertedId($color)), $color->getDisplayName() . " Dye", $color));
$this->register(new Bed(new ItemIdentifier(ItemIds::BED, $colorIdMap->toId($color)), $color->getDisplayName() . " Bed", $color));
$this->register(new Dye(new IID(Ids::DYE, $dyeMap[$color->id()] ?? $colorIdMap->toInvertedId($color)), $color->getDisplayName() . " Dye", $color));
$this->register(new Bed(new IID(Ids::BED, $colorIdMap->toId($color)), $color->getDisplayName() . " Bed", $color));
$this->register((new Banner(
new ItemIdentifier(ItemIds::BANNER, 0),
VanillaBlocks::BANNER(),
VanillaBlocks::WALL_BANNER()
new IID(Ids::BANNER, 0),
Blocks::BANNER(),
Blocks::WALL_BANNER()
))->setColor($color));
}
foreach(PotionType::getAll() as $type){
$typeId = PotionTypeIdMap::getInstance()->toId($type);
$this->register(new Potion(new ItemIdentifier(ItemIds::POTION, $typeId), $type->getDisplayName() . " Potion", $type));
$this->register(new SplashPotion(new ItemIdentifier(ItemIds::SPLASH_POTION, $typeId), $type->getDisplayName() . " Splash Potion", $type));
$this->register(new Potion(new IID(Ids::POTION, $typeId), $type->getDisplayName() . " Potion", $type));
$this->register(new SplashPotion(new IID(Ids::SPLASH_POTION, $typeId), $type->getDisplayName() . " Splash Potion", $type));
}
foreach(TreeType::getAll() as $type){
$this->register(new Boat(new ItemIdentifier(ItemIds::BOAT, $type->getMagicNumber()), $type->getDisplayName() . " Boat", $type));
$this->register(new Boat(new IID(Ids::BOAT, $type->getMagicNumber()), $type->getDisplayName() . " Boat", $type));
}
//region --- auto-generated TODOs ---
@ -352,17 +354,17 @@ class ItemFactory{
private function registerSpawnEggs() : void{
//TODO: the meta values should probably be hardcoded; they won't change, but the EntityLegacyIds might
$this->register(new class(new ItemIdentifier(ItemIds::SPAWN_EGG, EntityLegacyIds::ZOMBIE), "Zombie Spawn Egg") extends SpawnEgg{
$this->register(new class(new IID(Ids::SPAWN_EGG, EntityLegacyIds::ZOMBIE), "Zombie Spawn Egg") extends SpawnEgg{
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Zombie(Location::fromObject($pos, $world, $yaw, $pitch));
}
});
$this->register(new class(new ItemIdentifier(ItemIds::SPAWN_EGG, EntityLegacyIds::SQUID), "Squid Spawn Egg") extends SpawnEgg{
$this->register(new class(new IID(Ids::SPAWN_EGG, EntityLegacyIds::SQUID), "Squid Spawn Egg") extends SpawnEgg{
public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Squid(Location::fromObject($pos, $world, $yaw, $pitch));
}
});
$this->register(new class(new ItemIdentifier(ItemIds::SPAWN_EGG, EntityLegacyIds::VILLAGER), "Villager Spawn Egg") extends SpawnEgg{
$this->register(new class(new IID(Ids::SPAWN_EGG, EntityLegacyIds::VILLAGER), "Villager Spawn Egg") extends SpawnEgg{
public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Villager(Location::fromObject($pos, $world, $yaw, $pitch));
}
@ -370,54 +372,54 @@ class ItemFactory{
}
private function registerTierToolItems() : void{
$this->register(new Axe(new ItemIdentifier(ItemIds::DIAMOND_AXE, 0), "Diamond Axe", ToolTier::DIAMOND()));
$this->register(new Axe(new ItemIdentifier(ItemIds::GOLDEN_AXE, 0), "Golden Axe", ToolTier::GOLD()));
$this->register(new Axe(new ItemIdentifier(ItemIds::IRON_AXE, 0), "Iron Axe", ToolTier::IRON()));
$this->register(new Axe(new ItemIdentifier(ItemIds::STONE_AXE, 0), "Stone Axe", ToolTier::STONE()));
$this->register(new Axe(new ItemIdentifier(ItemIds::WOODEN_AXE, 0), "Wooden Axe", ToolTier::WOOD()));
$this->register(new Hoe(new ItemIdentifier(ItemIds::DIAMOND_HOE, 0), "Diamond Hoe", ToolTier::DIAMOND()));
$this->register(new Hoe(new ItemIdentifier(ItemIds::GOLDEN_HOE, 0), "Golden Hoe", ToolTier::GOLD()));
$this->register(new Hoe(new ItemIdentifier(ItemIds::IRON_HOE, 0), "Iron Hoe", ToolTier::IRON()));
$this->register(new Hoe(new ItemIdentifier(ItemIds::STONE_HOE, 0), "Stone Hoe", ToolTier::STONE()));
$this->register(new Hoe(new ItemIdentifier(ItemIds::WOODEN_HOE, 0), "Wooden Hoe", ToolTier::WOOD()));
$this->register(new Pickaxe(new ItemIdentifier(ItemIds::DIAMOND_PICKAXE, 0), "Diamond Pickaxe", ToolTier::DIAMOND()));
$this->register(new Pickaxe(new ItemIdentifier(ItemIds::GOLDEN_PICKAXE, 0), "Golden Pickaxe", ToolTier::GOLD()));
$this->register(new Pickaxe(new ItemIdentifier(ItemIds::IRON_PICKAXE, 0), "Iron Pickaxe", ToolTier::IRON()));
$this->register(new Pickaxe(new ItemIdentifier(ItemIds::STONE_PICKAXE, 0), "Stone Pickaxe", ToolTier::STONE()));
$this->register(new Pickaxe(new ItemIdentifier(ItemIds::WOODEN_PICKAXE, 0), "Wooden Pickaxe", ToolTier::WOOD()));
$this->register(new Shovel(new ItemIdentifier(ItemIds::DIAMOND_SHOVEL, 0), "Diamond Shovel", ToolTier::DIAMOND()));
$this->register(new Shovel(new ItemIdentifier(ItemIds::GOLDEN_SHOVEL, 0), "Golden Shovel", ToolTier::GOLD()));
$this->register(new Shovel(new ItemIdentifier(ItemIds::IRON_SHOVEL, 0), "Iron Shovel", ToolTier::IRON()));
$this->register(new Shovel(new ItemIdentifier(ItemIds::STONE_SHOVEL, 0), "Stone Shovel", ToolTier::STONE()));
$this->register(new Shovel(new ItemIdentifier(ItemIds::WOODEN_SHOVEL, 0), "Wooden Shovel", ToolTier::WOOD()));
$this->register(new Sword(new ItemIdentifier(ItemIds::DIAMOND_SWORD, 0), "Diamond Sword", ToolTier::DIAMOND()));
$this->register(new Sword(new ItemIdentifier(ItemIds::GOLDEN_SWORD, 0), "Golden Sword", ToolTier::GOLD()));
$this->register(new Sword(new ItemIdentifier(ItemIds::IRON_SWORD, 0), "Iron Sword", ToolTier::IRON()));
$this->register(new Sword(new ItemIdentifier(ItemIds::STONE_SWORD, 0), "Stone Sword", ToolTier::STONE()));
$this->register(new Sword(new ItemIdentifier(ItemIds::WOODEN_SWORD, 0), "Wooden Sword", ToolTier::WOOD()));
$this->register(new Axe(new IID(Ids::DIAMOND_AXE, 0), "Diamond Axe", ToolTier::DIAMOND()));
$this->register(new Axe(new IID(Ids::GOLDEN_AXE, 0), "Golden Axe", ToolTier::GOLD()));
$this->register(new Axe(new IID(Ids::IRON_AXE, 0), "Iron Axe", ToolTier::IRON()));
$this->register(new Axe(new IID(Ids::STONE_AXE, 0), "Stone Axe", ToolTier::STONE()));
$this->register(new Axe(new IID(Ids::WOODEN_AXE, 0), "Wooden Axe", ToolTier::WOOD()));
$this->register(new Hoe(new IID(Ids::DIAMOND_HOE, 0), "Diamond Hoe", ToolTier::DIAMOND()));
$this->register(new Hoe(new IID(Ids::GOLDEN_HOE, 0), "Golden Hoe", ToolTier::GOLD()));
$this->register(new Hoe(new IID(Ids::IRON_HOE, 0), "Iron Hoe", ToolTier::IRON()));
$this->register(new Hoe(new IID(Ids::STONE_HOE, 0), "Stone Hoe", ToolTier::STONE()));
$this->register(new Hoe(new IID(Ids::WOODEN_HOE, 0), "Wooden Hoe", ToolTier::WOOD()));
$this->register(new Pickaxe(new IID(Ids::DIAMOND_PICKAXE, 0), "Diamond Pickaxe", ToolTier::DIAMOND()));
$this->register(new Pickaxe(new IID(Ids::GOLDEN_PICKAXE, 0), "Golden Pickaxe", ToolTier::GOLD()));
$this->register(new Pickaxe(new IID(Ids::IRON_PICKAXE, 0), "Iron Pickaxe", ToolTier::IRON()));
$this->register(new Pickaxe(new IID(Ids::STONE_PICKAXE, 0), "Stone Pickaxe", ToolTier::STONE()));
$this->register(new Pickaxe(new IID(Ids::WOODEN_PICKAXE, 0), "Wooden Pickaxe", ToolTier::WOOD()));
$this->register(new Shovel(new IID(Ids::DIAMOND_SHOVEL, 0), "Diamond Shovel", ToolTier::DIAMOND()));
$this->register(new Shovel(new IID(Ids::GOLDEN_SHOVEL, 0), "Golden Shovel", ToolTier::GOLD()));
$this->register(new Shovel(new IID(Ids::IRON_SHOVEL, 0), "Iron Shovel", ToolTier::IRON()));
$this->register(new Shovel(new IID(Ids::STONE_SHOVEL, 0), "Stone Shovel", ToolTier::STONE()));
$this->register(new Shovel(new IID(Ids::WOODEN_SHOVEL, 0), "Wooden Shovel", ToolTier::WOOD()));
$this->register(new Sword(new IID(Ids::DIAMOND_SWORD, 0), "Diamond Sword", ToolTier::DIAMOND()));
$this->register(new Sword(new IID(Ids::GOLDEN_SWORD, 0), "Golden Sword", ToolTier::GOLD()));
$this->register(new Sword(new IID(Ids::IRON_SWORD, 0), "Iron Sword", ToolTier::IRON()));
$this->register(new Sword(new IID(Ids::STONE_SWORD, 0), "Stone Sword", ToolTier::STONE()));
$this->register(new Sword(new IID(Ids::WOODEN_SWORD, 0), "Wooden Sword", ToolTier::WOOD()));
}
private function registerArmorItems() : void{
$this->register(new Armor(new ItemIdentifier(ItemIds::CHAIN_BOOTS, 0), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new ItemIdentifier(ItemIds::DIAMOND_BOOTS, 0), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new ItemIdentifier(ItemIds::GOLDEN_BOOTS, 0), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new ItemIdentifier(ItemIds::IRON_BOOTS, 0), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new ItemIdentifier(ItemIds::LEATHER_BOOTS, 0), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new ItemIdentifier(ItemIds::CHAIN_CHESTPLATE, 0), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new ItemIdentifier(ItemIds::DIAMOND_CHESTPLATE, 0), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new ItemIdentifier(ItemIds::GOLDEN_CHESTPLATE, 0), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new ItemIdentifier(ItemIds::IRON_CHESTPLATE, 0), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new ItemIdentifier(ItemIds::LEATHER_CHESTPLATE, 0), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new ItemIdentifier(ItemIds::CHAIN_HELMET, 0), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new ItemIdentifier(ItemIds::DIAMOND_HELMET, 0), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new ItemIdentifier(ItemIds::GOLDEN_HELMET, 0), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new ItemIdentifier(ItemIds::IRON_HELMET, 0), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new ItemIdentifier(ItemIds::LEATHER_HELMET, 0), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new ItemIdentifier(ItemIds::CHAIN_LEGGINGS, 0), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new ItemIdentifier(ItemIds::DIAMOND_LEGGINGS, 0), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new ItemIdentifier(ItemIds::GOLDEN_LEGGINGS, 0), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new ItemIdentifier(ItemIds::IRON_LEGGINGS, 0), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new ItemIdentifier(ItemIds::LEATHER_LEGGINGS, 0), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new IID(Ids::CHAIN_BOOTS, 0), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new IID(Ids::DIAMOND_BOOTS, 0), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new IID(Ids::GOLDEN_BOOTS, 0), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new IID(Ids::IRON_BOOTS, 0), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new IID(Ids::LEATHER_BOOTS, 0), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET)));
$this->register(new Armor(new IID(Ids::CHAIN_CHESTPLATE, 0), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new IID(Ids::DIAMOND_CHESTPLATE, 0), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new IID(Ids::GOLDEN_CHESTPLATE, 0), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new IID(Ids::IRON_CHESTPLATE, 0), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new IID(Ids::LEATHER_CHESTPLATE, 0), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST)));
$this->register(new Armor(new IID(Ids::CHAIN_HELMET, 0), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new IID(Ids::DIAMOND_HELMET, 0), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new IID(Ids::GOLDEN_HELMET, 0), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new IID(Ids::IRON_HELMET, 0), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new IID(Ids::LEATHER_HELMET, 0), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD)));
$this->register(new Armor(new IID(Ids::CHAIN_LEGGINGS, 0), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new IID(Ids::DIAMOND_LEGGINGS, 0), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new IID(Ids::GOLDEN_LEGGINGS, 0), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new IID(Ids::IRON_LEGGINGS, 0), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS)));
$this->register(new Armor(new IID(Ids::LEATHER_LEGGINGS, 0), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS)));
}
/**
@ -470,21 +472,21 @@ class ItemFactory{
if(isset($this->list[$offset = self::getListOffset($id, $meta)])){
$item = clone $this->list[$offset];
}elseif(isset($this->list[$zero = self::getListOffset($id, 0)]) && $this->list[$zero] instanceof Durable){
if($meta <= $this->list[$zero]->getMaxDurability()){
if($meta >= 0 && $meta <= $this->list[$zero]->getMaxDurability()){
$item = clone $this->list[$zero];
$item->setDamage($meta);
}else{
$item = new Item(new ItemIdentifier($id, $meta));
$item = new Item(new IID($id, $meta));
}
}elseif($id < 256){ //intentionally includes negatives, for extended block IDs
//TODO: do not assume that item IDs and block IDs are the same or related
$item = new ItemBlock(new ItemIdentifier($id, $meta), BlockFactory::getInstance()->get(self::itemToBlockId($id), $meta & 0xf));
$item = new ItemBlock(new IID($id, $meta), BlockFactory::getInstance()->get(self::itemToBlockId($id), $meta & 0xf));
}
}
if($item === null){
//negative damage values will fallthru to here, to avoid crazy shit with crafting wildcard hacks
$item = new Item(new ItemIdentifier($id, $meta));
$item = new Item(new IID($id, $meta));
}
$item->setCount($count);
@ -499,7 +501,7 @@ class ItemFactory{
* @see VanillaItems::AIR()
*/
public static function air() : Item{
return self::getInstance()->get(ItemIds::AIR, 0, 0);
return self::getInstance()->get(Ids::AIR, 0, 0);
}
/**

View File

@ -185,13 +185,16 @@ final class PotionType{
new EffectInstance(VanillaEffects::WITHER(), 800, 1)
]),
new self("turtle_master", "Turtle Master", fn() => [
//TODO
new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 3),
new EffectInstance(VanillaEffects::RESISTANCE(), 20 * 20, 2),
]),
new self("long_turtle_master", "Long Turtle Master", fn() => [
//TODO
new EffectInstance(VanillaEffects::SLOWNESS(), 40 * 20, 3),
new EffectInstance(VanillaEffects::RESISTANCE(), 40 * 20, 2),
]),
new self("strong_turtle_master", "Strong Turtle Master", fn() => [
//TODO
new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 5),
new EffectInstance(VanillaEffects::RESISTANCE(), 20 * 20, 3),
]),
new self("slow_falling", "Slow Falling", fn() => [
//TODO

File diff suppressed because it is too large Load Diff

View File

@ -384,6 +384,7 @@ final class VanillaItems{
/**
* @return Item[]
* @phpstan-return array<string, Item>
*/
public static function getAll() : array{
//phpstan doesn't support generic traits yet :(

View File

@ -35,7 +35,7 @@ final class StringToEnchantmentParser extends StringToTParser{
use SingletonTrait;
private static function make() : self{
$result = new self;
$result = new self();
$result->register("blast_protection", fn() => VanillaEnchantments::BLAST_PROTECTION());
$result->register("efficiency", fn() => VanillaEnchantments::EFFICIENCY());

View File

@ -27,6 +27,7 @@ use pocketmine\utils\Utils;
use Webmozart\PathUtil\Path;
use function array_filter;
use function array_map;
use function count;
use function explode;
use function file_exists;
use function is_dir;
@ -67,11 +68,15 @@ class Language{
$result = [];
foreach($files as $file){
try{
$code = explode(".", $file)[0];
$strings = self::loadLang($path, $code);
if(isset($strings["language.name"])){
$result[$code] = $strings["language.name"];
}
}catch(LanguageNotFoundException $e){
// no-op
}
}
return $result;
@ -124,7 +129,10 @@ class Language{
protected static function loadLang(string $path, string $languageCode) : array{
$file = Path::join($path, $languageCode . ".ini");
if(file_exists($file)){
return array_map('\stripcslashes', Utils::assumeNotFalse(parse_ini_file($file, false, INI_SCANNER_RAW), "Missing or inaccessible required resource files"));
$strings = array_map('stripcslashes', Utils::assumeNotFalse(parse_ini_file($file, false, INI_SCANNER_RAW), "Missing or inaccessible required resource files"));
if(count($strings) > 0){
return $strings;
}
}
throw new LanguageNotFoundException("Language \"$languageCode\" not found");

View File

@ -30,6 +30,7 @@ use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\protocol\LevelChunkPacket;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\ChunkPosition;
use pocketmine\network\mcpe\serializer\ChunkSerializer;
use pocketmine\scheduler\AsyncTask;
use pocketmine\world\format\Chunk;
@ -71,7 +72,7 @@ class ChunkRequestTask extends AsyncTask{
$subCount = ChunkSerializer::getSubChunkCount($chunk) + ChunkSerializer::LOWER_PADDING_SIZE;
$encoderContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
$payload = ChunkSerializer::serializeFullChunk($chunk, RuntimeBlockMapping::getInstance(), $encoderContext, $this->tiles);
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create($this->chunkX, $this->chunkZ, $subCount, false, null, $payload))->getBuffer()));
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $subCount, false, null, $payload))->getBuffer()));
}
public function onError() : void{

View File

@ -259,8 +259,10 @@ class InventoryManager{
public function onClientRemoveWindow(int $id) : void{
if($id === $this->lastInventoryNetworkId){
if(isset($this->windowMap[$id]) && $id !== $this->pendingCloseWindowId){
$this->remove($id);
$this->player->removeCurrentWindow();
}
}else{
$this->session->getLogger()->debug("Attempted to close inventory with network ID $id, but current is $this->lastInventoryNetworkId");
}
@ -269,13 +271,15 @@ class InventoryManager{
//initiated the close and expect an ack.
$this->session->sendDataPacket(ContainerClosePacket::create($id, false));
if($this->pendingOpenWindowCallback !== null && $id === $this->pendingCloseWindowId){
$this->session->getLogger()->debug("Opening deferred window after close ack of window $id");
if($this->pendingCloseWindowId === $id){
$this->pendingCloseWindowId = null;
if($this->pendingOpenWindowCallback !== null){
$this->session->getLogger()->debug("Opening deferred window after close ack of window $id");
($this->pendingOpenWindowCallback)();
$this->pendingOpenWindowCallback = null;
}
}
}
public function syncSlot(Inventory $inventory, int $slot) : void{
$windowId = $this->getWindowId($inventory);

View File

@ -56,8 +56,8 @@ use pocketmine\network\mcpe\handler\LoginPacketHandler;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\handler\PreSpawnPacketHandler;
use pocketmine\network\mcpe\handler\ResourcePacksPacketHandler;
use pocketmine\network\mcpe\handler\SessionStartPacketHandler;
use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
@ -93,6 +93,7 @@ use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\command\CommandData;
use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
@ -100,9 +101,13 @@ use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\UpdateAbilitiesPacketLayer;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\network\mcpe\protocol\UpdateAdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\NetworkSessionManager;
use pocketmine\network\PacketHandlingException;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\permission\DefaultPermissions;
use pocketmine\player\GameMode;
use pocketmine\player\Player;
@ -160,6 +165,7 @@ class NetworkSession{
*/
private \SplQueue $compressedQueue;
private bool $forceAsyncCompression = true;
private bool $enableCompression = false; //disabled until handshake completed
private PacketSerializerContext $packetSerializerContext;
@ -192,17 +198,10 @@ class NetworkSession{
$this->connectTime = time();
$this->setHandler(new LoginPacketHandler(
$this->setHandler(new SessionStartPacketHandler(
$this->server,
$this,
function(PlayerInfo $info) : void{
$this->info = $info;
$this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET);
$this->logger->setPrefix($this->getLogPrefix());
},
function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{
$this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey);
}
fn() => $this->onSessionStartSuccess()
));
$this->manager->add($this);
@ -217,6 +216,24 @@ class NetworkSession{
return $this->logger;
}
private function onSessionStartSuccess() : void{
$this->logger->debug("Session start handshake completed, awaiting login packet");
$this->flushSendBuffer(true);
$this->enableCompression = true;
$this->setHandler(new LoginPacketHandler(
$this->server,
$this,
function(PlayerInfo $info) : void{
$this->info = $info;
$this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET);
$this->logger->setPrefix($this->getLogPrefix());
},
function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{
$this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey);
}
));
}
protected function createPlayer() : void{
$this->server->createPlayer($this, $this->info, $this->authenticated, $this->cachedOfflinePlayerData)->onCompletion(
\Closure::fromCallable([$this, 'onPlayerCreated']),
@ -250,8 +267,8 @@ class NetworkSession{
$permissionHooks = $this->player->getPermissionRecalculationCallbacks();
$permissionHooks->add($permHook = function() : void{
$this->logger->debug("Syncing available commands and adventure settings due to permission recalculation");
$this->syncAdventureSettings($this->player);
$this->logger->debug("Syncing available commands and abilities/permissions due to permission recalculation");
$this->syncAbilities($this->player);
$this->syncAvailableCommands();
});
$this->disposeHooks->add(static function() use ($permissionHooks, $permHook) : void{
@ -331,18 +348,22 @@ class NetworkSession{
}
}
if($this->enableCompression){
Timings::$playerNetworkReceiveDecompress->startTiming();
try{
$stream = new PacketBatch($this->compressor->decompress($payload));
$decompressed = $this->compressor->decompress($payload);
}catch(DecompressionException $e){
$this->logger->debug("Failed to decompress packet: " . base64_encode($payload));
throw PacketHandlingException::wrap($e, "Compressed packet batch decode error");
}finally{
Timings::$playerNetworkReceiveDecompress->stopTiming();
}
}else{
$decompressed = $payload;
}
try{
foreach($stream->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){
foreach((new PacketBatch($decompressed))->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){
if($packet === null){
$this->logger->debug("Unknown packet: " . base64_encode($buffer));
throw new PacketHandlingException("Unknown packet received");
@ -440,7 +461,14 @@ class NetworkSession{
}elseif($this->forceAsyncCompression){
$syncMode = false;
}
$promise = $this->server->prepareBatch(PacketBatch::fromPackets($this->packetSerializerContext, ...$this->sendBuffer), $this->compressor, $syncMode);
$batch = PacketBatch::fromPackets($this->packetSerializerContext, ...$this->sendBuffer);
if($this->enableCompression){
$promise = $this->server->prepareBatch($batch, $this->compressor, $syncMode);
}else{
$promise = new CompressBatchPromise();
$promise->resolve($batch->getBuffer());
}
$this->sendBuffer = [];
$this->queueCompressedNoBufferFlush($promise, $immediate);
}
@ -716,7 +744,7 @@ class NetworkSession{
$this->syncAttributes($this->player, $this->player->getAttributeMap()->getAll());
$this->player->sendData(null);
$this->syncAdventureSettings($this->player);
$this->syncAbilities($this->player);
$this->invManager->syncAll();
$this->setHandler(new InGamePacketHandler($this->player, $this, $this->invManager));
}
@ -750,7 +778,7 @@ class NetworkSession{
}
public function syncViewAreaCenterPoint(Vector3 $newPos, int $viewDistance) : void{
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create(BlockPosition::fromVector3($newPos), $viewDistance * 16)); //blocks, not chunks >.>
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create(BlockPosition::fromVector3($newPos), $viewDistance * 16, [])); //blocks, not chunks >.>
}
public function syncPlayerSpawnPoint(Position $newSpawn) : void{
@ -766,37 +794,60 @@ class NetworkSession{
public function syncGameMode(GameMode $mode, bool $isRollback = false) : void{
$this->sendDataPacket(SetPlayerGameTypePacket::create(TypeConverter::getInstance()->coreGameModeToProtocol($mode)));
if($this->player !== null){
$this->syncAdventureSettings($this->player);
$this->syncAbilities($this->player);
$this->syncAdventureSettings(); //TODO: we might be able to do this with the abilities packet alone
}
if(!$isRollback && $this->invManager !== null){
$this->invManager->syncCreative();
}
}
/**
* TODO: make this less specialized
*/
public function syncAdventureSettings(Player $for) : void{
public function syncAbilities(Player $for) : void{
$isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR);
$pk = AdventureSettingsPacket::create(
0,
$isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL,
0,
//ALL of these need to be set for the base layer, otherwise the client will cry
$boolAbilities = [
UpdateAbilitiesPacketLayer::ABILITY_ALLOW_FLIGHT => $for->getAllowFlight(),
UpdateAbilitiesPacketLayer::ABILITY_FLYING => $for->isFlying(),
UpdateAbilitiesPacketLayer::ABILITY_NO_CLIP => !$for->hasBlockCollision(),
UpdateAbilitiesPacketLayer::ABILITY_OPERATOR => $isOp,
UpdateAbilitiesPacketLayer::ABILITY_TELEPORT => $for->hasPermission(DefaultPermissionNames::COMMAND_TELEPORT),
UpdateAbilitiesPacketLayer::ABILITY_INVULNERABLE => $for->isCreative(),
UpdateAbilitiesPacketLayer::ABILITY_MUTED => false,
UpdateAbilitiesPacketLayer::ABILITY_WORLD_BUILDER => false,
UpdateAbilitiesPacketLayer::ABILITY_INFINITE_RESOURCES => !$for->hasFiniteResources(),
UpdateAbilitiesPacketLayer::ABILITY_LIGHTNING => false,
UpdateAbilitiesPacketLayer::ABILITY_BUILD => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_MINE => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_DOORS_AND_SWITCHES => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_OPEN_CONTAINERS => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_ATTACK_PLAYERS => !$for->isSpectator(),
UpdateAbilitiesPacketLayer::ABILITY_ATTACK_MOBS => !$for->isSpectator(),
];
$this->sendDataPacket(UpdateAbilitiesPacket::create(
$isOp ? CommandPermissions::OPERATOR : CommandPermissions::NORMAL,
$isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER,
0,
$for->getId()
);
$for->getId(),
[
//TODO: dynamic flying speed! FINALLY!!!!!!!!!!!!!!!!!
new UpdateAbilitiesPacketLayer(UpdateAbilitiesPacketLayer::LAYER_BASE, $boolAbilities, 0.05, 0.1),
]
));
}
$pk->setFlag(AdventureSettingsPacket::WORLD_IMMUTABLE, $for->isSpectator());
$pk->setFlag(AdventureSettingsPacket::NO_PVP, $for->isSpectator());
$pk->setFlag(AdventureSettingsPacket::AUTO_JUMP, $for->hasAutoJump());
$pk->setFlag(AdventureSettingsPacket::ALLOW_FLIGHT, $for->getAllowFlight());
$pk->setFlag(AdventureSettingsPacket::NO_CLIP, !$for->hasBlockCollision());
$pk->setFlag(AdventureSettingsPacket::FLYING, $for->isFlying());
//TODO: permission flags
$this->sendDataPacket($pk);
public function syncAdventureSettings() : void{
if($this->player === null){
throw new \LogicException("Cannot sync adventure settings for a player that is not yet created");
}
//everything except auto jump is handled via UpdateAbilitiesPacket
$this->sendDataPacket(UpdateAdventureSettingsPacket::create(
noAttackingMobs: false,
noAttackingPlayers: false,
worldImmutable: false,
showNameTags: true,
autoJump: $this->player->hasAutoJump()
));
}
/**
@ -805,7 +856,7 @@ class NetworkSession{
public function syncAttributes(Living $entity, array $attributes) : void{
if(count($attributes) > 0){
$this->sendDataPacket(UpdateAttributesPacket::create($entity->getId(), array_map(function(Attribute $attr) : NetworkAttribute{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
}, $attributes), 0));
}
}

View File

@ -43,8 +43,6 @@ class ChunkCache implements ChunkListener{
/**
* Fetches the ChunkCache instance for the given world. This lazily creates cache systems as needed.
*
* @return ChunkCache
*/
public static function getInstance(World $world, Compressor $compressor) : self{
$worldId = spl_object_id($world);

View File

@ -1,4 +1,5 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
@ -37,6 +38,7 @@ use pocketmine\item\Durable;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\ItemIds;
use pocketmine\item\VanillaItems;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
@ -46,7 +48,9 @@ use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient;
use pocketmine\network\mcpe\protocol\types\recipe\StringIdMetaItemDescriptor;
use pocketmine\player\GameMode;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
@ -113,7 +117,7 @@ class TypeConverter{
public function coreItemStackToRecipeIngredient(Item $itemStack) : RecipeIngredient{
if($itemStack->isNull()){
return new RecipeIngredient(0, 0, 0);
return new RecipeIngredient(null, 0);
}
if($itemStack->hasAnyDamageValue()){
[$id, ] = ItemTranslator::getInstance()->toNetworkId($itemStack->getId(), 0);
@ -121,16 +125,26 @@ class TypeConverter{
}else{
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId($itemStack->getId(), $itemStack->getMeta());
}
return new RecipeIngredient($id, $meta, $itemStack->getCount());
return new RecipeIngredient(new IntIdMetaItemDescriptor($id, $meta), $itemStack->getCount());
}
public function recipeIngredientToCoreItemStack(RecipeIngredient $ingredient) : Item{
if($ingredient->getId() === 0){
return ItemFactory::getInstance()->get(ItemIds::AIR, 0, 0);
$descriptor = $ingredient->getDescriptor();
if($descriptor === null){
return VanillaItems::AIR();
}
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($ingredient->getId(), $ingredient->getMeta());
if($descriptor instanceof IntIdMetaItemDescriptor){
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($descriptor->getId(), $descriptor->getMeta());
return ItemFactory::getInstance()->get($id, $meta, $ingredient->getCount());
}
if($descriptor instanceof StringIdMetaItemDescriptor){
$intId = GlobalItemTypeDictionary::getInstance()->getDictionary()->fromStringId($descriptor->getId());
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($intId, $descriptor->getMeta());
return ItemFactory::getInstance()->get($id, $meta, $ingredient->getCount());
}
throw new \LogicException("Unsupported conversion of recipe ingredient to core item stack");
}
public function coreItemStackToNet(Item $itemStack) : ItemStack{
if($itemStack->isNull()){
@ -202,7 +216,7 @@ class TypeConverter{
*/
public function netItemStackToCore(ItemStack $itemStack) : Item{
if($itemStack->getId() === 0){
return ItemFactory::getInstance()->get(ItemIds::AIR, 0, 0);
return VanillaItems::AIR();
}
$compound = $itemStack->getNbt();
@ -232,6 +246,9 @@ class TypeConverter{
$compound = null;
}
}
if($id < -0x8000 || $id >= 0x7fff){
throw new TypeConversionException("Item ID must be in range " . -0x8000 . " ... " . 0x7fff . " (received $id)");
}
if($meta < 0 || $meta >= 0x7fff){ //this meta value may have been restored from the NBT
throw new TypeConversionException("Item meta must be in range 0 ... " . 0x7fff . " (received $meta)");
}

View File

@ -51,7 +51,6 @@ use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
@ -80,6 +79,7 @@ use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\RequestAbilityPacket;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
@ -114,6 +114,7 @@ use function count;
use function fmod;
use function implode;
use function in_array;
use function is_bool;
use function is_infinite;
use function is_nan;
use function json_decode;
@ -143,6 +144,11 @@ class InGamePacketHandler extends PacketHandler{
/** @var UseItemTransactionData|null */
protected $lastRightClickData = null;
protected ?Vector3 $lastPlayerAuthInputPosition = null;
protected ?float $lastPlayerAuthInputYaw = null;
protected ?float $lastPlayerAuthInputPitch = null;
protected ?int $lastPlayerAuthInputFlags = null;
/** @var bool */
public $forceMoveSync = false;
@ -166,9 +172,10 @@ class InGamePacketHandler extends PacketHandler{
return true;
}
private function resolveOnOffInputFlags(PlayerAuthInputPacket $packet, int $startFlag, int $stopFlag) : ?bool{
$enabled = $packet->hasFlag($startFlag);
if($enabled !== $packet->hasFlag($stopFlag)){
private function resolveOnOffInputFlags(int $inputFlags, int $startFlag, int $stopFlag) : ?bool{
$enabled = ($inputFlags & (1 << $startFlag)) !== 0;
$disabled = ($inputFlags & (1 << $stopFlag)) !== 0;
if($enabled !== $disabled){
return $enabled;
}
//neither flag was set, or both were set
@ -177,25 +184,35 @@ class InGamePacketHandler extends PacketHandler{
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
$rawPos = $packet->getPosition();
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $packet->getYaw(), $packet->getHeadYaw(), $packet->getPitch()] as $float){
$rawYaw = $packet->getYaw();
$rawPitch = $packet->getPitch();
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $rawYaw, $packet->getHeadYaw(), $rawPitch] as $float){
if(is_infinite($float) || is_nan($float)){
$this->session->getLogger()->debug("Invalid movement received, contains NAN/INF components");
return false;
}
}
$yaw = fmod($packet->getYaw(), 360);
$pitch = fmod($packet->getPitch(), 360);
if($rawYaw !== $this->lastPlayerAuthInputYaw || $rawPitch !== $this->lastPlayerAuthInputPitch){
$this->lastPlayerAuthInputYaw = $rawYaw;
$this->lastPlayerAuthInputPitch = $rawPitch;
$yaw = fmod($rawYaw, 360);
$pitch = fmod($rawPitch, 360);
if($yaw < 0){
$yaw += 360;
}
$this->player->setRotation($yaw, $pitch);
}
$curPos = $this->player->getLocation();
$hasMoved = $this->lastPlayerAuthInputPosition === null || !$this->lastPlayerAuthInputPosition->equals($rawPos);
$newPos = $rawPos->round(4)->subtract(0, 1.62, 0);
if($this->forceMoveSync && $newPos->distanceSquared($curPos) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
if($this->forceMoveSync && $hasMoved){
$curPos = $this->player->getLocation();
if($newPos->distanceSquared($curPos) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->session->getLogger()->debug("Got outdated pre-teleport movement, received " . $newPos . ", expected " . $curPos);
//Still getting movements from before teleport, ignore them
return false;
@ -203,11 +220,16 @@ class InGamePacketHandler extends PacketHandler{
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
$this->forceMoveSync = false;
}
$sneaking = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING);
$sprinting = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
$swimming = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
$gliding = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$inputFlags = $packet->getInputFlags();
if($inputFlags !== $this->lastPlayerAuthInputFlags){
$this->lastPlayerAuthInputFlags = $inputFlags;
$sneaking = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING);
$sprinting = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
$swimming = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
$gliding = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$mismatch =
($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
@ -220,8 +242,10 @@ class InGamePacketHandler extends PacketHandler{
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
$this->player->jump();
}
}
if(!$this->forceMoveSync){
if(!$this->forceMoveSync && $hasMoved){
$this->lastPlayerAuthInputPosition = $rawPos;
//TODO: this packet has WAYYYYY more useful information that we're not using
$this->player->handleMovement($newPos);
}
@ -267,7 +291,6 @@ class InGamePacketHandler extends PacketHandler{
//TODO HACK: EATING_ITEM is sent back to the server when the server sends it for other players (1.14 bug, maybe earlier)
return $packet->actorRuntimeId === ActorEvent::EATING_ITEM;
}
$this->player->removeCurrentWindow();
switch($packet->eventId){
case ActorEvent::EATING_ITEM: //TODO: ignore this and handle it server-side
@ -610,6 +633,10 @@ class InGamePacketHandler extends PacketHandler{
case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK:
//TODO: do we need to handle this?
break;
case PlayerAction::START_ITEM_USE_ON:
case PlayerAction::STOP_ITEM_USE_ON:
//TODO: this has no obvious use and seems only used for analytics in vanilla - ignore it
break;
default:
$this->session->getLogger()->debug("Unhandled/unknown player action type " . $action);
return false;
@ -641,26 +668,6 @@ class InGamePacketHandler extends PacketHandler{
return true; //this is a broken useless packet, so we don't use it
}
public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{
if($packet->targetActorUniqueId !== $this->player->getId()){
return false; //TODO: operators can change other people's permissions using this
}
$handled = false;
$isFlying = $packet->getFlag(AdventureSettingsPacket::FLYING);
if($isFlying !== $this->player->isFlying()){
if(!$this->player->toggleFlight($isFlying)){
$this->session->syncAdventureSettings($this->player);
}
$handled = true;
}
//TODO: check for other changes
return $handled;
}
public function handleBlockActorData(BlockActorDataPacket $packet) : bool{
$pos = new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ());
if($pos->distanceSquared($this->player->getLocation()) > 10000){
@ -878,7 +885,14 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleModalFormResponse(ModalFormResponsePacket $packet) : bool{
if($packet->cancelReason !== null){
//TODO: make APIs for this to allow plugins to use this information
return $this->player->onFormSubmit($packet->formId, null);
}elseif($packet->formData !== null){
return $this->player->onFormSubmit($packet->formId, self::stupid_json_decode($packet->formData, true));
}else{
throw new PacketHandlingException("Expected either formData or cancelReason to be set in ModalFormResponsePacket");
}
}
/**
@ -980,4 +994,22 @@ class InGamePacketHandler extends PacketHandler{
$this->player->emote($packet->getEmoteId());
return true;
}
public function handleRequestAbility(RequestAbilityPacket $packet) : bool{
if($packet->getAbilityId() === RequestAbilityPacket::ABILITY_FLYING){
$isFlying = $packet->getAbilityValue();
if(!is_bool($isFlying)){
throw new PacketHandlingException("Flying ability should always have a bool value");
}
if($isFlying !== $this->player->isFlying()){
if(!$this->player->toggleFlight($isFlying)){
$this->session->syncAbilities($this->player);
}
}
return true;
}
return false;
}
}

View File

@ -165,13 +165,13 @@ class LoginPacketHandler extends PacketHandler{
if(!is_array($claims["extraData"])){
throw new PacketHandlingException("'extraData' key should be an array");
}
$mapper = new \JsonMapper;
$mapper = new \JsonMapper();
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
try{
/** @var AuthenticationData $extraData */
$extraData = $mapper->map($claims["extraData"], new AuthenticationData);
$extraData = $mapper->map($claims["extraData"], new AuthenticationData());
}catch(\JsonMapper_Exception $e){
throw PacketHandlingException::wrap($e);
}
@ -193,12 +193,12 @@ class LoginPacketHandler extends PacketHandler{
throw PacketHandlingException::wrap($e);
}
$mapper = new \JsonMapper;
$mapper = new \JsonMapper();
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
try{
$clientData = $mapper->map($clientDataClaims, new ClientData);
$clientData = $mapper->map($clientDataClaims, new ClientData());
}catch(\JsonMapper_Exception $e){
throw PacketHandlingException::wrap($e);
}

View File

@ -23,16 +23,19 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\handler;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\cache\CraftingDataCache;
use pocketmine\network\mcpe\cache\StaticPacketCache;
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\StartGamePacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\BoolGameRule;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\LevelSettings;
@ -42,6 +45,7 @@ use pocketmine\network\mcpe\protocol\types\SpawnSettings;
use pocketmine\player\Player;
use pocketmine\Server;
use pocketmine\VersionInfo;
use Ramsey\Uuid\Uuid;
use function sprintf;
/**
@ -58,6 +62,7 @@ class PreSpawnPacketHandler extends PacketHandler{
public function setUp() : void{
$location = $this->player->getLocation();
$this->session->getLogger()->debug("Preparing StartGamePacket");
$levelSettings = new LevelSettings();
$levelSettings->seed = -1;
$levelSettings->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly
@ -82,6 +87,7 @@ class PreSpawnPacketHandler extends PacketHandler{
$this->player->getOffsetPosition($location),
$location->pitch,
$location->yaw,
new CacheableNbt(CompoundTag::create()), //TODO: we don't care about this right now
$levelSettings,
"",
$this->server->getMotd(),
@ -93,26 +99,48 @@ class PreSpawnPacketHandler extends PacketHandler{
"",
false,
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
Uuid::fromString(Uuid::NIL),
false,
[],
0,
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries()
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries(),
));
$this->session->getLogger()->debug("Sending actor identifiers");
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getAvailableActorIdentifiers());
$this->session->getLogger()->debug("Sending biome definitions");
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs());
$this->session->getLogger()->debug("Sending attributes");
$this->session->syncAttributes($this->player, $this->player->getAttributeMap()->getAll());
$this->session->getLogger()->debug("Sending available commands");
$this->session->syncAvailableCommands();
$this->session->syncAdventureSettings($this->player);
$this->session->getLogger()->debug("Sending abilities");
$this->session->syncAbilities($this->player);
$this->session->syncAdventureSettings();
$this->session->getLogger()->debug("Sending effects");
foreach($this->player->getEffects()->all() as $effect){
$this->session->onEntityEffectAdded($this->player, $effect, false);
}
$this->session->getLogger()->debug("Sending actor metadata");
$this->player->sendData([$this->player]);
$this->session->getLogger()->debug("Sending inventory");
$this->inventoryManager->syncAll();
$this->inventoryManager->syncCreative();
$this->inventoryManager->syncSelectedHotbarSlot();
$this->session->getLogger()->debug("Sending creative inventory data");
$this->inventoryManager->syncCreative();
$this->session->getLogger()->debug("Sending crafting data");
$this->session->sendDataPacket(CraftingDataCache::getInstance()->getCache($this->server->getCraftingManager()));
$this->session->getLogger()->debug("Sending player list");
$this->session->syncPlayerList($this->server->getOnlinePlayers());
}
@ -121,4 +149,10 @@ class PreSpawnPacketHandler extends PacketHandler{
return true;
}
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
//the client will send this every tick once we start sending chunks, but we don't handle it in this stage
//this is very spammy so we filter it out
return true;
}
}

View File

@ -0,0 +1,76 @@
<?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\network\mcpe\handler;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\NetworkSettingsPacket;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\RequestNetworkSettingsPacket;
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
use pocketmine\Server;
final class SessionStartPacketHandler extends PacketHandler{
/**
* @phpstan-param \Closure() : void $onSuccess
*/
public function __construct(
private Server $server,
private NetworkSession $session,
private \Closure $onSuccess
){}
public function handleRequestNetworkSettings(RequestNetworkSettingsPacket $packet) : bool{
$protocolVersion = $packet->getProtocolVersion();
if(!$this->isCompatibleProtocol($protocolVersion)){
$this->session->sendDataPacket(PlayStatusPacket::create($protocolVersion < ProtocolInfo::CURRENT_PROTOCOL ? PlayStatusPacket::LOGIN_FAILED_CLIENT : PlayStatusPacket::LOGIN_FAILED_SERVER), true);
//This pocketmine disconnect message will only be seen by the console (PlayStatusPacket causes the messages to be shown for the client)
$this->session->disconnect(
$this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_disconnect_incompatibleProtocol((string) $protocolVersion)),
false
);
return true;
}
//TODO: we're filling in the defaults to get pre-1.19.30 behaviour back for now, but we should explore the new options in the future
$this->session->sendDataPacket(NetworkSettingsPacket::create(
NetworkSettingsPacket::COMPRESS_EVERYTHING,
CompressionAlgorithm::ZLIB,
false,
0,
0
));
($this->onSuccess)();
return true;
}
protected function isCompatibleProtocol(int $protocolVersion) : bool{
return $protocolVersion === ProtocolInfo::CURRENT_PROTOCOL;
}
}

View File

@ -1,16 +1,22 @@
<?php
/*
* RakLib network library
*
*
* This project is not affiliated with Jenkins Software LLC nor RakNet.
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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);

View File

@ -58,7 +58,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
* Sometimes this gets changed when the MCPE-layer protocol gets broken to the point where old and new can't
* communicate. It's important that we check this to avoid catastrophes.
*/
private const MCPE_RAKNET_PROTOCOL_VERSION = 10;
private const MCPE_RAKNET_PROTOCOL_VERSION = 11;
private const MCPE_RAKNET_PACKET_ID = "\xfe";
@ -84,8 +84,8 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->sleeper = new SleeperNotifier();
$mainToThreadBuffer = new \Threaded;
$threadToMainBuffer = new \Threaded;
$mainToThreadBuffer = new \Threaded();
$threadToMainBuffer = new \Threaded();
$this->rakLib = new RakLibServer(
$this->server->getLogger(),

Some files were not shown because too many files have changed in this diff Show More