mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
Compare commits
126 Commits
4.0.0-BETA
...
4.0.0-BETA
Author | SHA1 | Date | |
---|---|---|---|
002feacf8e | |||
b8523f7a18 | |||
640e88009b | |||
6cd272c9e1 | |||
566c57bcd3 | |||
3c754b079c | |||
dbf9a33160 | |||
07b1cff306 | |||
0989c77037 | |||
5107d0df4e | |||
579ef63663 | |||
8abc952c74 | |||
4c3a5fdd73 | |||
54f287feb6 | |||
84f8b3eb2d | |||
15fca84f3b | |||
c60144210f | |||
8ac999cbd4 | |||
4f8501ff34 | |||
2405e45b35 | |||
e0b07ff308 | |||
729f831b8f | |||
0356716e8e | |||
5c81b04813 | |||
1ebb206762 | |||
29e2d92098 | |||
ef82a2cd79 | |||
87031627bf | |||
f066199971 | |||
a0e9eec652 | |||
fa6a432d58 | |||
102277c636 | |||
f50f26d52e | |||
4ca7c29cde | |||
9f64bc8180 | |||
f75a05d7fa | |||
3dae873731 | |||
aa0dc60c32 | |||
d184838ba0 | |||
ead8ccf08d | |||
38f97bed52 | |||
275f145418 | |||
6b07f7a5ec | |||
e131c2cefa | |||
e34364412b | |||
4eef458d29 | |||
8b3565b75d | |||
facfd7c04a | |||
65ef9f786a | |||
4dc13ab3da | |||
ede4157814 | |||
34ea199fb0 | |||
1775699f05 | |||
32a857b8b4 | |||
7e4be29fc4 | |||
c17587d436 | |||
0f6b7e48cb | |||
f2912fcdd8 | |||
f6cb4f9597 | |||
54442f7e4b | |||
5257755dc5 | |||
8c16ecaa5b | |||
3214da8642 | |||
f827a555d5 | |||
61145baded | |||
4d54d6c552 | |||
485bc2c565 | |||
07b4f844a9 | |||
19e5775f6b | |||
804fb3f603 | |||
6175b03433 | |||
a78248a19c | |||
414ccb9f10 | |||
794142fe49 | |||
ff27c5f7db | |||
4d4362801f | |||
0babe0a1ab | |||
d696ebcda3 | |||
c3768b997a | |||
f6480017ce | |||
9f5c16bc46 | |||
8865bb73ba | |||
2dee1dbc28 | |||
0f0b6f0efa | |||
d5f13d8be2 | |||
27ae959e89 | |||
f8f39687e2 | |||
94737934de | |||
debb469de1 | |||
616eb0050d | |||
9d30bc8b95 | |||
46b7d35cd3 | |||
c781efcf90 | |||
e4a54f5b6a | |||
afb54f1ae4 | |||
8f803df511 | |||
f4a3c40b5c | |||
9dec82cdbc | |||
74031d2fbe | |||
bd60e41268 | |||
96cfdc79b8 | |||
2fa0a914ff | |||
08636d079d | |||
3265d3f6b4 | |||
0f78a2b5ef | |||
f1a791ef75 | |||
866020dfdb | |||
c580bb2434 | |||
4fe3f69702 | |||
018006541e | |||
fbb91d123d | |||
3dc75644d9 | |||
1cabe4baf3 | |||
73dc0598e4 | |||
faad2365e2 | |||
4f816d03a7 | |||
1d22761d27 | |||
5b8ce7e3e2 | |||
08f3c18de9 | |||
141fbde660 | |||
465a509858 | |||
69952ae2af | |||
71f2a34616 | |||
63dfcc60c3 | |||
d17cd65803 | |||
a8d5e8c5f6 |
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
||||
sed -i "s/const BUILD_NUMBER = 0/const BUILD_NUMBER = ${BUILD_NUMBER}/" src/VersionInfo.php
|
||||
|
||||
- name: Minify BedrockData JSON files
|
||||
run: php resources/vanilla/.minify_json.php
|
||||
run: php vendor/pocketmine/bedrock-data/.minify_json.php
|
||||
|
||||
- name: Build PocketMine-MP.phar
|
||||
run: php -dphar.readonly=0 build/server-phar.php --git ${{ github.sha }}
|
||||
|
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -91,8 +91,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
@ -195,8 +193,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,12 +1,6 @@
|
||||
[submodule "resources/locale"]
|
||||
path = resources/locale
|
||||
url = https://github.com/pmmp/Language.git
|
||||
[submodule "tests/plugins/DevTools"]
|
||||
path = tests/plugins/DevTools
|
||||
url = https://github.com/pmmp/DevTools.git
|
||||
[submodule "build/php"]
|
||||
path = build/php
|
||||
url = https://github.com/pmmp/php-build-scripts.git
|
||||
[submodule "resources/vanilla"]
|
||||
path = resources/vanilla
|
||||
url = https://github.com/pmmp/BedrockData.git
|
||||
|
@ -172,7 +172,7 @@ HEADER;
|
||||
echo "Done generating KnownTranslationFactory.\n";
|
||||
}
|
||||
|
||||
$lang = parse_ini_file(Path::join(\pocketmine\RESOURCE_PATH, "locale", "eng.ini"), false, INI_SCANNER_RAW);
|
||||
$lang = parse_ini_file(Path::join(\pocketmine\LOCALE_DATA_PATH, "eng.ini"), false, INI_SCANNER_RAW);
|
||||
if($lang === false){
|
||||
fwrite(STDERR, "Missing language files!\n");
|
||||
exit(1);
|
||||
|
Submodule build/php updated: 9a042c5593...58ea62d7ca
@ -9,3 +9,15 @@ Plugin developers should **only** update their required API to this version if y
|
||||
# 3.25.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.40.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
# 3.25.1
|
||||
- Fixed autosave bug that caused unmodified chunks to be saved at least once (during the first autosave after they were loaded).
|
||||
- `Entity->spawnTo()` now has an additional sanity check for matching worlds (might expose a few new errors in plugins).
|
||||
- Fixed a missing field in `CraftRecipeAuto` item stack request type.
|
||||
|
||||
# 3.25.2
|
||||
- Now analysed using level 9 on PHPStan 1.0.0.
|
||||
- `ext-pthreads` v4.0.0 or newer is now required.
|
||||
- Fixed crash in `Player->showPlayer()` when the target is not in the same world.
|
||||
- `Human->setLifetimeTotalXp()` now limits the maximum value to 2^31.
|
||||
- Fixed players, who died in hardcore mode and were unbanned, getting re-banned on next server join.
|
||||
|
@ -1274,6 +1274,8 @@ However, if we add `src-namespace-prefix: pmmp\TesterPlugin` to the `plugin.yml`
|
||||
|
||||
### Inventory
|
||||
- Implemented offhand inventory.
|
||||
- Block-picking is now supported in survival mode.
|
||||
- Block picking behaviour now matches vanilla (no longer overwrites held item, jumps to existing item where possible).
|
||||
|
||||
# 4.0.0-BETA2
|
||||
Released 10th September 2021.
|
||||
@ -1525,3 +1527,110 @@ Released 29th October 2021.
|
||||
- All parameters are now mandatory.
|
||||
- Reverted addition of chunk modification counters in previous beta.
|
||||
- `Chunk::DIRTY_FLAG_TERRAIN` has been renamed to `Chunk::DIRTY_FLAG_BLOCKS`.
|
||||
|
||||
# 4.0.0-BETA9
|
||||
Released 2nd November 2021.
|
||||
|
||||
## General
|
||||
- Now analysed using level 9 on PHPStan 1.0.0.
|
||||
- `ext-pthreads` v4.0.0 or newer is now required.
|
||||
- `resources/vanilla` submodule has been removed. BedrockData is now included via Composer dependency [`pocketmine/bedrock-data`](https://packagist.org/packages/pocketmine/bedrock-data).
|
||||
- `pocketmine/spl` Composer dependency has been dropped.
|
||||
- The following Composer dependency versions are now required:
|
||||
- [`pocketmine/bedrock-protocol` v5.0.0](https://github.com/pmmp/BedrockProtocol/tree/5.0.0+bedrock-1.17.40) features substantial changes to its API compared to 3.0.1, which was used in 4.0.0-BETA8. Please see its [release notes](https://github.com/pmmp/BedrockData/releases/tag/5.0.0+bedrock-1.17.40).
|
||||
- [`pocketmine/log` v0.4.0](https://github.com/pmmp/Log/tree/0.4.0) removes the `LoggerAttachment` interface and replaces logger attachment objects with closures.
|
||||
- [`pocketmine/log-pthreads` v0.4.0](https://github.com/pmmp/LogPthreads/tree/0.4.0)
|
||||
- [`pocketmine/classloader` v0.2.0](https://github.com/pmmp/ClassLoader/tree/0.2.0)
|
||||
- A noisy debug message in `World->updateAllLight()` has been removed.
|
||||
|
||||
## API
|
||||
### Entity
|
||||
- `Human->setLifetimeTotalXp()` now limits the maximum value to 2^31.
|
||||
|
||||
### Event
|
||||
- `BlockGrowEvent` is now called when cocoa pods grow.
|
||||
|
||||
### Item
|
||||
- Added `Releasable->canStartUsingItem()`.
|
||||
|
||||
### Network
|
||||
- Added `NetworkInterfaceStartException`, which may be thrown by `Network->registerInterface()` and `NetworkInterface->start()`.
|
||||
|
||||
### Player
|
||||
- `SurvivalBlockBreakHandler::createIfNecessary()` has been removed.
|
||||
- `SurvivalBlockBreakHandler->__construct()` is now public.
|
||||
- `UsedChunkStatus::REQUESTED()` has been renamed to `REQUESTED_SENDING`.
|
||||
- `UsedChunkStatus::REQUESTED_GENERATION()` has been added.
|
||||
|
||||
### Utils
|
||||
- `Promise` API has changed:
|
||||
- Promise-related classes have been moved to `pocketmine\promise` namespace.
|
||||
- It's now split into `Promise` and `PromiseResolver`.
|
||||
- `PromiseResolver` provides only `resolve()` and `reject()`. It should be used by callbacks to resolve a promise.
|
||||
- `Promise` now provides only `onCompletion()` and `isResolved()` APIs. This should be given to consumers to allow them to handle the result of the async operation.
|
||||
- `PromiseResolver` must not be created directly. Use `new PromiseResolver` and `PromiseResolver->getPromise()`.
|
||||
|
||||
### World
|
||||
- Improved performance of `setBlock()` by around 35% when the `$update` parameter is set to `true`.
|
||||
- Improved performance of `setBlock()` by around 30% when the `$update` parameter is set to `false`.
|
||||
- `World->generateChunkCallback()` is no longer exposed to public API.
|
||||
- `World->getAdjacentChunks()` now returns an array indexed using `World::chunkHash()`, where the `x` and `z` components are the relative offsets from the target chunk (range -1 to +1).
|
||||
- `World->lockChunk()` now requires `ChunkLockId $lockId` parameter.
|
||||
- `World->unlockChunk()` now requires a `?ChunkLockId $lockId` parameter. If a non-null lockID is given, the lock on the chunk will only be removed if it matches the given lockID.
|
||||
- `World->unlockChunk()` now returns `bool` instead of `void` (to signal whether unlocking succeded or not).
|
||||
- Added `ChunkLockId` class.
|
||||
|
||||
## Fixes
|
||||
### World
|
||||
- Fixed server crash when tiles with colliding positions are loaded from saved data. Now, an error is logged, but the server won't crash.
|
||||
- Fixed server crash when liquids and other items flow into terrain locked for population. Now, an advisory locking mechanism is used, and population results will be discarded and recalculated if modifications are detected.
|
||||
- Fixed various crashes that could occur if a chunk was flagged with `setPopulated(true)` after a promise had already been created for its population.
|
||||
- Fixed `AssumptionFailedError` in `PopulationTask` when workers previously used for generation are shutdown, and then restarted on the fly by a generation request.
|
||||
- Fixed assertion failure in `World->drainPopulationRequestQueue()` when requesting, cancelling and then re-requesting generation of a chunk while the generator was busy.
|
||||
- Fixed generation potentially getting stuck if a population request was cancelled while the population task was running (failure to remove locks from used chunks).
|
||||
- Fixed `World->requestChunkPopulation()` not taking into account that the target chunk may already be populated. This caused a variety of strange bugs and performance issues.
|
||||
- Fixed potential memory leak caused by `World->unregisterChunkListenerFromAll()` not taking players into account.
|
||||
- Fixed debug spam of `chunk has no loaders registered` messages during chunk generation.
|
||||
|
||||
### Other fixes
|
||||
- Fixed server crash when unable to bind to the desired port. Now, the server will show an error and gracefully stop without a crashdump instead.
|
||||
- Fixed server crash in `Player->showPlayer()` when the target is not in the same world.
|
||||
- Fixed players, who died in hardcore mode and were unbanned, getting re-banned on next server join.
|
||||
- Fixed cake block desync when attempting to eat in creative (eating in creative is not yet supported, but the block rollback was missing).
|
||||
- Fixed players being able to eat items more quickly by dropping them while eating.
|
||||
- Fixed arrows getting added to creative players' inventories when picked up.
|
||||
- Fixed players re-requesting the same ungenerated chunks multiple times before they were sent.
|
||||
- Fixed commands not working in some cases after using some control sequences on the console.
|
||||
|
||||
# 4.0.0-BETA10
|
||||
Released 2nd November 2021.
|
||||
|
||||
## Fixes
|
||||
- Fixed an issue with BedrockData JSON minification which broke the release build of 4.0.0-BETA9.
|
||||
|
||||
# 4.0.0-BETA11
|
||||
Released 6th November 2021.
|
||||
|
||||
## General
|
||||
- `resources/locale` submodule has been removed. Language files are now included via Composer dependency [`pocketmine/locale-data`](https://packagist.org/packages/pocketmine/locale-data).
|
||||
- This means it's now possible to run a server from git sources without cloning submodules :)
|
||||
- All remaining submodules (DevTools, build/php) are non-essential for building and running a server.
|
||||
- Added a tool `tools/simulate-chunk-sending.php` to visualise the behaviour of `ChunkSelector`.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash on saving when player XP has reached int32 max (XP is now capped, similar to Java Edition).
|
||||
- Fixed another edge case in chunk generation that led to assertion failures.
|
||||
- Fixed server crash when finding a list of `TAG_Float` for entity positions instead of `TAG_Double`.
|
||||
- Fixed fast eating when picking up items.
|
||||
- Fixed terrain being invisible for a long time when teleporting into ungenerated terrain.
|
||||
- Fixed weird chunk loading when teleporting into ungenerated terrain (sometimes farther chunks would render before closer ones, leaving holes in the map temporarily).
|
||||
- Fixed players re-requesting chunks when turning their heads or jumping.
|
||||
- Fixed bonemeal sometimes being consumed even when cancelling `BlockGrowEvent` and `StructureGrowEvent`.
|
||||
|
||||
## API
|
||||
### Event
|
||||
- Added `PlayerEmoteEvent`.
|
||||
|
||||
### Gameplay
|
||||
- Chunks are now sent in proper circles. This improves the experience when flying quickly parallel to X or Z axis, since now more chunks in front of the player will load sooner.
|
||||
- Added support for emotes.
|
||||
|
@ -22,7 +22,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-pthreads": "^4.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
@ -34,27 +34,28 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-protocol": "dev-master",
|
||||
"pocketmine/bedrock-data": "^1.4.0+bedrock-1.17.40",
|
||||
"pocketmine/bedrock-protocol": "^5.0.0+bedrock-1.17.40",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "dev-master",
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/errorhandler": "^0.3.0",
|
||||
"pocketmine/log": "^0.3.0",
|
||||
"pocketmine/log-pthreads": "^0.2.0",
|
||||
"pocketmine/locale-data": "^1.0.3",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.4.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
"pocketmine/nbt": "^0.3.0",
|
||||
"pocketmine/raklib": "^0.14.2",
|
||||
"pocketmine/raklib-ipc": "^0.1.0",
|
||||
"pocketmine/snooze": "^0.3.0",
|
||||
"pocketmine/spl": "dev-master",
|
||||
"ramsey/uuid": "^4.1",
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpstan/phpstan": "1.0.2",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
},
|
||||
"autoload": {
|
||||
|
267
composer.lock
generated
267
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": "3f467dca67940d465ceafbe5774d6977",
|
||||
"content-hash": "e36db7fb94bd79034dcc599c3029a621",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -248,17 +248,43 @@
|
||||
"time": "2020-12-01T19:48:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "dev-master",
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "1.4.0+bedrock-1.17.40",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "c8d891b4dff9817d5fcd373dfec0608b20be3b0a"
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c8d891b4dff9817d5fcd373dfec0608b20be3b0a",
|
||||
"reference": "c8d891b4dff9817d5fcd373dfec0608b20be3b0a",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/f29b7be8fa3046d2ee4c6421485b97b3f5b07774",
|
||||
"reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.17.40"
|
||||
},
|
||||
"time": "2021-10-19T16:55:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "5.0.0+bedrock-1.17.40",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "67c0c15b4044cab2190501933912c3d02c5f63ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/67c0c15b4044cab2190501933912c3d02c5f63ab",
|
||||
"reference": "67c0c15b4044cab2190501933912c3d02c5f63ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -272,12 +298,11 @@
|
||||
"ramsey/uuid": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-phpunit": "^0.12.21",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.10",
|
||||
"phpstan/phpstan": "1.0.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -291,9 +316,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/master"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/5.0.0+bedrock-1.17.40"
|
||||
},
|
||||
"time": "2021-10-29T20:54:42+00:00"
|
||||
"time": "2021-11-02T01:27:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -387,31 +412,31 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/classloader",
|
||||
"version": "dev-master",
|
||||
"version": "0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/ClassLoader.git",
|
||||
"reference": "80226e0917be79ac3230606113e25134a31e6a85"
|
||||
"reference": "49ea303993efdfb39cd302e2156d50aa78209e78"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/80226e0917be79ac3230606113e25134a31e6a85",
|
||||
"reference": "80226e0917be79ac3230606113e25134a31e6a85",
|
||||
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/49ea303993efdfb39cd302e2156d50aa78209e78",
|
||||
"reference": "49ea303993efdfb39cd302e2156d50aa78209e78",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-pthreads": "~3.2.0 || ^4.0",
|
||||
"ext-reflection": "*",
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": "^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"pocketmine/spl": "<0.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.80",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4",
|
||||
"phpunit/phpunit": "^8.5 || ^9.5"
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -426,9 +451,9 @@
|
||||
"description": "Ad-hoc autoloading components used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/ClassLoader/issues",
|
||||
"source": "https://github.com/pmmp/ClassLoader/tree/master"
|
||||
"source": "https://github.com/pmmp/ClassLoader/tree/0.2.0"
|
||||
},
|
||||
"time": "2021-05-29T23:09:32+00:00"
|
||||
"time": "2021-11-01T20:17:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/color",
|
||||
@ -507,17 +532,40 @@
|
||||
"time": "2021-02-12T18:56:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
"version": "0.3.0",
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Log.git",
|
||||
"reference": "03ab1316da0b1978a7a1c8dd73e1c2a973cb62ec"
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "7342b4eb593036c739e7f0c0ed95299ada69ff19"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Log/zipball/03ab1316da0b1978a7a1c8dd73e1c2a973cb62ec",
|
||||
"reference": "03ab1316da0b1978a7a1c8dd73e1c2a973cb62ec",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/7342b4eb593036c739e7f0c0ed95299ada69ff19",
|
||||
"reference": "7342b4eb593036c739e7f0c0ed95299ada69ff19",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/1.0.3"
|
||||
},
|
||||
"time": "2021-11-06T00:27:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Log.git",
|
||||
"reference": "e6c912c0f9055c81d23108ec2d179b96f404c043"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Log/zipball/e6c912c0f9055c81d23108ec2d179b96f404c043",
|
||||
"reference": "e6c912c0f9055c81d23108ec2d179b96f404c043",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -527,7 +575,7 @@
|
||||
"pocketmine/spl": "<0.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.80",
|
||||
"phpstan/phpstan": "0.12.88",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2"
|
||||
},
|
||||
"type": "library",
|
||||
@ -543,28 +591,28 @@
|
||||
"description": "Logging components used by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Log/issues",
|
||||
"source": "https://github.com/pmmp/Log/tree/0.3.0"
|
||||
"source": "https://github.com/pmmp/Log/tree/0.4.0"
|
||||
},
|
||||
"time": "2021-05-18T21:00:49+00:00"
|
||||
"time": "2021-06-18T19:08:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log-pthreads",
|
||||
"version": "0.2.0",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/LogPthreads.git",
|
||||
"reference": "6be3445c48c62eba3922f987f000bb20c81d161f"
|
||||
"reference": "61f709e8cf36bcc24e4efe02acded680a1ce23cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/6be3445c48c62eba3922f987f000bb20c81d161f",
|
||||
"reference": "6be3445c48c62eba3922f987f000bb20c81d161f",
|
||||
"url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/61f709e8cf36bcc24e4efe02acded680a1ce23cd",
|
||||
"reference": "61f709e8cf36bcc24e4efe02acded680a1ce23cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-pthreads": "~3.2.0 || ^4.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"pocketmine/log": "^0.2.0 || ^0.3.0"
|
||||
"pocketmine/log": "^0.4.0"
|
||||
},
|
||||
"conflict": {
|
||||
"pocketmine/spl": "<0.4"
|
||||
@ -587,9 +635,9 @@
|
||||
"description": "Logging components specialized for pthreads used by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/LogPthreads/issues",
|
||||
"source": "https://github.com/pmmp/LogPthreads/tree/0.2.0"
|
||||
"source": "https://github.com/pmmp/LogPthreads/tree/0.4.0"
|
||||
},
|
||||
"time": "2021-05-18T22:15:28+00:00"
|
||||
"time": "2021-11-01T21:42:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
@ -760,25 +808,25 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Snooze.git",
|
||||
"reference": "fe5b1dbf0d6267da882d1f67924772bd93db833d"
|
||||
"reference": "0ac8fc2a781c419a1f64ebca4d5835028f59e29b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/fe5b1dbf0d6267da882d1f67924772bd93db833d",
|
||||
"reference": "fe5b1dbf0d6267da882d1f67924772bd93db833d",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/0ac8fc2a781c419a1f64ebca4d5835028f59e29b",
|
||||
"reference": "0ac8fc2a781c419a1f64ebca4d5835028f59e29b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pthreads": ">=3.1.7dev",
|
||||
"ext-pthreads": "~3.2.0 || ^4.0",
|
||||
"php-64bit": "^7.3 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.88",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
},
|
||||
"type": "library",
|
||||
@ -794,46 +842,9 @@
|
||||
"description": "Thread notification management library for code using the pthreads extension",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Snooze/issues",
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.3.0"
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.3.1"
|
||||
},
|
||||
"time": "2021-06-13T13:57:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/SPL.git",
|
||||
"reference": "b7a8904f912c1f6d38ad867ff1120614ccb80171"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/SPL/zipball/b7a8904f912c1f6d38ad867ff1120614ccb80171",
|
||||
"reference": "b7a8904f912c1f6d38ad867ff1120614ccb80171",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^0.12.8"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"./src"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"description": "Standard library files required by PocketMine-MP and related projects",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/SPL/issues",
|
||||
"source": "https://github.com/pmmp/SPL/tree/master"
|
||||
},
|
||||
"time": "2021-01-15T15:19:34+00:00"
|
||||
"time": "2021-11-01T20:50:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ramsey/collection",
|
||||
@ -1492,16 +1503,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.13.0",
|
||||
"version": "v4.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "50953a2691a922aa1769461637869a0a2faa3f53"
|
||||
"reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53",
|
||||
"reference": "50953a2691a922aa1769461637869a0a2faa3f53",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd",
|
||||
"reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1542,9 +1553,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1"
|
||||
},
|
||||
"time": "2021-09-20T12:20:58+00:00"
|
||||
"time": "2021-11-03T20:52:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1886,16 +1897,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "0.12.99",
|
||||
"version": "1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7"
|
||||
"reference": "e9e2a501102ba0b126b2f63a7f0a3b151056fe91"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7",
|
||||
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e9e2a501102ba0b126b2f63a7f0a3b151056fe91",
|
||||
"reference": "e9e2a501102ba0b126b2f63a7f0a3b151056fe91",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1911,7 +1922,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.12-dev"
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1926,7 +1937,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.99"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1946,38 +1957,39 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-12T20:09:55+00:00"
|
||||
"time": "2021-11-03T16:09:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
"version": "0.12.22",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||
"reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc"
|
||||
"reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc",
|
||||
"reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
|
||||
"reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"phpstan/phpstan": "^0.12.92"
|
||||
"phpstan/phpstan": "^1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.12-dev"
|
||||
"dev-master": "1.0-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
@ -1998,37 +2010,38 @@
|
||||
"description": "PHPUnit extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.22"
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.0.0"
|
||||
},
|
||||
"time": "2021-08-12T10:53:43+00:00"
|
||||
"time": "2021-10-14T08:03:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "0.12.11",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "2b72e8e17d2034145f239126e876e5fb659675e2"
|
||||
"reference": "7f50eb112f37fda2ef956813d3f1e9b1e69d7940"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/2b72e8e17d2034145f239126e876e5fb659675e2",
|
||||
"reference": "2b72e8e17d2034145f239126e876e5fb659675e2",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7f50eb112f37fda2ef956813d3f1e9b1e69d7940",
|
||||
"reference": "7f50eb112f37fda2ef956813d3f1e9b1e69d7940",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"phpstan/phpstan": "^0.12.96"
|
||||
"phpstan/phpstan": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/phpstan-phpunit": "^0.12.16",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.12-dev"
|
||||
"dev-master": "1.0-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
@ -2048,29 +2061,29 @@
|
||||
"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/0.12.11"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.0.0"
|
||||
},
|
||||
"time": "2021-08-21T11:36:27+00:00"
|
||||
"time": "2021-10-11T06:57:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.7",
|
||||
"version": "9.2.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
|
||||
"reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
|
||||
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e",
|
||||
"reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.12.0",
|
||||
"nikic/php-parser": "^4.13.0",
|
||||
"php": ">=7.3",
|
||||
"phpunit/php-file-iterator": "^3.0.3",
|
||||
"phpunit/php-text-template": "^2.0.2",
|
||||
@ -2119,7 +2132,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2127,7 +2140,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-17T05:39:03+00:00"
|
||||
"time": "2021-10-30T08:01:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -3490,11 +3503,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"pocketmine/bedrock-protocol": 20,
|
||||
"pocketmine/classloader": 20,
|
||||
"pocketmine/spl": 20
|
||||
},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@ -3515,7 +3524,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-pthreads": "^4.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
|
@ -1,10 +1,7 @@
|
||||
includes:
|
||||
- tests/phpstan/configs/actual-problems.neon
|
||||
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/impossible-generics.neon
|
||||
- tests/phpstan/configs/l7-baseline.neon
|
||||
- tests/phpstan/configs/l8-baseline.neon
|
||||
- tests/phpstan/configs/php-bugs.neon
|
||||
- tests/phpstan/configs/phpstan-bugs.neon
|
||||
- tests/phpstan/configs/pthreads-bugs.neon
|
||||
@ -19,8 +16,7 @@ rules:
|
||||
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
|
||||
|
||||
parameters:
|
||||
level: 8
|
||||
checkExplicitMixed: true
|
||||
level: 9
|
||||
checkMissingCallableSignature: true
|
||||
treatPhpDocTypesAsCertain: false
|
||||
bootstrapFiles:
|
||||
|
Submodule resources/locale deleted from 09c709f242
Submodule resources/vanilla deleted from f29b7be8fa
@ -35,4 +35,6 @@ define('pocketmine\_CORE_CONSTANTS_INCLUDED', true);
|
||||
|
||||
define('pocketmine\PATH', dirname(__DIR__) . '/');
|
||||
define('pocketmine\RESOURCE_PATH', dirname(__DIR__) . '/resources/');
|
||||
define('pocketmine\BEDROCK_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/bedrock-data/');
|
||||
define('pocketmine\LOCALE_DATA_PATH', dirname(__DIR__) . '/vendor/pocketmine/locale-data/');
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');
|
||||
|
@ -116,8 +116,8 @@ namespace pocketmine {
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
if(version_compare($pthreads_version, "3.2.0") < 0){
|
||||
$messages[] = "pthreads >= 3.2.0 is required, while you have $pthreads_version.";
|
||||
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") > 0){
|
||||
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\console\ConsoleCommandSender;
|
||||
use pocketmine\console\ConsoleReaderThread;
|
||||
use pocketmine\crafting\CraftingManager;
|
||||
use pocketmine\crafting\CraftingManagerFromDataHelper;
|
||||
use pocketmine\crash\CrashDump;
|
||||
use pocketmine\data\java\GameModeIdMap;
|
||||
use pocketmine\entity\EntityDataHelper;
|
||||
use pocketmine\entity\Location;
|
||||
@ -64,6 +65,7 @@ use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\raklib\RakLibInterface;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\NetworkInterfaceStartException;
|
||||
use pocketmine\network\query\DedicatedQueryNetworkInterface;
|
||||
use pocketmine\network\query\QueryHandler;
|
||||
use pocketmine\network\query\QueryInfo;
|
||||
@ -81,6 +83,8 @@ use pocketmine\plugin\PluginGraylist;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\plugin\PluginOwned;
|
||||
use pocketmine\plugin\ScriptPluginLoader;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
@ -97,7 +101,6 @@ use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\NotCloneable;
|
||||
use pocketmine\utils\NotSerializable;
|
||||
use pocketmine\utils\Process;
|
||||
use pocketmine\utils\Promise;
|
||||
use pocketmine\utils\SignalHandler;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
@ -548,11 +551,11 @@ class Server{
|
||||
$playerPos = null;
|
||||
$spawn = $world->getSpawnLocation();
|
||||
}
|
||||
$playerPromise = new Promise();
|
||||
$playerPromiseResolver = new PromiseResolver();
|
||||
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
||||
function() use ($playerPromise, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
|
||||
function() use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
|
||||
if(!$session->isConnected()){
|
||||
$playerPromise->reject();
|
||||
$playerPromiseResolver->reject();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -572,16 +575,16 @@ class Server{
|
||||
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
|
||||
}
|
||||
$playerPromise->resolve($player);
|
||||
$playerPromiseResolver->resolve($player);
|
||||
},
|
||||
static function() use ($playerPromise, $session) : void{
|
||||
static function() use ($playerPromiseResolver, $session) : void{
|
||||
if($session->isConnected()){
|
||||
$session->disconnect("Spawn terrain generation failed");
|
||||
}
|
||||
$playerPromise->reject();
|
||||
$playerPromiseResolver->reject();
|
||||
}
|
||||
);
|
||||
return $playerPromise;
|
||||
return $playerPromiseResolver->getPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -737,7 +740,7 @@ class Server{
|
||||
|
||||
public function __construct(\DynamicClassLoader $autoloader, \AttachableThreadedLogger $logger, string $dataPath, string $pluginPath){
|
||||
if(self::$instance !== null){
|
||||
throw new \InvalidStateException("Only one server instance can exist at once");
|
||||
throw new \LogicException("Only one server instance can exist at once");
|
||||
}
|
||||
self::$instance = $this;
|
||||
$this->startTime = microtime(true);
|
||||
@ -930,7 +933,7 @@ class Server{
|
||||
|
||||
$this->commandMap = new SimpleCommandMap($this);
|
||||
|
||||
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\RESOURCE_PATH, "vanilla", "recipes.json"));
|
||||
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes.json"));
|
||||
|
||||
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
|
||||
|
||||
@ -979,6 +982,7 @@ class Server{
|
||||
$this->enablePlugins(PluginEnableOrder::POSTWORLD());
|
||||
|
||||
if(!$this->startupPrepareNetworkInterfaces()){
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1112,7 +1116,18 @@ class Server{
|
||||
|
||||
private function startupPrepareNetworkInterfaces() : bool{
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
if(!$this->network->registerInterface(new RakLibInterface($this)) && $useQuery){
|
||||
|
||||
try{
|
||||
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this));
|
||||
}catch(NetworkInterfaceStartException $e){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
|
||||
$this->getIp(),
|
||||
(string) $this->getPort(),
|
||||
$e->getMessage()
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
if(!$rakLibRegistered && $useQuery){
|
||||
//RakLib would normally handle the transport for Query packets
|
||||
//if it's not registered we need to make sure Query still works
|
||||
$this->network->registerInterface(new DedicatedQueryNetworkInterface($this->getIp(), $this->getPort(), new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
|
||||
@ -1490,8 +1505,8 @@ class Server{
|
||||
}
|
||||
@touch($stamp); //update file timestamp
|
||||
|
||||
$plugin = $dump->getData()["plugin"];
|
||||
if(is_string($plugin)){
|
||||
$plugin = $dump->getData()->plugin;
|
||||
if($plugin !== ""){
|
||||
$p = $this->pluginManager->getPlugin($plugin);
|
||||
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
|
||||
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
|
||||
@ -1499,7 +1514,7 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
if($dump->getData()["error"]["type"] === \ParseError::class){
|
||||
if($dump->getData()->error["type"] === \ParseError::class){
|
||||
$report = false;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.0.0-BETA8";
|
||||
public const BASE_VERSION = "4.0.0-BETA11";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_NUMBER = 0;
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
|
@ -493,7 +493,7 @@ class Block{
|
||||
return $this->position->getWorld()->getBlock($this->position->getSide($side, $step));
|
||||
}
|
||||
|
||||
throw new \InvalidStateException("Block does not have a valid world");
|
||||
throw new \LogicException("Block does not have a valid world");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,8 +94,7 @@ class Cake extends Transparent implements FoodSource{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
$player->consumeObject($this);
|
||||
return true;
|
||||
return $player->consumeObject($this);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\TreeType;
|
||||
use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
@ -94,10 +95,7 @@ class CocoaBlock extends Transparent{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->age < 2 and $item instanceof Fertilizer){
|
||||
$this->age++;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
|
||||
if($item instanceof Fertilizer && $this->grow()){
|
||||
$item->pop();
|
||||
|
||||
return true;
|
||||
@ -117,12 +115,25 @@ class CocoaBlock extends Transparent{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->age < 2 and mt_rand(1, 5) === 1){
|
||||
$this->age++;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
if(mt_rand(1, 5) === 1){
|
||||
$this->grow();
|
||||
}
|
||||
}
|
||||
|
||||
private function grow() : bool{
|
||||
if($this->age < 2){
|
||||
$block = clone $this;
|
||||
$block->age++;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::COCOA_BEANS()->setCount($this->age === 2 ? mt_rand(2, 3) : 1)
|
||||
|
@ -80,10 +80,9 @@ abstract class Crops extends Flowable{
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
$item->pop();
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -76,9 +76,7 @@ class Sapling extends Flowable{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$this->grow($player);
|
||||
|
||||
if($item instanceof Fertilizer && $this->grow($player)){
|
||||
$item->pop();
|
||||
|
||||
return true;
|
||||
@ -108,19 +106,20 @@ class Sapling extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : void{
|
||||
private function grow(?Player $player) : bool{
|
||||
$random = new Random(mt_rand());
|
||||
$tree = TreeFactory::get($random, $this->treeType);
|
||||
$transaction = $tree?->getBlockTransaction($this->position->getWorld(), $this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), $random);
|
||||
if($transaction === null){
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$ev = new StructureGrowEvent($this, $transaction, $player);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$transaction->apply();
|
||||
return $transaction->apply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
|
@ -48,7 +48,8 @@ class Sugarcane extends Flowable{
|
||||
return 0b1111;
|
||||
}
|
||||
|
||||
private function grow() : void{
|
||||
private function grow() : bool{
|
||||
$grew = false;
|
||||
for($y = 1; $y < 3; ++$y){
|
||||
if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){
|
||||
break;
|
||||
@ -61,12 +62,14 @@ class Sugarcane extends Flowable{
|
||||
break;
|
||||
}
|
||||
$this->position->getWorld()->setBlock($b->position, $ev->getNewState());
|
||||
$grew = true;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->age = 0;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
return $grew;
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
@ -82,12 +85,10 @@ class Sugarcane extends Flowable{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
|
||||
$this->grow();
|
||||
if(!$this->getSide(Facing::DOWN)->isSameType($this) && $this->grow()){
|
||||
$item->pop();
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -99,9 +99,9 @@ class SweetBerryBush extends Flowable{
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
|
||||
$item->pop();
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
|
||||
$this->position->getWorld()->dropItem($this->position, $this->asItem()->setCount($dropAmount));
|
||||
|
@ -114,7 +114,9 @@ final class ConsoleReaderThread extends Thread{
|
||||
break;
|
||||
}
|
||||
|
||||
$buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command));
|
||||
$command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
|
||||
$command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
|
||||
$buffer[] = $command;
|
||||
if($notifier !== null){
|
||||
$notifier->wakeupSleeper();
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ class CraftingGrid extends SimpleInventory{
|
||||
return $this->getItem(($y + $this->startY) * $this->gridWidth + ($x + $this->startX));
|
||||
}
|
||||
|
||||
throw new \InvalidStateException("No ingredients found in grid");
|
||||
throw new \LogicException("No ingredients found in grid");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,16 +21,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
namespace pocketmine\crash;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\errorhandler\ErrorTypeToStringMap;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\VersionInfo;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function base64_encode;
|
||||
use function date;
|
||||
@ -69,6 +71,7 @@ use const FILE_IGNORE_NEW_LINES;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const PHP_EOL;
|
||||
use const PHP_OS;
|
||||
use const PHP_VERSION;
|
||||
use const SORT_STRING;
|
||||
|
||||
class CrashDump{
|
||||
@ -91,13 +94,9 @@ class CrashDump{
|
||||
private $fp;
|
||||
/** @var float */
|
||||
private $time;
|
||||
/**
|
||||
* @var mixed[]
|
||||
* @phpstan-var array<string, mixed>
|
||||
*/
|
||||
private $data = [];
|
||||
private CrashDumpData $data;
|
||||
/** @var string */
|
||||
private $encodedData = "";
|
||||
private $encodedData;
|
||||
/** @var string */
|
||||
private $path;
|
||||
|
||||
@ -118,9 +117,10 @@ class CrashDump{
|
||||
throw new \RuntimeException("Could not create Crash Dump");
|
||||
}
|
||||
$this->fp = $fp;
|
||||
$this->data["format_version"] = self::FORMAT_VERSION;
|
||||
$this->data["time"] = $this->time;
|
||||
$this->data["uptime"] = $this->time - $this->server->getStartTime();
|
||||
$this->data = new CrashDumpData();
|
||||
$this->data->format_version = self::FORMAT_VERSION;
|
||||
$this->data->time = $this->time;
|
||||
$this->data->uptime = $this->time - $this->server->getStartTime();
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->time));
|
||||
$this->addLine();
|
||||
$this->baseCrash();
|
||||
@ -142,11 +142,7 @@ class CrashDump{
|
||||
return $this->encodedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @phpstan-return array<string, mixed>
|
||||
*/
|
||||
public function getData() : array{
|
||||
public function getData() : CrashDumpData{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
@ -173,22 +169,21 @@ class CrashDump{
|
||||
$plugins = $this->pluginManager->getPlugins();
|
||||
$this->addLine();
|
||||
$this->addLine("Loaded plugins:");
|
||||
$this->data["plugins"] = [];
|
||||
ksort($plugins, SORT_STRING);
|
||||
foreach($plugins as $p){
|
||||
$d = $p->getDescription();
|
||||
$this->data["plugins"][$d->getName()] = [
|
||||
"name" => $d->getName(),
|
||||
"version" => $d->getVersion(),
|
||||
"authors" => $d->getAuthors(),
|
||||
"api" => $d->getCompatibleApis(),
|
||||
"enabled" => $p->isEnabled(),
|
||||
"depends" => $d->getDepend(),
|
||||
"softDepends" => $d->getSoftDepend(),
|
||||
"main" => $d->getMain(),
|
||||
"load" => mb_strtoupper($d->getOrder()->name()),
|
||||
"website" => $d->getWebsite()
|
||||
];
|
||||
$this->data->plugins[$d->getName()] = new CrashDumpDataPluginEntry(
|
||||
name: $d->getName(),
|
||||
version: $d->getVersion(),
|
||||
authors: $d->getAuthors(),
|
||||
api: $d->getCompatibleApis(),
|
||||
enabled: $p->isEnabled(),
|
||||
depends: $d->getDepend(),
|
||||
softDepends: $d->getSoftDepend(),
|
||||
main: $d->getMain(),
|
||||
load: mb_strtoupper($d->getOrder()->name()),
|
||||
website: $d->getWebsite()
|
||||
);
|
||||
$this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis()));
|
||||
}
|
||||
}
|
||||
@ -198,32 +193,26 @@ class CrashDump{
|
||||
global $argv;
|
||||
|
||||
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){
|
||||
$this->data["parameters"] = (array) $argv;
|
||||
$this->data->parameters = (array) $argv;
|
||||
if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){
|
||||
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties);
|
||||
}else{
|
||||
$this->data["server.properties"] = $serverDotProperties;
|
||||
$this->data->serverDotProperties = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties) ?? throw new AssumptionFailedError("Pattern is valid");
|
||||
}
|
||||
if(($pocketmineDotYml = @file_get_contents(Path::join($this->server->getDataPath(), "pocketmine.yml"))) !== false){
|
||||
$this->data["pocketmine.yml"] = $pocketmineDotYml;
|
||||
}else{
|
||||
$this->data["pocketmine.yml"] = "";
|
||||
$this->data->pocketmineDotYml = $pocketmineDotYml;
|
||||
}
|
||||
}else{
|
||||
$this->data["pocketmine.yml"] = "";
|
||||
$this->data["server.properties"] = "";
|
||||
$this->data["parameters"] = [];
|
||||
}
|
||||
$extensions = [];
|
||||
foreach(get_loaded_extensions() as $ext){
|
||||
$extensions[$ext] = phpversion($ext);
|
||||
$version = phpversion($ext);
|
||||
if($version === false) throw new AssumptionFailedError();
|
||||
$extensions[$ext] = $version;
|
||||
}
|
||||
$this->data["extensions"] = $extensions;
|
||||
$this->data->extensions = $extensions;
|
||||
|
||||
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){
|
||||
ob_start();
|
||||
phpinfo();
|
||||
$this->data["phpinfo"] = ob_get_contents();
|
||||
$this->data->phpinfo = ob_get_contents(); // @phpstan-ignore-line
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
@ -255,18 +244,18 @@ class CrashDump{
|
||||
if(isset($lastError["trace"])){
|
||||
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
|
||||
}
|
||||
$this->data["lastError"] = $lastError;
|
||||
$this->data->lastError = $lastError;
|
||||
}
|
||||
|
||||
$this->data["error"] = $error;
|
||||
unset($this->data["error"]["fullFile"]);
|
||||
unset($this->data["error"]["trace"]);
|
||||
$this->data->error = $error;
|
||||
unset($this->data->error["fullFile"]);
|
||||
unset($this->data->error["trace"]);
|
||||
$this->addLine("Error: " . $error["message"]);
|
||||
$this->addLine("File: " . $error["file"]);
|
||||
$this->addLine("Line: " . $error["line"]);
|
||||
$this->addLine("Type: " . $error["type"]);
|
||||
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_NONE;
|
||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
|
||||
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
|
||||
foreach($error["trace"] as $frame){
|
||||
if(!isset($frame["file"])){
|
||||
@ -280,21 +269,20 @@ class CrashDump{
|
||||
|
||||
$this->addLine();
|
||||
$this->addLine("Code:");
|
||||
$this->data["code"] = [];
|
||||
|
||||
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) and file_exists($error["fullFile"])){
|
||||
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
|
||||
if($file !== false){
|
||||
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
|
||||
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
|
||||
$this->data["code"][$l + 1] = $file[$l];
|
||||
$this->data->code[$l + 1] = $file[$l];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addLine();
|
||||
$this->addLine("Backtrace:");
|
||||
foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){
|
||||
foreach(($this->data->trace = Utils::printableTrace($error["trace"])) as $line){
|
||||
$this->addLine($line);
|
||||
}
|
||||
$this->addLine();
|
||||
@ -306,10 +294,10 @@ class CrashDump{
|
||||
$this->addLine();
|
||||
if($crashFrame){
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT;
|
||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT;
|
||||
}else{
|
||||
$this->addLine("A PLUGIN WAS INVOLVED IN THIS CRASH");
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
|
||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_INDIRECT;
|
||||
}
|
||||
|
||||
if(file_exists($filePath)){
|
||||
@ -319,7 +307,7 @@ class CrashDump{
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Filesystem::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->data->plugin = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
}
|
||||
@ -341,25 +329,26 @@ class CrashDump{
|
||||
);
|
||||
}
|
||||
|
||||
$this->data["general"] = [];
|
||||
$this->data["general"]["name"] = $this->server->getName();
|
||||
$this->data["general"]["base_version"] = VersionInfo::BASE_VERSION;
|
||||
$this->data["general"]["build"] = VersionInfo::BUILD_NUMBER;
|
||||
$this->data["general"]["is_dev"] = VersionInfo::IS_DEVELOPMENT_BUILD;
|
||||
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
|
||||
$this->data["general"]["git"] = VersionInfo::GIT_HASH();
|
||||
$this->data["general"]["uname"] = php_uname("a");
|
||||
$this->data["general"]["php"] = phpversion();
|
||||
$this->data["general"]["zend"] = zend_version();
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->data["general"]["composer_libraries"] = $composerLibraries;
|
||||
$this->data->general = new CrashDumpDataGeneral(
|
||||
name: $this->server->getName(),
|
||||
base_version: VersionInfo::BASE_VERSION,
|
||||
build: VersionInfo::BUILD_NUMBER,
|
||||
is_dev: VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
protocol: ProtocolInfo::CURRENT_PROTOCOL,
|
||||
git: VersionInfo::GIT_HASH(),
|
||||
uname: php_uname("a"),
|
||||
php: PHP_VERSION,
|
||||
zend: zend_version(),
|
||||
php_os: PHP_OS,
|
||||
os: Utils::getOS(),
|
||||
composer_libraries: $composerLibraries,
|
||||
);
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . VersionInfo::GIT_HASH());
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
$this->addLine("PHP Version: " . phpversion());
|
||||
$this->addLine("Zend version: " . zend_version());
|
||||
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
|
||||
$this->addLine("OS: " . PHP_OS . ", " . Utils::getOS());
|
||||
$this->addLine("Composer libraries: ");
|
||||
foreach($composerLibraries as $library => $libraryVersion){
|
||||
$this->addLine("- $library $libraryVersion");
|
84
src/crash/CrashDumpData.php
Normal file
84
src/crash/CrashDumpData.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?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\crash;
|
||||
|
||||
final class CrashDumpData implements \JsonSerializable{
|
||||
|
||||
public int $format_version;
|
||||
|
||||
public float $time;
|
||||
|
||||
public float $uptime;
|
||||
|
||||
/** @var mixed[] */
|
||||
public array $lastError = [];
|
||||
|
||||
/** @var mixed[] */
|
||||
public array $error;
|
||||
|
||||
public string $plugin_involvement;
|
||||
|
||||
public string $plugin = "";
|
||||
|
||||
/** @var string[] */
|
||||
public array $code = [];
|
||||
|
||||
/** @var string[] */
|
||||
public array $trace;
|
||||
|
||||
/**
|
||||
* @var CrashDumpDataPluginEntry[]
|
||||
* @phpstan-var array<string, CrashDumpDataPluginEntry>
|
||||
*/
|
||||
public array $plugins = [];
|
||||
|
||||
/** @var string[] */
|
||||
public array $parameters = [];
|
||||
|
||||
public string $serverDotProperties = "";
|
||||
|
||||
public string $pocketmineDotYml = "";
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array<string, string>
|
||||
*/
|
||||
public array $extensions = [];
|
||||
|
||||
public string $phpinfo = "";
|
||||
|
||||
public CrashDumpDataGeneral $general;
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function jsonSerialize() : array{
|
||||
$result = (array) $this;
|
||||
unset($result["serverDotProperties"]);
|
||||
unset($result["pocketmineDotYml"]);
|
||||
$result["pocketmine.yml"] = $this->pocketmineDotYml;
|
||||
$result["server.properties"] = $this->serverDotProperties;
|
||||
return $result;
|
||||
}
|
||||
}
|
46
src/crash/CrashDumpDataGeneral.php
Normal file
46
src/crash/CrashDumpDataGeneral.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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\crash;
|
||||
|
||||
final class CrashDumpDataGeneral{
|
||||
|
||||
/**
|
||||
* @param string[] $composer_libraries
|
||||
* @phpstan-param array<string, string> $composer_libraries
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public string $base_version,
|
||||
public int $build,
|
||||
public bool $is_dev,
|
||||
public int $protocol,
|
||||
public string $git,
|
||||
public string $uname,
|
||||
public string $php,
|
||||
public string $zend,
|
||||
public string $php_os,
|
||||
public string $os,
|
||||
public array $composer_libraries,
|
||||
){}
|
||||
}
|
45
src/crash/CrashDumpDataPluginEntry.php
Normal file
45
src/crash/CrashDumpDataPluginEntry.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?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\crash;
|
||||
|
||||
final class CrashDumpDataPluginEntry{
|
||||
/**
|
||||
* @param string[] $authors
|
||||
* @param string[] $api
|
||||
* @param string[] $depends
|
||||
* @param string[] $softDepends
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public string $version,
|
||||
public array $authors,
|
||||
public array $api,
|
||||
public bool $enabled,
|
||||
public array $depends,
|
||||
public array $softDepends,
|
||||
public string $main,
|
||||
public string $load,
|
||||
public string $website,
|
||||
){}
|
||||
}
|
@ -30,6 +30,6 @@ final class LegacyBlockIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'block_id_map.json'));
|
||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'block_id_map.json'));
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ final class LegacyEntityIdToStringIdMap extends LegacyToStringBidirectionalIdMap
|
||||
use SingletonTrait;
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'entity_id_map.json'));
|
||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'entity_id_map.json'));
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ final class LegacyItemIdToStringIdMap extends LegacyToStringBidirectionalIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'item_id_map.json'));
|
||||
parent::__construct(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'item_id_map.json'));
|
||||
}
|
||||
}
|
||||
|
@ -980,7 +980,7 @@ abstract class Entity{
|
||||
|
||||
final public function scheduleUpdate() : void{
|
||||
if($this->closed){
|
||||
throw new \InvalidStateException("Cannot schedule update on garbage entity " . get_class($this));
|
||||
throw new \LogicException("Cannot schedule update on garbage entity " . get_class($this));
|
||||
}
|
||||
$this->getWorld()->updateEntities[$this->id] = $this;
|
||||
}
|
||||
@ -1436,14 +1436,11 @@ abstract class Entity{
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player) : void{
|
||||
if($player->getWorld() !== $this->getWorld()){
|
||||
throw new \InvalidArgumentException("Player is not in the same world");
|
||||
}
|
||||
$id = spl_object_id($player);
|
||||
//TODO: this will cause some visible lag during chunk resends; if the player uses a spawn egg in a chunk, the
|
||||
//created entity won't be visible until after the resend arrives. However, this is better than possibly crashing
|
||||
//the player by sending them entities too early.
|
||||
if(!isset($this->hasSpawned[$id]) and $player->hasReceivedChunk($this->location->getFloorX() >> Chunk::COORD_BIT_SIZE, $this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
|
||||
if(!isset($this->hasSpawned[$id]) and $player->getWorld() === $this->getWorld() and $player->hasReceivedChunk($this->location->getFloorX() >> Chunk::COORD_BIT_SIZE, $this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
|
||||
$this->hasSpawned[$id] = $player;
|
||||
|
||||
$this->sendSpawnPacket($player);
|
||||
|
@ -59,10 +59,10 @@ final class EntityDataHelper{
|
||||
if($pos === null and $optional){
|
||||
return new Vector3(0, 0, 0);
|
||||
}
|
||||
if(!($pos instanceof ListTag) or $pos->getTagType() !== NBT::TAG_Double){
|
||||
throw new \UnexpectedValueException("'$tagName' should be a List<Double>");
|
||||
if(!($pos instanceof ListTag) or ($pos->getTagType() !== NBT::TAG_Double && $pos->getTagType() !== NBT::TAG_Float)){
|
||||
throw new \UnexpectedValueException("'$tagName' should be a List<Double> or List<Float>");
|
||||
}
|
||||
/** @var DoubleTag[] $values */
|
||||
/** @var DoubleTag[]|FloatTag[] $values */
|
||||
$values = $pos->getValue();
|
||||
if(count($values) !== 3){
|
||||
throw new \UnexpectedValueException("Expected exactly 3 entries in '$tagName' tag");
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\entity\utils\ExperienceUtils;
|
||||
use pocketmine\event\player\PlayerExperienceChangeEvent;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\world\sound\XpCollectSound;
|
||||
use pocketmine\world\sound\XpLevelUpSound;
|
||||
use function array_rand;
|
||||
@ -151,6 +152,7 @@ class ExperienceManager{
|
||||
* @param bool $playSound Whether to play level-up and XP gained sounds.
|
||||
*/
|
||||
public function addXp(int $amount, bool $playSound = true) : bool{
|
||||
$amount = min($amount, Limits::INT32_MAX - $this->totalXp);
|
||||
$oldLevel = $this->getXpLevel();
|
||||
$oldTotal = $this->getCurrentTotalXp();
|
||||
|
||||
@ -223,8 +225,8 @@ class ExperienceManager{
|
||||
* score when they die. (TODO: add this when MCPE supports it)
|
||||
*/
|
||||
public function setLifetimeTotalXp(int $amount) : void{
|
||||
if($amount < 0){
|
||||
throw new \InvalidArgumentException("XP must be greater than 0");
|
||||
if($amount < 0 || $amount > Limits::INT32_MAX){
|
||||
throw new \InvalidArgumentException("XP must be greater than 0 and less than " . Limits::INT32_MAX);
|
||||
}
|
||||
|
||||
$this->totalXp = $amount;
|
||||
|
@ -175,6 +175,7 @@ class Arrow extends Projectile{
|
||||
|
||||
$item = VanillaItems::ARROW();
|
||||
$playerInventory = match(true){
|
||||
!$player->hasFiniteResources() => null, //arrows are not picked up in creative
|
||||
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->canAddItem($item) => $player->getOffHandInventory(),
|
||||
$player->getInventory()->canAddItem($item) => $player->getInventory(),
|
||||
default => null
|
||||
|
@ -77,32 +77,28 @@ abstract class Projectile extends Entity{
|
||||
$this->setHealth(1);
|
||||
$this->damage = $nbt->getDouble("damage", $this->damage);
|
||||
|
||||
do{
|
||||
$blockPos = null;
|
||||
$blockId = null;
|
||||
$blockData = null;
|
||||
|
||||
(function() use ($nbt) : void{
|
||||
if(($tileXTag = $nbt->getTag("tileX")) instanceof IntTag and ($tileYTag = $nbt->getTag("tileY")) instanceof IntTag and ($tileZTag = $nbt->getTag("tileZ")) instanceof IntTag){
|
||||
$blockPos = new Vector3($tileXTag->getValue(), $tileYTag->getValue(), $tileZTag->getValue());
|
||||
}else{
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if(($blockIdTag = $nbt->getTag("blockId")) instanceof IntTag){
|
||||
$blockId = $blockIdTag->getValue();
|
||||
}else{
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if(($blockDataTag = $nbt->getTag("blockData")) instanceof ByteTag){
|
||||
$blockData = $blockDataTag->getValue();
|
||||
}else{
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->blockHit = BlockFactory::getInstance()->get($blockId, $blockData);
|
||||
$this->blockHit->position($this->getWorld(), $blockPos->getFloorX(), $blockPos->getFloorY(), $blockPos->getFloorZ());
|
||||
}while(false);
|
||||
})();
|
||||
}
|
||||
|
||||
public function canCollideWith(Entity $entity) : bool{
|
||||
|
@ -47,7 +47,7 @@ class HandlerList{
|
||||
*/
|
||||
public function register(RegisteredListener $listener) : void{
|
||||
if(isset($this->handlerSlots[$listener->getPriority()][spl_object_id($listener)])){
|
||||
throw new \InvalidStateException("This listener is already registered to priority {$listener->getPriority()} of event {$this->class}");
|
||||
throw new \InvalidArgumentException("This listener is already registered to priority {$listener->getPriority()} of event {$this->class}");
|
||||
}
|
||||
$this->handlerSlots[$listener->getPriority()][spl_object_id($listener)] = $listener;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace pocketmine\event\entity;
|
||||
class EntityEffectRemoveEvent extends EntityEffectEvent{
|
||||
public function cancel() : void{
|
||||
if($this->getEffect()->getDuration() <= 0){
|
||||
throw new \InvalidStateException("Removal of expired effects cannot be cancelled");
|
||||
throw new \LogicException("Removal of expired effects cannot be cancelled");
|
||||
}
|
||||
parent::cancel();
|
||||
}
|
||||
|
51
src/event/player/PlayerEmoteEvent.php
Normal file
51
src/event/player/PlayerEmoteEvent.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Called when a player uses an emote.
|
||||
*/
|
||||
class PlayerEmoteEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
Player $player,
|
||||
private string $emoteId
|
||||
){
|
||||
$this->player = $player;
|
||||
}
|
||||
|
||||
public function getEmoteId() : string{
|
||||
return $this->emoteId;
|
||||
}
|
||||
|
||||
public function setEmoteId(string $emoteId) : void{
|
||||
$this->emoteId = $emoteId;
|
||||
}
|
||||
|
||||
}
|
@ -37,7 +37,7 @@ final class CreativeInventory{
|
||||
private $creative = [];
|
||||
|
||||
private function __construct(){
|
||||
$creativeItems = json_decode(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "vanilla", "creativeitems.json")), true);
|
||||
$creativeItems = json_decode(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "creativeitems.json")), true);
|
||||
|
||||
foreach($creativeItems as $data){
|
||||
$item = Item::jsonDeserialize($data);
|
||||
|
@ -123,4 +123,8 @@ class Bow extends Tool implements Releasable{
|
||||
|
||||
return ItemUseResult::SUCCESS();
|
||||
}
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool{
|
||||
return !$player->hasFiniteResources() || $player->getOffHandInventory()->contains($arrow = VanillaItems::ARROW()) || $player->getInventory()->contains($arrow);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
abstract class Food extends Item implements FoodSourceItem{
|
||||
public function requiresHunger() : bool{
|
||||
@ -41,4 +42,8 @@ abstract class Food extends Item implements FoodSourceItem{
|
||||
public function onConsume(Living $consumer) : void{
|
||||
|
||||
}
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool{
|
||||
return !$this->requiresHunger() || $player->getHungerManager()->isHungry();
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class MilkBucket extends Item implements ConsumableItem{
|
||||
|
||||
@ -42,4 +43,8 @@ class MilkBucket extends Item implements ConsumableItem{
|
||||
public function onConsume(Living $consumer) : void{
|
||||
$consumer->getEffects()->clear();
|
||||
}
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class Potion extends Item implements ConsumableItem{
|
||||
|
||||
@ -52,4 +53,8 @@ class Potion extends Item implements ConsumableItem{
|
||||
public function getResidue() : Item{
|
||||
return VanillaItems::GLASS_BOTTLE();
|
||||
}
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Interface implemented by objects that can be used.
|
||||
*/
|
||||
interface Releasable{
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool;
|
||||
|
||||
}
|
||||
|
@ -1950,6 +1950,14 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_server_networkStartFailed(Translatable|string $ipAddress, Translatable|string $port, Translatable|string $errorMessage) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_NETWORKSTARTFAILED, [
|
||||
"ipAddress" => $ipAddress,
|
||||
"port" => $port,
|
||||
"errorMessage" => $errorMessage,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_server_query_running(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_QUERY_RUNNING, [
|
||||
0 => $param0,
|
||||
|
@ -401,6 +401,7 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_SERVER_INFO_EXTENDED = "pocketmine.server.info.extended";
|
||||
public const POCKETMINE_SERVER_LICENSE = "pocketmine.server.license";
|
||||
public const POCKETMINE_SERVER_NETWORKSTART = "pocketmine.server.networkStart";
|
||||
public const POCKETMINE_SERVER_NETWORKSTARTFAILED = "pocketmine.server.networkStartFailed";
|
||||
public const POCKETMINE_SERVER_QUERY_RUNNING = "pocketmine.server.query.running";
|
||||
public const POCKETMINE_SERVER_START = "pocketmine.server.start";
|
||||
public const POCKETMINE_SERVER_STARTFINISHED = "pocketmine.server.startFinished";
|
||||
|
@ -52,7 +52,7 @@ class Language{
|
||||
*/
|
||||
public static function getLanguageList(string $path = "") : array{
|
||||
if($path === ""){
|
||||
$path = Path::join(\pocketmine\RESOURCE_PATH, "locale");
|
||||
$path = \pocketmine\LOCALE_DATA_PATH;
|
||||
}
|
||||
|
||||
if(is_dir($path)){
|
||||
@ -101,7 +101,7 @@ class Language{
|
||||
$this->langName = strtolower($lang);
|
||||
|
||||
if($path === null){
|
||||
$path = Path::join(\pocketmine\RESOURCE_PATH, "locale");
|
||||
$path = \pocketmine\LOCALE_DATA_PATH;
|
||||
}
|
||||
|
||||
$this->lang = self::loadLang($path, $this->langName);
|
||||
|
@ -91,6 +91,9 @@ class Network{
|
||||
$this->sessionManager->tick();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NetworkInterfaceStartException
|
||||
*/
|
||||
public function registerInterface(NetworkInterface $interface) : bool{
|
||||
$ev = new NetworkInterfaceRegisterEvent($interface);
|
||||
$ev->call();
|
||||
|
@ -33,6 +33,7 @@ interface NetworkInterface{
|
||||
|
||||
/**
|
||||
* Performs actions needed to start the interface after it is registered.
|
||||
* @throws NetworkInterfaceStartException
|
||||
*/
|
||||
public function start() : void;
|
||||
|
||||
|
32
src/network/NetworkInterfaceStartException.php
Normal file
32
src/network/NetworkInterfaceStartException.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\network;
|
||||
|
||||
/**
|
||||
* Thrown when a network interface fails to start for some reason.
|
||||
* This should be used when, for example, your network interface can't bind to the port it wants.
|
||||
*/
|
||||
final class NetworkInterfaceStartException extends \RuntimeException{
|
||||
|
||||
}
|
@ -72,7 +72,7 @@ class ChunkRequestTask extends AsyncTask{
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk);
|
||||
$encoderContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, RuntimeBlockMapping::getInstance(), $encoderContext, $this->tiles);
|
||||
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::withoutCache($this->chunkX, $this->chunkZ, $subCount, $payload))->getBuffer()));
|
||||
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create($this->chunkX, $this->chunkZ, $subCount, null, $payload))->getBuffer()));
|
||||
}
|
||||
|
||||
public function onError() : void{
|
||||
|
@ -151,7 +151,7 @@ class InventoryManager{
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new \UnsupportedOperationException("Unsupported inventory type");
|
||||
throw new \LogicException("Unsupported inventory type");
|
||||
}
|
||||
|
||||
/** @phpstan-return ObjectSet<ContainerOpenClosure> */
|
||||
|
@ -62,6 +62,7 @@ use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmotePacket;
|
||||
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
@ -348,6 +349,10 @@ class NetworkSession{
|
||||
|
||||
try{
|
||||
foreach($stream->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){
|
||||
if($packet === null){
|
||||
$this->logger->debug("Unknown packet: " . base64_encode($buffer));
|
||||
throw new PacketHandlingException("Unknown packet received");
|
||||
}
|
||||
try{
|
||||
$this->handleDataPacket($packet, $buffer);
|
||||
}catch(PacketHandlingException $e){
|
||||
@ -532,8 +537,6 @@ class NetworkSession{
|
||||
|
||||
/**
|
||||
* Instructs the remote client to connect to a different server.
|
||||
*
|
||||
* @throws \UnsupportedOperationException
|
||||
*/
|
||||
public function transfer(string $ip, int $port, string $reason = "transfer") : void{
|
||||
$this->tryDisconnect(function() use ($ip, $port, $reason) : void{
|
||||
@ -559,7 +562,7 @@ class NetworkSession{
|
||||
*/
|
||||
private function doServerDisconnect(string $reason, bool $notify = true) : void{
|
||||
if($notify){
|
||||
$this->sendDataPacket($reason === "" ? DisconnectPacket::silent() : DisconnectPacket::message($reason), true);
|
||||
$this->sendDataPacket(DisconnectPacket::create($reason !== "" ? $reason : null), true);
|
||||
}
|
||||
|
||||
$this->sender->close($notify ? $reason : "");
|
||||
@ -924,7 +927,7 @@ class NetworkSession{
|
||||
$this->logger->debug("Tried to send no-longer-active chunk $chunkX $chunkZ in world " . $world->getFolderName());
|
||||
return;
|
||||
}
|
||||
if(!$status->equals(UsedChunkStatus::REQUESTED())){
|
||||
if(!$status->equals(UsedChunkStatus::REQUESTED_SENDING())){
|
||||
//TODO: make this an error
|
||||
//this could be triggered due to the shitty way that chunk resends are handled
|
||||
//right now - not because of the spammy re-requesting, but because the chunk status reverts
|
||||
@ -1041,6 +1044,10 @@ class NetworkSession{
|
||||
$this->sendDataPacket(SetTitlePacket::setAnimationTimes($fadeIn, $stay, $fadeOut));
|
||||
}
|
||||
|
||||
public function onEmote(Player $from, string $emoteId) : void{
|
||||
$this->sendDataPacket(EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER));
|
||||
}
|
||||
|
||||
public function tick() : void{
|
||||
if($this->info === null){
|
||||
if(time() >= $this->connectTime + 10){
|
||||
|
4
src/network/mcpe/cache/StaticPacketCache.php
vendored
4
src/network/mcpe/cache/StaticPacketCache.php
vendored
@ -50,8 +50,8 @@ class StaticPacketCache{
|
||||
|
||||
private static function make() : self{
|
||||
return new self(
|
||||
BiomeDefinitionListPacket::create(self::loadCompoundFromFile(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'biome_definitions.nbt'))),
|
||||
AvailableActorIdentifiersPacket::create(self::loadCompoundFromFile(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'entity_identifiers.nbt')))
|
||||
BiomeDefinitionListPacket::create(self::loadCompoundFromFile(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'biome_definitions.nbt'))),
|
||||
AvailableActorIdentifiersPacket::create(self::loadCompoundFromFile(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'entity_identifiers.nbt')))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ class CompressBatchPromise{
|
||||
public function resolve(string $result) : void{
|
||||
if(!$this->cancelled){
|
||||
if($this->result !== null){
|
||||
throw new \InvalidStateException("Cannot resolve promise more than once");
|
||||
throw new \LogicException("Cannot resolve promise more than once");
|
||||
}
|
||||
$this->result = $result;
|
||||
foreach($this->callbacks as $callback){
|
||||
@ -80,7 +80,7 @@ class CompressBatchPromise{
|
||||
public function getResult() : string{
|
||||
$this->checkCancelled();
|
||||
if($this->result === null){
|
||||
throw new \InvalidStateException("Promise has not yet been resolved");
|
||||
throw new \LogicException("Promise has not yet been resolved");
|
||||
}
|
||||
return $this->result;
|
||||
}
|
||||
@ -95,7 +95,7 @@ class CompressBatchPromise{
|
||||
|
||||
public function cancel() : void{
|
||||
if($this->hasResult()){
|
||||
throw new \InvalidStateException("Cannot cancel a resolved promise");
|
||||
throw new \LogicException("Cannot cancel a resolved promise");
|
||||
}
|
||||
$this->cancelled = true;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ final class GlobalItemTypeDictionary{
|
||||
use SingletonTrait;
|
||||
|
||||
private static function make() : self{
|
||||
$data = file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'required_item_list.json'));
|
||||
$data = file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'required_item_list.json'));
|
||||
if($data === false) throw new AssumptionFailedError("Missing required resource file");
|
||||
$table = json_decode($data, true);
|
||||
if(!is_array($table)){
|
||||
|
@ -66,14 +66,14 @@ final class ItemTranslator{
|
||||
private $complexNetToCoreMapping = [];
|
||||
|
||||
private static function make() : self{
|
||||
$data = file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'r16_to_current_item_map.json'));
|
||||
$data = file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'r16_to_current_item_map.json'));
|
||||
if($data === false) throw new AssumptionFailedError("Missing required resource file");
|
||||
$json = json_decode($data, true);
|
||||
if(!is_array($json) or !isset($json["simple"], $json["complex"]) || !is_array($json["simple"]) || !is_array($json["complex"])){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
|
||||
$legacyStringToIntMapRaw = file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, 'vanilla', 'item_id_map.json'));
|
||||
$legacyStringToIntMapRaw = file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'item_id_map.json'));
|
||||
if($legacyStringToIntMapRaw === false){
|
||||
throw new AssumptionFailedError("Missing required resource file");
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ final class RuntimeBlockMapping{
|
||||
private $bedrockKnownStates;
|
||||
|
||||
private function __construct(){
|
||||
$canonicalBlockStatesFile = file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "vanilla", "canonical_block_states.nbt"));
|
||||
$canonicalBlockStatesFile = file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "canonical_block_states.nbt"));
|
||||
if($canonicalBlockStatesFile === false){
|
||||
throw new AssumptionFailedError("Missing required resource file");
|
||||
}
|
||||
@ -67,7 +67,7 @@ final class RuntimeBlockMapping{
|
||||
$legacyIdMap = LegacyBlockIdToStringIdMap::getInstance();
|
||||
/** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */
|
||||
$legacyStateMap = [];
|
||||
$legacyStateMapReader = PacketSerializer::decoder(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "vanilla", "r12_to_current_block_map.bin")), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()));
|
||||
$legacyStateMapReader = PacketSerializer::decoder(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "r12_to_current_block_map.bin")), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()));
|
||||
$nbtReader = new NetworkNbtSerializer();
|
||||
while(!$legacyStateMapReader->feof()){
|
||||
$id = $legacyStateMapReader->getString();
|
||||
|
@ -59,6 +59,7 @@ use pocketmine\network\mcpe\protocol\CommandRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmotePacket;
|
||||
use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
@ -297,6 +298,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
//all of the parts before we can execute it
|
||||
return true;
|
||||
}
|
||||
$this->player->setUsingItem(false);
|
||||
try{
|
||||
$this->inventoryManager->onTransactionStart($this->craftingTransaction);
|
||||
$this->craftingTransaction->execute();
|
||||
@ -332,6 +334,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->player->setUsingItem(false);
|
||||
$transaction = new InventoryTransaction($this->player, $actions);
|
||||
$this->inventoryManager->onTransactionStart($transaction);
|
||||
try{
|
||||
@ -888,4 +891,9 @@ class InGamePacketHandler extends PacketHandler{
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleEmote(EmotePacket $packet) : bool{
|
||||
$this->player->emote($packet->getEmoteId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,12 @@ use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\StandardPacketBroadcaster;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\NetworkInterfaceStartException;
|
||||
use pocketmine\network\PacketHandlingException;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\utils\Utils;
|
||||
use raklib\generic\SocketException;
|
||||
use raklib\protocol\EncapsulatedPacket;
|
||||
use raklib\protocol\PacketReliability;
|
||||
use raklib\server\ipc\RakLibToUserThreadMessageReceiver;
|
||||
@ -120,7 +122,11 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
while($this->eventReceiver->handle($this));
|
||||
});
|
||||
$this->server->getLogger()->debug("Waiting for RakLib to start...");
|
||||
$this->rakLib->startAndWait();
|
||||
try{
|
||||
$this->rakLib->startAndWait();
|
||||
}catch(SocketException $e){
|
||||
throw new NetworkInterfaceStartException($e->getMessage(), 0, $e);
|
||||
}
|
||||
$this->server->getLogger()->debug("RakLib booted successfully");
|
||||
}
|
||||
|
||||
@ -132,7 +138,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
if(!$this->rakLib->isRunning()){
|
||||
$e = $this->rakLib->getCrashInfo();
|
||||
if($e !== null){
|
||||
throw new \RuntimeException("RakLib crashed: $e");
|
||||
throw new \RuntimeException("RakLib crashed: " . $e->makePrettyMessage());
|
||||
}
|
||||
throw new \Exception("RakLib Thread crashed without crash information");
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\raklib;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\thread\Thread;
|
||||
use raklib\generic\Socket;
|
||||
use raklib\generic\SocketException;
|
||||
use raklib\server\ipc\RakLibToUserThreadMessageSender;
|
||||
use raklib\server\ipc\UserToRakLibThreadMessageReceiver;
|
||||
use raklib\server\Server;
|
||||
@ -68,7 +69,7 @@ class RakLibServer extends Thread{
|
||||
/** @var SleeperNotifier */
|
||||
protected $mainThreadNotifier;
|
||||
|
||||
/** @var string|null */
|
||||
/** @var RakLibThreadCrashInfo|null */
|
||||
public $crashInfo = null;
|
||||
|
||||
public function __construct(
|
||||
@ -102,24 +103,24 @@ class RakLibServer extends Thread{
|
||||
* @return void
|
||||
*/
|
||||
public function shutdownHandler(){
|
||||
if($this->cleanShutdown !== true){
|
||||
if($this->cleanShutdown !== true && $this->crashInfo === null){
|
||||
$error = error_get_last();
|
||||
|
||||
if($error !== null){
|
||||
$this->logger->emergency("Fatal error: " . $error["message"] . " in " . $error["file"] . " on line " . $error["line"]);
|
||||
$this->setCrashInfo($error['message']);
|
||||
$this->setCrashInfo(RakLibThreadCrashInfo::fromLastErrorInfo($error));
|
||||
}else{
|
||||
$this->logger->emergency("RakLib shutdown unexpectedly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCrashInfo() : ?string{
|
||||
public function getCrashInfo() : ?RakLibThreadCrashInfo{
|
||||
return $this->crashInfo;
|
||||
}
|
||||
|
||||
private function setCrashInfo(string $info) : void{
|
||||
$this->synchronized(function(string $info) : void{
|
||||
private function setCrashInfo(RakLibThreadCrashInfo $info) : void{
|
||||
$this->synchronized(function(RakLibThreadCrashInfo $info) : void{
|
||||
$this->crashInfo = $info;
|
||||
$this->notify();
|
||||
}, $info);
|
||||
@ -131,8 +132,12 @@ class RakLibServer extends Thread{
|
||||
while(!$this->ready and $this->crashInfo === null){
|
||||
$this->wait();
|
||||
}
|
||||
if($this->crashInfo !== null){
|
||||
throw new \RuntimeException("RakLib failed to start: $this->crashInfo");
|
||||
$crashInfo = $this->crashInfo;
|
||||
if($crashInfo !== null){
|
||||
if($crashInfo->getClass() === SocketException::class){
|
||||
throw new SocketException($crashInfo->getMessage());
|
||||
}
|
||||
throw new \RuntimeException("RakLib failed to start: " . $crashInfo->makePrettyMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -145,7 +150,12 @@ class RakLibServer extends Thread{
|
||||
|
||||
register_shutdown_function([$this, "shutdownHandler"]);
|
||||
|
||||
$socket = new Socket($this->address);
|
||||
try{
|
||||
$socket = new Socket($this->address);
|
||||
}catch(SocketException $e){
|
||||
$this->setCrashInfo(RakLibThreadCrashInfo::fromThrowable($e));
|
||||
return;
|
||||
}
|
||||
$manager = new Server(
|
||||
$this->serverId,
|
||||
$this->logger,
|
||||
@ -166,7 +176,7 @@ class RakLibServer extends Thread{
|
||||
$manager->waitShutdown();
|
||||
$this->cleanShutdown = true;
|
||||
}catch(\Throwable $e){
|
||||
$this->setCrashInfo($e->getMessage());
|
||||
$this->setCrashInfo(RakLibThreadCrashInfo::fromThrowable($e));
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
|
62
src/network/mcpe/raklib/RakLibThreadCrashInfo.php
Normal file
62
src/network/mcpe/raklib/RakLibThreadCrashInfo.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\raklib;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use function get_class;
|
||||
use function sprintf;
|
||||
|
||||
final class RakLibThreadCrashInfo{
|
||||
|
||||
public function __construct(
|
||||
private ?string $class,
|
||||
private string $message,
|
||||
private string $file,
|
||||
private int $line
|
||||
){}
|
||||
|
||||
public static function fromThrowable(\Throwable $e) : self{
|
||||
return new self(get_class($e), $e->getMessage(), $e->getFile(), $e->getLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param array{message: string, file: string, line: int} $info
|
||||
*/
|
||||
public static function fromLastErrorInfo(array $info) : self{
|
||||
return new self(null, $info["message"], $info["file"], $info["line"]);
|
||||
}
|
||||
|
||||
/** @return string|null */
|
||||
public function getClass() : ?string{ return $this->class; }
|
||||
|
||||
public function getMessage() : string{ return $this->message; }
|
||||
|
||||
public function getFile() : string{ return $this->file; }
|
||||
|
||||
public function getLine() : int{ return $this->line; }
|
||||
|
||||
public function makePrettyMessage() : string{
|
||||
return sprintf("%s: \"%s\" in %s on line %d", $this->class ?? "Fatal error", $this->message, Filesystem::cleanPath($this->file), $this->line);
|
||||
}
|
||||
}
|
@ -146,7 +146,7 @@ class PermissibleInternal implements Permissible{
|
||||
foreach($this->rootPermissions as $name => $isGranted){
|
||||
$perm = $permManager->getPermission($name);
|
||||
if($perm === null){
|
||||
throw new \InvalidStateException("Unregistered root permission $name");
|
||||
throw new \LogicException("Unregistered root permission $name");
|
||||
}
|
||||
$this->permissions[$name] = new PermissionAttachmentInfo($name, null, $isGranted, null);
|
||||
$permManager->subscribeToPermission($name, $this);
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\player;
|
||||
|
||||
use pocketmine\world\World;
|
||||
use const M_SQRT2;
|
||||
|
||||
//TODO: turn this into an interface?
|
||||
final class ChunkSelector{
|
||||
@ -33,34 +34,44 @@ final class ChunkSelector{
|
||||
* @phpstan-return \Generator<int, int, void, void>
|
||||
*/
|
||||
public function selectChunks(int $radius, int $centerX, int $centerZ) : \Generator{
|
||||
$radiusSquared = $radius ** 2;
|
||||
for($subRadius = 0; $subRadius < $radius; $subRadius++){
|
||||
$subRadiusSquared = $subRadius ** 2;
|
||||
$nextSubRadiusSquared = ($subRadius + 1) ** 2;
|
||||
$minX = (int) ($subRadius / M_SQRT2);
|
||||
|
||||
for($x = 0; $x < $radius; ++$x){
|
||||
for($z = 0; $z <= $x; ++$z){
|
||||
if(($x ** 2 + $z ** 2) > $radiusSquared){
|
||||
break; //skip to next band
|
||||
}
|
||||
$lastZ = 0;
|
||||
|
||||
//If the chunk is in the radius, others at the same offsets in different quadrants are also guaranteed to be.
|
||||
for($x = $subRadius; $x >= $minX; --$x){
|
||||
for($z = $lastZ; $z <= $x; ++$z){
|
||||
$distanceSquared = ($x ** 2 + $z ** 2);
|
||||
if($distanceSquared < $subRadiusSquared){
|
||||
continue;
|
||||
}elseif($distanceSquared >= $nextSubRadiusSquared){
|
||||
break; //skip to next X
|
||||
}
|
||||
|
||||
/* Top right quadrant */
|
||||
yield World::chunkHash($centerX + $x, $centerZ + $z);
|
||||
/* Top left quadrant */
|
||||
yield World::chunkHash($centerX - $x - 1, $centerZ + $z);
|
||||
/* Bottom right quadrant */
|
||||
yield World::chunkHash($centerX + $x, $centerZ - $z - 1);
|
||||
/* Bottom left quadrant */
|
||||
yield World::chunkHash($centerX - $x - 1, $centerZ - $z - 1);
|
||||
$lastZ = $z;
|
||||
//If the chunk is in the radius, others at the same offsets in different quadrants are also guaranteed to be.
|
||||
|
||||
if($x !== $z){
|
||||
/* Top right quadrant mirror */
|
||||
yield World::chunkHash($centerX + $z, $centerZ + $x);
|
||||
/* Top left quadrant mirror */
|
||||
yield World::chunkHash($centerX - $z - 1, $centerZ + $x);
|
||||
/* Bottom right quadrant mirror */
|
||||
yield World::chunkHash($centerX + $z, $centerZ - $x - 1);
|
||||
/* Bottom left quadrant mirror */
|
||||
yield World::chunkHash($centerX - $z - 1, $centerZ - $x - 1);
|
||||
/* Top right quadrant */
|
||||
yield World::chunkHash($centerX + $x, $centerZ + $z);
|
||||
/* Top left quadrant */
|
||||
yield World::chunkHash($centerX - $x - 1, $centerZ + $z);
|
||||
/* Bottom right quadrant */
|
||||
yield World::chunkHash($centerX + $x, $centerZ - $z - 1);
|
||||
/* Bottom left quadrant */
|
||||
yield World::chunkHash($centerX - $x - 1, $centerZ - $z - 1);
|
||||
|
||||
if($x !== $z){
|
||||
/* Top right quadrant mirror */
|
||||
yield World::chunkHash($centerX + $z, $centerZ + $x);
|
||||
/* Top left quadrant mirror */
|
||||
yield World::chunkHash($centerX - $z - 1, $centerZ + $x);
|
||||
/* Bottom right quadrant mirror */
|
||||
yield World::chunkHash($centerX + $z, $centerZ - $x - 1);
|
||||
/* Bottom left quadrant mirror */
|
||||
yield World::chunkHash($centerX - $z - 1, $centerZ - $x - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ use pocketmine\event\player\PlayerChatEvent;
|
||||
use pocketmine\event\player\PlayerCommandPreprocessEvent;
|
||||
use pocketmine\event\player\PlayerDeathEvent;
|
||||
use pocketmine\event\player\PlayerDisplayNameChangeEvent;
|
||||
use pocketmine\event\player\PlayerEmoteEvent;
|
||||
use pocketmine\event\player\PlayerEntityInteractEvent;
|
||||
use pocketmine\event\player\PlayerExhaustEvent;
|
||||
use pocketmine\event\player\PlayerGameModeChangeEvent;
|
||||
@ -72,6 +73,7 @@ use pocketmine\event\player\PlayerToggleSprintEvent;
|
||||
use pocketmine\event\player\PlayerTransferEvent;
|
||||
use pocketmine\form\Form;
|
||||
use pocketmine\form\FormValidationException;
|
||||
use pocketmine\inventory\CallbackInventoryListener;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\PlayerCursorInventory;
|
||||
use pocketmine\inventory\transaction\action\DropItemAction;
|
||||
@ -193,6 +195,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
* @phpstan-var array<int, UsedChunkStatus>
|
||||
*/
|
||||
protected array $usedChunks = [];
|
||||
/**
|
||||
* @var true[]
|
||||
* @phpstan-var array<int, true>
|
||||
*/
|
||||
private array $activeChunkGenerationRequests = [];
|
||||
/**
|
||||
* @var true[] chunkHash => dummy
|
||||
* @phpstan-var array<int, true>
|
||||
@ -234,6 +241,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
/** @var int[] ID => ticks map */
|
||||
protected array $usedItemsCooldown = [];
|
||||
|
||||
private int $lastEmoteTick = 0;
|
||||
|
||||
protected int $formIdCounter = 0;
|
||||
/** @var Form[] */
|
||||
protected array $forms = [];
|
||||
@ -288,6 +297,17 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
parent::initEntity($nbt);
|
||||
$this->addDefaultWindows();
|
||||
|
||||
$this->inventory->getListeners()->add(new CallbackInventoryListener(
|
||||
function(Inventory $unused, int $slot) : void{
|
||||
if($slot === $this->inventory->getHeldItemIndex()){
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
},
|
||||
function() : void{
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
));
|
||||
|
||||
$this->firstPlayed = $nbt->getLong("firstPlayed", $now = (int) (microtime(true) * 1000));
|
||||
$this->lastPlayed = $nbt->getLong("lastPlayed", $now);
|
||||
|
||||
@ -475,7 +495,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
public function getNetworkSession() : NetworkSession{
|
||||
if($this->networkSession === null){
|
||||
throw new \InvalidStateException("Player is not connected");
|
||||
throw new \LogicException("Player is not connected");
|
||||
}
|
||||
return $this->networkSession;
|
||||
}
|
||||
@ -628,6 +648,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
$this->getNetworkSession()->stopUsingChunk($x, $z);
|
||||
unset($this->usedChunks[$index]);
|
||||
unset($this->activeChunkGenerationRequests[$index]);
|
||||
}
|
||||
$world->unregisterChunkLoader($this->chunkLoader, $x, $z);
|
||||
$world->unregisterChunkListener($this, $x, $z);
|
||||
@ -664,8 +685,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
$count = 0;
|
||||
$world = $this->getWorld();
|
||||
|
||||
$limit = $this->chunksPerTick - count($this->activeChunkGenerationRequests);
|
||||
foreach($this->loadQueue as $index => $distance){
|
||||
if($count >= $this->chunksPerTick){
|
||||
if($count >= $limit){
|
||||
break;
|
||||
}
|
||||
|
||||
@ -676,7 +699,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
++$count;
|
||||
|
||||
$this->usedChunks[$index] = UsedChunkStatus::NEEDED();
|
||||
$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_GENERATION();
|
||||
$this->activeChunkGenerationRequests[$index] = true;
|
||||
unset($this->loadQueue[$index]);
|
||||
$this->getWorld()->registerChunkLoader($this->chunkLoader, $X, $Z, true);
|
||||
$this->getWorld()->registerChunkListener($this, $X, $Z);
|
||||
|
||||
@ -685,15 +710,14 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
if(!$this->isConnected() || !isset($this->usedChunks[$index]) || $world !== $this->getWorld()){
|
||||
return;
|
||||
}
|
||||
if(!$this->usedChunks[$index]->equals(UsedChunkStatus::NEEDED())){
|
||||
//TODO: make this an error
|
||||
//we may have added multiple completion handlers, since the Player keeps re-requesting chunks
|
||||
//it doesn't have yet (a relic from the old system, but also currently relied on for chunk resends).
|
||||
//in this event, make sure we don't try to send the chunk multiple times.
|
||||
if(!$this->usedChunks[$index]->equals(UsedChunkStatus::REQUESTED_GENERATION())){
|
||||
//We may have previously requested this, decided we didn't want it, and then decided we did want
|
||||
//it again, all before the generation request got executed. In that case, the promise would have
|
||||
//multiple callbacks for this player. In that case, only the first one matters.
|
||||
return;
|
||||
}
|
||||
unset($this->loadQueue[$index]);
|
||||
$this->usedChunks[$index] = UsedChunkStatus::REQUESTED();
|
||||
unset($this->activeChunkGenerationRequests[$index]);
|
||||
$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_SENDING();
|
||||
|
||||
$this->getNetworkSession()->startUsingChunk($X, $Z, function() use ($X, $Z, $index) : void{
|
||||
$this->usedChunks[$index] = UsedChunkStatus::SENT();
|
||||
@ -760,7 +784,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
if($this->getHealth() <= 0){
|
||||
$this->logger->debug("Quit while dead, forcing respawn");
|
||||
$this->respawn();
|
||||
$this->actuallyRespawn();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1164,16 +1188,18 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->lastLocation = $to;
|
||||
$this->broadcastMovement();
|
||||
|
||||
$distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
|
||||
//TODO: check swimming (adds 0.015 exhaustion in MCPE)
|
||||
if($this->isSprinting()){
|
||||
$this->hungerManager->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING);
|
||||
}else{
|
||||
$this->hungerManager->exhaust(0.01 * $distance, PlayerExhaustEvent::CAUSE_WALKING);
|
||||
}
|
||||
$horizontalDistanceTravelled = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
|
||||
if($horizontalDistanceTravelled > 0){
|
||||
//TODO: check swimming (adds 0.015 exhaustion in MCPE)
|
||||
if($this->isSprinting()){
|
||||
$this->hungerManager->exhaust(0.1 * $horizontalDistanceTravelled, PlayerExhaustEvent::CAUSE_SPRINTING);
|
||||
}else{
|
||||
$this->hungerManager->exhaust(0.01 * $horizontalDistanceTravelled, PlayerExhaustEvent::CAUSE_WALKING);
|
||||
}
|
||||
|
||||
if($this->nextChunkOrderRun > 20){
|
||||
$this->nextChunkOrderRun = 20;
|
||||
if($this->nextChunkOrderRun > 20){
|
||||
$this->nextChunkOrderRun = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1375,7 +1401,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
|
||||
$this->setUsingItem($item instanceof Releasable);
|
||||
$this->setUsingItem($item instanceof Releasable && $item->canStartUsingItem($this));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1518,8 +1544,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!$this->isCreative()){
|
||||
$this->blockBreakHandler = SurvivalBlockBreakHandler::createIfNecessary($this, $pos, $target, $face, 16);
|
||||
if(!$this->isCreative() && !$block->getBreakInfo()->breaksInstantly()){
|
||||
$this->blockBreakHandler = new SurvivalBlockBreakHandler($this, $pos, $target, $face, 16);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1728,6 +1754,21 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function emote(string $emoteId) : void{
|
||||
$currentTick = $this->server->getTick();
|
||||
if($currentTick - $this->lastEmoteTick > 5){
|
||||
$this->lastEmoteTick = $currentTick;
|
||||
$event = new PlayerEmoteEvent($this, $emoteId);
|
||||
$event->call();
|
||||
if(!$event->isCancelled()){
|
||||
$emoteId = $event->getEmoteId();
|
||||
foreach($this->getViewers() as $player){
|
||||
$player->getNetworkSession()->onEmote($this, $emoteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an item on the ground in front of the player.
|
||||
*/
|
||||
@ -1942,7 +1983,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
*/
|
||||
public function onPostDisconnect(string $reason, Translatable|string|null $quitMessage) : void{
|
||||
if($this->isConnected()){
|
||||
throw new \InvalidStateException("Player is still connected");
|
||||
throw new \LogicException("Player is still connected");
|
||||
}
|
||||
|
||||
//prevent the player receiving their own disconnect message
|
||||
@ -2101,10 +2142,6 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
public function respawn() : void{
|
||||
if($this->respawnLocked){
|
||||
return;
|
||||
}
|
||||
$this->respawnLocked = true;
|
||||
if($this->server->isHardcore()){
|
||||
if($this->kick("You have been banned because you died in hardcore mode")){ //this allows plugins to prevent the ban by cancelling PlayerKickEvent
|
||||
$this->server->getNameBans()->addBan($this->getName(), "Died in hardcore mode");
|
||||
@ -2112,6 +2149,15 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->actuallyRespawn();
|
||||
}
|
||||
|
||||
protected function actuallyRespawn() : void{
|
||||
if($this->respawnLocked){
|
||||
return;
|
||||
}
|
||||
$this->respawnLocked = true;
|
||||
|
||||
$this->logger->debug("Waiting for spawn terrain generation for respawn");
|
||||
$spawn = $this->getSpawn();
|
||||
$spawn->getWorld()->orderChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
||||
|
@ -59,7 +59,7 @@ final class SurvivalBlockBreakHandler{
|
||||
/** @var float */
|
||||
private $breakProgress = 0;
|
||||
|
||||
private function __construct(Player $player, Vector3 $blockPos, Block $block, int $targetedFace, int $maxPlayerDistance, int $fxTickInterval = self::DEFAULT_FX_INTERVAL_TICKS){
|
||||
public function __construct(Player $player, Vector3 $blockPos, Block $block, int $targetedFace, int $maxPlayerDistance, int $fxTickInterval = self::DEFAULT_FX_INTERVAL_TICKS){
|
||||
$this->player = $player;
|
||||
$this->blockPos = $blockPos;
|
||||
$this->block = $block;
|
||||
@ -76,14 +76,6 @@ final class SurvivalBlockBreakHandler{
|
||||
}
|
||||
}
|
||||
|
||||
public static function createIfNecessary(Player $player, Vector3 $blockPos, Block $block, int $targetedFace, int $maxPlayerDistance, int $fxTickInterval = self::DEFAULT_FX_INTERVAL_TICKS) : ?self{
|
||||
$breakInfo = $block->getBreakInfo();
|
||||
if(!$breakInfo->breaksInstantly()){
|
||||
return new self($player, $blockPos, $block, $targetedFace, $maxPlayerDistance, $fxTickInterval);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the calculated break speed as percentage progress per game tick.
|
||||
*/
|
||||
@ -101,8 +93,7 @@ final class SurvivalBlockBreakHandler{
|
||||
}
|
||||
|
||||
public function update() : bool{
|
||||
if(
|
||||
$this->player->getPosition()->distanceSquared($this->blockPos->add(0.5, 0.5, 0.5)) > $this->maxPlayerDistance ** 2){
|
||||
if($this->player->getPosition()->distanceSquared($this->blockPos->add(0.5, 0.5, 0.5)) > $this->maxPlayerDistance ** 2){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,8 @@ use pocketmine\utils\EnumTrait;
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static UsedChunkStatus NEEDED()
|
||||
* @method static UsedChunkStatus REQUESTED()
|
||||
* @method static UsedChunkStatus REQUESTED_GENERATION()
|
||||
* @method static UsedChunkStatus REQUESTED_SENDING()
|
||||
* @method static UsedChunkStatus SENT()
|
||||
*/
|
||||
final class UsedChunkStatus{
|
||||
@ -41,7 +42,8 @@ final class UsedChunkStatus{
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new self("NEEDED"),
|
||||
new self("REQUESTED"),
|
||||
new self("REQUESTED_GENERATION"),
|
||||
new self("REQUESTED_SENDING"),
|
||||
new self("SENT")
|
||||
);
|
||||
}
|
||||
|
@ -25,16 +25,28 @@ namespace pocketmine\plugin;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* @phpstan-import-type LoggerAttachment from \AttachableLogger
|
||||
*/
|
||||
class PluginLogger extends \PrefixedLogger implements \AttachableLogger{
|
||||
|
||||
/** @var \LoggerAttachment[] */
|
||||
/**
|
||||
* @var \Closure[]
|
||||
* @phpstan-var LoggerAttachment[]
|
||||
*/
|
||||
private $attachments = [];
|
||||
|
||||
public function addAttachment(\LoggerAttachment $attachment){
|
||||
/**
|
||||
* @phpstan-param LoggerAttachment $attachment
|
||||
*/
|
||||
public function addAttachment(\Closure $attachment){
|
||||
$this->attachments[spl_object_id($attachment)] = $attachment;
|
||||
}
|
||||
|
||||
public function removeAttachment(\LoggerAttachment $attachment){
|
||||
/**
|
||||
* @phpstan-param LoggerAttachment $attachment
|
||||
*/
|
||||
public function removeAttachment(\Closure $attachment){
|
||||
unset($this->attachments[spl_object_id($attachment)]);
|
||||
}
|
||||
|
||||
@ -49,7 +61,7 @@ class PluginLogger extends \PrefixedLogger implements \AttachableLogger{
|
||||
public function log($level, $message){
|
||||
parent::log($level, $message);
|
||||
foreach($this->attachments as $attachment){
|
||||
$attachment->log($level, $message);
|
||||
$attachment($level, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
55
src/promise/Promise.php
Normal file
55
src/promise/Promise.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?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\promise;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* @phpstan-template TValue
|
||||
*/
|
||||
final class Promise{
|
||||
/**
|
||||
* @internal Do NOT call this directly; create a new Resolver and call Resolver->promise()
|
||||
* @see PromiseResolver
|
||||
* @phpstan-param PromiseSharedData<TValue> $shared
|
||||
*/
|
||||
public function __construct(private PromiseSharedData $shared){}
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure(TValue) : void $onSuccess
|
||||
* @phpstan-param \Closure() : void $onFailure
|
||||
*/
|
||||
public function onCompletion(\Closure $onSuccess, \Closure $onFailure) : void{
|
||||
if($this->shared->resolved){
|
||||
$this->shared->result === null ? $onFailure() : $onSuccess($this->shared->result);
|
||||
}else{
|
||||
$this->shared->onSuccess[spl_object_id($onSuccess)] = $onSuccess;
|
||||
$this->shared->onFailure[spl_object_id($onFailure)] = $onFailure;
|
||||
}
|
||||
}
|
||||
|
||||
public function isResolved() : bool{
|
||||
return $this->shared->resolved;
|
||||
}
|
||||
}
|
75
src/promise/PromiseResolver.php
Normal file
75
src/promise/PromiseResolver.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?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\promise;
|
||||
|
||||
/**
|
||||
* @phpstan-template TValue
|
||||
*/
|
||||
final class PromiseResolver{
|
||||
/** @phpstan-var PromiseSharedData<TValue> */
|
||||
private PromiseSharedData $shared;
|
||||
/** @phpstan-var Promise<TValue> */
|
||||
private Promise $promise;
|
||||
|
||||
public function __construct(){
|
||||
$this->shared = new PromiseSharedData();
|
||||
$this->promise = new Promise($this->shared);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @phpstan-param TValue $value
|
||||
*/
|
||||
public function resolve($value) : void{
|
||||
if($this->shared->resolved){
|
||||
throw new \LogicException("Promise has already been resolved/rejected");
|
||||
}
|
||||
$this->shared->resolved = true;
|
||||
$this->shared->result = $value;
|
||||
foreach($this->shared->onSuccess as $c){
|
||||
$c($value);
|
||||
}
|
||||
$this->shared->onSuccess = [];
|
||||
$this->shared->onFailure = [];
|
||||
}
|
||||
|
||||
public function reject() : void{
|
||||
if($this->shared->resolved){
|
||||
throw new \LogicException("Promise has already been resolved/rejected");
|
||||
}
|
||||
$this->shared->resolved = true;
|
||||
foreach($this->shared->onFailure as $c){
|
||||
$c();
|
||||
}
|
||||
$this->shared->onSuccess = [];
|
||||
$this->shared->onFailure = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<TValue>
|
||||
*/
|
||||
public function getPromise() : Promise{
|
||||
return $this->promise;
|
||||
}
|
||||
}
|
51
src/promise/PromiseSharedData.php
Normal file
51
src/promise/PromiseSharedData.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\promise;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @see PromiseResolver
|
||||
* @phpstan-template TValue
|
||||
*/
|
||||
final class PromiseSharedData{
|
||||
/**
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure(TValue) : void>
|
||||
*/
|
||||
public array $onSuccess = [];
|
||||
|
||||
/**
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure() : void>
|
||||
*/
|
||||
public array $onFailure = [];
|
||||
|
||||
public bool $resolved = false;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
* @phpstan-var TValue|null
|
||||
*/
|
||||
public $result = null;
|
||||
}
|
@ -32,6 +32,8 @@ use function file_exists;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function mkdir;
|
||||
use function strtolower;
|
||||
@ -81,10 +83,11 @@ class ResourcePackManager{
|
||||
}
|
||||
|
||||
foreach($resourceStack as $pos => $pack){
|
||||
if(!is_string($pack)){
|
||||
if(!is_string($pack) && !is_int($pack) && !is_float($pack)){
|
||||
$logger->critical("Found invalid entry in resource pack list at offset $pos of type " . gettype($pack));
|
||||
continue;
|
||||
}
|
||||
$pack = (string) $pack;
|
||||
try{
|
||||
$packPath = Path::join($this->path, $pack);
|
||||
if(!file_exists($packPath)){
|
||||
|
@ -92,7 +92,7 @@ class AsyncWorker extends Worker{
|
||||
*/
|
||||
public function saveToThreadStore(string $identifier, $value) : void{
|
||||
if(\Thread::getCurrentThread() !== $this){
|
||||
throw new \InvalidStateException("Thread-local data can only be stored in the thread context");
|
||||
throw new \LogicException("Thread-local data can only be stored in the thread context");
|
||||
}
|
||||
self::$store[$identifier] = $value;
|
||||
}
|
||||
@ -109,7 +109,7 @@ class AsyncWorker extends Worker{
|
||||
*/
|
||||
public function getFromThreadStore(string $identifier){
|
||||
if(\Thread::getCurrentThread() !== $this){
|
||||
throw new \InvalidStateException("Thread-local data can only be fetched in the thread context");
|
||||
throw new \LogicException("Thread-local data can only be fetched in the thread context");
|
||||
}
|
||||
return self::$store[$identifier] ?? null;
|
||||
}
|
||||
@ -119,7 +119,7 @@ class AsyncWorker extends Worker{
|
||||
*/
|
||||
public function removeFromThreadStore(string $identifier) : void{
|
||||
if(\Thread::getCurrentThread() !== $this){
|
||||
throw new \InvalidStateException("Thread-local data can only be removed in the thread context");
|
||||
throw new \LogicException("Thread-local data can only be removed in the thread context");
|
||||
}
|
||||
unset(self::$store[$identifier]);
|
||||
}
|
||||
|
@ -88,12 +88,9 @@ class TaskScheduler{
|
||||
return $this->tasks->contains($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidStateException
|
||||
*/
|
||||
private function addTask(Task $task, int $delay, int $period) : TaskHandler{
|
||||
if(!$this->enabled){
|
||||
throw new \InvalidStateException("Tried to schedule task to disabled scheduler");
|
||||
throw new \LogicException("Tried to schedule task to disabled scheduler");
|
||||
}
|
||||
|
||||
if($delay <= 0){
|
||||
|
@ -144,8 +144,7 @@ class Config{
|
||||
* @param mixed[] $default
|
||||
* @phpstan-param array<string, mixed> $default
|
||||
*
|
||||
* @throws \InvalidArgumentException if config type could not be auto-detected
|
||||
* @throws \InvalidStateException if config type is invalid
|
||||
* @throws \InvalidArgumentException if config type is invalid or could not be auto-detected
|
||||
*/
|
||||
private function load(string $file, int $type = Config::DETECT, array $default = []) : void{
|
||||
$this->file = $file;
|
||||
@ -187,7 +186,7 @@ class Config{
|
||||
$config = array_fill_keys(self::parseList($content), true);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidStateException("Config type is unknown");
|
||||
throw new \InvalidArgumentException("Invalid config type specified");
|
||||
}
|
||||
$this->config = is_array($config) ? $config : $default;
|
||||
if($this->fillDefaults($default, $this->config) > 0){
|
||||
@ -205,8 +204,6 @@ class Config{
|
||||
|
||||
/**
|
||||
* Flushes the config to disk in the appropriate format.
|
||||
*
|
||||
* @throws \InvalidStateException if config type is not valid
|
||||
*/
|
||||
public function save() : void{
|
||||
$content = null;
|
||||
@ -227,7 +224,7 @@ class Config{
|
||||
$content = self::writeList(array_keys($this->config));
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidStateException("Config type is unknown, has not been set or not detected");
|
||||
throw new AssumptionFailedError("Config type is unknown, has not been set or not detected");
|
||||
}
|
||||
|
||||
file_put_contents($this->file, $content);
|
||||
|
@ -210,7 +210,7 @@ class MainLogger extends \AttachableThreadedLogger implements \BufferedLogger{
|
||||
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
|
||||
|
||||
foreach($this->attachments as $attachment){
|
||||
$attachment->call($level, $message);
|
||||
$attachment->log($level, $message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* @phpstan-template TValue
|
||||
*/
|
||||
final class Promise{
|
||||
/**
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure(TValue) : void>
|
||||
*/
|
||||
private array $onSuccess = [];
|
||||
|
||||
/**
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure() : void>
|
||||
*/
|
||||
private array $onFailure = [];
|
||||
|
||||
private bool $resolved = false;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
* @phpstan-var TValue|null
|
||||
*/
|
||||
private $result = null;
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure(TValue) : void $onSuccess
|
||||
* @phpstan-param \Closure() : void $onFailure
|
||||
*/
|
||||
public function onCompletion(\Closure $onSuccess, \Closure $onFailure) : void{
|
||||
if($this->resolved){
|
||||
$this->result === null ? $onFailure() : $onSuccess($this->result);
|
||||
}else{
|
||||
$this->onSuccess[spl_object_id($onSuccess)] = $onSuccess;
|
||||
$this->onFailure[spl_object_id($onFailure)] = $onFailure;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @phpstan-param TValue $value
|
||||
*/
|
||||
public function resolve($value) : void{
|
||||
if($this->resolved){
|
||||
throw new \InvalidStateException("Promise has already been resolved/rejected");
|
||||
}
|
||||
$this->resolved = true;
|
||||
$this->result = $value;
|
||||
foreach($this->onSuccess as $c){
|
||||
$c($value);
|
||||
}
|
||||
$this->onSuccess = [];
|
||||
$this->onFailure = [];
|
||||
}
|
||||
|
||||
public function reject() : void{
|
||||
if($this->resolved){
|
||||
throw new \InvalidStateException("Promise has already been resolved/rejected");
|
||||
}
|
||||
$this->resolved = true;
|
||||
foreach($this->onFailure as $c){
|
||||
$c();
|
||||
}
|
||||
$this->onSuccess = [];
|
||||
$this->onFailure = [];
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@ abstract class Terminal{
|
||||
|
||||
public static function hasFormattingCodes() : bool{
|
||||
if(self::$formattingCodes === null){
|
||||
throw new \InvalidStateException("Formatting codes have not been initialized");
|
||||
throw new \LogicException("Formatting codes have not been initialized");
|
||||
}
|
||||
return self::$formattingCodes;
|
||||
}
|
||||
|
@ -86,7 +86,9 @@ abstract class Timezone{
|
||||
|
||||
if(($response = Internet::getURL("http://ip-api.com/json")) !== null //If system timezone detection fails or timezone is an invalid value.
|
||||
and is_array($ip_geolocation_data = json_decode($response->getBody(), true))
|
||||
and isset($ip_geolocation_data['status'])
|
||||
and $ip_geolocation_data['status'] !== 'fail'
|
||||
and is_string($ip_geolocation_data['timezone'])
|
||||
and date_default_timezone_set($ip_geolocation_data['timezone'])
|
||||
){
|
||||
//Again, for redundancy.
|
||||
|
@ -71,6 +71,7 @@ use function preg_grep;
|
||||
use function preg_match;
|
||||
use function preg_match_all;
|
||||
use function preg_replace;
|
||||
use function shell_exec;
|
||||
use function spl_object_id;
|
||||
use function str_pad;
|
||||
use function str_split;
|
||||
@ -233,7 +234,7 @@ final class Utils{
|
||||
}elseif($os === Utils::OS_ANDROID){
|
||||
$machine .= @file_get_contents("/system/build.prop");
|
||||
}elseif($os === Utils::OS_MACOS){
|
||||
$machine .= `system_profiler SPHardwareDataType | grep UUID`;
|
||||
$machine .= shell_exec("system_profiler SPHardwareDataType | grep UUID");
|
||||
}
|
||||
$data = $machine . PHP_MAXPATHLEN;
|
||||
$data .= PHP_INT_MAX;
|
||||
@ -316,7 +317,7 @@ final class Utils{
|
||||
break;
|
||||
case Utils::OS_BSD:
|
||||
case Utils::OS_MACOS:
|
||||
$processors = (int) `sysctl -n hw.ncpu`;
|
||||
$processors = (int) shell_exec("sysctl -n hw.ncpu");
|
||||
break;
|
||||
case Utils::OS_WINDOWS:
|
||||
$processors = (int) getenv("NUMBER_OF_PROCESSORS");
|
||||
|
38
src/world/ChunkLockId.php
Normal file
38
src/world/ChunkLockId.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\world;
|
||||
|
||||
use pocketmine\utils\NotCloneable;
|
||||
use pocketmine\utils\NotSerializable;
|
||||
|
||||
/**
|
||||
* Represents a unique lock ID for use with World chunk locking.
|
||||
*
|
||||
* @see World::lockChunk()
|
||||
* @see World::unlockChunk()
|
||||
*/
|
||||
final class ChunkLockId{
|
||||
use NotCloneable;
|
||||
use NotSerializable;
|
||||
}
|
@ -66,12 +66,13 @@ use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\Promise;
|
||||
use pocketmine\utils\ReversePriorityQueue;
|
||||
use pocketmine\world\biome\Biome;
|
||||
use pocketmine\world\biome\BiomeRegistry;
|
||||
@ -248,13 +249,13 @@ class World implements ChunkManager{
|
||||
|
||||
/** @var bool[] */
|
||||
private $activeChunkPopulationTasks = [];
|
||||
/** @var bool[] */
|
||||
/** @var ChunkLockId[] */
|
||||
private $chunkLock = [];
|
||||
/** @var int */
|
||||
private $maxConcurrentChunkPopulationTasks = 2;
|
||||
/**
|
||||
* @var Promise[] chunkHash => promise
|
||||
* @phpstan-var array<int, Promise<Chunk>>
|
||||
* @var PromiseResolver[] chunkHash => promise
|
||||
* @phpstan-var array<int, PromiseResolver<Chunk>>
|
||||
*/
|
||||
private array $chunkPopulationRequestMap = [];
|
||||
/**
|
||||
@ -262,6 +263,12 @@ class World implements ChunkManager{
|
||||
* @phpstan-var \SplQueue<int>
|
||||
*/
|
||||
private \SplQueue $chunkPopulationRequestQueue;
|
||||
/**
|
||||
* @var true[] chunkHash => dummy
|
||||
* @phpstan-var array<int, true>
|
||||
*/
|
||||
private array $chunkPopulationRequestQueueIndex = [];
|
||||
|
||||
/** @var bool[] */
|
||||
private $generatorRegisteredWorkers = [];
|
||||
|
||||
@ -515,7 +522,7 @@ class World implements ChunkManager{
|
||||
*/
|
||||
public function onUnload() : void{
|
||||
if($this->unloaded){
|
||||
throw new \InvalidStateException("Tried to close a world which is already closed");
|
||||
throw new \LogicException("Tried to close a world which is already closed");
|
||||
}
|
||||
|
||||
foreach($this->unloadCallbacks as $callback){
|
||||
@ -731,14 +738,9 @@ class World implements ChunkManager{
|
||||
* Unregisters a chunk listener from all chunks it is listening on in this World.
|
||||
*/
|
||||
public function unregisterChunkListenerFromAll(ChunkListener $listener) : void{
|
||||
$id = spl_object_id($listener);
|
||||
foreach($this->chunkListeners as $hash => $listeners){
|
||||
if(isset($listeners[$id])){
|
||||
unset($this->chunkListeners[$hash][$id]);
|
||||
if(count($this->chunkListeners[$hash]) === 0){
|
||||
unset($this->chunkListeners[$hash]);
|
||||
}
|
||||
}
|
||||
World::getXZ($hash, $chunkX, $chunkZ);
|
||||
$this->unregisterChunkListener($listener, $chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
@ -774,7 +776,7 @@ class World implements ChunkManager{
|
||||
*/
|
||||
public function doTick(int $currentTick) : void{
|
||||
if($this->unloaded){
|
||||
throw new \InvalidStateException("Attempted to tick a world which has been closed");
|
||||
throw new \LogicException("Attempted to tick a world which has been closed");
|
||||
}
|
||||
|
||||
$this->timings->doTick->startTiming();
|
||||
@ -1412,7 +1414,6 @@ class World implements ChunkManager{
|
||||
|
||||
public function updateAllLight(int $x, int $y, int $z) : void{
|
||||
if(($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) === null || $chunk->isLightPopulated() !== true){
|
||||
$this->logger->debug("Skipped runtime light update of x=$x,y=$y,z=$z because the target area has not received base light calculation");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1593,22 +1594,19 @@ class World implements ChunkManager{
|
||||
}
|
||||
$chunkX = $x >> Chunk::COORD_BIT_SIZE;
|
||||
$chunkZ = $z >> Chunk::COORD_BIT_SIZE;
|
||||
if($this->isChunkLocked($chunkX, $chunkZ)){
|
||||
throw new WorldException("Terrain is locked for generation/population");
|
||||
}
|
||||
if($this->loadChunk($chunkX, $chunkZ) === null){ //current expected behaviour is to try to load the terrain synchronously
|
||||
throw new WorldException("Cannot set a block in un-generated terrain");
|
||||
}
|
||||
|
||||
$this->timings->setBlock->startTiming();
|
||||
|
||||
$oldBlock = $this->getBlockAt($x, $y, $z, true, false);
|
||||
$this->unlockChunk($chunkX, $chunkZ, null);
|
||||
|
||||
$block = clone $block;
|
||||
|
||||
$block->position($this, $x, $y, $z);
|
||||
$block->writeStateToWorld();
|
||||
$pos = $block->getPosition();
|
||||
$pos = new Vector3($x, $y, $z);
|
||||
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$relativeBlockHash = World::chunkBlockHash($x, $y, $z);
|
||||
@ -1625,9 +1623,7 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
if($update){
|
||||
if($oldBlock->getLightFilter() !== $block->getLightFilter() or $oldBlock->getLightLevel() !== $block->getLightLevel()){
|
||||
$this->updateAllLight($x, $y, $z);
|
||||
}
|
||||
$this->updateAllLight($x, $y, $z);
|
||||
$this->tryAddToNeighbourUpdateQueue($pos);
|
||||
foreach($pos->sides() as $side){
|
||||
$this->tryAddToNeighbourUpdateQueue($side);
|
||||
@ -2051,10 +2047,7 @@ class World implements ChunkManager{
|
||||
public function setBiomeId(int $x, int $z, int $biomeId) : void{
|
||||
$chunkX = $x >> Chunk::COORD_BIT_SIZE;
|
||||
$chunkZ = $z >> Chunk::COORD_BIT_SIZE;
|
||||
if($this->isChunkLocked($chunkX, $chunkZ)){
|
||||
//the changes would be overwritten when the generation finishes
|
||||
throw new WorldException("Chunk is currently locked for async generation/population");
|
||||
}
|
||||
$this->unlockChunk($chunkX, $chunkZ, null);
|
||||
if(($chunk = $this->loadChunk($chunkX, $chunkZ)) !== null){
|
||||
$chunk->setBiomeId($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $biomeId);
|
||||
}else{
|
||||
@ -2095,31 +2088,62 @@ class World implements ChunkManager{
|
||||
*/
|
||||
public function getAdjacentChunks(int $x, int $z) : array{
|
||||
$result = [];
|
||||
for($xx = 0; $xx <= 2; ++$xx){
|
||||
for($zz = 0; $zz <= 2; ++$zz){
|
||||
$i = $zz * 3 + $xx;
|
||||
if($i === 4){
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
if($xx === 0 && $zz === 0){
|
||||
continue; //center chunk
|
||||
}
|
||||
$result[$i] = $this->loadChunk($x + $xx - 1, $z + $zz - 1);
|
||||
$result[World::chunkHash($xx, $zz)] = $this->loadChunk($x + $xx, $z + $zz);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function lockChunk(int $chunkX, int $chunkZ) : void{
|
||||
/**
|
||||
* Flags a chunk as locked, usually for async modification.
|
||||
*
|
||||
* This is an **advisory lock**. This means that the lock does **not** prevent the chunk from being modified on the
|
||||
* main thread, such as by setBlock() or setBiomeId(). However, you can use it to detect when such modifications
|
||||
* have taken place - unlockChunk() with the same lockID will fail and return false if this happens.
|
||||
*
|
||||
* This is used internally by the generation system to ensure that two PopulationTasks don't try to modify the same
|
||||
* chunk at the same time. Generation will respect these locks and won't try to do generation of chunks over which
|
||||
* a lock is held.
|
||||
*
|
||||
* WARNING: Be sure to release all locks once you're done with them, or you WILL have problems with terrain not
|
||||
* being generated.
|
||||
*/
|
||||
public function lockChunk(int $chunkX, int $chunkZ, ChunkLockId $lockId) : void{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunkLock[$chunkHash])){
|
||||
throw new \InvalidArgumentException("Chunk $chunkX $chunkZ is already locked");
|
||||
}
|
||||
$this->chunkLock[$chunkHash] = true;
|
||||
$this->chunkLock[$chunkHash] = $lockId;
|
||||
}
|
||||
|
||||
public function unlockChunk(int $chunkX, int $chunkZ) : void{
|
||||
unset($this->chunkLock[World::chunkHash($chunkX, $chunkZ)]);
|
||||
/**
|
||||
* Unlocks a chunk previously locked by lockChunk().
|
||||
*
|
||||
* You must provide the same lockID as provided to lockChunk().
|
||||
* If a null lockID is given, any existing lock will be removed from the chunk, regardless of who owns it.
|
||||
*
|
||||
* Returns true if unlocking was successful, false otherwise.
|
||||
*/
|
||||
public function unlockChunk(int $chunkX, int $chunkZ, ?ChunkLockId $lockId) : bool{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunkLock[$chunkHash]) && ($lockId === null || $this->chunkLock[$chunkHash] === $lockId)){
|
||||
unset($this->chunkLock[$chunkHash]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether anyone currently has a lock on the chunk at the given coordinates.
|
||||
* You should check this to make sure that population tasks aren't currently modifying chunks that you want to use
|
||||
* in async tasks.
|
||||
*/
|
||||
public function isChunkLocked(int $chunkX, int $chunkZ) : bool{
|
||||
return isset($this->chunkLock[World::chunkHash($chunkX, $chunkZ)]);
|
||||
}
|
||||
@ -2356,7 +2380,7 @@ class World implements ChunkManager{
|
||||
if(isset($this->chunks[$hash = World::chunkHash($chunkX, $chunkZ)])){
|
||||
$this->chunks[$hash]->addTile($tile);
|
||||
}else{
|
||||
throw new \InvalidStateException("Attempted to create tile " . get_class($tile) . " in unloaded chunk $chunkX $chunkZ");
|
||||
throw new \InvalidArgumentException("Attempted to create tile " . get_class($tile) . " in unloaded chunk $chunkX $chunkZ");
|
||||
}
|
||||
|
||||
//delegate tile ticking to the corresponding block
|
||||
@ -2393,8 +2417,6 @@ class World implements ChunkManager{
|
||||
* returned directly.
|
||||
*
|
||||
* @return Chunk|null the requested chunk, or null on failure.
|
||||
*
|
||||
* @throws \InvalidStateException
|
||||
*/
|
||||
public function loadChunk(int $x, int $z) : ?Chunk{
|
||||
if(isset($this->chunks[$chunkHash = World::chunkHash($x, $z)])){
|
||||
@ -2443,6 +2465,7 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
private function initChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{
|
||||
$logger = new \PrefixedLogger($this->logger, "Loading chunk $chunkX $chunkZ");
|
||||
if(count($chunkData->getEntityNBT()) !== 0){
|
||||
$this->timings->syncChunkLoadEntities->startTiming();
|
||||
$entityFactory = EntityFactory::getInstance();
|
||||
@ -2450,8 +2473,8 @@ class World implements ChunkManager{
|
||||
try{
|
||||
$entity = $entityFactory->createFromData($this, $nbt);
|
||||
}catch(NbtDataException $e){
|
||||
$this->getLogger()->error("Chunk $chunkX $chunkZ: Bad entity data at list position $k: " . $e->getMessage());
|
||||
$this->getLogger()->logException($e);
|
||||
$logger->error("Bad entity data at list position $k: " . $e->getMessage());
|
||||
$logger->logException($e);
|
||||
continue;
|
||||
}
|
||||
if($entity === null){
|
||||
@ -2462,7 +2485,7 @@ class World implements ChunkManager{
|
||||
}elseif($saveIdTag instanceof IntTag){ //legacy MCPE format
|
||||
$saveId = "legacy(" . $saveIdTag->getValue() . ")";
|
||||
}
|
||||
$this->getLogger()->warning("Chunk $chunkX $chunkZ: Deleted unknown entity type $saveId");
|
||||
$logger->warning("Deleted unknown entity type $saveId");
|
||||
}
|
||||
//TODO: we can't prevent entities getting added to unloaded chunks if they were saved in the wrong place
|
||||
//here, because entities currently add themselves to the world
|
||||
@ -2478,14 +2501,16 @@ class World implements ChunkManager{
|
||||
try{
|
||||
$tile = $tileFactory->createFromData($this, $nbt);
|
||||
}catch(NbtDataException $e){
|
||||
$this->getLogger()->error("Chunk $chunkX $chunkZ: Bad tile entity data at list position $k: " . $e->getMessage());
|
||||
$this->getLogger()->logException($e);
|
||||
$logger->error("Bad tile entity data at list position $k: " . $e->getMessage());
|
||||
$logger->logException($e);
|
||||
continue;
|
||||
}
|
||||
if($tile === null){
|
||||
$this->getLogger()->warning("Chunk $chunkX $chunkZ: Deleted unknown tile entity type " . $nbt->getString("id", "<unknown>"));
|
||||
$logger->warning("Deleted unknown tile entity type " . $nbt->getString("id", "<unknown>"));
|
||||
}elseif(!$this->isChunkLoaded($tile->getPosition()->getFloorX() >> Chunk::COORD_BIT_SIZE, $tile->getPosition()->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
|
||||
$this->logger->error("Chunk $chunkX $chunkZ: Found tile saved on wrong chunk - unable to fix due to correct chunk not loaded");
|
||||
$logger->error("Found tile saved on wrong chunk - unable to fix due to correct chunk not loaded");
|
||||
}elseif($this->getTile($tilePosition = $tile->getPosition()) !== null){
|
||||
$logger->error("Cannot add tile at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z: Another tile is already at that position");
|
||||
}else{
|
||||
$this->addTile($tile);
|
||||
}
|
||||
@ -2714,33 +2739,43 @@ class World implements ChunkManager{
|
||||
}
|
||||
}
|
||||
|
||||
private function addChunkHashToPopulationRequestQueue(int $chunkHash) : void{
|
||||
if(!isset($this->chunkPopulationRequestQueueIndex[$chunkHash])){
|
||||
$this->chunkPopulationRequestQueue->enqueue($chunkHash);
|
||||
$this->chunkPopulationRequestQueueIndex[$chunkHash] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<Chunk>
|
||||
*/
|
||||
private function enqueuePopulationRequest(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$this->chunkPopulationRequestQueue->enqueue($chunkHash);
|
||||
$promise = $this->chunkPopulationRequestMap[$chunkHash] = new Promise();
|
||||
$this->addChunkHashToPopulationRequestQueue($chunkHash);
|
||||
$resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver();
|
||||
if($associatedChunkLoader === null){
|
||||
$temporaryLoader = new class implements ChunkLoader{};
|
||||
$this->registerChunkLoader($temporaryLoader, $chunkX, $chunkZ);
|
||||
$promise->onCompletion(
|
||||
$resolver->getPromise()->onCompletion(
|
||||
fn() => $this->unregisterChunkLoader($temporaryLoader, $chunkX, $chunkZ),
|
||||
static function() : void{}
|
||||
);
|
||||
}
|
||||
return $promise;
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
|
||||
private function drainPopulationRequestQueue() : void{
|
||||
$failed = [];
|
||||
while(count($this->activeChunkPopulationTasks) < $this->maxConcurrentChunkPopulationTasks && !$this->chunkPopulationRequestQueue->isEmpty()){
|
||||
$nextChunkHash = $this->chunkPopulationRequestQueue->dequeue();
|
||||
unset($this->chunkPopulationRequestQueueIndex[$nextChunkHash]);
|
||||
World::getXZ($nextChunkHash, $nextChunkX, $nextChunkZ);
|
||||
if(isset($this->chunkPopulationRequestMap[$nextChunkHash])){
|
||||
assert(!isset($this->activeChunkPopulationTasks[$nextChunkHash]), "Population for chunk $nextChunkX $nextChunkZ already running");
|
||||
$this->orderChunkPopulation($nextChunkX, $nextChunkZ, null);
|
||||
if(!isset($this->activeChunkPopulationTasks[$nextChunkHash])){
|
||||
if(
|
||||
!$this->orderChunkPopulation($nextChunkX, $nextChunkZ, null)->isResolved() &&
|
||||
!isset($this->activeChunkPopulationTasks[$nextChunkHash])
|
||||
){
|
||||
$failed[] = $nextChunkHash;
|
||||
}
|
||||
}
|
||||
@ -2749,10 +2784,37 @@ class World implements ChunkManager{
|
||||
//these requests failed even though they weren't rate limited; we can't directly re-add them to the back of the
|
||||
//queue because it would result in an infinite loop
|
||||
foreach($failed as $hash){
|
||||
$this->chunkPopulationRequestQueue->enqueue($hash);
|
||||
$this->addChunkHashToPopulationRequestQueue($hash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a chunk needs to be populated, and whether it's ready to do so.
|
||||
* @return bool[]|PromiseResolver[]|null[]
|
||||
* @phpstan-return array{?PromiseResolver<Chunk>, bool}
|
||||
*/
|
||||
private function checkChunkPopulationPreconditions(int $chunkX, int $chunkZ) : array{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$resolver = $this->chunkPopulationRequestMap[$chunkHash] ?? null;
|
||||
if($resolver !== null && isset($this->activeChunkPopulationTasks[$chunkHash])){
|
||||
//generation is already running
|
||||
return [$resolver, false];
|
||||
}
|
||||
|
||||
$temporaryChunkLoader = new class implements ChunkLoader{};
|
||||
$this->registerChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ);
|
||||
$chunk = $this->loadChunk($chunkX, $chunkZ);
|
||||
$this->unregisterChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ);
|
||||
if($chunk !== null && $chunk->isPopulated()){
|
||||
//chunk is already populated; return a pre-resolved promise that will directly fire callbacks assigned
|
||||
$resolver ??= new PromiseResolver();
|
||||
unset($this->chunkPopulationRequestMap[$chunkHash]);
|
||||
$resolver->resolve($chunk);
|
||||
return [$resolver, false];
|
||||
}
|
||||
return [$resolver, true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to initiate asynchronous generation/population of the target chunk, if it's currently reasonable to do
|
||||
* so (and if it isn't already generated/populated).
|
||||
@ -2765,17 +2827,16 @@ class World implements ChunkManager{
|
||||
* @phpstan-return Promise<Chunk>
|
||||
*/
|
||||
public function requestChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$promise = $this->chunkPopulationRequestMap[$chunkHash] ?? null;
|
||||
if($promise !== null && isset($this->activeChunkPopulationTasks[$chunkHash])){
|
||||
//generation is already running
|
||||
return $promise;
|
||||
[$resolver, $proceedWithPopulation] = $this->checkChunkPopulationPreconditions($chunkX, $chunkZ);
|
||||
if(!$proceedWithPopulation){
|
||||
return $resolver?->getPromise() ?? $this->enqueuePopulationRequest($chunkX, $chunkZ, $associatedChunkLoader);
|
||||
}
|
||||
|
||||
if(count($this->activeChunkPopulationTasks) >= $this->maxConcurrentChunkPopulationTasks){
|
||||
//too many chunks are already generating; delay resolution of the request until later
|
||||
return $promise ?? $this->enqueuePopulationRequest($chunkX, $chunkZ, $associatedChunkLoader);
|
||||
return $resolver?->getPromise() ?? $this->enqueuePopulationRequest($chunkX, $chunkZ, $associatedChunkLoader);
|
||||
}
|
||||
return $this->orderChunkPopulation($chunkX, $chunkZ, $associatedChunkLoader);
|
||||
return $this->internalOrderChunkPopulation($chunkX, $chunkZ, $associatedChunkLoader, $resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2788,87 +2849,142 @@ class World implements ChunkManager{
|
||||
*
|
||||
* @phpstan-return Promise<Chunk>
|
||||
*/
|
||||
public function orderChunkPopulation(int $x, int $z, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||
$index = World::chunkHash($x, $z);
|
||||
$promise = $this->chunkPopulationRequestMap[$index] ?? null;
|
||||
if($promise !== null && isset($this->activeChunkPopulationTasks[$index])){
|
||||
//generation is already running
|
||||
return $promise;
|
||||
}
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
if($this->isChunkLocked($x + $xx, $z + $zz)){
|
||||
//chunk is already in use by another generation request; queue the request for later
|
||||
return $promise ?? $this->enqueuePopulationRequest($x, $z, $associatedChunkLoader);
|
||||
}
|
||||
}
|
||||
public function orderChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||
[$resolver, $proceedWithPopulation] = $this->checkChunkPopulationPreconditions($chunkX, $chunkZ);
|
||||
if(!$proceedWithPopulation){
|
||||
return $resolver?->getPromise() ?? $this->enqueuePopulationRequest($chunkX, $chunkZ, $associatedChunkLoader);
|
||||
}
|
||||
|
||||
$chunk = $this->loadChunk($x, $z);
|
||||
if($chunk === null || !$chunk->isPopulated()){
|
||||
Timings::$population->startTiming();
|
||||
|
||||
$this->activeChunkPopulationTasks[$index] = true;
|
||||
if($promise === null){
|
||||
$promise = new Promise();
|
||||
$this->chunkPopulationRequestMap[$index] = $promise;
|
||||
}
|
||||
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->lockChunk($x + $xx, $z + $zz);
|
||||
}
|
||||
}
|
||||
|
||||
$task = new PopulationTask($this, $x, $z, $chunk);
|
||||
$workerId = $this->workerPool->selectWorker();
|
||||
if(!isset($this->generatorRegisteredWorkers[$workerId])){
|
||||
$this->registerGeneratorToWorker($workerId);
|
||||
}
|
||||
$this->workerPool->submitTaskToWorker($task, $workerId);
|
||||
|
||||
Timings::$population->stopTiming();
|
||||
return $promise;
|
||||
}
|
||||
|
||||
//chunk is already populated; return a pre-resolved promise that will directly fire callbacks assigned
|
||||
$result = new Promise();
|
||||
$result->resolve($chunk);
|
||||
return $result;
|
||||
return $this->internalOrderChunkPopulation($chunkX, $chunkZ, $associatedChunkLoader, $resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Chunk[] $adjacentChunks
|
||||
* @phpstan-param PromiseResolver<Chunk>|null $resolver
|
||||
* @phpstan-return Promise<Chunk>
|
||||
*/
|
||||
private function internalOrderChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader, ?PromiseResolver $resolver) : Promise{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
|
||||
Timings::$population->startTiming();
|
||||
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
if($this->isChunkLocked($chunkX + $xx, $chunkZ + $zz)){
|
||||
//chunk is already in use by another generation request; queue the request for later
|
||||
return $resolver?->getPromise() ?? $this->enqueuePopulationRequest($chunkX, $chunkZ, $associatedChunkLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->activeChunkPopulationTasks[$chunkHash] = true;
|
||||
if($resolver === null){
|
||||
$resolver = new PromiseResolver();
|
||||
$this->chunkPopulationRequestMap[$chunkHash] = $resolver;
|
||||
}
|
||||
|
||||
$chunkPopulationLockId = new ChunkLockId();
|
||||
|
||||
$temporaryChunkLoader = new class implements ChunkLoader{};
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->lockChunk($chunkX + $xx, $chunkZ + $zz, $chunkPopulationLockId);
|
||||
$this->registerChunkLoader($temporaryChunkLoader, $chunkX + $xx, $chunkZ + $zz);
|
||||
}
|
||||
}
|
||||
|
||||
$centerChunk = $this->loadChunk($chunkX, $chunkZ);
|
||||
$adjacentChunks = $this->getAdjacentChunks($chunkX, $chunkZ);
|
||||
$task = new PopulationTask(
|
||||
$this->worldId,
|
||||
$chunkX,
|
||||
$chunkZ,
|
||||
$centerChunk,
|
||||
$adjacentChunks,
|
||||
function(Chunk $centerChunk, array $adjacentChunks) use ($chunkPopulationLockId, $chunkX, $chunkZ, $temporaryChunkLoader) : void{
|
||||
if(!$this->isLoaded()){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->generateChunkCallback($chunkPopulationLockId, $chunkX, $chunkZ, $centerChunk, $adjacentChunks, $temporaryChunkLoader);
|
||||
}
|
||||
);
|
||||
$workerId = $this->workerPool->selectWorker();
|
||||
if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){
|
||||
$this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline");
|
||||
unset($this->generatorRegisteredWorkers[$workerId]);
|
||||
}
|
||||
if(!isset($this->generatorRegisteredWorkers[$workerId])){
|
||||
$this->registerGeneratorToWorker($workerId);
|
||||
}
|
||||
$this->workerPool->submitTaskToWorker($task, $workerId);
|
||||
|
||||
Timings::$population->stopTiming();
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Chunk[] $adjacentChunks chunkHash => chunk
|
||||
* @phpstan-param array<int, Chunk> $adjacentChunks
|
||||
*/
|
||||
public function generateChunkCallback(int $x, int $z, Chunk $chunk, array $adjacentChunks) : void{
|
||||
private function generateChunkCallback(ChunkLockId $chunkLockId, int $x, int $z, Chunk $chunk, array $adjacentChunks, ChunkLoader $temporaryChunkLoader) : void{
|
||||
Timings::$generationCallback->startTiming();
|
||||
if(isset($this->chunkPopulationRequestMap[$index = World::chunkHash($x, $z)]) && isset($this->activeChunkPopulationTasks[$index])){
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->unlockChunk($x + $xx, $z + $zz);
|
||||
|
||||
$dirtyChunks = 0;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->unregisterChunkLoader($temporaryChunkLoader, $x + $xx, $z + $zz);
|
||||
if(!$this->unlockChunk($x + $xx, $z + $zz, $chunkLockId)){
|
||||
$dirtyChunks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oldChunk = $this->loadChunk($x, $z);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
|
||||
foreach($adjacentChunks as $adjacentChunkHash => $adjacentChunk){
|
||||
World::getXZ($adjacentChunkHash, $xAdjacentChunk, $zAdjacentChunk);
|
||||
$this->setChunk($xAdjacentChunk, $zAdjacentChunk, $adjacentChunk);
|
||||
}
|
||||
|
||||
if(($oldChunk === null or !$oldChunk->isPopulated()) and $chunk->isPopulated()){
|
||||
(new ChunkPopulateEvent($this, $x, $z, $chunk))->call();
|
||||
|
||||
foreach($this->getChunkListeners($x, $z) as $listener){
|
||||
$listener->onChunkPopulated($x, $z, $chunk);
|
||||
}
|
||||
}
|
||||
$index = World::chunkHash($x, $z);
|
||||
if(!isset($this->chunkPopulationRequestMap[$index])){
|
||||
$this->logger->debug("Discarding population result for chunk x=$x,z=$z - promise was already broken");
|
||||
unset($this->activeChunkPopulationTasks[$index]);
|
||||
$promise = $this->chunkPopulationRequestMap[$index];
|
||||
unset($this->chunkPopulationRequestMap[$index]);
|
||||
$promise->resolve($chunk);
|
||||
}elseif(isset($this->activeChunkPopulationTasks[$index])){
|
||||
if($dirtyChunks === 0){
|
||||
$oldChunk = $this->loadChunk($x, $z);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
|
||||
foreach($adjacentChunks as $relativeChunkHash => $adjacentChunk){
|
||||
World::getXZ($relativeChunkHash, $relativeX, $relativeZ);
|
||||
if($relativeX < -1 || $relativeX > 1 || $relativeZ < -1 || $relativeZ > 1){
|
||||
throw new AssumptionFailedError("Adjacent chunks should be in range -1 ... +1 coordinates");
|
||||
}
|
||||
$this->setChunk($x + $relativeX, $z + $relativeZ, $adjacentChunk);
|
||||
}
|
||||
|
||||
if(($oldChunk === null or !$oldChunk->isPopulated()) and $chunk->isPopulated()){
|
||||
(new ChunkPopulateEvent($this, $x, $z, $chunk))->call();
|
||||
|
||||
foreach($this->getChunkListeners($x, $z) as $listener){
|
||||
$listener->onChunkPopulated($x, $z, $chunk);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->logger->debug("Discarding population result for chunk x=$x,z=$z - terrain was modified on the main thread before async population completed");
|
||||
}
|
||||
|
||||
//This needs to be in this specific spot because user code might call back to orderChunkPopulation().
|
||||
//If it does, and finds the promise, and doesn't find an active task associated with it, it will schedule
|
||||
//another PopulationTask. We don't want that because we're here processing the results.
|
||||
//We can't remove the promise from the array before setting the chunks in the world because that would lead
|
||||
//to the same problem. Therefore, it's necessary that this code be split into two if/else, with this in the
|
||||
//middle.
|
||||
unset($this->activeChunkPopulationTasks[$index]);
|
||||
|
||||
if($dirtyChunks === 0){
|
||||
$promise = $this->chunkPopulationRequestMap[$index];
|
||||
unset($this->chunkPopulationRequestMap[$index]);
|
||||
$promise->resolve($chunk);
|
||||
}else{
|
||||
//request failed, stick it back on the queue
|
||||
//we didn't resolve the promise or touch it in any way, so any fake chunk loaders are still valid and
|
||||
//don't need to be added a second time.
|
||||
$this->addChunkHashToPopulationRequestQueue($index);
|
||||
}
|
||||
|
||||
$this->drainPopulationRequestQueue();
|
||||
}
|
||||
|
@ -34,35 +34,38 @@ use pocketmine\world\World;
|
||||
use function array_map;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function intdiv;
|
||||
|
||||
/**
|
||||
* @phpstan-type OnCompletion \Closure(Chunk $centerChunk, array<int, Chunk> $adjacentChunks) : void
|
||||
*/
|
||||
class PopulationTask extends AsyncTask{
|
||||
private const TLS_KEY_WORLD = "world";
|
||||
private const TLS_KEY_ON_COMPLETION = "onCompletion";
|
||||
|
||||
/** @var int */
|
||||
public $worldId;
|
||||
/** @var int */
|
||||
private $chunkX;
|
||||
/** @var int */
|
||||
private $chunkZ;
|
||||
private int $worldId;
|
||||
private int $chunkX;
|
||||
private int $chunkZ;
|
||||
|
||||
/** @var string|null */
|
||||
public $chunk;
|
||||
private ?string $chunk;
|
||||
|
||||
private string $adjacentChunks;
|
||||
|
||||
public function __construct(World $world, int $chunkX, int $chunkZ, ?Chunk $chunk){
|
||||
$this->worldId = $world->getId();
|
||||
/**
|
||||
* @param Chunk[]|null[] $adjacentChunks
|
||||
* @phpstan-param array<int, Chunk|null> $adjacentChunks
|
||||
* @phpstan-param OnCompletion $onCompletion
|
||||
*/
|
||||
public function __construct(int $worldId, int $chunkX, int $chunkZ, ?Chunk $chunk, array $adjacentChunks, \Closure $onCompletion){
|
||||
$this->worldId = $worldId;
|
||||
$this->chunkX = $chunkX;
|
||||
$this->chunkZ = $chunkZ;
|
||||
$this->chunk = $chunk !== null ? FastChunkSerializer::serializeTerrain($chunk) : null;
|
||||
|
||||
$this->adjacentChunks = igbinary_serialize(array_map(
|
||||
fn(?Chunk $c) => $c !== null ? FastChunkSerializer::serializeTerrain($c) : null,
|
||||
$world->getAdjacentChunks($chunkX, $chunkZ)
|
||||
$adjacentChunks
|
||||
)) ?? throw new AssumptionFailedError("igbinary_serialize() returned null");
|
||||
|
||||
$this->storeLocal(self::TLS_KEY_WORLD, $world);
|
||||
$this->storeLocal(self::TLS_KEY_ON_COMPLETION, $onCompletion);
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
@ -86,10 +89,9 @@ class PopulationTask extends AsyncTask{
|
||||
|
||||
/** @var Chunk[] $resultChunks */
|
||||
$resultChunks = []; //this is just to keep phpstan's type inference happy
|
||||
foreach($chunks as $i => $c){
|
||||
$cX = (-1 + $i % 3) + $this->chunkX;
|
||||
$cZ = (-1 + intdiv($i, 3)) + $this->chunkZ;
|
||||
$resultChunks[$i] = self::setOrGenerateChunk($manager, $generator, $cX, $cZ, $c);
|
||||
foreach($chunks as $relativeChunkHash => $c){
|
||||
World::getXZ($relativeChunkHash, $relativeX, $relativeZ);
|
||||
$resultChunks[$relativeChunkHash] = self::setOrGenerateChunk($manager, $generator, $this->chunkX + $relativeX, $this->chunkZ + $relativeZ, $c);
|
||||
}
|
||||
$chunks = $resultChunks;
|
||||
|
||||
@ -103,8 +105,8 @@ class PopulationTask extends AsyncTask{
|
||||
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
|
||||
|
||||
$serialChunks = [];
|
||||
foreach($chunks as $i => $c){
|
||||
$serialChunks[$i] = $c->isTerrainDirty() ? FastChunkSerializer::serializeTerrain($c) : null;
|
||||
foreach($chunks as $relativeChunkHash => $c){
|
||||
$serialChunks[$relativeChunkHash] = $c->isTerrainDirty() ? FastChunkSerializer::serializeTerrain($c) : null;
|
||||
}
|
||||
$this->adjacentChunks = igbinary_serialize($serialChunks) ?? throw new AssumptionFailedError("igbinary_serialize() returned null");
|
||||
}
|
||||
@ -124,29 +126,28 @@ class PopulationTask extends AsyncTask{
|
||||
}
|
||||
|
||||
public function onCompletion() : void{
|
||||
/** @var World $world */
|
||||
$world = $this->fetchLocal(self::TLS_KEY_WORLD);
|
||||
if($world->isLoaded()){
|
||||
$chunk = $this->chunk !== null ?
|
||||
FastChunkSerializer::deserializeTerrain($this->chunk) :
|
||||
throw new AssumptionFailedError("Center chunk should never be null");
|
||||
/**
|
||||
* @var \Closure $onCompletion
|
||||
* @phpstan-var OnCompletion $onCompletion
|
||||
*/
|
||||
$onCompletion = $this->fetchLocal(self::TLS_KEY_ON_COMPLETION);
|
||||
|
||||
/**
|
||||
* @var string[]|null[] $serialAdjacentChunks
|
||||
* @phpstan-var array<int, string|null> $serialAdjacentChunks
|
||||
*/
|
||||
$serialAdjacentChunks = igbinary_unserialize($this->adjacentChunks);
|
||||
$adjacentChunks = [];
|
||||
foreach($serialAdjacentChunks as $i => $c){
|
||||
if($c !== null){
|
||||
$xx = -1 + $i % 3;
|
||||
$zz = -1 + intdiv($i, 3);
|
||||
$chunk = $this->chunk !== null ?
|
||||
FastChunkSerializer::deserializeTerrain($this->chunk) :
|
||||
throw new AssumptionFailedError("Center chunk should never be null");
|
||||
|
||||
$adjacentChunks[World::chunkHash($this->chunkX + $xx, $this->chunkZ + $zz)] = FastChunkSerializer::deserializeTerrain($c);
|
||||
}
|
||||
/**
|
||||
* @var string[]|null[] $serialAdjacentChunks
|
||||
* @phpstan-var array<int, string|null> $serialAdjacentChunks
|
||||
*/
|
||||
$serialAdjacentChunks = igbinary_unserialize($this->adjacentChunks);
|
||||
$adjacentChunks = [];
|
||||
foreach($serialAdjacentChunks as $relativeChunkHash => $c){
|
||||
if($c !== null){
|
||||
$adjacentChunks[$relativeChunkHash] = FastChunkSerializer::deserializeTerrain($c);
|
||||
}
|
||||
|
||||
$world->generateChunkCallback($this->chunkX, $this->chunkZ, $chunk, $adjacentChunks);
|
||||
}
|
||||
|
||||
$onCompletion($chunk, $adjacentChunks);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ echo '"leveldb",,"https://github.com/pmmp/php-leveldb.git",,"--with-leveldb='$IN
|
||||
echo '"chunkutils2",,"https://github.com/pmmp/ext-chunkutils2.git",,,"extension",' >> share/php-build/extension/definition
|
||||
echo '"morton",,"https://github.com/pmmp/ext-morton.git",,,"extension",' >> share/php-build/extension/definition
|
||||
PHP_BUILD_INSTALL_EXTENSION="\
|
||||
pthreads=@a6afc0434f91c1e9541444aef6ac7a1f16c595be \
|
||||
pthreads=@4.0.0 \
|
||||
yaml=2.2.1 \
|
||||
leveldb=@60763a09bf5c7a10376d16e25b078b99a35c5c37 \
|
||||
chunkutils2=@0.3.1 \
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,207 +0,0 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Cannot access offset \\(float\\|int\\) on mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/CrashDump.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset string on mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/CrashDump.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/CrashDump.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'type' on mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$array of static method pocketmine\\\\plugin\\\\PluginGraylist\\:\\:fromArray\\(\\) expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to int\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/ServerConfigGroup.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/ServerConfigGroup.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'git' on mixed\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/VersionInfo.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/inventory/CreativeInventory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$result of method pocketmine\\\\network\\\\mcpe\\\\compression\\\\CompressBatchPromise\\:\\:resolve\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/ChunkRequestTask.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$result of method pocketmine\\\\network\\\\mcpe\\\\compression\\\\CompressBatchPromise\\:\\:resolve\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/compression/CompressBatchTask.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\network\\\\mcpe\\\\raklib\\\\PthreadsChannelReader\\:\\:read\\(\\) should return string\\|null but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/raklib/PthreadsChannelReader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$value of static method pocketmine\\\\permission\\\\PermissionParser\\:\\:defaultFromString\\(\\) expects bool\\|string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/permission/PermissionParser.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$description of class pocketmine\\\\permission\\\\Permission constructor expects string\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/permission/PermissionParser.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Array \\(array\\<string\\>\\) does not accept mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 4
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$data of static method pocketmine\\\\permission\\\\PermissionParser\\:\\:loadPermissions\\(\\) expects array\\<string, array\\<string, mixed\\>\\>, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$haystack of function stripos expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of static method pocketmine\\\\plugin\\\\PluginEnableOrder\\:\\:fromString\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$plugin of method pocketmine\\\\plugin\\\\PluginDescription\\:\\:loadMap\\(\\) expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\plugin\\\\PluginDescription\\:\\:\\$main \\(string\\) does not accept mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\plugin\\\\PluginDescription\\:\\:\\$name \\(string\\) does not accept mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\plugin\\\\PluginDescription\\:\\:\\$srcNamespacePrefix \\(string\\) does not accept mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$code of class pocketmine\\\\resourcepacks\\\\ResourcePackException constructor expects int, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of function igbinary_unserialize expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/AsyncTask.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\scheduler\\\\AsyncTask\\:\\:\\$result \\(bool\\|float\\|int\\|string\\|null\\) does not accept mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/AsyncTask.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\thread\\\\ThreadManager\\:\\:getAll\\(\\) should return array\\<pocketmine\\\\thread\\\\Thread\\|pocketmine\\\\thread\\\\Worker\\> but returns array\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/thread/ThreadManager.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$config of static method pocketmine\\\\utils\\\\Config\\:\\:writeProperties\\(\\) expects array\\<string, bool\\|float\\|int\\|string\\>, array\\<string, mixed\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Config.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$offset of function substr expects int, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Internet.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Internet.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$trace of static method pocketmine\\\\utils\\\\Utils\\:\\:printableTrace\\(\\) expects array\\<int, array\\<string, mixed\\>\\>, array given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Part \\$errno \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$keys of function array_fill_keys expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 1 on mixed\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/format/io/region/RegionWorldProvider.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 2 on mixed\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/format/io/region/RegionWorldProvider.php
|
||||
|
@ -1,782 +0,0 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../build/make-release.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$pharPath of function pocketmine\\\\build\\\\server_phar\\\\buildPhar expects string, array\\<int, mixed\\>\\|string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../build/server-phar.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$strings of function pocketmine\\\\build\\\\server_phar\\\\preg_quote_array expects array\\<string\\>, array\\<int, string\\|false\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../build/server-phar.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/MemoryManager.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/MemoryManager.php
|
||||
|
||||
-
|
||||
message: "#^Binary operation \"\\.\" between array\\<int, mixed\\>\\|string\\|false and '/'\\|'\\\\\\\\' results in an error\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/PocketMine.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$haystack of function substr_count expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/PocketMine.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$path of function realpath expects string, string\\|false given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/PocketMine.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$version1 of function version_compare expects string, string\\|false given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/PocketMine.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in \\+, int\\|false given on the left side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$input of function yaml_parse expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/Server.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast array\\<int, mixed\\>\\|string\\|false to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/ServerConfigGroup.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Cactus.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Cactus.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Cactus.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Cactus.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Cactus.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Cactus.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getRealBlockSkyLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DaylightSensor.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getRealBlockSkyLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DaylightSensor.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getRealBlockSkyLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DaylightSensor.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DragonEgg.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$xDiff of class pocketmine\\\\world\\\\particle\\\\DragonEggTeleportParticle constructor expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DragonEgg.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int\\<0, max\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DragonEgg.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$yDiff of class pocketmine\\\\world\\\\particle\\\\DragonEggTeleportParticle constructor expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DragonEgg.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DragonEgg.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$zDiff of class pocketmine\\\\world\\\\particle\\\\DragonEggTeleportParticle constructor expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/DragonEgg.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Farmland.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Farmland.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Farmland.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/FrostedIce.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/FrostedIce.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/FrostedIce.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/FrostedIce.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/FrostedIce.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/FrostedIce.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$min of function mt_rand expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$max of function mt_rand expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Grass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentBlockLight\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Ice.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentBlockLight\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Ice.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentBlockLight\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Ice.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Leaves.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Leaves.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Leaves.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 11
|
||||
path: ../../../src/block/Liquid.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Liquid.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 11
|
||||
path: ../../../src/block/Liquid.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Liquid.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 11
|
||||
path: ../../../src/block/Liquid.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Liquid.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$min of function mt_rand expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/block/Mycelium.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$max of function mt_rand expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/block/Mycelium.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/SnowLayer.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/SnowLayer.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/SnowLayer.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Sugarcane.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Sugarcane.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Sugarcane.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Sugarcane.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Sugarcane.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Sugarcane.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\block\\\\tile\\\\Chest\\:\\:\\$pairX \\(int\\|null\\) does not accept float\\|int\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\block\\\\tile\\\\Chest\\:\\:\\$pairZ \\(int\\|null\\) does not accept float\\|int\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$value of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setInt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/block/tile/Spawnable.php
|
||||
|
||||
-
|
||||
message: "#^Array \\(array\\<class\\-string\\<pocketmine\\\\block\\\\tile\\\\Tile\\>, string\\>\\) does not accept string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/tile/TileFactory.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/BanIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/PardonIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/command/defaults/TimingsCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of function fseek expects resource, resource\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/TimingsCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/TimingsCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$stream of function stream_get_contents expects resource, resource\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/TimingsCommand.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\console\\\\ConsoleReader\\:\\:\\$stdin \\(resource\\) does not accept resource\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/console/ConsoleReader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/crafting/CraftingManagerFromDataHelper.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/data/bedrock/LegacyToStringBidirectionalIdMap.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$index of method pocketmine\\\\inventory\\\\BaseInventory\\:\\:setItem\\(\\) expects int, int\\|string given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/ExperienceManager.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/Living.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/Living.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/Living.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\entity\\\\Skin\\:\\:\\$geometryData \\(string\\) does not accept string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/Skin.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$x of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/object/FallingBlock.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$y of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/object/FallingBlock.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$z of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/object/FallingBlock.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/object/Painting.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/object/Painting.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/object/Painting.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/projectile/Projectile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$value of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setInt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/entity/projectile/Projectile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/projectile/Projectile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/projectile/Projectile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/inventory/CreativeInventory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$buffer of method pocketmine\\\\nbt\\\\BaseNbtSerializer\\:\\:read\\(\\) expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/item/Item.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/lang/Language.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$buffer of static method pocketmine\\\\network\\\\mcpe\\\\protocol\\\\serializer\\\\PacketSerializer\\:\\:decoder\\(\\) expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/convert/RuntimeBlockMapping.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\network\\\\mcpe\\\\encryption\\\\EncryptionUtils\\:\\:generateKey\\(\\) should return string but returns string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/encryption/EncryptionUtils.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:getPackChunk\\(\\) should return string but returns string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:getPackSize\\(\\) should return int but returns int\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:getSha256\\(\\) should return string but returns string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:\\$fileResource \\(resource\\) does not accept resource\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:\\$sha256 \\(string\\|null\\) does not accept string\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getNextRun\\(\\) on array\\<string, int\\|pocketmine\\\\scheduler\\\\TaskHandler\\>\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/TaskScheduler.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Filesystem.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$abbr of function timezone_name_from_abbr expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Timezone.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Timezone.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function trim expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Timezone.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$timezoneId of function date_default_timezone_set expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Timezone.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'data' on array\\('priority' \\=\\> int, 'data' \\=\\> pocketmine\\\\math\\\\Vector3\\)\\|int\\|pocketmine\\\\math\\\\Vector3\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'priority' on array\\('priority' \\=\\> int, 'data' \\=\\> pocketmine\\\\math\\\\Vector3\\)\\|int\\|pocketmine\\\\math\\\\Vector3\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$x of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$y of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$z of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in %%, int\\|false given on the left side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/format/io/region/RegionLoader.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type array\\<int, string\\>\\|false supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/format/io/region/RegionWorldProvider.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$start of method pocketmine\\\\utils\\\\Random\\:\\:nextRange\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/generator/object/TallGrass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$end of method pocketmine\\\\utils\\\\Random\\:\\:nextRange\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/generator/object/TallGrass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\ChunkManager\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/generator/object/TallGrass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\ChunkManager\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/object/TallGrass.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/block/BlockTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/block/regenerate_consistency_check.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$logFile of class pocketmine\\\\utils\\\\MainLogger constructor expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/scheduler/AsyncPoolTest.php
|
||||
|
@ -1,432 +0,0 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
|
||||
count: 2
|
||||
path: ../../../build/make-release.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/Block.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of class pocketmine\\\\math\\\\Vector3 constructor expects float\\|int, int\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, int\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$value of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setInt\\(\\) expects int, int\\|null given\\.$#"
|
||||
count: 4
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of class pocketmine\\\\math\\\\Vector3 constructor expects float\\|int, int\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, int\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/tile/Chest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$replace of function str_replace expects array\\|string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/Command.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method startTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/SimpleCommandMap.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method stopTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/SimpleCommandMap.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method addParticle\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/ParticleCommand.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getSeed\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/SeedCommand.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setSpawnLocation\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/SetWorldSpawnCommand.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getTime\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/TimeCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$recipe of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects pocketmine\\\\crafting\\\\CraftingRecipe, pocketmine\\\\crafting\\\\CraftingRecipe\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/inventory/transaction/CraftingTransaction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$repetitions of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects int, int\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/inventory/transaction/CraftingTransaction.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method doFirstSpawn\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getLanguage\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getLocation\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getUsedChunkStatus\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getUsername\\(\\) on pocketmine\\\\player\\\\PlayerInfo\\|null\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getUuid\\(\\) on pocketmine\\\\player\\\\PlayerInfo\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method sendData\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setImmobile\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method syncAll\\(\\) on pocketmine\\\\network\\\\mcpe\\\\InventoryManager\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$clientPub of class pocketmine\\\\network\\\\mcpe\\\\encryption\\\\PrepareEncryptionTask constructor expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:onEntityEffectAdded\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:onEntityEffectRemoved\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAdventureSettings\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\DeathPacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\InGamePacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$target of method pocketmine\\\\command\\\\Command\\:\\:testPermissionSilent\\(\\) expects pocketmine\\\\command\\\\CommandSender, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\PreSpawnPacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$playerInfo of method pocketmine\\\\Server\\:\\:createPlayer\\(\\) expects pocketmine\\\\player\\\\PlayerInfo, pocketmine\\\\player\\\\PlayerInfo\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$inventoryManager of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\InGamePacketHandler constructor expects pocketmine\\\\network\\\\mcpe\\\\InventoryManager, pocketmine\\\\network\\\\mcpe\\\\InventoryManager\\|null given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$inventoryManager of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\PreSpawnPacketHandler constructor expects pocketmine\\\\network\\\\mcpe\\\\InventoryManager, pocketmine\\\\network\\\\mcpe\\\\InventoryManager\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/network/mcpe/NetworkSession.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\permission\\\\DefaultPermissions\\:\\:registerPermission\\(\\) should return pocketmine\\\\permission\\\\Permission but returns pocketmine\\\\permission\\\\Permission\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/permission/DefaultPermissions.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getSpawnLocation\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/player/Player.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\player\\\\Player\\:\\:getSpawn\\(\\) should return pocketmine\\\\world\\\\Position but returns pocketmine\\\\world\\\\Position\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/player/Player.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\plugin\\\\PluginBase\\:\\:getConfig\\(\\) should return pocketmine\\\\utils\\\\Config but returns pocketmine\\\\utils\\\\Config\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginBase.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method addChild\\(\\) on pocketmine\\\\permission\\\\Permission\\|null\\.$#"
|
||||
count: 4
|
||||
path: ../../../src/plugin/PluginManager.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method count\\(\\) on ArrayObject\\<int, array\\<string, mixed\\>\\>\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/AsyncTask.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getNotifier\\(\\) on pocketmine\\\\scheduler\\\\AsyncWorker\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/AsyncTask.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method handleException\\(\\) on pocketmine\\\\scheduler\\\\AsyncWorker\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/AsyncTask.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getAsyncWorkerId\\(\\) on pocketmine\\\\scheduler\\\\AsyncWorker\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/DumpWorkerMemoryTask.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getLogger\\(\\) on pocketmine\\\\scheduler\\\\AsyncWorker\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/DumpWorkerMemoryTask.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\utils\\\\Config\\:\\:fixYAMLIndexes\\(\\) should return string but returns string\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Config.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\utils\\\\Utils\\:\\:printable\\(\\) should return string but returns string\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in /, float\\|null given on the left side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$chunk of method pocketmine\\\\player\\\\Player\\:\\:onChunkChanged\\(\\) expects pocketmine\\\\world\\\\format\\\\Chunk, pocketmine\\\\world\\\\format\\\\Chunk\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\world\\\\biome\\\\BiomeRegistry\\:\\:getBiome\\(\\) should return pocketmine\\\\world\\\\biome\\\\Biome but returns pocketmine\\\\world\\\\biome\\\\Biome\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/biome/BiomeRegistry.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:getSubChunk\\(\\) should return pocketmine\\\\world\\\\format\\\\SubChunk but returns pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:get\\(\\) should return int but returns int\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/format/HeightArray.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\world\\\\generator\\\\biome\\\\BiomeSelector\\:\\:pickBiome\\(\\) should return pocketmine\\\\world\\\\biome\\\\Biome but returns pocketmine\\\\world\\\\biome\\\\Biome\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/biome/BiomeSelector.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/hell/Nether.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/hell/Nether.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/generator/hell/Nether.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/normal/Normal.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/normal/Normal.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/generator/normal/Normal.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/populator/GroundCover.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/generator/populator/GroundCover.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/generator/populator/GroundCover.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getBlockLightArray\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/BlockLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/BlockLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getSubChunks\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/BlockLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in \\-, int\\|null given on the right side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/BlockLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$newLevel of method pocketmine\\\\world\\\\light\\\\LightUpdate\\:\\:setAndUpdateLight\\(\\) expects int, int\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/BlockLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/LightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in \\-, int\\|null given on the right side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/LightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getBlockSkyLightArray\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getFullBlock\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getHeightMap\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 6
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getHeightMapArray\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getSubChunk\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getSubChunks\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setHeightMap\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method setHeightMapArray\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, bool\\|null given\\.$#"
|
||||
count: 3
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in \\+, int\\|false given on the left side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in \\-, int\\|null given on the right side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$chunk of static method pocketmine\\\\world\\\\light\\\\SkyLightUpdate\\:\\:recalculateHeightMap\\(\\) expects pocketmine\\\\world\\\\format\\\\Chunk, pocketmine\\\\world\\\\format\\\\Chunk\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$chunk of static method pocketmine\\\\world\\\\light\\\\SkyLightUpdate\\:\\:recalculateHeightMapColumn\\(\\) expects pocketmine\\\\world\\\\format\\\\Chunk, pocketmine\\\\world\\\\format\\\\Chunk\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\event\\\\HandlerListManagerTest\\:\\:\\$isValidFunc \\(Closure\\(ReflectionClass\\<pocketmine\\\\event\\\\Event\\>\\)\\: bool\\) does not accept Closure\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/event/HandlerListManagerTest.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\event\\\\HandlerListManagerTest\\:\\:\\$resolveParentFunc \\(Closure\\(ReflectionClass\\<pocketmine\\\\event\\\\Event\\>\\)\\: ReflectionClass\\<pocketmine\\\\event\\\\Event\\>\\|null\\) does not accept Closure\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/event/HandlerListManagerTest.php
|
||||
|
@ -5,16 +5,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/block/BaseBanner.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(pocketmine\\\\block\\\\Block\\|null\\)\\: bool given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/BlockFactory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(pocketmine\\\\player\\\\Player\\)\\: bool given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/command/defaults/ListCommand.php
|
||||
|
||||
-
|
||||
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
|
||||
count: 2
|
||||
@ -25,25 +15,15 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/crafting/CraftingManager.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function usort expects callable\\(mixed, mixed\\)\\: int, array\\('pocketmine\\\\\\\\crafting\\\\\\\\CraftingManager', 'sort'\\) given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/crafting/CraftingManager.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\crafting\\\\CraftingManager\\:\\:\\$destructorCallbacks \\(pocketmine\\\\utils\\\\ObjectSet\\<Closure\\(\\)\\: void\\>\\|null\\) does not accept pocketmine\\\\utils\\\\ObjectSet\\<object\\>\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/crafting/CraftingManager.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(pocketmine\\\\entity\\\\Attribute\\)\\: bool given\\.$#"
|
||||
message: "#^Property pocketmine\\\\crash\\\\CrashDumpData\\:\\:\\$extensions \\(array\\<string, string\\>\\) does not accept array\\<int\\|string, string\\>\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/AttributeMap.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(pocketmine\\\\item\\\\Item\\)\\: bool given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/entity/Human.php
|
||||
path: ../../../src/crash/CrashDump.php
|
||||
|
||||
-
|
||||
message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#"
|
||||
@ -51,14 +31,9 @@ parameters:
|
||||
path: ../../../src/entity/projectile/Projectile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(string\\)\\: bool given\\.$#"
|
||||
message: "#^Dead catch \\- RuntimeException is never thrown in the try block\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/lang/Language.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function usort expects callable\\(mixed, mixed\\)\\: int, Closure\\(pocketmine\\\\utils\\\\VersionString, pocketmine\\\\utils\\\\VersionString\\)\\: int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/plugin/ApiVersion.php
|
||||
path: ../../../src/plugin/PluginManager.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$closure of static method pocketmine\\\\utils\\\\Utils\\:\\:getNiceClosureName\\(\\) expects Closure\\(\\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*, \\*NEVER\\*\\)\\: mixed, Closure\\(TEvent of pocketmine\\\\event\\\\Event\\)\\: void given\\.$#"
|
||||
@ -70,23 +45,8 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(pocketmine\\\\entity\\\\Entity\\)\\: bool given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/format/io/region/RegionLoader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$count of function array_fill expects int\\<0, max\\>, int given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/world/generator/noise/Noise.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed, mixed\\)\\: bool\\)\\|null, Closure\\(pocketmine\\\\world\\\\format\\\\io\\\\WorldProviderManagerEntry\\)\\: bool given\\.$#"
|
||||
count: 1
|
||||
path: ../../../tools/convert-world.php
|
||||
|
||||
|
@ -10,6 +10,11 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/world/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\)\\: mixed\\)\\|null, Closure\\(pocketmine\\\\world\\\\format\\\\SubChunk\\)\\: pocketmine\\\\world\\\\format\\\\SubChunk given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:getValues\\(\\) should return array\\<int, int\\> but returns array\\<int, int\\|null\\>\\.$#"
|
||||
count: 1
|
||||
|
171
tools/simulate-chunk-selector.php
Normal file
171
tools/simulate-chunk-selector.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\tools\simulate_chunk_selector;
|
||||
|
||||
use pocketmine\player\ChunkSelector;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\World;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function fwrite;
|
||||
use function getopt;
|
||||
use function imagearc;
|
||||
use function imagecolorallocate;
|
||||
use function imagecreatetruecolor;
|
||||
use function imagefill;
|
||||
use function imagefilledrectangle;
|
||||
use function imagepng;
|
||||
use function imagerectangle;
|
||||
use function imagesavealpha;
|
||||
use function is_dir;
|
||||
use function is_string;
|
||||
use function mkdir;
|
||||
use function realpath;
|
||||
use function scandir;
|
||||
use function str_pad;
|
||||
use function strval;
|
||||
use const SCANDIR_SORT_NONE;
|
||||
use const STDERR;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
function newImage(int $scale, int $radius) : \GdImage{
|
||||
$image = imagecreatetruecolor($scale * $radius * 2, $scale * $radius * 2);
|
||||
if($image === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
imagesavealpha($image, true);
|
||||
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
if($black === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
imagefill($image, 0, 0, $black);
|
||||
return $image;
|
||||
}
|
||||
|
||||
function render(int $radius, int $baseX, int $baseZ, int $chunksPerStep, int $scale, \GdImage $image, int $chunkColor, int $offsetX, int $offsetZ, string $outputFolder) : void{
|
||||
echo "Render start: radius $radius, chunks per step $chunksPerStep\n";
|
||||
$iterator = (new ChunkSelector())->selectChunks($radius, $baseX, $baseZ);
|
||||
|
||||
$middleOffsetX = $scale * ($radius + $offsetX);
|
||||
$middleOffsetZ = $scale * ($radius + $offsetZ);
|
||||
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
$yellow = imagecolorallocate($image, 255, 255, 51);
|
||||
$red = imagecolorallocate($image, 255, 0, 0);
|
||||
if($black === false || $yellow === false || $red === false) throw new AssumptionFailedError();
|
||||
|
||||
$frame = 0;
|
||||
$seen = [];
|
||||
while($iterator->valid()){
|
||||
$frame++;
|
||||
|
||||
for($i = 0; $i < $chunksPerStep; ++$i){
|
||||
$chunkHash = $iterator->current();
|
||||
if(!isset($seen[$chunkHash])){
|
||||
$color = $chunkColor;
|
||||
$seen[$chunkHash] = true;
|
||||
}else{
|
||||
$color = $yellow;
|
||||
}
|
||||
World::getXZ($chunkHash, $chunkX, $chunkZ);
|
||||
$imageX = $middleOffsetX + (($chunkX - $baseX) * $scale);
|
||||
$imageZ = $middleOffsetZ + (($chunkZ - $baseZ) * $scale);
|
||||
|
||||
imagefilledrectangle($image, $imageX, $imageZ, $imageX + $scale, $imageZ + $scale, $color);
|
||||
imagerectangle($image, $imageX, $imageZ, $imageX + $scale, $imageZ + $scale, $black);
|
||||
|
||||
$iterator->next();
|
||||
if(!$iterator->valid()){
|
||||
break;
|
||||
}
|
||||
}
|
||||
imagearc($image, $middleOffsetX, $middleOffsetZ, $radius * $scale * 2, $radius * $scale * 2, 0, 360, $red);
|
||||
|
||||
imagepng($image, Path::join($outputFolder, "frame" . str_pad(strval($frame), 5, "0", STR_PAD_LEFT) . ".png"));
|
||||
echo "\rRendered step $frame";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$radius = null;
|
||||
$baseX = 10000 >> Chunk::COORD_BIT_SIZE;
|
||||
$baseZ = 10000 >> Chunk::COORD_BIT_SIZE;
|
||||
|
||||
$nChunksPerStep = 32;
|
||||
$scale = 10;
|
||||
|
||||
if(count(getopt("", ["help"])) !== 0){
|
||||
echo "Required parameters:\n";
|
||||
echo "--output=path/to/dir: Output folder to put the generated images into (will attempt to create if it doesn't exist)\n";
|
||||
echo "--radius=N: Radius of chunks to render (default $radius)\n";
|
||||
echo "\n";
|
||||
echo "Optional parameters:\n";
|
||||
echo "--baseX=N: Base X coordinate to use for simulation (default $baseX\n";
|
||||
echo "--baseZ=N: Base Z coordinate to use for simulation (default $baseZ)\n";
|
||||
echo "--scale=N: Height/width of square of pixels to use for each chunk (default $scale)\n";
|
||||
echo "--chunksPerStep=N: Number of chunks to process in each frame (default $nChunksPerStep)\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
foreach(getopt("", ["radius:", "baseX:", "baseZ:", "scale:", "chunksPerStep:"]) as $name => $value){
|
||||
if(!is_string($value) || (string) ((int) $value) !== $value){
|
||||
fwrite(STDERR, "Value for --$name must be an integer\n");
|
||||
exit(1);
|
||||
}
|
||||
$value = (int) $value;
|
||||
match($name){
|
||||
"radius" => ($radius = $value),
|
||||
"baseX" => ($baseX = $value),
|
||||
"baseZ" => ($baseZ = $value),
|
||||
"scale" => ($scale = $value),
|
||||
"chunksPerStep" => ($nChunksPerStep = $value),
|
||||
default => throw new AssumptionFailedError("getopt() returned unknown option")
|
||||
};
|
||||
}
|
||||
if($radius === null){
|
||||
fwrite(STDERR, "Please specify a radius using --radius\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$outputDirectory = null;
|
||||
foreach(getopt("", ["output:"]) as $name => $value){
|
||||
assert($name === "output");
|
||||
if(!is_string($value)){
|
||||
fwrite(STDERR, "Value for --$name must be a string\n");
|
||||
exit(1);
|
||||
}
|
||||
if(!@mkdir($value) && !is_dir($value)){
|
||||
fwrite(STDERR, "Output directory $value could not be created\n");
|
||||
exit(1);
|
||||
}
|
||||
$files = scandir($value, SCANDIR_SORT_NONE);
|
||||
if($files !== false && count($files) > 2){ //always returns . and ..
|
||||
fwrite(STDERR, "Output directory $value is not empty\n");
|
||||
exit(1);
|
||||
}
|
||||
$realPath = realpath($value);
|
||||
if($realPath === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
$outputDirectory = $realPath;
|
||||
}
|
||||
if($outputDirectory === null){
|
||||
fwrite(STDERR, "Please specify an output directory using --output\n");
|
||||
exit(1);
|
||||
}
|
||||
$image = newImage($scale, $radius);
|
||||
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
$green = imagecolorallocate($image, 0, 220, 0);
|
||||
if($black === false || $green === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
render($radius, $baseX, $baseZ, $nChunksPerStep, $scale, $image, $green, 0, 0, $outputDirectory);
|
Reference in New Issue
Block a user