mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-10 03:34:06 +00:00
Compare commits
166 Commits
5.0.0-ALPH
...
5.0.0-ALPH
Author | SHA1 | Date | |
---|---|---|---|
023010370b | |||
de3ba00684 | |||
b47035fbab | |||
072a9202ef | |||
f0925ff9dc | |||
d9324b9951 | |||
1d9336ed67 | |||
d37142af4b | |||
7c068101b7 | |||
217f9aea02 | |||
2f5e08067d | |||
a8556dff02 | |||
664089861a | |||
7314151c47 | |||
7abfc46567 | |||
3a13f5cf5f | |||
edb8f19a0c | |||
6c0254c1eb | |||
0bb9fb09cc | |||
ab21fcdd67 | |||
d9b8251f7b | |||
ad6a423d12 | |||
b03df4f1e6 | |||
0a2a6e2b3a | |||
0eb751c1c9 | |||
b59b1e491e | |||
95e8c68fde | |||
7e16f9be8f | |||
768650cee0 | |||
c2c529e2da | |||
c55e23a2c6 | |||
5c2ed210fc | |||
289e86e899 | |||
7d59bafd83 | |||
950eddf405 | |||
1bbe053848 | |||
69967a0e55 | |||
0132ff47cb | |||
2ed48c8469 | |||
d786ed5ebf | |||
8909aa6a18 | |||
a9f06fc5f4 | |||
a14346e98c | |||
b76265cd37 | |||
dff3f45d22 | |||
1e17d86421 | |||
ba18a81e88 | |||
6dd006e730 | |||
b57fcb52d4 | |||
2c20b20ad2 | |||
329c2a6c0f | |||
39218017ca | |||
a4494a2133 | |||
fc487b17be | |||
ecd8f151f1 | |||
c671d8a80b | |||
ca1f1bf09f | |||
91ac47ecba | |||
f4a1d69075 | |||
cbeae906e1 | |||
b25e8e26f0 | |||
a79be994de | |||
e26c8b9e9f | |||
4e9c3e101d | |||
d295e1be54 | |||
2f3fcef97c | |||
4df1f7f502 | |||
d74719704e | |||
c5056e0a43 | |||
a47aa50477 | |||
5021096bdd | |||
9c391a6809 | |||
f7930a3a0b | |||
bb7df60a4d | |||
992cb06da6 | |||
bb3f87f862 | |||
d2eddf9d33 | |||
81ca0c8fbf | |||
fc77b14760 | |||
52b6f1a492 | |||
0233e74f4f | |||
dd355c58d8 | |||
267032cff9 | |||
545d18eea7 | |||
9b4b960eb2 | |||
d4b8c47a65 | |||
4a3d9f8f83 | |||
1c96e7936c | |||
c5ca0857ee | |||
6cecd690b2 | |||
91e38d1f97 | |||
653178c1fd | |||
eb06535ed1 | |||
f9bcc8e862 | |||
c2918709a3 | |||
ece49f011c | |||
f43ca405d4 | |||
d146175d27 | |||
3baa5ab712 | |||
e647e8c933 | |||
c02bead12b | |||
e9ca25c1cb | |||
a513cca582 | |||
9a47c1d401 | |||
433f5451d7 | |||
85231215e7 | |||
64e505defb | |||
33d1755eae | |||
fc63c54116 | |||
db07976aab | |||
0e4b79ea77 | |||
588c9b114b | |||
b095f606c1 | |||
b312e93176 | |||
42db3abf5e | |||
172ce659b8 | |||
61933624d2 | |||
0d31b25fba | |||
90beaeaeb4 | |||
466f7e98ed | |||
59be901efe | |||
2da9b76452 | |||
c79806eaf0 | |||
7ac6bd79a9 | |||
6b8b7311f0 | |||
f173b91ca1 | |||
9796dfd4d9 | |||
c1ba735c9e | |||
8fd4918429 | |||
c89df7eb1c | |||
0d169b4e80 | |||
b1c0eae1f6 | |||
80832ff763 | |||
f7d0d16eb3 | |||
6375139d0b | |||
a8edb92565 | |||
133884da72 | |||
b4c7d33388 | |||
d37841c214 | |||
2a81a421f3 | |||
567bd8abb5 | |||
3d038b28ff | |||
0c9b6a6797 | |||
639f089c55 | |||
9010b2743c | |||
4c91c4aaf1 | |||
f8cc015c51 | |||
17125ce0e3 | |||
3987ee6cb2 | |||
51a684c0ea | |||
43e69041fc | |||
5d2b0acfc8 | |||
7d1d62042c | |||
2a33c9ed3b | |||
b03733442b | |||
9c9929ff39 | |||
566a8a261f | |||
a9e5f92958 | |||
17dde140a5 | |||
b3473960b4 | |||
044d35956e | |||
ee7d4728d8 | |||
923dcec4e7 | |||
1e5597f0d5 | |||
aebcfc516f | |||
97ef209c5f |
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@ -6,6 +6,12 @@ updates:
|
||||
interval: daily
|
||||
time: "10:00"
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
#only allow patch updates for locale-data - this has to be updated manually due to codegen
|
||||
- dependency-name: pocketmine/locale-data
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
|
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
uses: docker/build-push-action@v3.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
uses: docker/build-push-action@v3.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
uses: docker/build-push-action@v3.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -85,7 +85,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
uses: docker/build-push-action@v3.3.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
2
.github/workflows/discord-release-notify.yml
vendored
2
.github/workflows/discord-release-notify.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.22.0
|
||||
uses: shivammathur/setup-php@2.23.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
|
||||
|
5
.github/workflows/draft-release.yml
vendored
5
.github/workflows/draft-release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.22.0
|
||||
uses: shivammathur/setup-php@2.23.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
|
||||
@ -55,6 +55,7 @@ jobs:
|
||||
echo ::set-output name=MCPE_VERSION::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;')
|
||||
echo ::set-output name=PM_VERSION_SHORT::$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);')
|
||||
echo ::set-output name=PM_VERSION_MD::$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);')
|
||||
echo ::set-output name=CHANGELOG_SUFFIX::$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;')
|
||||
|
||||
- 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 }} ${{ github.run_id }} > build_info.json
|
||||
@ -80,4 +81,4 @@ jobs:
|
||||
body: |
|
||||
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
|
||||
|
||||
Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.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 }}${{ steps.get-pm-version.outputs.CHANGELOG_SUFFIX }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
|
||||
|
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -198,10 +198,12 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.22.0
|
||||
uses: shivammathur/setup-php@2.23.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer:3.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff --ansi
|
||||
|
@ -36,8 +36,8 @@ use function ksort;
|
||||
use function mb_strtoupper;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
use function str_ends_with;
|
||||
use function str_replace;
|
||||
use function substr;
|
||||
use const SORT_STRING;
|
||||
use const STDERR;
|
||||
|
||||
@ -121,7 +121,7 @@ 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"){
|
||||
if(!str_ends_with($file, ".php")){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\block\utils\LeverFacing;
|
||||
use pocketmine\block\utils\MushroomBlockType;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\item\MedicineType;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\SuspiciousStewType;
|
||||
use function array_key_first;
|
||||
@ -166,6 +167,7 @@ $enumsUsed = [
|
||||
DyeColor::getAll(),
|
||||
FroglightType::getAll(),
|
||||
LeverFacing::getAll(),
|
||||
MedicineType::getAll(),
|
||||
MushroomBlockType::getAll(),
|
||||
SkullType::getAll(),
|
||||
SlabType::getAll(),
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\make_release;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\VersionInfo;
|
||||
@ -30,7 +31,6 @@ use function array_keys;
|
||||
use function array_map;
|
||||
use function dirname;
|
||||
use function fgets;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function getopt;
|
||||
@ -50,7 +50,7 @@ use const STR_PAD_LEFT;
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{
|
||||
$versionInfo = Utils::assumeNotFalse(file_get_contents($versionInfoPath), $versionInfoPath . " should always exist");
|
||||
$versionInfo = Filesystem::fileGetContents($versionInfoPath);
|
||||
$versionInfo = preg_replace(
|
||||
$pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m',
|
||||
'$1const BASE_VERSION = "' . $newVersion . '";',
|
||||
|
@ -30,3 +30,77 @@ Released 15th December 2022.
|
||||
|
||||
## Dependencies
|
||||
- Updated BedrockProtocol to [17.1.0](https://github.com/pmmp/BedrockProtocol/releases/tag/17.1.0+bedrock-1.19.50). This adds some missing `LevelSoundEvent` constants and fixes the values for `ContainerUIIds`.
|
||||
|
||||
# 4.12.3
|
||||
Released 28th December 2022.
|
||||
|
||||
## Fixes
|
||||
- Fixed unauthenticated connections taking up player count slots, preventing players from joining.
|
||||
- Fixed a possible crash in `World->tickChunk()` when plugins unload chunks during some events.
|
||||
- `/gamemode` will now report a failure to change game mode if the player is already in the requested game mode.
|
||||
|
||||
# 4.12.4
|
||||
Released 3rd January 2023.
|
||||
|
||||
## Fixes
|
||||
- Added workarounds for an active exploit being used to deny service to servers.
|
||||
|
||||
# 4.12.5
|
||||
Released 6th January 2023.
|
||||
|
||||
## Fixes
|
||||
- Removed a workaround for an old client bug in custom form responses. The code contained a denial-of-service vulnerability.
|
||||
|
||||
# 4.12.6
|
||||
Released 7th January 2023.
|
||||
|
||||
## Changes
|
||||
- Added a new security measure to `NetworkSession` to detect and ban players who flood the server with packets.
|
||||
|
||||
# 4.12.7
|
||||
Released 8th January 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed players getting kicked when the server lags for too long.
|
||||
- Fixed players getting kicked when a debugging session is active and a breakpoint is hit.
|
||||
|
||||
# 4.12.8
|
||||
Released 9th January 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed players getting kicked during PvP.
|
||||
- Fixed players randomly getting kicked on Windows (improper rate limit handling wrt. 15ms timer resolution).
|
||||
|
||||
# 4.12.9
|
||||
Released 16th January 2023.
|
||||
|
||||
## Improvements
|
||||
### Timings
|
||||
- Added new timers:
|
||||
- `Server Mid-Tick Processing` - time spent processing Snooze interrupts between ticks (e.g. incoming network packets)
|
||||
- `Server Tick Update Cycle` - time spent processing regular per-tick updates (e.g. entity movement, world updates, etc.) (`Server->tick()`)
|
||||
- `Full Server Tick` timer now counts the total of `Server Mid-Tick Processing` and `Server Tick Update Cycle`, which generates more accurate performance metrics.
|
||||
- Previously, this timer only counted the time spent during regular per-tick updates, and the time recorded by `Server Mid-Tick Processing` was not included in the report at all.
|
||||
|
||||
## Fixes
|
||||
- Fixed blocks such as pressure plates being able to be placed without the correct supporting blocks if the clicked block was solid.
|
||||
- Pressure plates now self-destruct when the block below them is removed.
|
||||
- Fixed being unable to place blocks by clicking on the side of a bell (when the click doesn't result in ringing the bell).
|
||||
- Fixed various rotation-aware blocks (e.g. stairs) behaving incorrectly when placed by clicking on the side of a replaceable block (e.g. tall grass).
|
||||
- Fixed banners being able to be placed on top of blocks such as skulls.
|
||||
- Fixed server-side collision boxes of walls and glass (which should connect, but didn't). Note that wall connections still don't show client side - this just fixes the collision boxes.
|
||||
- Fixed `PlayerInteractEvent` with `LEFT_CLICK` sometimes firing before `BlockBreakEvent` when breaking blocks.
|
||||
|
||||
## Other changes
|
||||
- Increased packet batch budget for player sessions.
|
||||
|
||||
# 4.12.10
|
||||
Released 18th January 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed reported server load not including the time spent processing Snooze interrupts between ticks (e.g. incoming network packets).
|
||||
- Fixed `Connection Handler` entry in timings report not including time spent receiving packets.
|
||||
|
||||
## Note about server load & performance
|
||||
This version will report higher apparent server load than previous versions. The actual performance of the server is unchanged; the previous reported load was inaccurate.
|
||||
These bugs have been present for nearly 5 years (ever since the first introduction of Snooze in 3.0.0).
|
||||
|
94
changelogs/4.13-beta.md
Normal file
94
changelogs/4.13-beta.md
Normal file
@ -0,0 +1,94 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.50**
|
||||
|
||||
This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.13.0-BETA1
|
||||
Released 18th January 2023.
|
||||
|
||||
## Gameplay
|
||||
- Death message is now shown on the death screen when a player dies.
|
||||
- Armour damage is now only increased if the armour reduced the damage taken.
|
||||
- Implemented Swift Sneak enchantment.
|
||||
- Fixed incorrect collision box calculation of walls and glass/bars when connected. Note: Client-side, wall connections are still broken; this only fixes projectile flight server-side.
|
||||
|
||||
## Performance
|
||||
- Improved performance of chunk selection for chunk random ticking using a cache. This improves performance of chunk random ticking by 10-20%.
|
||||
|
||||
## Localization
|
||||
- Added localized description for the `/dumpmemory` command.
|
||||
|
||||
## Permissions
|
||||
- Added the following new core permissions:
|
||||
- `pocketmine.command.effect.other` - allows the player to use the `/effect` command on other players (default operator only)
|
||||
- `pocketmine.command.effect.self` - allows the player to use the `/effect` command on themselves (default operator only)
|
||||
- `pocketmine.command.enchant.other` - allows the player to use the `/enchant` command on other players (default operator only)
|
||||
- `pocketmine.command.enchant.self` - allows the player to use the `/enchant` command on themselves (default operator only)
|
||||
- `pocketmine.command.gamemode.other` - allows the player to use the `/gamemode` command on other players (default operator only)
|
||||
- `pocketmine.command.gamemode.self` - allows the player to use the `/gamemode` command on themselves (default operator only)
|
||||
- `pocketmine.command.give.other` - allows the player to use the `/give` command on other players (default operator only)
|
||||
- `pocketmine.command.give.self` - allows the player to use the `/give` command on themselves (default operator only)
|
||||
- `pocketmine.command.spawnpoint.other` - allows the player to use the `/spawnpoint` command on other players (default operator only)
|
||||
- `pocketmine.command.spawnpoint.self` - allows the player to use the `/spawnpoint` command on themselves (default operator only)
|
||||
- `pocketmine.command.teleport.other` - allows the player to use the `/teleport` command on other players (default operator only)
|
||||
- `pocketmine.command.teleport.self` - allows the player to use the `/teleport` command on themselves (default operator only)
|
||||
- `pocketmine.command.title.other` - allows the player to use the `/title` command on other players (default operator only)
|
||||
- `pocketmine.command.title.self` - allows the player to use the `/title` command on themselves (default operator only)
|
||||
|
||||
## Internals
|
||||
- Decoupled `Player->sendMessage()` and `Player->sendTranslation()`.
|
||||
- Refactored resource pack loading in `ResourcePackManager` to make it easier to understand.
|
||||
- Client-aware translation processing has been moved to `NetworkSession` due to being client-specific.
|
||||
- Replaced hardcoded strings with constants in various places.
|
||||
- `NetworkSession` destructive cleanup is now deferred to the next session tick. This fixes various `InventoryManager` crashes when kicking players during events.
|
||||
- Updated code using `strpos()` to use `str_starts_with()`, `str_ends_with()` and `str_contains()` where appropriate.
|
||||
- Added documentation for some internal methods.
|
||||
|
||||
## API
|
||||
### `pocketmine\command`
|
||||
- The following new API methods have been added:
|
||||
- `protected VanillaCommand->fetchPermittedPlayerTarget(...) : ?Player` - fetches a player target according to the given sender permissions, or null if not found or not permitted
|
||||
|
||||
### `pocketmine\entity`
|
||||
- The following new API methods have been added:
|
||||
- `public Living->getDisplayName() : string` - the name of the entity to be shown in death messages, commands etc.
|
||||
|
||||
### `pocketmine\event\world`
|
||||
- The following new classes have been added:
|
||||
- `WorldSoundEvent` - called when a sound is played in a world
|
||||
- `WorldParticleEvent` - called when a particle is spawned in a world
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new API methods have been added:
|
||||
- `public Item->onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool` - called when a player interacts with an entity with this item in hand
|
||||
|
||||
### `pocketmine\lang`
|
||||
- `Language->translate()` and `Language->translateString()` no longer parse nested translation in the "base text". This was never intended behaviour, and didn't work beyond the first level anyway.
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following new interfaces have been added:
|
||||
- `PlayerDataProvider` - implemented by classes which want to offer storage for player data
|
||||
- The following new classes have been added:
|
||||
- `DatFilePlayerDataProvider` - the default player data provider, which stores `.dat` files in the `players` folder
|
||||
- `PlayerDataLoadException` - thrown when an error occurs while loading player data
|
||||
- `PlayerDataSaveException` - thrown when an error occurs while saving player data
|
||||
- The following API methods have been deprecated:
|
||||
- `Player->sendTranslation()` - use `Player->sendMessage()` instead with a `Translatable` message
|
||||
|
||||
### `pocketmine\resourcepacks`
|
||||
- The following new API methods have been added:
|
||||
- `public ResourcePackManager->setResourceStack(list<ResourcePack> $resourceStack) : void` - sets the list of resource packs to be applied by players
|
||||
- `public ResourcePackManager->setPackEncryptionKey(string $id, ?string $key) : void` - sets the encryption key to be used for a resource pack
|
||||
|
||||
### `pocketmine\utils`
|
||||
- The following new API methods have been added:
|
||||
- `public static Filesystem::fileGetContents(...) : string` - a wrapper around `file_get_contents()` which throws an exception on failure
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following new API methods have been added:
|
||||
- `public World->requestSafeSpawn(?Vector3 $spawn = null) : Promise<Position>` - an async version of `getSafeSpawn()` which generates all the needed chunks before returning
|
@ -737,3 +737,135 @@ Released 19th December 2022.
|
||||
- `EntityLegacyIds` has been removed. Legacy entity IDs found during world loading are now converted via `LegacyEntityIdToStringIdMap`.
|
||||
- All usages of NBT keys now use class constants instead of hardcoded strings (except for an occasional overlooked one).
|
||||
- All members of `BlockTypeTags` now have a `pocketmine:` prefix on the value. This does not affect constant usages.
|
||||
|
||||
# 5.0.0-ALPHA7
|
||||
Released 18th January 2023.
|
||||
|
||||
**This release includes changes from the following releases, which may not be mentioned:**
|
||||
- [4.12.3](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.3)
|
||||
- [4.12.4](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.4)
|
||||
- [4.12.5](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.5)
|
||||
- [4.12.6](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.6)
|
||||
- [4.12.7](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.7)
|
||||
- [4.12.8](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.8)
|
||||
- [4.12.9](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.9)
|
||||
- [4.12.10](https://github.com/pmmp/PocketMine-MP/releases/tag/4.12.10)
|
||||
- [4.13.0-BETA1](https://github.com/pmmp/PocketMine-MP/releases/tag/4.13.0-BETA1)
|
||||
|
||||
## Fixes
|
||||
- Fixed glowing item frame placement creating the wrong tile, causing invisible items.
|
||||
|
||||
## Localization
|
||||
- Localized disconnect messages are now used in the following cases:
|
||||
- Server full
|
||||
- Player not on the server whitelist
|
||||
- Player on the server ban list
|
||||
- Invalid skin
|
||||
- Invalid username
|
||||
- Kicked using `/kick`
|
||||
- Banned using `/ban`
|
||||
- Failure to find a safe spawn position
|
||||
- Session open, session close and session player name discovery messages are now localized.
|
||||
- All permissions now have localized descriptions. These are not currently used by PocketMine-MP, but may be useful for plugins.
|
||||
|
||||
## Gameplay
|
||||
### Worlds
|
||||
- Added support for 3D biomes. This isn't used by PocketMine-MP yet, but is necessary to be able to fully load 1.18 worlds.
|
||||
|
||||
### Blocks
|
||||
- Added the following new blocks:
|
||||
- Chain
|
||||
- Sculk
|
||||
|
||||
### Items
|
||||
- Added the following new items:
|
||||
- Eye Drops (from Education Edition)
|
||||
- Antidote (from Education Edition)
|
||||
- Elixir (from Education Edition)
|
||||
- Tonic (from Education Edition)
|
||||
|
||||
## API
|
||||
### Overview
|
||||
- Biome-related APIs have changed to accommodate 3D biomes.
|
||||
- Disconnect-related APIs have changed to accommodate localized disconnect messages.
|
||||
- New, more powerful chat formatting API introduced to `PlayerChatEvent`.
|
||||
- Glowing item frames moved to a separate block type instead of being a property of regular item frames (due to technical limitations).
|
||||
|
||||
### `pocketmine\block`
|
||||
- The following API methods have been removed:
|
||||
- `ItemFrame->isGlowing() : bool`
|
||||
- `ItemFrame->setGlowing(bool $glowing) : void`
|
||||
- The following new API methods have been added:
|
||||
- `public static VanillaBlocks::GLOWING_ITEM_FRAME() : ItemFrame`
|
||||
- The following constants have been added:
|
||||
- `BlockTypeIds::GLOWING_ITEM_FRAME`
|
||||
- `BlockTypeIds::CHAIN`
|
||||
- `BlockTypeIds::SCULK`
|
||||
- The following new classes have been added:
|
||||
- `Chain`
|
||||
- `Sculk`
|
||||
|
||||
### `pocketmine\event\player`
|
||||
- The following API methods have changed signatures:
|
||||
- `PlayerDuplicateLoginEvent->getDisconnectMessage()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerDuplicateLoginEvent->setDisconnectMessage()` now accepts `Translatable|string` instead of `string`
|
||||
- `PlayerKickEvent->getReason()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerKickEvent->setReason()` now accepts `Translatable|string` instead of `string`
|
||||
- `PlayerLoginEvent->getKickMessage()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerLoginEvent->setKickMessage()` now accepts `Translatable|string` instead of `string`
|
||||
- `PlayerPreLoginEvent->getFinalKickMessage()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerPreLoginEvent->getKickMessage()` now returns `Translatable|string|null` instead of `string|null`
|
||||
- `PlayerPreLoginEvent->setKickReason()` now accepts `Translatable|string` for the `$message` parameter instead of `string`
|
||||
- `PlayerQuitEvent->getQuitReason()` now returns `Translatable|string` instead of `string`
|
||||
- The following API methods have been removed:
|
||||
- `PlayerChatEvent->getFormat()` (use `PlayerChatEvent->getChatFormatter()` instead)
|
||||
- `PlayerChatEvent->setFormat()` (use `PlayerChatEvent->setChatFormatter()` instead)
|
||||
- The following new API methods have been added:
|
||||
- `public PlayerChatEvent->setChatFormatter(\pocketmine\player\chat\ChatFormatter $formatter) : void` - sets the chat formatter to be used for this event
|
||||
- `public PlayerChatEvent->getChatFormatter() : \pocketmine\player\chat\ChatFormatter` - returns the chat formatter to be used for this event
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new classes have been added:
|
||||
- `Medicine`
|
||||
- `MedicineType`
|
||||
- The following new class constants have been added:
|
||||
- `ItemTypeIds::MEDICINE`
|
||||
|
||||
### `pocketmine\network\query`
|
||||
- The following API methods have changed signatures:
|
||||
- `QueryInfo->getPlayerList()` now returns `list<string>` instead of `list<Player>`
|
||||
- `QueryInfo->setPlayerList()` now accepts `list<string>` instead of `list<Player>`
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following API methods have changed signatures:
|
||||
- `Player->kick()` now accepts `Translatable|string` for `$reason` instead of `string` (to allow localized kick messages)
|
||||
- `Player->disconnect()` now accepts `Translatable|string` for `$reason` instead of `string` (to allow localized disconnect messages)
|
||||
- `Player->sendJukeboxPopup()` now accepts `Translatable|string` instead of `string, string[]`
|
||||
|
||||
#### `pocketmine\player\chat`
|
||||
- The following new classes have been added:
|
||||
- `ChatFormatter` - interface implemented by chat formatters
|
||||
- `StandardChatFormatter` - formats chat messages in the vanilla Minecraft style
|
||||
- `LegacyRawChatFormatter` - implements the same behaviour previously used by `PlayerChatEvent->setFormat()`
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following API methods have changed signatures:
|
||||
- `World->getBiome()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
- `World->getBiomeId()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
- `World->setBiomeId()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
|
||||
#### `pocketmine\world\format`
|
||||
- The following new API methods have been added:
|
||||
- `public SubChunk->getBiomeArray() : PalettedBlockArray`
|
||||
- The following API methods have changed signatures:
|
||||
- `Chunk->getBiomeId()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
- `Chunk->setBiomeId()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
- `Chunk->__construct()` no longer accepts `BiomeArray` as a parameter (contained in each subchunk instead)
|
||||
- `SubChunk->__construct()` now accepts `int $emptyBlockId, list<PalettedBlockArray> $blocks, PalettedBlockArray $biomes, ?LightArray $blockLight, ?LightArray $skyLight` instead of `int, list<PalettedBlockArray>, ?LightArray, ?LightArray`
|
||||
- The following classes have been removed
|
||||
- `BiomeArray`
|
||||
|
||||
## Internals
|
||||
- Built-in commands now declare their names inside the class constructor, rather than accepting them as parameters. This improves code consistency.
|
||||
- `NetworkSession` disconnect APIs now accept `Translatable|string` instead of `string` to allow localized disconnect messages.
|
||||
- All external usages of `KnownTranslationKeys` are now removed. All localized messages are now sent using `Translatable` objects (usually from `KnownTranslationFactory`).
|
||||
|
@ -37,13 +37,13 @@
|
||||
"pocketmine/bedrock-block-upgrade-schema": "dev-master@dev",
|
||||
"pocketmine/bedrock-data": "dev-modern-world-support@dev",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "dev-master",
|
||||
"pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50",
|
||||
"pocketmine/bedrock-protocol": "~18.0.0+bedrock-1.19.50",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.11.0",
|
||||
"pocketmine/locale-data": "~2.18.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.4.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
@ -55,7 +55,7 @@
|
||||
"symfony/filesystem": "^5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.9.4",
|
||||
"phpstan/phpstan": "1.9.12",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
199
composer.lock
generated
199
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2d4da4bb4787764fbe706a04311c3cbf",
|
||||
"content-hash": "d6cafa9f9e236ad8d55c2eebdb51416a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -330,16 +330,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "17.1.0+bedrock-1.19.50",
|
||||
"version": "18.0.0+bedrock-1.19.50",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "c572706cf5e3202718dd35a35dd30fe08cd671de"
|
||||
"reference": "b558ec883bd967dd3339f513cba62d2fbcd63349"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de",
|
||||
"reference": "c572706cf5e3202718dd35a35dd30fe08cd671de",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/b558ec883bd967dd3339f513cba62d2fbcd63349",
|
||||
"reference": "b558ec883bd967dd3339f513cba62d2fbcd63349",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -347,13 +347,13 @@
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"php": "^8.0",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0 || ^0.3.0",
|
||||
"pocketmine/math": "^0.3.0 || ^0.4.0",
|
||||
"pocketmine/nbt": "^0.3.0",
|
||||
"ramsey/uuid": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.9.3",
|
||||
"phpstan/phpstan": "1.9.4",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
@ -371,9 +371,9 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/17.1.0+bedrock-1.19.50"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/18.0.0+bedrock-1.19.50"
|
||||
},
|
||||
"time": "2022-12-15T20:34:49+00:00"
|
||||
"time": "2023-01-06T21:47:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -514,24 +514,24 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/color",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Color.git",
|
||||
"reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2"
|
||||
"reference": "8cb346d0a21ad3287cc8d7175e4b643416607249"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Color/zipball/09be6ea6d76f2e33d6813c39d29c22c46c17e1d2",
|
||||
"reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2",
|
||||
"url": "https://api.github.com/repos/pmmp/Color/zipball/8cb346d0a21ad3287cc8d7175e4b643416607249",
|
||||
"reference": "8cb346d0a21ad3287cc8d7175e4b643416607249",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.59",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2"
|
||||
"phpstan/phpstan": "1.9.4",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -546,9 +546,9 @@
|
||||
"description": "Color handling library used by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Color/issues",
|
||||
"source": "https://github.com/pmmp/Color/tree/0.2.0"
|
||||
"source": "https://github.com/pmmp/Color/tree/0.3.0"
|
||||
},
|
||||
"time": "2020-12-11T01:24:32+00:00"
|
||||
"time": "2022-12-18T19:49:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/errorhandler",
|
||||
@ -591,16 +591,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "2.11.0",
|
||||
"version": "2.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53"
|
||||
"reference": "0f50afc3d0fec29f769a62e93c71f8a0fb968f76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/4b33d8fa53eda53d9662a7478806ebae2e4a5c53",
|
||||
"reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/0f50afc3d0fec29f769a62e93c71f8a0fb968f76",
|
||||
"reference": "0f50afc3d0fec29f769a62e93c71f8a0fb968f76",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -608,9 +608,9 @@
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/2.11.0"
|
||||
"source": "https://github.com/pmmp/Language/tree/2.18.0"
|
||||
},
|
||||
"time": "2022-11-25T14:24:34+00:00"
|
||||
"time": "2023-01-14T17:52:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
@ -906,42 +906,53 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/collection",
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/collection.git",
|
||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a"
|
||||
"reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4",
|
||||
"reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.3 || ^8",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"symfony/polyfill-php81": "^1.23"
|
||||
},
|
||||
"require-dev": {
|
||||
"captainhook/captainhook": "^5.3",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
|
||||
"ergebnis/composer-normalize": "^2.6",
|
||||
"fakerphp/faker": "^1.5",
|
||||
"hamcrest/hamcrest-php": "^2",
|
||||
"jangregor/phpstan-prophecy": "^0.8",
|
||||
"mockery/mockery": "^1.3",
|
||||
"captainhook/plugin-composer": "^5.3",
|
||||
"ergebnis/composer-normalize": "^2.28.3",
|
||||
"fakerphp/faker": "^1.21",
|
||||
"hamcrest/hamcrest-php": "^2.0",
|
||||
"jangregor/phpstan-prophecy": "^1.0",
|
||||
"mockery/mockery": "^1.5",
|
||||
"php-parallel-lint/php-console-highlighter": "^1.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.3",
|
||||
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpstan/extension-installer": "^1",
|
||||
"phpstan/phpstan": "^0.12.32",
|
||||
"phpstan/phpstan-mockery": "^0.12.5",
|
||||
"phpstan/phpstan-phpunit": "^0.12.11",
|
||||
"phpunit/phpunit": "^8.5 || ^9",
|
||||
"psy/psysh": "^0.10.4",
|
||||
"slevomat/coding-standard": "^6.3",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"vimeo/psalm": "^4.4"
|
||||
"phpstan/extension-installer": "^1.2",
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"phpstan/phpstan-mockery": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.3",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"psalm/plugin-mockery": "^1.1",
|
||||
"psalm/plugin-phpunit": "^0.18.4",
|
||||
"ramsey/coding-standard": "^2.0.3",
|
||||
"ramsey/conventional-commits": "^1.3",
|
||||
"vimeo/psalm": "^5.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"captainhook": {
|
||||
"force-install": true
|
||||
},
|
||||
"ramsey/conventional-commits": {
|
||||
"configFile": "conventional-commits.json"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ramsey\\Collection\\": "src/"
|
||||
@ -969,7 +980,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/collection/issues",
|
||||
"source": "https://github.com/ramsey/collection/tree/1.2.2"
|
||||
"source": "https://github.com/ramsey/collection/tree/1.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -981,27 +992,27 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-10T03:01:02+00:00"
|
||||
"time": "2022-12-27T19:12:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.6.0",
|
||||
"version": "4.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f"
|
||||
"reference": "433b2014e3979047db08a17a205f410ba3869cf2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/ad63bc700e7d021039e30ce464eba384c4a1d40f",
|
||||
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
|
||||
"reference": "433b2014e3979047db08a17a205f410ba3869cf2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10",
|
||||
"ext-json": "*",
|
||||
"php": "^8.0",
|
||||
"ramsey/collection": "^1.0"
|
||||
"ramsey/collection": "^1.2 || ^2.0"
|
||||
},
|
||||
"replace": {
|
||||
"rhumsaa/uuid": "self.version"
|
||||
@ -1061,7 +1072,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.6.0"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1073,7 +1084,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-11-05T23:03:38+00:00"
|
||||
"time": "2023-01-12T18:13:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
@ -1470,30 +1481,30 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.4.1",
|
||||
"version": "1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/instantiator.git",
|
||||
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
|
||||
"reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
|
||||
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
|
||||
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
|
||||
"reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9",
|
||||
"doctrine/coding-standard": "^9 || ^11",
|
||||
"ext-pdo": "*",
|
||||
"ext-phar": "*",
|
||||
"phpbench/phpbench": "^0.16 || ^1",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpstan/phpstan-phpunit": "^1",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
||||
"vimeo/psalm": "^4.22"
|
||||
"vimeo/psalm": "^4.30 || ^5.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -1520,7 +1531,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/instantiator/issues",
|
||||
"source": "https://github.com/doctrine/instantiator/tree/1.4.1"
|
||||
"source": "https://github.com/doctrine/instantiator/tree/1.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1536,7 +1547,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-03T08:28:38+00:00"
|
||||
"time": "2022-12-30T00:15:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
@ -1766,16 +1777,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2"
|
||||
"reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
||||
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/44a338ff0d5572c13fd77dfd91addb96e48c29f8",
|
||||
"reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1805,7 +1816,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.9.4"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.9.12"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1821,20 +1832,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-12-17T13:33:52+00:00"
|
||||
"time": "2023-01-17T10:44:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||
"reference": "cd9c6938f8bbfcb6da3ed5a3c7ea60873825d088"
|
||||
"reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/cd9c6938f8bbfcb6da3ed5a3c7ea60873825d088",
|
||||
"reference": "cd9c6938f8bbfcb6da3ed5a3c7ea60873825d088",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/54a24bd23e9e80ee918cdc24f909d376c2e273f7",
|
||||
"reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1871,27 +1882,27 @@
|
||||
"description": "PHPUnit extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.2"
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.3"
|
||||
},
|
||||
"time": "2022-12-13T15:08:22+00:00"
|
||||
"time": "2022-12-21T15:25:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "1.4.4",
|
||||
"version": "1.4.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6"
|
||||
"reference": "361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6",
|
||||
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d",
|
||||
"reference": "361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"phpstan/phpstan": "^1.8.6"
|
||||
"phpstan/phpstan": "^1.9.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
@ -1919,22 +1930,22 @@
|
||||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.5"
|
||||
},
|
||||
"time": "2022-09-21T11:38:17+00:00"
|
||||
"time": "2023-01-11T14:16:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.22",
|
||||
"version": "9.2.23",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "e4bf60d2220b4baaa0572986b5d69870226b06df"
|
||||
"reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e4bf60d2220b4baaa0572986b5d69870226b06df",
|
||||
"reference": "e4bf60d2220b4baaa0572986b5d69870226b06df",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c",
|
||||
"reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1990,7 +2001,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.22"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1998,7 +2009,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-12-18T16:40:55+00:00"
|
||||
"time": "2022-12-28T12:41:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -2243,20 +2254,20 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.27",
|
||||
"version": "9.5.28",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "a2bc7ffdca99f92d959b3f2270529334030bba38"
|
||||
"reference": "954ca3113a03bf780d22f07bf055d883ee04b65e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2bc7ffdca99f92d959b3f2270529334030bba38",
|
||||
"reference": "a2bc7ffdca99f92d959b3f2270529334030bba38",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e",
|
||||
"reference": "954ca3113a03bf780d22f07bf055d883ee04b65e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.3.1",
|
||||
"doctrine/instantiator": "^1.3.1 || ^2",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
@ -2325,7 +2336,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.27"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2341,7 +2352,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-12-09T07:31:23+00:00"
|
||||
"time": "2023-01-14T12:32:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
@ -69,6 +69,10 @@ use const JSON_UNESCAPED_SLASHES;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
class MemoryManager{
|
||||
private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND;
|
||||
private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2;
|
||||
private const DEEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
private int $memoryLimit;
|
||||
private int $globalMemoryLimit;
|
||||
private int $checkRate;
|
||||
@ -113,20 +117,12 @@ class MemoryManager{
|
||||
if($m <= 0){
|
||||
$defaultMemory = 0;
|
||||
}else{
|
||||
switch(mb_strtoupper($matches[2])){
|
||||
case "K":
|
||||
$defaultMemory = intdiv($m, 1024);
|
||||
break;
|
||||
case "M":
|
||||
$defaultMemory = $m;
|
||||
break;
|
||||
case "G":
|
||||
$defaultMemory = $m * 1024;
|
||||
break;
|
||||
default:
|
||||
$defaultMemory = $m;
|
||||
break;
|
||||
}
|
||||
$defaultMemory = match(mb_strtoupper($matches[2])){
|
||||
"K" => intdiv($m, 1024),
|
||||
"M" => $m,
|
||||
"G" => $m * 1024,
|
||||
default => $m,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,11 +135,11 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
$this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024;
|
||||
$this->checkRate = $config->getPropertyInt("memory.check-rate", 20);
|
||||
$this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE);
|
||||
$this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true);
|
||||
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", 30);
|
||||
$this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
|
||||
|
||||
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", 36000);
|
||||
$this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEEFAULT_TICKS_PER_GC);
|
||||
$this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true);
|
||||
$this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true);
|
||||
|
||||
|
195
src/Server.php
195
src/Server.php
@ -49,10 +49,7 @@ use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\lang\Language;
|
||||
use pocketmine\lang\LanguageNotFoundException;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
||||
use pocketmine\network\mcpe\compression\CompressBatchTask;
|
||||
use pocketmine\network\mcpe\compression\Compressor;
|
||||
@ -72,9 +69,13 @@ use pocketmine\network\query\QueryInfo;
|
||||
use pocketmine\network\upnp\UPnPNetworkInterface;
|
||||
use pocketmine\permission\BanList;
|
||||
use pocketmine\permission\DefaultPermissions;
|
||||
use pocketmine\player\DatFilePlayerDataProvider;
|
||||
use pocketmine\player\GameMode;
|
||||
use pocketmine\player\OfflinePlayer;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\player\PlayerDataLoadException;
|
||||
use pocketmine\player\PlayerDataProvider;
|
||||
use pocketmine\player\PlayerDataSaveException;
|
||||
use pocketmine\player\PlayerInfo;
|
||||
use pocketmine\plugin\PharPluginLoader;
|
||||
use pocketmine\plugin\Plugin;
|
||||
@ -105,17 +106,18 @@ use pocketmine\utils\SignalHandler;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\io\WorldProviderManager;
|
||||
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
||||
use pocketmine\world\generator\Generator;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\World;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use pocketmine\world\WorldManager;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_fill;
|
||||
use function array_sum;
|
||||
use function base64_encode;
|
||||
use function cli_set_process_title;
|
||||
@ -124,7 +126,6 @@ use function count;
|
||||
use function date;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function fopen;
|
||||
@ -161,12 +162,9 @@ use function time;
|
||||
use function touch;
|
||||
use function trim;
|
||||
use function yaml_parse;
|
||||
use function zlib_decode;
|
||||
use function zlib_encode;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_EOL;
|
||||
use const PHP_INT_MAX;
|
||||
use const ZLIB_ENCODING_GZIP;
|
||||
|
||||
/**
|
||||
* The class that manages everything
|
||||
@ -184,9 +182,30 @@ class Server{
|
||||
public const DEFAULT_PORT_IPV6 = 19133;
|
||||
public const DEFAULT_MAX_VIEW_DISTANCE = 16;
|
||||
|
||||
/**
|
||||
* Worlds, network, commands and most other things are polled this many times per second on average.
|
||||
* Between ticks, the server will sleep to ensure that the average tick rate is maintained.
|
||||
* It may wake up between ticks if a Snooze notification source is triggered (e.g. to process network packets).
|
||||
*/
|
||||
public const TARGET_TICKS_PER_SECOND = 20;
|
||||
/**
|
||||
* The average time between ticks, in seconds.
|
||||
*/
|
||||
public const TARGET_SECONDS_PER_TICK = 1 / self::TARGET_TICKS_PER_SECOND;
|
||||
public const TARGET_NANOSECONDS_PER_TICK = 1_000_000_000 / self::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
/**
|
||||
* The TPS threshold below which the server will generate log warnings.
|
||||
*/
|
||||
private const TPS_OVERLOAD_WARNING_THRESHOLD = self::TARGET_TICKS_PER_SECOND * 0.6;
|
||||
|
||||
private const TICKS_PER_WORLD_CACHE_CLEAR = 5 * self::TARGET_TICKS_PER_SECOND;
|
||||
private const TICKS_PER_TPS_OVERLOAD_WARNING = 5 * self::TARGET_TICKS_PER_SECOND;
|
||||
private const TICKS_PER_STATS_REPORT = 300 * self::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
private static ?Server $instance = null;
|
||||
|
||||
private SleeperHandler $tickSleeper;
|
||||
private TimeTrackingSleeperHandler $tickSleeper;
|
||||
|
||||
private BanList $banByName;
|
||||
|
||||
@ -202,7 +221,7 @@ class Server{
|
||||
|
||||
private PluginManager $pluginManager;
|
||||
|
||||
private float $profilingTickRate = 20;
|
||||
private float $profilingTickRate = self::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
private UpdateChecker $updater;
|
||||
|
||||
@ -212,10 +231,10 @@ class Server{
|
||||
private int $tickCounter = 0;
|
||||
private float $nextTick = 0;
|
||||
/** @var float[] */
|
||||
private array $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
|
||||
private array $tickAverage;
|
||||
/** @var float[] */
|
||||
private array $useAverage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
private float $currentTPS = 20;
|
||||
private array $useAverage;
|
||||
private float $currentTPS = self::TARGET_TICKS_PER_SECOND;
|
||||
private float $currentUse = 0;
|
||||
private float $startTime;
|
||||
|
||||
@ -251,6 +270,8 @@ class Server{
|
||||
private string $dataPath;
|
||||
private string $pluginPath;
|
||||
|
||||
private PlayerDataProvider $playerDataProvider;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array<string, string>
|
||||
@ -481,49 +502,22 @@ class Server{
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getPlayerDataPath(string $username) : string{
|
||||
return Path::join($this->getDataPath(), 'players', strtolower($username) . '.dat');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the server has stored any saved data for this player.
|
||||
*/
|
||||
public function hasOfflinePlayerData(string $name) : bool{
|
||||
return file_exists($this->getPlayerDataPath($name));
|
||||
}
|
||||
|
||||
private function handleCorruptedPlayerData(string $name) : void{
|
||||
$path = $this->getPlayerDataPath($name);
|
||||
rename($path, $path . '.bak');
|
||||
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
||||
return $this->playerDataProvider->hasData($name);
|
||||
}
|
||||
|
||||
public function getOfflinePlayerData(string $name) : ?CompoundTag{
|
||||
return Timings::$syncPlayerDataLoad->time(function() use ($name) : ?CompoundTag{
|
||||
$name = strtolower($name);
|
||||
$path = $this->getPlayerDataPath($name);
|
||||
|
||||
if(file_exists($path)){
|
||||
$contents = @file_get_contents($path);
|
||||
if($contents === false){
|
||||
throw new \RuntimeException("Failed to read player data file \"$path\" (permission denied?)");
|
||||
}
|
||||
$decompressed = @zlib_decode($contents);
|
||||
if($decompressed === false){
|
||||
$this->logger->debug("Failed to decompress raw player data for \"$name\"");
|
||||
$this->handleCorruptedPlayerData($name);
|
||||
return null;
|
||||
}
|
||||
|
||||
try{
|
||||
return (new BigEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag();
|
||||
}catch(NbtDataException $e){ //corrupt data
|
||||
$this->logger->debug("Failed to decode NBT data for \"$name\": " . $e->getMessage());
|
||||
$this->handleCorruptedPlayerData($name);
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
return $this->playerDataProvider->loadData($name);
|
||||
}catch(PlayerDataLoadException $e){
|
||||
$this->logger->debug("Failed to load player data for $name: " . $e->getMessage());
|
||||
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@ -537,11 +531,9 @@ class Server{
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
|
||||
try{
|
||||
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
|
||||
}catch(\RuntimeException $e){
|
||||
$this->playerDataProvider->saveData($name, $ev->getSaveData());
|
||||
}catch(PlayerDataSaveException $e){
|
||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
@ -559,49 +551,44 @@ class Server{
|
||||
|
||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
|
||||
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
||||
$spawn = $playerPos->asVector3();
|
||||
}else{
|
||||
$world = $this->worldManager->getDefaultWorld();
|
||||
if($world === null){
|
||||
throw new AssumptionFailedError("Default world should always be loaded");
|
||||
}
|
||||
$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{
|
||||
if(!$session->isConnected()){
|
||||
$playerPromiseResolver->reject();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stick with the original spawn at the time of generation request, even if it changed since then.
|
||||
* This is because we know for sure that that chunk will be generated, but the one at the new location
|
||||
* might not be, and it would be much more complex to go back and redo the whole thing.
|
||||
*
|
||||
* TODO: this relies on the assumption that getSafeSpawn() will only alter the Y coordinate of the
|
||||
* provided position. If this assumption is broken, we'll start seeing crashes in here.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see Player::__construct()
|
||||
* @var Player $player
|
||||
*/
|
||||
$player = new $class($this, $session, $playerInfo, $authenticated, $playerPos ?? Location::fromObject($world->getSafeSpawn($spawn), $world), $offlinePlayerData);
|
||||
if(!$player->hasPlayedBefore()){
|
||||
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
|
||||
}
|
||||
$playerPromiseResolver->resolve($player);
|
||||
},
|
||||
static function() use ($playerPromiseResolver, $session) : void{
|
||||
if($session->isConnected()){
|
||||
$session->disconnect("Spawn terrain generation failed");
|
||||
}
|
||||
$playerPromiseResolver->reject();
|
||||
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
|
||||
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
|
||||
if(!$player->hasPlayedBefore()){
|
||||
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
|
||||
}
|
||||
);
|
||||
$playerPromiseResolver->resolve($player);
|
||||
};
|
||||
|
||||
if($playerPos === null){ //new player or no valid position due to world not being loaded
|
||||
$world->requestSafeSpawn()->onCompletion(
|
||||
function(Position $spawn) use ($createPlayer, $playerPromiseResolver, $session, $world) : void{
|
||||
if(!$session->isConnected()){
|
||||
$playerPromiseResolver->reject();
|
||||
return;
|
||||
}
|
||||
$createPlayer(Location::fromObject($spawn, $world));
|
||||
},
|
||||
function() use ($playerPromiseResolver, $session) : void{
|
||||
if($session->isConnected()){
|
||||
$session->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_error_respawn());
|
||||
}
|
||||
$playerPromiseResolver->reject();
|
||||
}
|
||||
);
|
||||
}else{ //returning player with a valid position - safe spawn not required
|
||||
$createPlayer($playerPos);
|
||||
}
|
||||
|
||||
return $playerPromiseResolver->getPromise();
|
||||
}
|
||||
|
||||
@ -777,8 +764,11 @@ class Server{
|
||||
}
|
||||
self::$instance = $this;
|
||||
$this->startTime = microtime(true);
|
||||
$this->tickAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, self::TARGET_TICKS_PER_SECOND);
|
||||
$this->useAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, 0);
|
||||
|
||||
$this->tickSleeper = new SleeperHandler();
|
||||
Timings::init();
|
||||
$this->tickSleeper = new TimeTrackingSleeperHandler(Timings::$serverInterrupts);
|
||||
|
||||
$this->signalHandler = new SignalHandler(function() : void{
|
||||
$this->logger->info("Received signal interrupt, stopping the server");
|
||||
@ -803,7 +793,7 @@ class Server{
|
||||
$this->logger->info("Loading server configuration");
|
||||
$pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml");
|
||||
if(!file_exists($pocketmineYmlPath)){
|
||||
$content = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")), "Missing required resource file");
|
||||
$content = Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml"));
|
||||
if(VersionInfo::IS_DEVELOPMENT_BUILD){
|
||||
$content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content);
|
||||
}
|
||||
@ -958,9 +948,8 @@ class Server{
|
||||
)));
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||
|
||||
Timings::init();
|
||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false));
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", 20);
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND);
|
||||
|
||||
DefaultPermissions::registerCorePermissions();
|
||||
|
||||
@ -976,7 +965,7 @@ class Server{
|
||||
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
|
||||
}
|
||||
try{
|
||||
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(file_get_contents($graylistFile)));
|
||||
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile)));
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
|
||||
$this->forceShutdownExit();
|
||||
@ -998,12 +987,14 @@ class Server{
|
||||
|
||||
$this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager);
|
||||
$this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave()));
|
||||
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", 6000));
|
||||
$this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval()));
|
||||
|
||||
$this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io"));
|
||||
|
||||
$this->queryInfo = new QueryInfo($this);
|
||||
|
||||
$this->playerDataProvider = new DatFilePlayerDataProvider(Path::join($this->dataPath, "players"));
|
||||
|
||||
register_shutdown_function([$this, "crashDump"]);
|
||||
|
||||
$loadErrorCount = 0;
|
||||
@ -1036,7 +1027,7 @@ class Server{
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
|
||||
$this->sendUsageTicker = 6000;
|
||||
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
|
||||
$this->sendUsage(SendUsageTask::TYPE_OPEN);
|
||||
}
|
||||
|
||||
@ -1819,11 +1810,11 @@ class Server{
|
||||
$this->network->tick();
|
||||
Timings::$connection->stopTiming();
|
||||
|
||||
if(($this->tickCounter % 20) === 0){
|
||||
if(($this->tickCounter % self::TARGET_TICKS_PER_SECOND) === 0){
|
||||
if($this->doTitleTick){
|
||||
$this->titleTick();
|
||||
}
|
||||
$this->currentTPS = 20;
|
||||
$this->currentTPS = self::TARGET_TICKS_PER_SECOND;
|
||||
$this->currentUse = 0;
|
||||
|
||||
$queryRegenerateEvent = new QueryRegenerateEvent(new QueryInfo($this));
|
||||
@ -1835,18 +1826,18 @@ class Server{
|
||||
}
|
||||
|
||||
if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){
|
||||
$this->sendUsageTicker = 6000;
|
||||
$this->sendUsageTicker = self::TICKS_PER_STATS_REPORT;
|
||||
$this->sendUsage(SendUsageTask::TYPE_STATUS);
|
||||
}
|
||||
|
||||
if(($this->tickCounter % 100) === 0){
|
||||
if(($this->tickCounter % self::TICKS_PER_WORLD_CACHE_CLEAR) === 0){
|
||||
foreach($this->worldManager->getWorlds() as $world){
|
||||
$world->clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
if($this->getTicksPerSecondAverage() < 12){
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
|
||||
}
|
||||
if(($this->tickCounter % self::TICKS_PER_TPS_OVERLOAD_WARNING) === 0 && $this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
|
||||
}
|
||||
|
||||
$this->getMemoryManager()->check();
|
||||
@ -1863,19 +1854,21 @@ class Server{
|
||||
Timings::$serverTick->stopTiming();
|
||||
|
||||
$now = microtime(true);
|
||||
$this->currentTPS = min(20, 1 / max(0.001, $now - $tickTime));
|
||||
$this->currentUse = min(1, ($now - $tickTime) / 0.05);
|
||||
$totalTickTimeSeconds = $now - $tickTime + ($this->tickSleeper->getNotificationProcessingTime() / 1_000_000_000);
|
||||
$this->currentTPS = min(self::TARGET_TICKS_PER_SECOND, 1 / max(0.001, $totalTickTimeSeconds));
|
||||
$this->currentUse = min(1, $totalTickTimeSeconds / self::TARGET_SECONDS_PER_TICK);
|
||||
|
||||
TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate);
|
||||
|
||||
$idx = $this->tickCounter % 20;
|
||||
$idx = $this->tickCounter % self::TARGET_TICKS_PER_SECOND;
|
||||
$this->tickAverage[$idx] = $this->currentTPS;
|
||||
$this->useAverage[$idx] = $this->currentUse;
|
||||
$this->tickSleeper->resetNotificationProcessingTime();
|
||||
|
||||
if(($this->nextTick - $tickTime) < -1){
|
||||
$this->nextTick = $tickTime;
|
||||
}else{
|
||||
$this->nextTick += 0.05;
|
||||
$this->nextTick += self::TARGET_SECONDS_PER_TICK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
src/TimeTrackingSleeperHandler.php
Normal file
64
src/TimeTrackingSleeperHandler.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use function hrtime;
|
||||
|
||||
/**
|
||||
* Custom Snooze sleeper handler which captures notification processing time.
|
||||
* @internal
|
||||
*/
|
||||
final class TimeTrackingSleeperHandler extends SleeperHandler{
|
||||
|
||||
private int $notificationProcessingTimeNs = 0;
|
||||
|
||||
public function __construct(
|
||||
private TimingsHandler $timings
|
||||
){
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time in nanoseconds spent processing notifications since the last reset.
|
||||
*/
|
||||
public function getNotificationProcessingTime() : int{ return $this->notificationProcessingTimeNs; }
|
||||
|
||||
/**
|
||||
* Resets the notification processing time tracker to zero.
|
||||
*/
|
||||
public function resetNotificationProcessingTime() : void{ $this->notificationProcessingTimeNs = 0; }
|
||||
|
||||
public function processNotifications() : void{
|
||||
$startTime = hrtime(true);
|
||||
$this->timings->startTiming();
|
||||
try{
|
||||
parent::processNotifications();
|
||||
}finally{
|
||||
$this->notificationProcessingTimeNs += hrtime(true) - $startTime;
|
||||
$this->timings->stopTiming();
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.0.0-ALPHA6";
|
||||
public const BASE_VERSION = "5.0.0-ALPHA7";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "alpha";
|
||||
|
||||
|
@ -114,7 +114,14 @@ abstract class BaseBanner extends Transparent{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->isSolid();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
|
||||
return false;
|
||||
}
|
||||
if($item instanceof ItemBanner){
|
||||
$this->color = $item->getColor();
|
||||
$this->setPatterns($item->getPatterns());
|
||||
@ -126,7 +133,7 @@ abstract class BaseBanner extends Transparent{
|
||||
abstract protected function getSupportingFace() : int;
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){
|
||||
if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
@ -88,14 +88,13 @@ final class Bell extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
//TODO: this isn't the actual logic, but it's the closest approximation we can support for now
|
||||
return $block->isSolid();
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return !$block->getSupportType($face)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face === Facing::UP){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()))){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()), Facing::UP)){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
@ -103,18 +102,18 @@ final class Bell extends Transparent{
|
||||
}
|
||||
$this->setAttachmentType(BellAttachmentType::FLOOR());
|
||||
}elseif($face === Facing::DOWN){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()))){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()), Facing::DOWN)){
|
||||
return false;
|
||||
}
|
||||
$this->setAttachmentType(BellAttachmentType::CEILING());
|
||||
}else{
|
||||
$this->setFacing($face);
|
||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))))){
|
||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))), $face)){
|
||||
$this->setAttachmentType(BellAttachmentType::ONE_WALL());
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)))){
|
||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)), Facing::opposite($face))){
|
||||
$this->setAttachmentType(BellAttachmentType::TWO_WALLS());
|
||||
}
|
||||
}
|
||||
@ -123,10 +122,10 @@ final class Bell extends Transparent{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(
|
||||
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP))) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN))) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))))
|
||||
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP), Facing::DOWN)) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN), Facing::UP)) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing), Facing::opposite($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)))
|
||||
){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
@ -135,21 +134,20 @@ final class Bell extends Transparent{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
$faceHit = Facing::opposite($player->getHorizontalFacing());
|
||||
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
|
||||
$this->ring($faceHit);
|
||||
}
|
||||
if($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)){
|
||||
$this->ring($faceHit);
|
||||
}
|
||||
if(
|
||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
|
||||
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
|
||||
$this->attachmentType->equals(BellAttachmentType::CEILING()) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)) ||
|
||||
(
|
||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
|
||||
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
|
||||
)
|
||||
){
|
||||
$this->ring($faceHit);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ring(int $faceHit) : void{
|
||||
|
@ -305,6 +305,14 @@ class Block{
|
||||
* 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).
|
||||
*
|
||||
* @param BlockTransaction $tx Blocks to be set should be added to this transaction (do not modify thr world directly)
|
||||
* @param Item $item Item used to place the block
|
||||
* @param Block $blockReplace Block expected to be replaced
|
||||
* @param Block $blockClicked Block that was clicked using the item
|
||||
* @param int $face Face of the clicked block which was clicked
|
||||
* @param Vector3 $clickVector Exact position inside the clicked block where the click occurred, relative to the block's position
|
||||
* @param Player|null $player Player who placed the block, or null if it was not a player
|
||||
*
|
||||
* @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{
|
||||
|
@ -706,8 +706,11 @@ final class BlockTypeIds{
|
||||
public const FROGLIGHT = 10679;
|
||||
public const TWISTING_VINES = 10680;
|
||||
public const WEEPING_VINES = 10681;
|
||||
public const CHAIN = 10682;
|
||||
public const SCULK = 10683;
|
||||
public const GLOWING_ITEM_FRAME = 10684;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10682;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10685;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
@ -55,7 +55,7 @@ abstract class Button extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockClicked, $face)){
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
48
src/block/Chain.php
Normal file
48
src/block/Chain.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\PillarRotationTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
final class Chain extends Transparent{
|
||||
use PillarRotationTrait;
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$bb = AxisAlignedBB::one();
|
||||
foreach([Axis::Y, Axis::Z, Axis::X] as $axis){
|
||||
if($axis !== $this->axis){
|
||||
$bb->squash($axis, 13 / 32);
|
||||
}
|
||||
}
|
||||
return [$bb];
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\ItemFrame as TileItemFrame;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataReader;
|
||||
use pocketmine\data\runtime\RuntimeDataWriter;
|
||||
use pocketmine\item\Item;
|
||||
@ -44,20 +45,12 @@ class ItemFrame extends Flowable{
|
||||
|
||||
public const ROTATIONS = 8;
|
||||
|
||||
protected bool $glowing = false;
|
||||
|
||||
protected bool $hasMap = false; //makes frame appear large if set
|
||||
|
||||
protected ?Item $framedItem = null;
|
||||
protected int $itemRotation = 0;
|
||||
protected float $itemDropChance = 1.0;
|
||||
|
||||
public function getRequiredTypeDataBits() : int{ return 1; }
|
||||
|
||||
protected function describeType(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
$w->bool($this->glowing);
|
||||
}
|
||||
|
||||
public function getRequiredStateDataBits() : int{ return 4; }
|
||||
|
||||
protected function describeState(RuntimeDataReader|RuntimeDataWriter $w) : void{
|
||||
@ -141,14 +134,6 @@ class ItemFrame extends Flowable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isGlowing() : bool{ return $this->glowing; }
|
||||
|
||||
/** @return $this */
|
||||
public function setGlowing(bool $glowing) : self{
|
||||
$this->glowing = $glowing;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($this->framedItem !== null){
|
||||
$this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS;
|
||||
@ -181,14 +166,18 @@ class ItemFrame extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return !$block->getSupportType($face)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->getSide(Facing::opposite($this->facing))->isSolid()){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockClicked->isSolid()){
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\Jukebox as JukeboxTile;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\Record;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\RecordSound;
|
||||
@ -44,7 +45,7 @@ class Jukebox extends Opaque{
|
||||
if($this->record !== null){
|
||||
$this->ejectRecord();
|
||||
}elseif($item instanceof Record){
|
||||
$player->sendJukeboxPopup("record.nowPlaying", [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]);
|
||||
$player->sendJukeboxPopup(KnownTranslationFactory::record_nowPlaying($item->getRecordType()->getTranslatableName()));
|
||||
$this->insertRecord($item->pop());
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockClicked, $face) && Facing::axis($face) !== Axis::Y){
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face) && Facing::axis($face) !== Axis::Y){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class Lever extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockClicked, $face)){
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ abstract class PressurePlate extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockClicked)){
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
@ -55,5 +55,11 @@ abstract class PressurePlate extends Transparent{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
41
src/block/Sculk.php
Normal file
41
src/block/Sculk.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
final class Sculk extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getXpDropAmount() : int{
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ class Thin extends Transparent{
|
||||
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
$side = $this->getSide($facing);
|
||||
if($side instanceof Thin || $side->isFullCube()){
|
||||
if($side instanceof Thin || $side instanceof Wall || $side->isFullCube()){
|
||||
$this->connections[$facing] = true;
|
||||
}else{
|
||||
unset($this->connections[$facing]);
|
||||
|
@ -59,7 +59,6 @@ class Torch extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$below = $this->getSide(Facing::DOWN);
|
||||
$face = Facing::opposite($this->facing);
|
||||
|
||||
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
|
||||
@ -68,10 +67,7 @@ class Torch extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){
|
||||
$this->facing = Facing::UP;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
|
||||
if($face !== Facing::DOWN && $this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}else{
|
||||
|
@ -43,6 +43,7 @@ use pocketmine\block\tile\DaylightSensor as TileDaylightSensor;
|
||||
use pocketmine\block\tile\EnchantTable as TileEnchantingTable;
|
||||
use pocketmine\block\tile\EnderChest as TileEnderChest;
|
||||
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
|
||||
use pocketmine\block\tile\GlowingItemFrame as TileGlowingItemFrame;
|
||||
use pocketmine\block\tile\Hopper as TileHopper;
|
||||
use pocketmine\block\tile\ItemFrame as TileItemFrame;
|
||||
use pocketmine\block\tile\Jukebox as TileJukebox;
|
||||
@ -153,6 +154,7 @@ use function mb_strtolower;
|
||||
* @method static CartographyTable CARTOGRAPHY_TABLE()
|
||||
* @method static CarvedPumpkin CARVED_PUMPKIN()
|
||||
* @method static Cauldron CAULDRON()
|
||||
* @method static Chain CHAIN()
|
||||
* @method static ChemicalHeat CHEMICAL_HEAT()
|
||||
* @method static Chest CHEST()
|
||||
* @method static Opaque CHISELED_DEEPSLATE()
|
||||
@ -408,6 +410,7 @@ use function mb_strtolower;
|
||||
* @method static Glass GLASS()
|
||||
* @method static GlassPane GLASS_PANE()
|
||||
* @method static GlazedTerracotta GLAZED_TERRACOTTA()
|
||||
* @method static ItemFrame GLOWING_ITEM_FRAME()
|
||||
* @method static GlowingObsidian GLOWING_OBSIDIAN()
|
||||
* @method static Glowstone GLOWSTONE()
|
||||
* @method static Opaque GOLD()
|
||||
@ -628,6 +631,7 @@ use function mb_strtolower;
|
||||
* @method static Slab SANDSTONE_SLAB()
|
||||
* @method static Stair SANDSTONE_STAIRS()
|
||||
* @method static Wall SANDSTONE_WALL()
|
||||
* @method static Sculk SCULK()
|
||||
* @method static SeaLantern SEA_LANTERN()
|
||||
* @method static SeaPickle SEA_PICKLE()
|
||||
* @method static Opaque SHROOMLIGHT()
|
||||
@ -884,7 +888,11 @@ final class VanillaBlocks{
|
||||
$ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD(), 25.0));
|
||||
self::register("iron_door", new Door(new BID(Ids::IRON_DOOR), "Iron Door", $ironDoorBreakInfo));
|
||||
self::register("iron_trapdoor", new Trapdoor(new BID(Ids::IRON_TRAPDOOR), "Iron Trapdoor", $ironDoorBreakInfo));
|
||||
self::register("item_frame", new ItemFrame(new BID(Ids::ITEM_FRAME, TileItemFrame::class), "Item Frame", new Info(new BreakInfo(0.25))));
|
||||
|
||||
$itemFrameInfo = new Info(new BreakInfo(0.25));
|
||||
self::register("item_frame", new ItemFrame(new BID(Ids::ITEM_FRAME, TileItemFrame::class), "Item Frame", $itemFrameInfo));
|
||||
self::register("glowing_item_frame", new ItemFrame(new BID(Ids::GLOWING_ITEM_FRAME, TileGlowingItemFrame::class), "Glow Item Frame", $itemFrameInfo));
|
||||
|
||||
self::register("jukebox", new Jukebox(new BID(Ids::JUKEBOX, TileJukebox::class), "Jukebox", new Info(BreakInfo::axe(0.8)))); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not
|
||||
self::register("ladder", new Ladder(new BID(Ids::LADDER), "Ladder", new Info(BreakInfo::axe(0.4))));
|
||||
|
||||
@ -1185,6 +1193,7 @@ final class VanillaBlocks{
|
||||
self::register("mangrove_roots", new MangroveRoots(new BID(Ids::MANGROVE_ROOTS), "Mangrove Roots", new Info(BreakInfo::axe(0.7))));
|
||||
self::register("muddy_mangrove_roots", new SimplePillar(new BID(Ids::MUDDY_MANGROVE_ROOTS), "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD])));
|
||||
self::register("froglight", new Froglight(new BID(Ids::FROGLIGHT), "Froglight", new Info(new BreakInfo(0.3))));
|
||||
self::register("sculk", new Sculk(new BID(Ids::SCULK), "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE))));
|
||||
|
||||
self::registerBlocksR13();
|
||||
self::registerBlocksR14();
|
||||
@ -1473,6 +1482,8 @@ final class VanillaBlocks{
|
||||
|
||||
self::register("twisting_vines", new NetherVines(new BID(Ids::TWISTING_VINES), "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP));
|
||||
self::register("weeping_vines", new NetherVines(new BID(Ids::WEEPING_VINES), "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN));
|
||||
|
||||
self::register("chain", new Chain(new BID(Ids::CHAIN), "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD()))));
|
||||
}
|
||||
|
||||
private static function registerBlocksR17() : void{
|
||||
|
@ -105,7 +105,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->isFullCube() || Facing::axis($face) === Axis::Y){
|
||||
if(!$blockReplace->getSide(Facing::opposite($face))->isFullCube() || Facing::axis($face) === Axis::Y){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ class Wall extends Transparent{
|
||||
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
$block = $this->getSide($facing);
|
||||
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){
|
||||
if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || ($block->isSolid() && !$block->isTransparent())){
|
||||
if(!isset($this->connections[$facing])){
|
||||
$this->connections[$facing] = WallConnectionType::SHORT();
|
||||
$changed++;
|
||||
|
@ -45,7 +45,7 @@ final class WallCoralFan extends BaseCoral{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$axis = Facing::axis($face);
|
||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockClicked, $face)){
|
||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
return false;
|
||||
}
|
||||
$this->facing = $face;
|
||||
|
@ -39,19 +39,23 @@ class WaterLily extends Flowable{
|
||||
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockClicked instanceof Water){
|
||||
$up = $blockClicked->getSide(Facing::UP);
|
||||
if($up->canBeReplaced()){
|
||||
return parent::place($tx, $item, $up, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
}
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
return false;
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block instanceof Water;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!($this->getSide(Facing::DOWN) instanceof Water)){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
32
src/block/tile/GlowingItemFrame.php
Normal file
32
src/block/tile/GlowingItemFrame.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\tile;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see \pocketmine\block\ItemFrame
|
||||
*/
|
||||
final class GlowingItemFrame extends ItemFrame{
|
||||
|
||||
}
|
@ -76,9 +76,9 @@ final class TileFactory{
|
||||
$this->register(Smoker::class, ["Smoker", "minecraft:smoker"]);
|
||||
$this->register(SporeBlossom::class, ["SporeBlossom", "minecraft:spore_blossom"]);
|
||||
$this->register(Skull::class, ["Skull", "minecraft:skull"]);
|
||||
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
|
||||
|
||||
//TODO: Campfire
|
||||
//TODO: Cauldron
|
||||
//TODO: ChalkboardBlock
|
||||
//TODO: ChemistryTable
|
||||
//TODO: CommandBlock
|
||||
|
@ -31,7 +31,7 @@ use function array_slice;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function is_int;
|
||||
use function strpos;
|
||||
use function str_contains;
|
||||
|
||||
class SignText{
|
||||
public const LINE_COUNT = 4;
|
||||
@ -57,7 +57,7 @@ class SignText{
|
||||
foreach($lines as $k => $line){
|
||||
$this->checkLineIndex($k);
|
||||
Utils::checkUTF8($line);
|
||||
if(strpos($line, "\n") !== false){
|
||||
if(str_contains($line, "\n")){
|
||||
throw new \InvalidArgumentException("Line must not contain newlines");
|
||||
}
|
||||
//TODO: add length checks
|
||||
|
@ -73,8 +73,8 @@ use pocketmine\utils\TextFormat;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function str_contains;
|
||||
use function strcasecmp;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
@ -89,46 +89,46 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
private function setDefaultCommands() : void{
|
||||
$this->registerAll("pocketmine", [
|
||||
new BanCommand("ban"),
|
||||
new BanIpCommand("ban-ip"),
|
||||
new BanListCommand("banlist"),
|
||||
new ClearCommand("clear"),
|
||||
new DefaultGamemodeCommand("defaultgamemode"),
|
||||
new DeopCommand("deop"),
|
||||
new DifficultyCommand("difficulty"),
|
||||
new DumpMemoryCommand("dumpmemory"),
|
||||
new EffectCommand("effect"),
|
||||
new EnchantCommand("enchant"),
|
||||
new GamemodeCommand("gamemode"),
|
||||
new GarbageCollectorCommand("gc"),
|
||||
new GiveCommand("give"),
|
||||
new HelpCommand("help"),
|
||||
new KickCommand("kick"),
|
||||
new KillCommand("kill"),
|
||||
new ListCommand("list"),
|
||||
new MeCommand("me"),
|
||||
new OpCommand("op"),
|
||||
new PardonCommand("pardon"),
|
||||
new PardonIpCommand("pardon-ip"),
|
||||
new ParticleCommand("particle"),
|
||||
new PluginsCommand("plugins"),
|
||||
new SaveCommand("save-all"),
|
||||
new SaveOffCommand("save-off"),
|
||||
new SaveOnCommand("save-on"),
|
||||
new SayCommand("say"),
|
||||
new SeedCommand("seed"),
|
||||
new SetWorldSpawnCommand("setworldspawn"),
|
||||
new SpawnpointCommand("spawnpoint"),
|
||||
new StatusCommand("status"),
|
||||
new StopCommand("stop"),
|
||||
new TeleportCommand("tp"),
|
||||
new TellCommand("tell"),
|
||||
new TimeCommand("time"),
|
||||
new TimingsCommand("timings"),
|
||||
new TitleCommand("title"),
|
||||
new TransferServerCommand("transferserver"),
|
||||
new VersionCommand("version"),
|
||||
new WhitelistCommand("whitelist")
|
||||
new BanCommand(),
|
||||
new BanIpCommand(),
|
||||
new BanListCommand(),
|
||||
new ClearCommand(),
|
||||
new DefaultGamemodeCommand(),
|
||||
new DeopCommand(),
|
||||
new DifficultyCommand(),
|
||||
new DumpMemoryCommand(),
|
||||
new EffectCommand(),
|
||||
new EnchantCommand(),
|
||||
new GamemodeCommand(),
|
||||
new GarbageCollectorCommand(),
|
||||
new GiveCommand(),
|
||||
new HelpCommand(),
|
||||
new KickCommand(),
|
||||
new KillCommand(),
|
||||
new ListCommand(),
|
||||
new MeCommand(),
|
||||
new OpCommand(),
|
||||
new PardonCommand(),
|
||||
new PardonIpCommand(),
|
||||
new ParticleCommand(),
|
||||
new PluginsCommand(),
|
||||
new SaveCommand(),
|
||||
new SaveOffCommand(),
|
||||
new SaveOnCommand(),
|
||||
new SayCommand(),
|
||||
new SeedCommand(),
|
||||
new SetWorldSpawnCommand(),
|
||||
new SpawnpointCommand(),
|
||||
new StatusCommand(),
|
||||
new StopCommand(),
|
||||
new TeleportCommand(),
|
||||
new TellCommand(),
|
||||
new TimeCommand(),
|
||||
new TimingsCommand(),
|
||||
new TitleCommand(),
|
||||
new TransferServerCommand(),
|
||||
new VersionCommand(),
|
||||
new WhitelistCommand()
|
||||
]);
|
||||
}
|
||||
|
||||
@ -242,7 +242,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
$values = $this->server->getCommandAliases();
|
||||
|
||||
foreach($values as $alias => $commandStrings){
|
||||
if(strpos($alias, ":") !== false){
|
||||
if(str_contains($alias, ":")){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
|
||||
continue;
|
||||
}
|
||||
|
@ -35,9 +35,9 @@ use function implode;
|
||||
|
||||
class BanCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"ban",
|
||||
KnownTranslationFactory::pocketmine_command_ban_player_description(),
|
||||
KnownTranslationFactory::commands_ban_usage()
|
||||
);
|
||||
@ -55,7 +55,7 @@ class BanCommand extends VanillaCommand{
|
||||
$sender->getServer()->getNameBans()->addBan($name, $reason, null, $sender->getName());
|
||||
|
||||
if(($player = $sender->getServer()->getPlayerExact($name)) instanceof Player){
|
||||
$player->kick($reason !== "" ? "Banned by admin. Reason: " . $reason : "Banned by admin.");
|
||||
$player->kick($reason !== "" ? KnownTranslationFactory::pocketmine_disconnect_ban($reason) : KnownTranslationFactory::pocketmine_disconnect_ban_noReason());
|
||||
}
|
||||
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_ban_success($player !== null ? $player->getName() : $name));
|
||||
|
@ -36,9 +36,9 @@ use function inet_pton;
|
||||
|
||||
class BanIpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"ban-ip",
|
||||
KnownTranslationFactory::pocketmine_command_ban_ip_description(),
|
||||
KnownTranslationFactory::commands_banip_usage()
|
||||
);
|
||||
@ -78,7 +78,7 @@ class BanIpCommand extends VanillaCommand{
|
||||
|
||||
foreach($sender->getServer()->getOnlinePlayers() as $player){
|
||||
if($player->getNetworkSession()->getIp() === $ip){
|
||||
$player->kick("Banned by admin. Reason: " . ($reason !== "" ? $reason : "IP banned."));
|
||||
$player->kick(KnownTranslationFactory::pocketmine_disconnect_ban($reason !== "" ? $reason : KnownTranslationFactory::pocketmine_disconnect_ban_ip()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,9 +37,9 @@ use const SORT_STRING;
|
||||
|
||||
class BanListCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"banlist",
|
||||
KnownTranslationFactory::pocketmine_command_banlist_description(),
|
||||
KnownTranslationFactory::commands_banlist_usage()
|
||||
);
|
||||
|
@ -33,7 +33,6 @@ use pocketmine\item\LegacyStringToItemParserException;
|
||||
use pocketmine\item\StringToItemParser;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function count;
|
||||
use function implode;
|
||||
@ -41,9 +40,9 @@ use function min;
|
||||
|
||||
class ClearCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"clear",
|
||||
KnownTranslationFactory::pocketmine_command_clear_description(),
|
||||
KnownTranslationFactory::pocketmine_command_clear_usage()
|
||||
);
|
||||
@ -55,23 +54,9 @@ class ClearCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
if(isset($args[0])){
|
||||
$target = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
if($target === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return true;
|
||||
}
|
||||
if($target !== $sender && !$this->testPermission($sender, DefaultPermissionNames::COMMAND_CLEAR_OTHER)){
|
||||
return true;
|
||||
}
|
||||
}elseif($sender instanceof Player){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_CLEAR_SELF)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $sender;
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
|
||||
if($target === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$targetItem = null;
|
||||
|
@ -32,9 +32,9 @@ use function count;
|
||||
|
||||
class DefaultGamemodeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"defaultgamemode",
|
||||
KnownTranslationFactory::pocketmine_command_defaultgamemode_description(),
|
||||
KnownTranslationFactory::commands_defaultgamemode_usage()
|
||||
);
|
||||
|
@ -35,9 +35,9 @@ use function count;
|
||||
|
||||
class DeopCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"deop",
|
||||
KnownTranslationFactory::pocketmine_command_deop_description(),
|
||||
KnownTranslationFactory::commands_deop_usage()
|
||||
);
|
||||
|
@ -33,9 +33,9 @@ use function count;
|
||||
|
||||
class DifficultyCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"difficulty",
|
||||
KnownTranslationFactory::pocketmine_command_difficulty_description(),
|
||||
KnownTranslationFactory::commands_difficulty_usage()
|
||||
);
|
||||
|
@ -24,17 +24,18 @@ declare(strict_types=1);
|
||||
namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function date;
|
||||
|
||||
class DumpMemoryCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"Dumps the memory",
|
||||
"/$name [path]"
|
||||
"dumpmemory",
|
||||
KnownTranslationFactory::pocketmine_command_dumpmemory_description(),
|
||||
"/dumpmemory [path]"
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_DUMPMEMORY);
|
||||
}
|
||||
|
@ -32,17 +32,21 @@ use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function strtolower;
|
||||
|
||||
class EffectCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"effect",
|
||||
KnownTranslationFactory::pocketmine_command_effect_description(),
|
||||
KnownTranslationFactory::commands_effect_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_EFFECT);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_EFFECT_SELF,
|
||||
DefaultPermissionNames::COMMAND_EFFECT_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -50,10 +54,8 @@ class EffectCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
|
||||
if($player === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return true;
|
||||
}
|
||||
$effectManager = $player->getEffects();
|
||||
|
@ -29,18 +29,21 @@ use pocketmine\item\enchantment\EnchantmentInstance;
|
||||
use pocketmine\item\enchantment\StringToEnchantmentParser;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
class EnchantCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"enchant",
|
||||
KnownTranslationFactory::pocketmine_command_enchant_description(),
|
||||
KnownTranslationFactory::commands_enchant_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_ENCHANT);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_ENCHANT_SELF,
|
||||
DefaultPermissionNames::COMMAND_ENCHANT_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -48,10 +51,8 @@ class EnchantCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
|
||||
if($player === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -29,19 +29,21 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\GameMode;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
class GamemodeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"gamemode",
|
||||
KnownTranslationFactory::pocketmine_command_gamemode_description(),
|
||||
KnownTranslationFactory::commands_gamemode_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_GAMEMODE);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_GAMEMODE_SELF,
|
||||
DefaultPermissionNames::COMMAND_GAMEMODE_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -55,17 +57,14 @@ class GamemodeCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($args[1])){
|
||||
$target = $sender->getServer()->getPlayerByPrefix($args[1]);
|
||||
if($target === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
$target = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
|
||||
if($target === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}elseif($sender instanceof Player){
|
||||
$target = $sender;
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
if($target->getGamemode()->equals($gameMode)){
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
$target->setGamemode($gameMode);
|
||||
|
@ -34,9 +34,9 @@ use function round;
|
||||
|
||||
class GarbageCollectorCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"gc",
|
||||
KnownTranslationFactory::pocketmine_command_gc_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_GC);
|
||||
|
@ -41,13 +41,16 @@ use function implode;
|
||||
|
||||
class GiveCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"give",
|
||||
KnownTranslationFactory::pocketmine_command_give_description(),
|
||||
KnownTranslationFactory::pocketmine_command_give_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_GIVE);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_GIVE_SELF,
|
||||
DefaultPermissionNames::COMMAND_GIVE_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -55,9 +58,8 @@ class GiveCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
|
||||
if($player === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -44,9 +44,9 @@ use const SORT_NATURAL;
|
||||
|
||||
class HelpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"help",
|
||||
KnownTranslationFactory::pocketmine_command_help_description(),
|
||||
KnownTranslationFactory::commands_help_usage(),
|
||||
["?"]
|
||||
|
@ -37,9 +37,9 @@ use function trim;
|
||||
|
||||
class KickCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"kick",
|
||||
KnownTranslationFactory::pocketmine_command_kick_description(),
|
||||
KnownTranslationFactory::commands_kick_usage()
|
||||
);
|
||||
@ -55,7 +55,7 @@ class KickCommand extends VanillaCommand{
|
||||
$reason = trim(implode(" ", $args));
|
||||
|
||||
if(($player = $sender->getServer()->getPlayerByPrefix($name)) instanceof Player){
|
||||
$player->kick("Kicked by admin." . ($reason !== "" ? " Reason: " . $reason : ""));
|
||||
$player->kick($reason !== "" ? KnownTranslationFactory::pocketmine_disconnect_kick($reason) : KnownTranslationFactory::pocketmine_disconnect_kick_noReason());
|
||||
if($reason !== ""){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kick_success_reason($player->getName(), $reason));
|
||||
}else{
|
||||
|
@ -29,16 +29,14 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
class KillCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"kill",
|
||||
KnownTranslationFactory::pocketmine_command_kill_description(),
|
||||
KnownTranslationFactory::pocketmine_command_kill_usage(),
|
||||
["suicide"]
|
||||
@ -51,32 +49,16 @@ class KillCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
if(count($args) === 1){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_KILL_OTHER)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
|
||||
if($player instanceof Player){
|
||||
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
|
||||
}else{
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
if($sender instanceof Player){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_KILL_SELF)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
if($player === $sender){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_kill_successful($sender->getName()));
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -36,9 +36,9 @@ use const SORT_STRING;
|
||||
|
||||
class ListCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"list",
|
||||
KnownTranslationFactory::pocketmine_command_list_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_LIST);
|
||||
|
@ -34,9 +34,9 @@ use function implode;
|
||||
|
||||
class MeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"me",
|
||||
KnownTranslationFactory::pocketmine_command_me_description(),
|
||||
KnownTranslationFactory::commands_me_usage()
|
||||
);
|
||||
|
@ -35,9 +35,9 @@ use function count;
|
||||
|
||||
class OpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"op",
|
||||
KnownTranslationFactory::pocketmine_command_op_description(),
|
||||
KnownTranslationFactory::commands_op_usage()
|
||||
);
|
||||
|
@ -32,9 +32,9 @@ use function count;
|
||||
|
||||
class PardonCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"pardon",
|
||||
KnownTranslationFactory::pocketmine_command_unban_player_description(),
|
||||
KnownTranslationFactory::commands_unban_usage(),
|
||||
["unban"]
|
||||
|
@ -33,9 +33,9 @@ use function inet_pton;
|
||||
|
||||
class PardonIpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"pardon-ip",
|
||||
KnownTranslationFactory::pocketmine_command_unban_ip_description(),
|
||||
KnownTranslationFactory::commands_unbanip_usage(),
|
||||
["unban-ip"]
|
||||
|
@ -74,9 +74,9 @@ use function strtolower;
|
||||
|
||||
class ParticleCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"particle",
|
||||
KnownTranslationFactory::pocketmine_command_particle_description(),
|
||||
KnownTranslationFactory::pocketmine_command_particle_usage()
|
||||
);
|
||||
|
@ -36,9 +36,9 @@ use const SORT_STRING;
|
||||
|
||||
class PluginsCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"plugins",
|
||||
KnownTranslationFactory::pocketmine_command_plugins_description(),
|
||||
null,
|
||||
["pl"]
|
||||
|
@ -32,9 +32,9 @@ use function round;
|
||||
|
||||
class SaveCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"save-all",
|
||||
KnownTranslationFactory::pocketmine_command_save_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_PERFORM);
|
||||
|
@ -30,9 +30,9 @@ use pocketmine\permission\DefaultPermissionNames;
|
||||
|
||||
class SaveOffCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"save-off",
|
||||
KnownTranslationFactory::pocketmine_command_saveoff_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_DISABLE);
|
||||
|
@ -30,9 +30,9 @@ use pocketmine\permission\DefaultPermissionNames;
|
||||
|
||||
class SaveOnCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"save-on",
|
||||
KnownTranslationFactory::pocketmine_command_saveon_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_ENABLE);
|
||||
|
@ -35,9 +35,9 @@ use function implode;
|
||||
|
||||
class SayCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"say",
|
||||
KnownTranslationFactory::pocketmine_command_say_description(),
|
||||
KnownTranslationFactory::commands_say_usage()
|
||||
);
|
||||
|
@ -30,9 +30,9 @@ use pocketmine\player\Player;
|
||||
|
||||
class SeedCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"seed",
|
||||
KnownTranslationFactory::pocketmine_command_seed_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SEED);
|
||||
|
@ -36,9 +36,9 @@ use function count;
|
||||
|
||||
class SetWorldSpawnCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"setworldspawn",
|
||||
KnownTranslationFactory::pocketmine_command_setworldspawn_description(),
|
||||
KnownTranslationFactory::commands_setworldspawn_usage()
|
||||
);
|
||||
|
@ -29,41 +29,30 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\World;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function round;
|
||||
|
||||
class SpawnpointCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"spawnpoint",
|
||||
KnownTranslationFactory::pocketmine_command_spawnpoint_description(),
|
||||
KnownTranslationFactory::commands_spawnpoint_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SPAWNPOINT);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF,
|
||||
DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
$target = null;
|
||||
|
||||
if(count($args) === 0){
|
||||
if($sender instanceof Player){
|
||||
$target = $sender;
|
||||
}else{
|
||||
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
|
||||
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
$target = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
if($target === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
|
||||
return true;
|
||||
}
|
||||
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF, DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER);
|
||||
if($target === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(count($args) === 4){
|
||||
@ -77,19 +66,13 @@ class SpawnpointCommand extends VanillaCommand{
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($x, 2), (string) round($y, 2), (string) round($z, 2)));
|
||||
|
||||
return true;
|
||||
}elseif(count($args) <= 1){
|
||||
if($sender instanceof Player){
|
||||
$cpos = $sender->getPosition();
|
||||
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
|
||||
$target->setSpawn($pos);
|
||||
}elseif(count($args) <= 1 && $sender instanceof Player){
|
||||
$cpos = $sender->getPosition();
|
||||
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
|
||||
$target->setSpawn($pos);
|
||||
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
|
||||
return true;
|
||||
}else{
|
||||
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
|
||||
|
||||
return true;
|
||||
}
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new InvalidCommandSyntaxException();
|
||||
|
@ -36,9 +36,9 @@ use function round;
|
||||
|
||||
class StatusCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"status",
|
||||
KnownTranslationFactory::pocketmine_command_status_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_STATUS);
|
||||
|
@ -30,9 +30,9 @@ use pocketmine\permission\DefaultPermissionNames;
|
||||
|
||||
class StopCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"stop",
|
||||
KnownTranslationFactory::pocketmine_command_stop_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_STOP);
|
||||
|
@ -35,18 +35,22 @@ use pocketmine\utils\TextFormat;
|
||||
use pocketmine\world\World;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function round;
|
||||
|
||||
class TeleportCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"tp",
|
||||
KnownTranslationFactory::pocketmine_command_tp_description(),
|
||||
KnownTranslationFactory::commands_tp_usage(),
|
||||
["teleport"]
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_TELEPORT);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_TELEPORT_SELF,
|
||||
DefaultPermissionNames::COMMAND_TELEPORT_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
private function findPlayer(CommandSender $sender, string $playerName) : ?Player{
|
||||
@ -63,31 +67,25 @@ class TeleportCommand extends VanillaCommand{
|
||||
case 1: // /tp targetPlayer
|
||||
case 3: // /tp x y z
|
||||
case 5: // /tp x y z yaw pitch - TODO: 5 args could be target x y z yaw :(
|
||||
if(!($sender instanceof Player)){
|
||||
$sender->sendMessage(TextFormat::RED . "Please provide a player!");
|
||||
return true;
|
||||
}
|
||||
|
||||
$subject = $sender;
|
||||
$targetArgs = $args;
|
||||
$subjectName = null; //self
|
||||
break;
|
||||
case 2: // /tp player1 player2
|
||||
case 4: // /tp player1 x y z - TODO: 4 args could be x y z yaw :(
|
||||
case 6: // /tp player1 x y z yaw pitch
|
||||
$subject = $this->findPlayer($sender, $args[0]);
|
||||
if($subject === null){
|
||||
return true;
|
||||
}
|
||||
$targetArgs = $args;
|
||||
array_shift($targetArgs);
|
||||
$subjectName = array_shift($args);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
switch(count($targetArgs)){
|
||||
$subject = $this->fetchPermittedPlayerTarget($sender, $subjectName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
|
||||
if($subject === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(count($args)){
|
||||
case 1:
|
||||
$targetPlayer = $this->findPlayer($sender, $targetArgs[0]);
|
||||
$targetPlayer = $this->findPlayer($sender, $args[0]);
|
||||
if($targetPlayer === null){
|
||||
return true;
|
||||
}
|
||||
@ -99,17 +97,17 @@ class TeleportCommand extends VanillaCommand{
|
||||
case 3:
|
||||
case 5:
|
||||
$base = $subject->getLocation();
|
||||
if(count($targetArgs) === 5){
|
||||
$yaw = (float) $targetArgs[3];
|
||||
$pitch = (float) $targetArgs[4];
|
||||
if(count($args) === 5){
|
||||
$yaw = (float) $args[3];
|
||||
$pitch = (float) $args[4];
|
||||
}else{
|
||||
$yaw = $base->yaw;
|
||||
$pitch = $base->pitch;
|
||||
}
|
||||
|
||||
$x = $this->getRelativeDouble($base->x, $sender, $targetArgs[0]);
|
||||
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], World::Y_MIN, World::Y_MAX);
|
||||
$z = $this->getRelativeDouble($base->z, $sender, $targetArgs[2]);
|
||||
$x = $this->getRelativeDouble($base->x, $sender, $args[0]);
|
||||
$y = $this->getRelativeDouble($base->y, $sender, $args[1], World::Y_MIN, World::Y_MAX);
|
||||
$z = $this->getRelativeDouble($base->z, $sender, $args[2]);
|
||||
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
|
||||
|
||||
$subject->teleport($targetLocation);
|
||||
|
@ -36,9 +36,9 @@ use function implode;
|
||||
|
||||
class TellCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"tell",
|
||||
KnownTranslationFactory::pocketmine_command_tell_description(),
|
||||
KnownTranslationFactory::commands_message_usage(),
|
||||
["w", "msg"]
|
||||
|
@ -35,9 +35,9 @@ use function implode;
|
||||
|
||||
class TimeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"time",
|
||||
KnownTranslationFactory::pocketmine_command_time_description(),
|
||||
KnownTranslationFactory::pocketmine_command_time_usage()
|
||||
);
|
||||
|
@ -57,9 +57,9 @@ use const PHP_EOL;
|
||||
|
||||
class TimingsCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"timings",
|
||||
KnownTranslationFactory::pocketmine_command_timings_description(),
|
||||
KnownTranslationFactory::pocketmine_command_timings_usage()
|
||||
);
|
||||
|
@ -27,20 +27,22 @@ use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function array_slice;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
class TitleCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"title",
|
||||
KnownTranslationFactory::pocketmine_command_title_description(),
|
||||
KnownTranslationFactory::commands_title_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_TITLE);
|
||||
$this->setPermission(implode(";", [
|
||||
DefaultPermissionNames::COMMAND_TITLE_SELF,
|
||||
DefaultPermissionNames::COMMAND_TITLE_OTHER
|
||||
]));
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -48,9 +50,8 @@ class TitleCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayerByPrefix($args[0]);
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_TITLE_SELF, DefaultPermissionNames::COMMAND_TITLE_OTHER);
|
||||
if($player === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -32,9 +32,9 @@ use function count;
|
||||
|
||||
class TransferServerCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"transferserver",
|
||||
KnownTranslationFactory::pocketmine_command_transferserver_description(),
|
||||
KnownTranslationFactory::pocketmine_command_transferserver_usage()
|
||||
);
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function is_numeric;
|
||||
use function substr;
|
||||
@ -35,6 +36,28 @@ abstract class VanillaCommand extends Command{
|
||||
public const MAX_COORD = 30000000;
|
||||
public const MIN_COORD = -30000000;
|
||||
|
||||
protected function fetchPermittedPlayerTarget(CommandSender $sender, ?string $target, string $selfPermission, string $otherPermission) : ?Player{
|
||||
if($target !== null){
|
||||
$player = $sender->getServer()->getPlayerByPrefix($target);
|
||||
}elseif($sender instanceof Player){
|
||||
$player = $sender;
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
if($player === null){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return null;
|
||||
}
|
||||
if(
|
||||
($player === $sender && $this->testPermission($sender, $selfPermission)) ||
|
||||
($player !== $sender && $this->testPermission($sender, $otherPermission))
|
||||
){
|
||||
return $player;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getInteger(CommandSender $sender, string $value, int $min = self::MIN_COORD, int $max = self::MAX_COORD) : int{
|
||||
$i = (int) $value;
|
||||
|
||||
|
@ -40,9 +40,9 @@ use const PHP_VERSION;
|
||||
|
||||
class VersionCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"version",
|
||||
KnownTranslationFactory::pocketmine_command_version_description(),
|
||||
KnownTranslationFactory::pocketmine_command_version_usage(),
|
||||
["ver", "about"]
|
||||
|
@ -38,9 +38,9 @@ use const SORT_STRING;
|
||||
|
||||
class WhitelistCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(string $name){
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"whitelist",
|
||||
KnownTranslationFactory::pocketmine_command_whitelist_description(),
|
||||
KnownTranslationFactory::commands_whitelist_usage()
|
||||
);
|
||||
@ -122,7 +122,7 @@ class WhitelistCommand extends VanillaCommand{
|
||||
$server = $sender->getServer();
|
||||
$server->removeWhitelist($args[1]);
|
||||
if(!$server->isWhitelisted($args[1])){
|
||||
$server->getPlayerExact($args[1])?->kick("Server whitelisted.");
|
||||
$server->getPlayerExact($args[1])?->kick(KnownTranslationFactory::pocketmine_disconnect_kick(KnownTranslationFactory::pocketmine_disconnect_whitelisted()));
|
||||
}
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_remove_success($args[1]));
|
||||
}
|
||||
@ -135,9 +135,10 @@ class WhitelistCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
private function kickNonWhitelistedPlayers(Server $server) : void{
|
||||
$message = KnownTranslationFactory::pocketmine_disconnect_kick(KnownTranslationFactory::pocketmine_disconnect_whitelisted());
|
||||
foreach($server->getOnlinePlayers() as $player){
|
||||
if(!$server->isWhitelisted($player->getName())){
|
||||
$player->kick("Server whitelisted.");
|
||||
$player->kick($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,9 +35,15 @@ use function usort;
|
||||
class CraftingManager{
|
||||
use DestructorCallbackTrait;
|
||||
|
||||
/** @var ShapedRecipe[][] */
|
||||
/**
|
||||
* @var ShapedRecipe[][]
|
||||
* @phpstan-var array<string, list<ShapedRecipe>>
|
||||
*/
|
||||
protected array $shapedRecipes = [];
|
||||
/** @var ShapelessRecipe[][] */
|
||||
/**
|
||||
* @var ShapelessRecipe[][]
|
||||
* @phpstan-var array<string, list<ShapelessRecipe>>
|
||||
*/
|
||||
protected array $shapelessRecipes = [];
|
||||
|
||||
/**
|
||||
@ -139,6 +145,7 @@ class CraftingManager{
|
||||
|
||||
/**
|
||||
* @return ShapelessRecipe[][]
|
||||
* @phpstan-return array<string, list<ShapelessRecipe>>
|
||||
*/
|
||||
public function getShapelessRecipes() : array{
|
||||
return $this->shapelessRecipes;
|
||||
@ -146,6 +153,7 @@ class CraftingManager{
|
||||
|
||||
/**
|
||||
* @return ShapedRecipe[][]
|
||||
* @phpstan-return array<string, list<ShapedRecipe>>
|
||||
*/
|
||||
public function getShapedRecipes() : array{
|
||||
return $this->shapedRecipes;
|
||||
|
@ -39,11 +39,11 @@ use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function base64_decode;
|
||||
use function file_get_contents;
|
||||
use function get_debug_type;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
@ -156,7 +156,7 @@ final class CraftingManagerFromDataHelper{
|
||||
* @phpstan-return list<TData>
|
||||
*/
|
||||
public static function loadJsonArrayOfObjectsFile(string $filePath, string $modelCLass) : array{
|
||||
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"));
|
||||
$recipes = json_decode(Filesystem::fileGetContents($filePath));
|
||||
if(!is_array($recipes)){
|
||||
throw new SavedDataLoadingException("$filePath root should be an array, got " . get_debug_type($recipes));
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ use pocketmine\utils\Utils;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function str_contains;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
|
||||
class ShapedRecipe implements CraftingRecipe{
|
||||
/** @var string[] */
|
||||
@ -85,7 +85,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
$this->shape = $shape;
|
||||
|
||||
foreach($ingredients as $char => $i){
|
||||
if(strpos(implode($this->shape), $char) === false){
|
||||
if(!str_contains(implode($this->shape), $char)){
|
||||
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,7 @@ use function phpversion;
|
||||
use function preg_replace;
|
||||
use function sprintf;
|
||||
use function str_split;
|
||||
use function str_starts_with;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function zend_version;
|
||||
@ -237,7 +238,7 @@ class CrashDump{
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
$frameCleanPath = Filesystem::cleanPath($filePath);
|
||||
if(strpos($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX) !== 0){
|
||||
if(!str_starts_with($frameCleanPath, Filesystem::CLEAN_PATH_SRC_PREFIX)){
|
||||
if($crashFrame){
|
||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
|
||||
}else{
|
||||
@ -250,7 +251,7 @@ class CrashDump{
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Filesystem::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
if(str_starts_with($frameCleanPath, $filePath)){
|
||||
$this->data->plugin = $plugin->getName();
|
||||
break;
|
||||
}
|
||||
|
@ -23,13 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_keys;
|
||||
use function file_get_contents;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
@ -46,7 +45,7 @@ final class ItemTagToIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
private static function make() : self{
|
||||
$map = json_decode(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents(Path::join(BEDROCK_DATA_PATH, 'item_tags.json'))), true, flags: JSON_THROW_ON_ERROR);
|
||||
$map = json_decode(Filesystem::fileGetContents(Path::join(BEDROCK_DATA_PATH, 'item_tags.json')), true, flags: JSON_THROW_ON_ERROR);
|
||||
if(!is_array($map)){
|
||||
throw new AssumptionFailedError("Invalid item tag map, expected array");
|
||||
}
|
||||
|
@ -24,8 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use function file_get_contents;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
@ -40,7 +39,7 @@ abstract class LegacyToStringIdMap{
|
||||
private array $legacyToString = [];
|
||||
|
||||
public function __construct(string $file){
|
||||
$stringToLegacyId = json_decode(Utils::assumeNotFalse(file_get_contents($file), "Missing required resource file"), true);
|
||||
$stringToLegacyId = json_decode(Filesystem::fileGetContents($file), true);
|
||||
if(!is_array($stringToLegacyId)){
|
||||
throw new AssumptionFailedError("Invalid format of ID map");
|
||||
}
|
||||
|
66
src/data/bedrock/MedicineTypeIdMap.php
Normal file
66
src/data/bedrock/MedicineTypeIdMap.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
use pocketmine\item\MedicineType;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class MedicineTypeIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var MedicineType[]
|
||||
* @phpstan-var array<int, MedicineType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE());
|
||||
$this->register(MedicineTypeIds::ELIXIR, MedicineType::ELIXIR());
|
||||
$this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS());
|
||||
$this->register(MedicineTypeIds::TONIC, MedicineType::TONIC());
|
||||
}
|
||||
|
||||
private function register(int $id, MedicineType $type) : void{
|
||||
$this->idToEnum[$id] = $type;
|
||||
$this->enumToId[$type->id()] = $id;
|
||||
}
|
||||
|
||||
public function fromId(int $id) : ?MedicineType{
|
||||
return $this->idToEnum[$id] ?? null;
|
||||
}
|
||||
|
||||
public function toId(MedicineType $type) : int{
|
||||
if(!isset($this->enumToId[$type->id()])){
|
||||
throw new \InvalidArgumentException("Type does not have a mapped ID");
|
||||
}
|
||||
return $this->enumToId[$type->id()];
|
||||
}
|
||||
}
|
31
src/data/bedrock/MedicineTypeIds.php
Normal file
31
src/data/bedrock/MedicineTypeIds.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
final class MedicineTypeIds{
|
||||
public const EYE_DROPS = 0;
|
||||
public const TONIC = 1;
|
||||
public const ANTIDOTE = 2;
|
||||
public const ELIXIR = 3;
|
||||
}
|
@ -45,6 +45,7 @@ use pocketmine\block\Candle;
|
||||
use pocketmine\block\Carpet;
|
||||
use pocketmine\block\Carrot;
|
||||
use pocketmine\block\CarvedPumpkin;
|
||||
use pocketmine\block\Chain;
|
||||
use pocketmine\block\ChemistryTable;
|
||||
use pocketmine\block\Chest;
|
||||
use pocketmine\block\ChorusFlower;
|
||||
@ -235,11 +236,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
|
||||
$locatedSerializer = $this->serializers[$typeId][get_class($blockState)] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
$parents = class_parents($blockState);
|
||||
if($parents === false){
|
||||
throw new AssumptionFailedError("A block class should always have at least one parent");
|
||||
}
|
||||
foreach($parents as $parent){
|
||||
foreach(class_parents($blockState) as $parent){
|
||||
if(isset($this->serializers[$typeId][$parent])){
|
||||
$locatedSerializer = $this->serializers[$typeId][$parent];
|
||||
break;
|
||||
@ -548,6 +545,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM);
|
||||
$this->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK);
|
||||
$this->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6);
|
||||
$this->mapSimple(Blocks::SCULK(), Ids::SCULK);
|
||||
$this->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN);
|
||||
$this->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT);
|
||||
$this->mapSimple(Blocks::SHULKER_BOX(), Ids::UNDYED_SHULKER_BOX);
|
||||
@ -712,6 +710,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::CARVED_PUMPKIN)
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::CHAIN(), function(Chain $block) : Writer{
|
||||
return Writer::create(Ids::CHAIN)
|
||||
->writePillarAxis($block->getAxis());
|
||||
});
|
||||
$this->map(Blocks::CHEST(), function(Chest $block) : Writer{
|
||||
return Writer::create(Ids::CHEST)
|
||||
->writeHorizontalFacing($block->getFacing());
|
||||
@ -984,6 +986,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
})
|
||||
->writeHorizontalFacing($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME));
|
||||
$this->map(Blocks::GRANITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_GRANITE));
|
||||
$this->map(Blocks::GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_GRANITE));
|
||||
$this->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS);
|
||||
@ -1013,12 +1016,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_STONE_BRICK));
|
||||
$this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR)));
|
||||
$this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR)));
|
||||
$this->map(Blocks::ITEM_FRAME(), function(ItemFrame $block) : Writer{
|
||||
return Writer::create($block->isGlowing() ? Ids::GLOW_FRAME : Ids::FRAME)
|
||||
->writeBool(StateNames::ITEM_FRAME_MAP_BIT, $block->hasMap())
|
||||
->writeBool(StateNames::ITEM_FRAME_PHOTO_BIT, false)
|
||||
->writeFacingDirection($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME));
|
||||
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
|
||||
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
|
||||
$this->map(Blocks::JUNGLE_FENCE(), fn() => Writer::create(Ids::FENCE)
|
||||
|
@ -49,7 +49,6 @@ use pocketmine\block\Trapdoor;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\block\VanillaBlocks as Blocks;
|
||||
use pocketmine\block\Wall;
|
||||
use pocketmine\block\WallCoralFan;
|
||||
use pocketmine\block\WallSign;
|
||||
@ -172,12 +171,11 @@ final class BlockStateDeserializerHelper{
|
||||
->setFacing($in->readHorizontalFacing());
|
||||
}
|
||||
|
||||
public static function decodeItemFrame(BlockStateReader $in, bool $glowing) : ItemFrame{
|
||||
public static function decodeItemFrame(ItemFrame $block, BlockStateReader $in) : ItemFrame{
|
||||
$in->todo(StateNames::ITEM_FRAME_PHOTO_BIT); //TODO: not sure what the point of this is
|
||||
return Blocks::ITEM_FRAME()
|
||||
return $block
|
||||
->setFacing($in->readFacingDirection())
|
||||
->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT))
|
||||
->setGlowing($glowing);
|
||||
->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT));
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\block\DoublePlant;
|
||||
use pocketmine\block\FenceGate;
|
||||
use pocketmine\block\FloorSign;
|
||||
use pocketmine\block\Furnace;
|
||||
use pocketmine\block\ItemFrame;
|
||||
use pocketmine\block\Leaves;
|
||||
use pocketmine\block\Liquid;
|
||||
use pocketmine\block\RedMushroomBlock;
|
||||
@ -50,6 +51,7 @@ use pocketmine\block\Wood;
|
||||
use pocketmine\data\bedrock\block\BlockStateNames;
|
||||
use pocketmine\data\bedrock\block\BlockStateNames as StateNames;
|
||||
use pocketmine\data\bedrock\block\BlockTypeNames as Ids;
|
||||
use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
|
||||
use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
@ -138,6 +140,13 @@ final class BlockStateSerializerHelper{
|
||||
->writeHorizontalFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeItemFrame(ItemFrame $block, string $id) : BlockStateWriter{
|
||||
return Writer::create($id)
|
||||
->writeBool(StateNames::ITEM_FRAME_MAP_BIT, $block->hasMap())
|
||||
->writeBool(StateNames::ITEM_FRAME_PHOTO_BIT, false)
|
||||
->writeFacingDirection($block->getFacing());
|
||||
}
|
||||
|
||||
private static function encodeLeaves(Leaves $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay())
|
||||
|
@ -407,6 +407,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::RED_NETHER_BRICK, fn() => Blocks::RED_NETHER_BRICKS());
|
||||
$this->mapSimple(Ids::REDSTONE_BLOCK, fn() => Blocks::REDSTONE());
|
||||
$this->mapSimple(Ids::RESERVED6, fn() => Blocks::RESERVED6());
|
||||
$this->mapSimple(Ids::SCULK, fn() => Blocks::SCULK());
|
||||
$this->mapSimple(Ids::SEA_LANTERN, fn() => Blocks::SEA_LANTERN());
|
||||
$this->mapSimple(Ids::SHROOMLIGHT, fn() => Blocks::SHROOMLIGHT());
|
||||
$this->mapSimple(Ids::SLIME, fn() => Blocks::SLIME());
|
||||
@ -549,6 +550,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::CARVED_PUMPKIN()
|
||||
->setFacing($in->readLegacyHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::CHAIN, function(Reader $in) : Block{
|
||||
return Blocks::CHAIN()
|
||||
->setAxis($in->readPillarAxis());
|
||||
});
|
||||
$this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{
|
||||
return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){
|
||||
StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(),
|
||||
@ -726,7 +731,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
});
|
||||
$this->map(Ids::FLOWING_LAVA, fn(Reader $in) => Helper::decodeFlowingLiquid(Blocks::LAVA(), $in));
|
||||
$this->map(Ids::FLOWING_WATER, fn(Reader $in) => Helper::decodeFlowingLiquid(Blocks::WATER(), $in));
|
||||
$this->map(Ids::FRAME, fn(Reader $in) => Helper::decodeItemFrame($in, false));
|
||||
$this->map(Ids::FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::ITEM_FRAME(), $in));
|
||||
$this->map(Ids::FROSTED_ICE, function(Reader $in) : Block{
|
||||
return Blocks::FROSTED_ICE()
|
||||
->setAge($in->readBoundedInt(StateNames::AGE, 0, 3));
|
||||
@ -736,7 +741,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readHorizontalFacing())
|
||||
->setLit(false);
|
||||
});
|
||||
$this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame($in, true));
|
||||
$this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::GLOWING_ITEM_FRAME(), $in));
|
||||
$this->map(Ids::GOLDEN_RAIL, function(Reader $in) : Block{
|
||||
return Blocks::POWERED_RAIL()
|
||||
->setPowered($in->readBool(StateNames::RAIL_DATA_BIT))
|
||||
|
@ -27,16 +27,15 @@ use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function file_get_contents;
|
||||
use function get_debug_type;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
@ -275,11 +274,7 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
|
||||
$fullPath = Path::join($path, $filename);
|
||||
|
||||
try{
|
||||
$raw = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($fullPath));
|
||||
}catch(\ErrorException $e){
|
||||
throw new \RuntimeException("Loading schema file $fullPath: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
$raw = Filesystem::fileGetContents($fullPath);
|
||||
|
||||
try{
|
||||
$schema = self::loadSchemaFromString($raw, $priority);
|
||||
|
@ -24,11 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\item;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_flip;
|
||||
use function file_get_contents;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
@ -42,7 +41,7 @@ final class BlockItemIdMap{
|
||||
|
||||
private static function make() : self{
|
||||
$map = json_decode(
|
||||
Utils::assumeNotFalse(file_get_contents(Path::join(BEDROCK_DATA_PATH, 'block_id_to_item_id_map.json')), "Missing required resource file"),
|
||||
Filesystem::fileGetContents(Path::join(BEDROCK_DATA_PATH, 'block_id_to_item_id_map.json')),
|
||||
associative: true,
|
||||
flags: JSON_THROW_ON_ERROR
|
||||
);
|
||||
|
@ -103,13 +103,10 @@ final class ItemSerializer{
|
||||
|
||||
$locatedSerializer = $this->itemSerializers[$index][get_class($item)] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
$parents = class_parents($item);
|
||||
if($parents !== false){
|
||||
foreach($parents as $parent){
|
||||
if(isset($this->itemSerializers[$index][$parent])){
|
||||
$locatedSerializer = $this->itemSerializers[$index][$parent];
|
||||
break;
|
||||
}
|
||||
foreach(class_parents($item) as $parent){
|
||||
if(isset($this->itemSerializers[$index][$parent])){
|
||||
$locatedSerializer = $this->itemSerializers[$index][$parent];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,13 +159,10 @@ final class ItemSerializer{
|
||||
|
||||
$locatedSerializer = $this->blockItemSerializers[$index][get_class($block)] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
$parents = class_parents($block);
|
||||
if($parents !== false){
|
||||
foreach($parents as $parent){
|
||||
if(isset($this->blockItemSerializers[$index][$parent])){
|
||||
$locatedSerializer = $this->blockItemSerializers[$index][$parent];
|
||||
break;
|
||||
}
|
||||
foreach(class_parents($block) as $parent){
|
||||
if(isset($this->blockItemSerializers[$index][$parent])){
|
||||
$locatedSerializer = $this->blockItemSerializers[$index][$parent];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\item;
|
||||
|
||||
use pocketmine\block\Bed;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\ItemFrame;
|
||||
use pocketmine\block\Skull;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
@ -34,11 +33,13 @@ use pocketmine\data\bedrock\CompoundTypeIds;
|
||||
use pocketmine\data\bedrock\DyeColorIdMap;
|
||||
use pocketmine\data\bedrock\item\ItemTypeNames as Ids;
|
||||
use pocketmine\data\bedrock\item\SavedItemData as Data;
|
||||
use pocketmine\data\bedrock\MedicineTypeIdMap;
|
||||
use pocketmine\data\bedrock\PotionTypeIdMap;
|
||||
use pocketmine\data\bedrock\SuspiciousStewTypeIdMap;
|
||||
use pocketmine\item\Banner;
|
||||
use pocketmine\item\Dye;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\Medicine;
|
||||
use pocketmine\item\Potion;
|
||||
use pocketmine\item\SplashPotion;
|
||||
use pocketmine\item\SuspiciousStew;
|
||||
@ -55,7 +56,6 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->register1to1BlockWithMetaMappings();
|
||||
$this->register1to1ItemWithMetaMappings();
|
||||
$this->register1ToNItemMappings();
|
||||
$this->registerMiscBlockMappings();
|
||||
$this->registerMiscItemMappings();
|
||||
}
|
||||
|
||||
@ -134,10 +134,13 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Block(Ids::BREWING_STAND, Blocks::BREWING_STAND());
|
||||
$this->map1to1Block(Ids::CAKE, Blocks::CAKE());
|
||||
$this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON());
|
||||
$this->map1to1Block(Ids::CHAIN, Blocks::CHAIN());
|
||||
$this->map1to1Block(Ids::COMPARATOR, Blocks::REDSTONE_COMPARATOR());
|
||||
$this->map1to1Block(Ids::CRIMSON_DOOR, Blocks::CRIMSON_DOOR());
|
||||
$this->map1to1Block(Ids::DARK_OAK_DOOR, Blocks::DARK_OAK_DOOR());
|
||||
$this->map1to1Block(Ids::FLOWER_POT, Blocks::FLOWER_POT());
|
||||
$this->map1to1Block(Ids::FRAME, Blocks::ITEM_FRAME());
|
||||
$this->map1to1Block(Ids::GLOW_FRAME, Blocks::GLOWING_ITEM_FRAME());
|
||||
$this->map1to1Block(Ids::HOPPER, Blocks::HOPPER());
|
||||
$this->map1to1Block(Ids::IRON_DOOR, Blocks::IRON_DOOR());
|
||||
$this->map1to1Block(Ids::JUNGLE_DOOR, Blocks::JUNGLE_DOOR());
|
||||
@ -465,6 +468,14 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
},
|
||||
fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor())
|
||||
);
|
||||
$this->map1to1ItemWithMeta(
|
||||
Ids::MEDICINE,
|
||||
Items::MEDICINE(),
|
||||
function(Medicine $item, int $meta) : void{
|
||||
$item->setType(MedicineTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown medicine type ID $meta"));
|
||||
},
|
||||
fn(Medicine $item) => MedicineTypeIdMap::getInstance()->toId($item->getType())
|
||||
);
|
||||
$this->map1to1ItemWithMeta(
|
||||
Ids::POTION,
|
||||
Items::POTION(),
|
||||
@ -491,19 +502,6 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers serializers and deserializers for blocks that don't fit any other pattern.
|
||||
* Ideally we want to get rid of this completely, if possible.
|
||||
*
|
||||
* Most of these are single PocketMine-MP items which map to multiple IDs depending on their properties, which is
|
||||
* complex to implement in a generic way.
|
||||
*/
|
||||
private function registerMiscBlockMappings() : void{
|
||||
$this->deserializer?->mapBlock(Ids::FRAME, fn() => Blocks::ITEM_FRAME()->setGlowing(false));
|
||||
$this->deserializer?->mapBlock(Ids::GLOW_FRAME, fn() => Blocks::ITEM_FRAME()->setGlowing(true));
|
||||
$this->serializer?->mapBlock(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => new Data($block->isGlowing() ? Ids::GLOW_FRAME : Ids::FRAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers serializers and deserializers for items that don't fit any other pattern.
|
||||
* Ideally we want to get rid of this completely, if possible.
|
||||
|
@ -24,9 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\item\upgrade;
|
||||
|
||||
use pocketmine\data\bedrock\item\upgrade\model\ItemIdMetaUpgradeSchemaModel;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function file_get_contents;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
use function json_decode;
|
||||
@ -60,11 +59,7 @@ final class ItemIdMetaUpgradeSchemaUtils{
|
||||
|
||||
$fullPath = Path::join($path, $filename);
|
||||
|
||||
try{
|
||||
$raw = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($fullPath));
|
||||
}catch(\ErrorException $e){
|
||||
throw new \RuntimeException("Loading schema file $fullPath: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
$raw = Filesystem::fileGetContents($fullPath);
|
||||
|
||||
try{
|
||||
$schema = self::loadSchemaFromString($raw, $priority);
|
||||
|
@ -24,10 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\item\upgrade;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function file_get_contents;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
@ -47,7 +47,7 @@ final class R12ItemIdToBlockIdMap{
|
||||
|
||||
private static function make() : self{
|
||||
$map = json_decode(
|
||||
Utils::assumeNotFalse(file_get_contents(Path::join(BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, '1.12.0_item_id_to_block_id_map.json')), "Missing required resource file"),
|
||||
Filesystem::fileGetContents(Path::join(BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, '1.12.0_item_id_to_block_id_map.json')),
|
||||
associative: true,
|
||||
flags: JSON_THROW_ON_ERROR
|
||||
);
|
||||
|
@ -116,6 +116,16 @@ trait RuntimeEnumDeserializerTrait{
|
||||
};
|
||||
}
|
||||
|
||||
public function medicineType(\pocketmine\item\MedicineType &$value) : void{
|
||||
$value = match($this->readInt(2)){
|
||||
0 => \pocketmine\item\MedicineType::ANTIDOTE(),
|
||||
1 => \pocketmine\item\MedicineType::ELIXIR(),
|
||||
2 => \pocketmine\item\MedicineType::EYE_DROPS(),
|
||||
3 => \pocketmine\item\MedicineType::TONIC(),
|
||||
default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MedicineType")
|
||||
};
|
||||
}
|
||||
|
||||
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
|
||||
$value = match($this->readInt(4)){
|
||||
0 => \pocketmine\block\utils\MushroomBlockType::ALL_CAP(),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user