mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-13 04:45:08 +00:00
Compare commits
251 Commits
4.0.0-BETA
...
4.0.0-BETA
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
428bd5ae91 | |||
d17cd65803 | |||
a8d5e8c5f6 | |||
19f448d074 | |||
1c18c731ef | |||
8a2ecfe1d4 | |||
089e62b44e | |||
32a34d2494 | |||
ee9f5e0044 | |||
88b7389080 | |||
f1cc168d26 | |||
fb5543a2ad | |||
a4eda9a8f5 | |||
eb75df6f8e | |||
c66790b6a6 | |||
d78801b9d5 | |||
d410db4302 | |||
a62ce64fdd | |||
5db3915aad | |||
eb40b741ae | |||
b3720b3f17 | |||
7effa03ba4 | |||
d0474ccd92 | |||
2b0768f720 | |||
dba148cfaa | |||
48f77abe7e | |||
bb05af103d | |||
0ef5c67b9b | |||
6d89265510 | |||
a7d8a598e1 | |||
51fbff204b | |||
1873457840 | |||
fca70efbb1 | |||
8f88393184 | |||
b9d9b69bbe | |||
1d99cd329a | |||
bd8cba1a7f | |||
24d4daec90 | |||
4178c81209 | |||
94f4ef5862 | |||
2e2515354c | |||
359d0835f3 | |||
d4cbde6f10 | |||
a5418a019d | |||
baba25953f | |||
d53347454b | |||
401e8d117b | |||
9835d75f65 | |||
b8519d1af4 | |||
f6e53f826b | |||
42ede30e77 | |||
04aedc6494 | |||
701a71a4ee | |||
e50072dc27 | |||
c77829f4ad | |||
c773e43eda | |||
c262c2e726 | |||
a4b65d6a3f | |||
986b4e0651 | |||
2971bf30a5 | |||
4f2bcb61d6 | |||
e2275cc8ec | |||
620874d902 | |||
44508a138f | |||
aa408c9a97 | |||
6d78a0b435 | |||
76b4b23d98 | |||
03fcd844eb | |||
fecc13f362 | |||
9646128d01 | |||
a788954551 | |||
dc07ac33d3 | |||
ec3986827c | |||
09c840b66a | |||
80b402e529 | |||
a3f8546ac4 | |||
46920818b5 | |||
69cb575789 | |||
fee6478cbe | |||
9c5cec77b1 | |||
f48b703533 | |||
70636f6eb4 | |||
c70b80c273 | |||
a794d24c81 | |||
8db5732b44 | |||
48f809d3fa | |||
0348236860 | |||
8c07748100 | |||
06e7338ff9 | |||
bdbfa70558 | |||
7a4af7a0bc | |||
34b1392598 | |||
321345fcc8 | |||
0ac9f4fe61 | |||
2db53775e0 | |||
8523f0fb0b | |||
b570324288 | |||
6284cd14c7 | |||
ce8af4e3bc | |||
b65e89b605 | |||
d3f74d6ce1 | |||
bbd925abc4 | |||
4bf6205a6c | |||
b5699679ef | |||
824a89edfe | |||
ead9aae23c | |||
aefa0afd7c | |||
ec2699ffee | |||
49c1e4c06e | |||
9b94a4661b | |||
62f11360ee | |||
a5833327f0 | |||
d73ea8efe4 | |||
835e18ce6e | |||
01c0602043 | |||
8fd475f87b | |||
34f54750c8 | |||
092aabeb97 | |||
89d7b7198f | |||
70deea0ef9 | |||
859cdfa5d2 | |||
fa93a8d78f | |||
7b6632941d | |||
e62794e4cf | |||
500c298aaf | |||
8ac16345a3 | |||
19a66a8d03 | |||
6d728e8d98 | |||
e1ee320c8d | |||
965a16d19d | |||
5bae458a91 | |||
2696698926 | |||
912e612743 | |||
fd2df637b6 | |||
aa53dc6709 | |||
c1f843a42c | |||
09715906c8 | |||
13068ba3a7 | |||
b54854529f | |||
974d08efd6 | |||
289553fa46 | |||
e38866c4ba | |||
58a95f8836 | |||
ccc881ee58 | |||
308d7c126a | |||
4910250a81 | |||
e0d2e24698 | |||
d5f02a0bf8 | |||
2a3a57c519 | |||
5115387feb | |||
dd0aaf59b5 | |||
a555f21b18 | |||
1be9b2f037 | |||
32fd9879e5 | |||
dc2e8e7e8f | |||
847e24fc41 | |||
e032b8fe20 |
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 }}
|
||||
|
13
.github/workflows/main.yml
vendored
13
.github/workflows/main.yml
vendored
@ -238,11 +238,10 @@ jobs:
|
||||
- name: Regenerate KnownTranslation APIs
|
||||
run: php build/generate-known-translation-apis.php
|
||||
|
||||
- name: Run git diff
|
||||
run: git diff
|
||||
|
||||
- name: Fail job if changes were made
|
||||
run: git diff --quiet
|
||||
- name: Verify code is unchanged
|
||||
run: |
|
||||
git diff
|
||||
git diff --quiet
|
||||
|
||||
codestyle:
|
||||
name: Code Style checks
|
||||
@ -254,10 +253,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.9.0
|
||||
uses: shivammathur/setup-php@2.15.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer
|
||||
tools: php-cs-fixer:3.2
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff
|
||||
|
2
.github/workflows/update-php-versions.php
vendored
2
.github/workflows/update-php-versions.php
vendored
@ -22,8 +22,6 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const VERSIONS = [
|
||||
"7.3",
|
||||
"7.4",
|
||||
"8.0"
|
||||
];
|
||||
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,6 +7,3 @@
|
||||
[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
|
||||
|
@ -61,6 +61,11 @@ return (new PhpCsFixer\Config)
|
||||
],
|
||||
'sort_algorithm' => 'alpha'
|
||||
],
|
||||
'phpdoc_line_span' => [
|
||||
'property' => 'single',
|
||||
'method' => null,
|
||||
'const' => null
|
||||
],
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_trim_consecutive_blank_line_separation' => true,
|
||||
'single_import_per_statement' => true,
|
||||
|
@ -2,13 +2,13 @@
|
||||
## Pre-requisites
|
||||
- A bash shell (git bash is sufficient for Windows)
|
||||
- [`git`](https://git-scm.com) available in your shell
|
||||
- PHP 7.4 or newer available in your shell
|
||||
- PHP 8.0 or newer available in your shell
|
||||
- [`composer`](https://getcomposer.org) available in your shell
|
||||
|
||||
## Custom PHP binaries
|
||||
Because PocketMine-MP requires several non-standard PHP extensions and configuration, PMMP provides scripts to build custom binaries for running PocketMine-MP, as well as prebuilt binaries.
|
||||
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.4-Aggregate)
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-8.0-Aggregate)
|
||||
- [Compile scripts](https://github.com/pmmp/php-build-scripts) are provided as a submodule in the path `build/php`
|
||||
|
||||
If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`.
|
||||
@ -34,7 +34,7 @@ There is a bug in PHP that might cause an error which looks like this:
|
||||
```
|
||||
Fatal error: Uncaught BadMethodCallException: unable to create temporary file in PocketMine-MP/build/server-phar.php:119
|
||||
```
|
||||
You can work around it by setting `ulimit -n` to some bigger number, e.g. `8192`, or by updating your PHP version to at least 7.4.16 or 8.0.3.
|
||||
You can work around it by setting `ulimit -n` to some bigger number, e.g. `8192`, or by updating your PHP version to at least 8.0.3.
|
||||
|
||||
## Running PocketMine-MP from source code
|
||||
Run `src/PocketMine.php` using your preferred PHP binary.
|
||||
|
@ -25,18 +25,26 @@ namespace pocketmine\build\make_release;
|
||||
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\VersionInfo;
|
||||
use function count;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function dirname;
|
||||
use function fgets;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function getopt;
|
||||
use function is_string;
|
||||
use function max;
|
||||
use function preg_replace;
|
||||
use function sleep;
|
||||
use function sprintf;
|
||||
use function str_pad;
|
||||
use function strlen;
|
||||
use function system;
|
||||
use const STDERR;
|
||||
use const STDIN;
|
||||
use const STDOUT;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
@ -60,22 +68,38 @@ function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev
|
||||
file_put_contents($versionInfoPath, $versionInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $argv
|
||||
* @phpstan-param list<string> $argv
|
||||
*/
|
||||
function main(array $argv) : void{
|
||||
if(count($argv) < 2){
|
||||
fwrite(STDERR, "Arguments: <channel> [release version] [next version]\n");
|
||||
exit(1);
|
||||
const ACCEPTED_OPTS = [
|
||||
"current" => "Version to insert and tag",
|
||||
"next" => "Version to put in the file after tagging",
|
||||
"channel" => "Release channel to post this build into"
|
||||
];
|
||||
|
||||
function main() : void{
|
||||
$filteredOpts = [];
|
||||
foreach(getopt("", ["current:", "next:", "channel:", "help"]) as $optName => $optValue){
|
||||
if($optName === "help"){
|
||||
fwrite(STDOUT, "Options:\n");
|
||||
|
||||
$maxLength = max(array_map(fn(string $str) => strlen($str), array_keys(ACCEPTED_OPTS)));
|
||||
foreach(ACCEPTED_OPTS as $acceptedName => $description){
|
||||
fwrite(STDOUT, str_pad("--$acceptedName", $maxLength + 4, " ", STR_PAD_LEFT) . ": $description\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
if(!is_string($optValue)){
|
||||
fwrite(STDERR, "--$optName expects exactly 1 value\n");
|
||||
exit(1);
|
||||
}
|
||||
$filteredOpts[$optName] = $optValue;
|
||||
}
|
||||
if(isset($argv[2])){
|
||||
$currentVer = new VersionString($argv[2]);
|
||||
|
||||
if(isset($filteredOpts["current"])){
|
||||
$currentVer = new VersionString($filteredOpts["current"]);
|
||||
}else{
|
||||
$currentVer = VersionInfo::VERSION();
|
||||
$currentVer = new VersionString(VersionInfo::BASE_VERSION);
|
||||
}
|
||||
if(isset($argv[3])){
|
||||
$nextVer = new VersionString($argv[3]);
|
||||
if(isset($filteredOpts["next"])){
|
||||
$nextVer = new VersionString($filteredOpts["next"]);
|
||||
}else{
|
||||
$nextVer = new VersionString(sprintf(
|
||||
"%u.%u.%u",
|
||||
@ -84,8 +108,10 @@ function main(array $argv) : void{
|
||||
$currentVer->getPatch() + 1
|
||||
));
|
||||
}
|
||||
$channel = $filteredOpts["channel"] ?? VersionInfo::BUILD_CHANNEL;
|
||||
|
||||
echo "About to tag version $currentVer. Next version will be $nextVer.\n";
|
||||
echo "$currentVer will be published on release channel \"$channel\".\n";
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
system('git add "' . dirname(__DIR__) . '/changelogs"');
|
||||
@ -95,10 +121,10 @@ function main(array $argv) : void{
|
||||
exit(1);
|
||||
}
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $argv[1]);
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
|
||||
system('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"');
|
||||
system('git tag ' . $currentVer->getBaseVersion());
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, "");
|
||||
replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
|
||||
system('git add "' . $versionInfoPath . '"');
|
||||
system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"');
|
||||
echo "pushing changes in 5 seconds\n";
|
||||
@ -106,4 +132,4 @@ function main(array $argv) : void{
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
}
|
||||
|
||||
main($argv);
|
||||
main();
|
||||
|
Submodule build/php updated: 7069f05845...58ea62d7ca
12
changelogs/3.24.md
Normal file
12
changelogs/3.24.md
Normal file
@ -0,0 +1,12 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.30**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.24.0
|
||||
- PHP 8.0 is now required as a minimum.
|
||||
- Fixed stats reporting checking the wrong `pocketmine.yml` property.
|
||||
- Fixed `Projectile->move()` not respecting the given `dx`/`dy`/`dz` and using its own motion instead.
|
23
changelogs/3.25.md
Normal file
23
changelogs/3.25.md
Normal file
@ -0,0 +1,23 @@
|
||||
**For Minecraft: Bedrock Edition 1.17.40**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 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.
|
@ -329,6 +329,8 @@ However, if we add `src-namespace-prefix: pmmp\TesterPlugin` to the `plugin.yml`
|
||||
- The following methods have signature changes:
|
||||
- `Entity->entityBaseTick()` is now `protected`.
|
||||
- `Entity->move()` is now `protected`.
|
||||
- `Entity->setPosition()` is now `protected` (use `Entity->teleport()` instead).
|
||||
- `Entity->setPositionAndRotation()` is now `protected` (use `Entity->teleport()` instead).
|
||||
- `Living->knockBack()` now accepts `float, float, float` (the first two parameters have been removed).
|
||||
- `Living->getEffects()` now returns `EffectManager` instead of `Effect[]`.
|
||||
- The following classes have been added:
|
||||
@ -1145,7 +1147,7 @@ However, if we add `src-namespace-prefix: pmmp\TesterPlugin` to the `plugin.yml`
|
||||
|
||||
#### Particles
|
||||
- `DestroyBlockParticle` has been renamed to `BlockBreakParticle` for consistency.
|
||||
- `DustParticle->__construct()` now accepts a `pocketmine\utils\Color` object instead of `r, g, b, a`.
|
||||
- `DustParticle->__construct()` now accepts a `pocketmine\color\Color` object instead of `r, g, b, a`.
|
||||
- `pocketmine\world\particle\Particle` no longer extends `pocketmine\math\Vector3`, and has been converted to an interface.
|
||||
- Added the following `Particle` classes:
|
||||
- `DragonEggTeleportParticle`
|
||||
@ -1272,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.
|
||||
@ -1391,3 +1395,215 @@ Released 6th October 2021.
|
||||
### Utils
|
||||
- The following API methods have signature changes:
|
||||
- `Process::kill()` now requires an additional `bool $subprocesses` parameter.
|
||||
|
||||
# 4.0.0-BETA5
|
||||
Released 12th October 2021.
|
||||
|
||||
## General
|
||||
- Exception log format has been changed. Now, exception info is logged in one big block message. This saves space on the console and improves readability, as well as reducing the ability for bad `ThreadedLoggerAttachment`s to break exception output.
|
||||
- Log messages are now pushed to `server.log` before calling logger attachment, instead of after. This fixes messages not being written to disk when an error occurs in a logger attachment.
|
||||
- Improved startup performance when loading many plugins.
|
||||
- The `worlds` config in `pocketmine.yml` no longer supports attaching the generator settings to the `generator` key (use the `preset` key instead).
|
||||
- Using an unknown generator in `server.properties` or `pocketmine.yml` will now cause a failure to generate the world.
|
||||
- Using invalid/incorrect world generator options (presets) in `server.properties` or `pocketmine.yml` will now cause a failure to generate the world.
|
||||
- Generator options of existing worlds are now validated before loading them. If they are invalid, the server will fail to load them.
|
||||
- Several more log messages have been localized, including plugin loading errors.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when using `/give` to give an item by ID which doesn't exist in Minecraft.
|
||||
- Fixed server crash when boolean `server.properties` options were given an integer value (e.g. `0` or `1` instead of `false` or `true`).
|
||||
- Fixed stats reporting checking for a nonexistent `pocketmine.yml` setting.
|
||||
- Fixed use of commands without the proper permission sending a message `commands.generic.permission` instead of the proper message.
|
||||
- Fixed entities set on fire appearing to stay on fire, although not taking any damage.
|
||||
- Fixed a duplicate `MB` suffix on the `Memory freed` output of `/gc`.
|
||||
- Fixed the server attempting to generate a world if it failed to load.
|
||||
|
||||
## API
|
||||
### Block
|
||||
- The following API methods have been renamed:
|
||||
- `Block->getPositionOffset()` -> `Block->getModelPositionOffset()`.
|
||||
|
||||
### Event
|
||||
- `@handleCancelled` PhpDoc annotation can no longer be used on event handlers for non-cancellable events.
|
||||
- The following API methods have been added:
|
||||
- `StructureGrowEvent->getPlayer()`
|
||||
|
||||
### Inventory
|
||||
- The following API methods have been added:
|
||||
- `Inventory->getAddableItemQuantity()`
|
||||
|
||||
### Scheduler
|
||||
- `ClosureTask` now permits closures without an explicit return type (useful for arrow functions).
|
||||
|
||||
### Utils
|
||||
- The following API methods have been added:
|
||||
- `Config::parseProperties()`
|
||||
- `Config::writeProperties()`
|
||||
- `Config::parseList()`
|
||||
- `Config::writeList()`
|
||||
|
||||
### World
|
||||
- The following API methods have signature changes:
|
||||
- `GeneratorManager->registerGenerator()` now requires a `\Closure $presetValidator` parameter. This is used to check generator options of worlds and configs before attempting to use them.
|
||||
|
||||
# 4.0.0-BETA6
|
||||
Released 19th October 2021.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.17.40.
|
||||
- Removed support for earlier versions.
|
||||
- CTRL+C signal handling has been restored, and is now supported on Windows. Pressing CTRL+C while the server is running will behave as if the `/stop` command was invoked.
|
||||
- Added a script `tools/generate-permission-doc.php` to generate a Markdown file with a list of all permissions and their relationships. In the future, this will be used to maintain the official documentation, but plugin developers might find it useful for their own purposes too.
|
||||
- [`respect/validation`](https://packagist.org/packages/respect/validation) is no longer a core dependency.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when using `/give` to give an item with a too-large item ID, or `/clear` to clear an item that does not exist.
|
||||
- Now, `LegacyStringToItemParser` is used exclusively, and numeric IDs are no longer parsed.
|
||||
|
||||
## Gameplay
|
||||
- Picking up some items from a dropped stack of items is now supported. This fixes various bugs with being unable to pick up items with an almost-full inventory.
|
||||
|
||||
# 4.0.0-BETA7
|
||||
Released 28th October 2021.
|
||||
|
||||
## General
|
||||
- Phar plugins are now able to depend on folder plugins loaded by DevTools.
|
||||
- Now uses [`pocketmine/bedrock-protocol@58c53a259e819a076bf8fe875d2a012da7d19d65`](https://github.com/pmmp/BedrockProtocol/tree/58c53a259e819a076bf8fe875d2a012da7d19d65). This version features significant changes, including:
|
||||
- Standardisation of various field names (e.g. `eid` -> `actorRuntimeId`, `evid` -> `eventId`)
|
||||
- Rename of `entity` related fields to `actor` where appropriate (e.g. `entityRuntimeId` -> `actorRuntimeId`)
|
||||
- Block position `x`/`y`/`z` fields replaced by `BlockPosition`
|
||||
- Static `::create()` functions for all packets, which ensure that fields can't be forgotten
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when clients send itemstacks with unmappable dynamic item IDs.
|
||||
- Fixed server crash on invalid ItemStackRequest action types.
|
||||
- Fixed autosave bug that caused unmodified chunks to be saved at least once (during the first autosave after they were loaded).
|
||||
- Fixed `ConsoleReaderThread` returning strings with newlines still on the end.
|
||||
- Fixed changes made to adjacent chunks in `ChunkPopulateEvent` (e.g. setting blocks) sometimes getting overwritten.
|
||||
|
||||
## API
|
||||
### Event
|
||||
- `PlayerCreationEvent` now verifies that the player class set is instantiable - this ensures that plugins get properly blamed for breaking things.
|
||||
|
||||
### World
|
||||
- `World->generateChunkCallback()` has been specialized for use by `PopulationTask`. This allows fixing various inconsistencies involving `ChunkPopulateEvent` (e.g. modifications to adjacent chunks in `ChunkPopulationEvent` might be wiped out, if the population of the target chunk modified the adjacent chunk).
|
||||
- It now accepts `Chunk $centerChunk, array<int, Chunk> $adjacentChunks` (instead of `?Chunk $chunk`).
|
||||
- It's no longer expected to be used by plugins - plugins should be using `World->setChunk()` anyway.
|
||||
- `Chunk->getModificationCounter()` has been added. This is a number starting from `0` when the `Chunk` object is first created (unless overridden by the constructor). It's incremented every time blocks or biomes are changed in the chunk. It resets after the chunk is unloaded and reloaded.
|
||||
- The following API methods have changed signatures:
|
||||
- `Sound->encode()` no longer accepts `null` for the position.
|
||||
- `Chunk->__construct()`: removed `HeightArray $heightMap` parameter, added `bool $terrainPopulated` and `int $modificationCounter` parameters.
|
||||
|
||||
### Plugin
|
||||
- `PluginManager->loadPlugins()` now accepts paths to files as well as directories, in which case it will load only the plugin found in the target file.
|
||||
- The following API methods have been removed:
|
||||
- `PluginManager->loadPlugin()`: use `PluginManager->loadPlugins()` instead
|
||||
|
||||
# 4.0.0-BETA8
|
||||
Released 29th October 2021.
|
||||
|
||||
## General
|
||||
- Chunk packet caches are now cleared by the memory manager on low memory.
|
||||
- `Entity->spawnTo()` now has an additional sanity check for matching worlds (might expose a few new errors in plugins).
|
||||
- [`pocketmine/math` 0.4.0](https://github.com/pmmp/Math/releases/tag/0.4.0) is now used. Please see its release notes for changes.
|
||||
|
||||
## Fixes
|
||||
- Zlib raw check for LevelDB is now done directly on startup, avoiding crashes when later trying to load worlds.
|
||||
- Fixed tiles and entities getting deleted from adjacent chunks during chunk population.
|
||||
- Fixed players being unable to open their inventories more than once.
|
||||
- Fixed entities not getting updated when a nearby chunk is replaced (e.g. dropped items would float in midair if the ground was lower than before)
|
||||
|
||||
## API
|
||||
### World
|
||||
- `World::setChunk()` has the following changes:
|
||||
- `$deleteEntitiesAndTiles` parameter has been removed.
|
||||
- Entities are no longer deleted on chunk replacement.
|
||||
- Tiles are no longer deleted on chunk replacement, unless one of the following conditions is met:
|
||||
- the target block in the new chunk doesn't expect a tile
|
||||
- the target block in the new chunk expects a different type of tile (responsibility of the plugin developer to create the new tile)
|
||||
- there's already a tile in the target chunk which conflicts with the old one
|
||||
- `Location::__construct()` has the following changes:
|
||||
- `world` parameter is now 4th instead of last.
|
||||
- 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.
|
||||
|
@ -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,28 +34,27 @@
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-protocol": "2.0.0+bedrock1.17.30",
|
||||
"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/math": "^0.3.0",
|
||||
"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",
|
||||
"respect/validation": "^2.0",
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.98",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpstan/phpstan": "1.0.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
},
|
||||
"autoload": {
|
||||
|
503
composer.lock
generated
503
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": "77f899b35819bca38246a665fd0233f3",
|
||||
"content-hash": "3fa50836a0e8560fe59ba9e73cc50c44",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -248,33 +248,59 @@
|
||||
"time": "2020-12-01T19:48:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "2.0.0+bedrock1.17.30",
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "1.4.0+bedrock-1.17.40",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "faff7da904e68f69b1a9128956dac3122e87308a"
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/faff7da904e68f69b1a9128956dac3122e87308a",
|
||||
"reference": "faff7da904e68f69b1a9128956dac3122e87308a",
|
||||
"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": {
|
||||
"ext-json": "*",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"php": "^8.0",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/math": "^0.3.0",
|
||||
"pocketmine/math": "^0.3.0 || ^0.4.0",
|
||||
"pocketmine/nbt": "^0.3.0",
|
||||
"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"
|
||||
},
|
||||
"type": "library",
|
||||
@ -290,22 +316,22 @@
|
||||
"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/bedrock-1.17.30"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/5.0.0+bedrock-1.17.40"
|
||||
},
|
||||
"time": "2021-09-21T23:25:51+00:00"
|
||||
"time": "2021-11-02T01:27:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "8cd078e2426f8100331f2d73bef10f481dad6cde"
|
||||
"reference": "f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/8cd078e2426f8100331f2d73bef10f481dad6cde",
|
||||
"reference": "8cd078e2426f8100331f2d73bef10f481dad6cde",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9",
|
||||
"reference": "f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -314,7 +340,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.85",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
},
|
||||
"type": "library",
|
||||
@ -330,9 +356,9 @@
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.1"
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.2"
|
||||
},
|
||||
"time": "2021-05-30T19:42:57+00:00"
|
||||
"time": "2021-10-22T19:54:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/callback-validator",
|
||||
@ -386,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": {
|
||||
@ -425,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,16 +533,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Log.git",
|
||||
"reference": "03ab1316da0b1978a7a1c8dd73e1c2a973cb62ec"
|
||||
"reference": "e6c912c0f9055c81d23108ec2d179b96f404c043"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Log/zipball/03ab1316da0b1978a7a1c8dd73e1c2a973cb62ec",
|
||||
"reference": "03ab1316da0b1978a7a1c8dd73e1c2a973cb62ec",
|
||||
"url": "https://api.github.com/repos/pmmp/Log/zipball/e6c912c0f9055c81d23108ec2d179b96f404c043",
|
||||
"reference": "e6c912c0f9055c81d23108ec2d179b96f404c043",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -526,7 +552,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",
|
||||
@ -542,28 +568,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"
|
||||
@ -586,32 +612,32 @@
|
||||
"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",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "83ec067b12c066fc61d9fb129daf7e61ef3b1d63"
|
||||
"reference": "6d64e2555bd2e95ed024574f75d1cefc135c89fc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/83ec067b12c066fc61d9fb129daf7e61ef3b1d63",
|
||||
"reference": "83ec067b12c066fc61d9fb129daf7e61ef3b1d63",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/6d64e2555bd2e95ed024574f75d1cefc135c89fc",
|
||||
"reference": "6d64e2555bd2e95ed024574f75d1cefc135c89fc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"php": "^8.0",
|
||||
"php-64bit": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"irstea/phpunit-shim": "^8.5 || ^9.5",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.90",
|
||||
"phpstan/phpstan": "0.12.99",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
},
|
||||
"type": "library",
|
||||
@ -627,9 +653,9 @@
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Math/issues",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.3.0"
|
||||
"source": "https://github.com/pmmp/Math/tree/0.4.0"
|
||||
},
|
||||
"time": "2021-07-14T18:39:31+00:00"
|
||||
"time": "2021-10-29T20:33:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
@ -759,25 +785,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",
|
||||
@ -793,59 +819,22 @@
|
||||
"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",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/collection.git",
|
||||
"reference": "eaca1dc1054ddd10cbd83c1461907bee6fb528fa"
|
||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/eaca1dc1054ddd10cbd83c1461907bee6fb528fa",
|
||||
"reference": "eaca1dc1054ddd10cbd83c1461907bee6fb528fa",
|
||||
"url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
||||
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -899,7 +888,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/collection/issues",
|
||||
"source": "https://github.com/ramsey/collection/tree/1.2.1"
|
||||
"source": "https://github.com/ramsey/collection/tree/1.2.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -911,7 +900,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-06T03:41:06+00:00"
|
||||
"time": "2021-10-10T03:01:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
@ -1011,130 +1000,6 @@
|
||||
],
|
||||
"time": "2021-09-25T23:10:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "respect/stringifier",
|
||||
"version": "0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Respect/Stringifier.git",
|
||||
"reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
|
||||
"reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.8",
|
||||
"malukenho/docheader": "^0.1.7",
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Respect\\Stringifier\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/stringify.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Respect/Stringifier Contributors",
|
||||
"homepage": "https://github.com/Respect/Stringifier/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Converts any value to a string",
|
||||
"homepage": "http://respect.github.io/Stringifier/",
|
||||
"keywords": [
|
||||
"respect",
|
||||
"stringifier",
|
||||
"stringify"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Respect/Stringifier/issues",
|
||||
"source": "https://github.com/Respect/Stringifier/tree/0.2.0"
|
||||
},
|
||||
"time": "2017-12-29T19:39:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "respect/validation",
|
||||
"version": "2.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Respect/Validation.git",
|
||||
"reference": "4c21a7ffc9a4915673cb2c2843963919e664e627"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Respect/Validation/zipball/4c21a7ffc9a4915673cb2c2843963919e664e627",
|
||||
"reference": "4c21a7ffc9a4915673cb2c2843963919e664e627",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"respect/stringifier": "^0.2.0",
|
||||
"symfony/polyfill-mbstring": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"egulias/email-validator": "^3.0",
|
||||
"malukenho/docheader": "^0.1",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12",
|
||||
"phpstan/phpstan-phpunit": "^0.12",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"psr/http-message": "^1.0",
|
||||
"respect/coding-standard": "^3.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/validator": "^3.0||^4.0",
|
||||
"zendframework/zend-validator": "^2.1"
|
||||
},
|
||||
"suggest": {
|
||||
"egulias/email-validator": "Strict (RFC compliant) email validation",
|
||||
"ext-bcmath": "Arbitrary Precision Mathematics",
|
||||
"ext-fileinfo": "File Information",
|
||||
"ext-mbstring": "Multibyte String Functions",
|
||||
"symfony/validator": "Use Symfony validator through Respect\\Validation",
|
||||
"zendframework/zend-validator": "Use Zend Framework validator through Respect\\Validation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Respect\\Validation\\": "library/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Respect/Validation Contributors",
|
||||
"homepage": "https://github.com/Respect/Validation/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "The most awesome validation engine ever created for PHP",
|
||||
"homepage": "http://respect.github.io/Validation/",
|
||||
"keywords": [
|
||||
"respect",
|
||||
"validation",
|
||||
"validator"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Respect/Validation/issues",
|
||||
"source": "https://github.com/Respect/Validation/tree/2.2.3"
|
||||
},
|
||||
"time": "2021-03-19T14:12:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.23.0",
|
||||
@ -1214,86 +1079,6 @@
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.23.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6",
|
||||
"reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-27T12:26:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.23.1",
|
||||
@ -1915,16 +1700,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "5.2.2",
|
||||
"version": "5.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||
"reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
|
||||
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
|
||||
"reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
|
||||
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1935,7 +1720,8 @@
|
||||
"webmozart/assert": "^1.9.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1.3.2"
|
||||
"mockery/mockery": "~1.3.2",
|
||||
"psalm/phar": "^4.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -1965,22 +1751,22 @@
|
||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
|
||||
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
|
||||
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
|
||||
},
|
||||
"time": "2020-09-03T19:13:55+00:00"
|
||||
"time": "2021-10-19T17:43:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f"
|
||||
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f",
|
||||
"reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae",
|
||||
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2015,9 +1801,9 @@
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1"
|
||||
},
|
||||
"time": "2021-09-17T15:28:14+00:00"
|
||||
"time": "2021-10-02T14:08:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
@ -2088,16 +1874,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "0.12.98",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "3bb7cc246c057405dd5e290c3ecc62ab51d57e00"
|
||||
"reference": "0d13a99513182e521271d46bde8f28caa4f84d97"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/3bb7cc246c057405dd5e290c3ecc62ab51d57e00",
|
||||
"reference": "3bb7cc246c057405dd5e290c3ecc62ab51d57e00",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d13a99513182e521271d46bde8f28caa4f84d97",
|
||||
"reference": "0d13a99513182e521271d46bde8f28caa4f84d97",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2113,7 +1899,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.12-dev"
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2128,7 +1914,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.98"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.0.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2148,38 +1934,39 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-02T12:33:01+00:00"
|
||||
"time": "2021-11-01T06:38:20+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": [
|
||||
@ -2200,37 +1987,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": [
|
||||
@ -2250,29 +2038,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",
|
||||
@ -2321,7 +2109,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": [
|
||||
{
|
||||
@ -2329,7 +2117,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-17T05:39:03+00:00"
|
||||
"time": "2021-10-30T08:01:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -3692,10 +3480,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"pocketmine/classloader": 20,
|
||||
"pocketmine/spl": 20
|
||||
},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@ -3716,7 +3501,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:
|
||||
@ -57,5 +53,5 @@ parameters:
|
||||
#variadics don't work for this - mixed probably shouldn't work either, but for now it does
|
||||
#what we actually need is something that accepts an infinite number of parameters, but in the absence of that,
|
||||
#we'll just fill it with 10 - it's very unlikely to encounter a callable with 10 parameters anyway.
|
||||
anyCallable: 'callable(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed) : mixed'
|
||||
anyClosure: '\Closure(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed) : mixed'
|
||||
anyCallable: 'callable(never, never, never, never, never, never, never, never, never, never) : mixed'
|
||||
anyClosure: '\Closure(never, never, never, never, never, never, never, never, never, never) : mixed'
|
||||
|
@ -1,4 +1,691 @@
|
||||
{
|
||||
"-2": -2,
|
||||
"-3": -3,
|
||||
"-4": -4,
|
||||
"-5": -5,
|
||||
"-6": -6,
|
||||
"-7": -7,
|
||||
"-8": -8,
|
||||
"-9": -9,
|
||||
"-10": -10,
|
||||
"-11": -11,
|
||||
"-12": -12,
|
||||
"-13": -13,
|
||||
"-14": -14,
|
||||
"-15": -15,
|
||||
"-16": -16,
|
||||
"-17": -17,
|
||||
"-18": -18,
|
||||
"-19": -19,
|
||||
"-20": -20,
|
||||
"-21": -21,
|
||||
"-22": -22,
|
||||
"-23": -23,
|
||||
"-24": -24,
|
||||
"-25": -25,
|
||||
"-26": -26,
|
||||
"-27": -27,
|
||||
"-28": -28,
|
||||
"-29": -29,
|
||||
"-30": -30,
|
||||
"-31": -31,
|
||||
"-32": -32,
|
||||
"-33": -33,
|
||||
"-34": -34,
|
||||
"-35": -35,
|
||||
"-36": -36,
|
||||
"-37": -37,
|
||||
"-38": -38,
|
||||
"-39": -39,
|
||||
"-40": -40,
|
||||
"-41": -41,
|
||||
"-42": -42,
|
||||
"-43": -43,
|
||||
"-44": -44,
|
||||
"-45": -45,
|
||||
"-46": -46,
|
||||
"-47": -47,
|
||||
"-48": -48,
|
||||
"-49": -49,
|
||||
"-50": -50,
|
||||
"-51": -51,
|
||||
"-52": -52,
|
||||
"-53": -53,
|
||||
"-54": -54,
|
||||
"-55": -55,
|
||||
"-56": -56,
|
||||
"-57": -57,
|
||||
"-58": -58,
|
||||
"-59": -59,
|
||||
"-60": -60,
|
||||
"-61": -61,
|
||||
"-62": -62,
|
||||
"-63": -63,
|
||||
"-64": -64,
|
||||
"-65": -65,
|
||||
"-66": -66,
|
||||
"-67": -67,
|
||||
"-68": -68,
|
||||
"-69": -69,
|
||||
"-70": -70,
|
||||
"-71": -71,
|
||||
"-72": -72,
|
||||
"-73": -73,
|
||||
"-74": -74,
|
||||
"-75": -75,
|
||||
"-76": -76,
|
||||
"-77": -77,
|
||||
"-78": -78,
|
||||
"-79": -79,
|
||||
"-80": -80,
|
||||
"-81": -81,
|
||||
"-82": -82,
|
||||
"-83": -83,
|
||||
"-84": -84,
|
||||
"-85": -85,
|
||||
"-86": -86,
|
||||
"-87": -87,
|
||||
"-88": -88,
|
||||
"-89": -89,
|
||||
"-90": -90,
|
||||
"-91": -91,
|
||||
"-92": -92,
|
||||
"-93": -93,
|
||||
"-94": -94,
|
||||
"-95": -95,
|
||||
"-96": -96,
|
||||
"-97": -97,
|
||||
"-98": -98,
|
||||
"-99": -99,
|
||||
"-100": -100,
|
||||
"-101": -101,
|
||||
"-102": -102,
|
||||
"-103": -103,
|
||||
"-104": -104,
|
||||
"-105": -105,
|
||||
"-106": -106,
|
||||
"-107": -107,
|
||||
"-108": -108,
|
||||
"-109": -109,
|
||||
"-110": -110,
|
||||
"-111": -111,
|
||||
"-112": -112,
|
||||
"-113": -113,
|
||||
"-114": -114,
|
||||
"-115": -115,
|
||||
"-116": -116,
|
||||
"-117": -117,
|
||||
"-118": -118,
|
||||
"-119": -119,
|
||||
"-120": -120,
|
||||
"-121": -121,
|
||||
"-122": -122,
|
||||
"-123": -123,
|
||||
"-124": -124,
|
||||
"-125": -125,
|
||||
"-126": -126,
|
||||
"-127": -127,
|
||||
"-128": -128,
|
||||
"-129": -129,
|
||||
"-130": -130,
|
||||
"-131": -131,
|
||||
"-132": -132,
|
||||
"-133": -133,
|
||||
"-134": -134,
|
||||
"-135": -135,
|
||||
"-136": -136,
|
||||
"-137": -137,
|
||||
"-138": -138,
|
||||
"-139": -139,
|
||||
"-140": -140,
|
||||
"-141": -141,
|
||||
"-142": -142,
|
||||
"-143": -143,
|
||||
"-144": -144,
|
||||
"-145": -145,
|
||||
"-146": -146,
|
||||
"-147": -147,
|
||||
"-148": -148,
|
||||
"-149": -149,
|
||||
"-150": -150,
|
||||
"-151": -151,
|
||||
"-152": -152,
|
||||
"-153": -153,
|
||||
"-154": -154,
|
||||
"-155": -155,
|
||||
"-156": -156,
|
||||
"-157": -157,
|
||||
"-159": -159,
|
||||
"-160": -160,
|
||||
"-161": -161,
|
||||
"-162": -162,
|
||||
"-163": -163,
|
||||
"-164": -164,
|
||||
"-165": -165,
|
||||
"-166": -166,
|
||||
"-167": -167,
|
||||
"-168": -168,
|
||||
"-169": -169,
|
||||
"-170": -170,
|
||||
"-171": -171,
|
||||
"-172": -172,
|
||||
"-173": -173,
|
||||
"-174": -174,
|
||||
"-175": -175,
|
||||
"-176": -176,
|
||||
"-177": -177,
|
||||
"-178": -178,
|
||||
"-179": -179,
|
||||
"-180": -180,
|
||||
"-181": -181,
|
||||
"-182": -182,
|
||||
"-183": -183,
|
||||
"-184": -184,
|
||||
"-185": -185,
|
||||
"-186": -186,
|
||||
"-187": -187,
|
||||
"-188": -188,
|
||||
"-189": -189,
|
||||
"-190": -190,
|
||||
"-191": -191,
|
||||
"-192": -192,
|
||||
"-193": -193,
|
||||
"-194": -194,
|
||||
"-195": -195,
|
||||
"-196": -196,
|
||||
"-197": -197,
|
||||
"-198": -198,
|
||||
"-199": -199,
|
||||
"-200": -200,
|
||||
"-201": -201,
|
||||
"-202": -202,
|
||||
"-203": -203,
|
||||
"-204": -204,
|
||||
"-206": -206,
|
||||
"-207": -207,
|
||||
"-208": -208,
|
||||
"-209": -209,
|
||||
"-210": -210,
|
||||
"-211": -211,
|
||||
"-213": -213,
|
||||
"-214": -214,
|
||||
"0": 0,
|
||||
"1": 1,
|
||||
"2": 2,
|
||||
"3": 3,
|
||||
"4": 4,
|
||||
"5": 5,
|
||||
"6": 6,
|
||||
"7": 7,
|
||||
"8": 8,
|
||||
"9": 9,
|
||||
"10": 10,
|
||||
"11": 11,
|
||||
"12": 12,
|
||||
"13": 13,
|
||||
"14": 14,
|
||||
"15": 15,
|
||||
"16": 16,
|
||||
"17": 17,
|
||||
"18": 18,
|
||||
"19": 19,
|
||||
"20": 20,
|
||||
"21": 21,
|
||||
"22": 22,
|
||||
"23": 23,
|
||||
"24": 24,
|
||||
"25": 25,
|
||||
"26": 26,
|
||||
"27": 27,
|
||||
"28": 28,
|
||||
"29": 29,
|
||||
"30": 30,
|
||||
"31": 31,
|
||||
"32": 32,
|
||||
"33": 33,
|
||||
"34": 34,
|
||||
"35": 35,
|
||||
"36": 36,
|
||||
"37": 37,
|
||||
"38": 38,
|
||||
"39": 39,
|
||||
"40": 40,
|
||||
"41": 41,
|
||||
"42": 42,
|
||||
"43": 43,
|
||||
"44": 44,
|
||||
"45": 45,
|
||||
"46": 46,
|
||||
"47": 47,
|
||||
"48": 48,
|
||||
"49": 49,
|
||||
"50": 50,
|
||||
"51": 51,
|
||||
"52": 52,
|
||||
"53": 53,
|
||||
"54": 54,
|
||||
"55": 55,
|
||||
"56": 56,
|
||||
"57": 57,
|
||||
"58": 58,
|
||||
"59": 59,
|
||||
"60": 60,
|
||||
"61": 61,
|
||||
"62": 62,
|
||||
"63": 63,
|
||||
"64": 64,
|
||||
"65": 65,
|
||||
"66": 66,
|
||||
"67": 67,
|
||||
"68": 68,
|
||||
"69": 69,
|
||||
"70": 70,
|
||||
"71": 71,
|
||||
"72": 72,
|
||||
"73": 73,
|
||||
"74": 74,
|
||||
"75": 75,
|
||||
"76": 76,
|
||||
"77": 77,
|
||||
"78": 78,
|
||||
"79": 79,
|
||||
"80": 80,
|
||||
"81": 81,
|
||||
"82": 82,
|
||||
"83": 83,
|
||||
"84": 84,
|
||||
"85": 85,
|
||||
"86": 86,
|
||||
"87": 87,
|
||||
"88": 88,
|
||||
"89": 89,
|
||||
"90": 90,
|
||||
"91": 91,
|
||||
"92": 92,
|
||||
"93": 93,
|
||||
"94": 94,
|
||||
"95": 95,
|
||||
"96": 96,
|
||||
"97": 97,
|
||||
"98": 98,
|
||||
"99": 99,
|
||||
"100": 100,
|
||||
"101": 101,
|
||||
"102": 102,
|
||||
"103": 103,
|
||||
"104": 104,
|
||||
"105": 105,
|
||||
"106": 106,
|
||||
"107": 107,
|
||||
"108": 108,
|
||||
"109": 109,
|
||||
"110": 110,
|
||||
"111": 111,
|
||||
"112": 112,
|
||||
"113": 113,
|
||||
"114": 114,
|
||||
"115": 115,
|
||||
"116": 116,
|
||||
"117": 117,
|
||||
"118": 118,
|
||||
"119": 119,
|
||||
"120": 120,
|
||||
"121": 121,
|
||||
"122": 122,
|
||||
"123": 123,
|
||||
"124": 124,
|
||||
"125": 125,
|
||||
"126": 126,
|
||||
"127": 127,
|
||||
"128": 128,
|
||||
"129": 129,
|
||||
"130": 130,
|
||||
"131": 131,
|
||||
"132": 132,
|
||||
"133": 133,
|
||||
"134": 134,
|
||||
"135": 135,
|
||||
"136": 136,
|
||||
"137": 137,
|
||||
"138": 138,
|
||||
"139": 139,
|
||||
"140": 140,
|
||||
"141": 141,
|
||||
"142": 142,
|
||||
"143": 143,
|
||||
"144": 144,
|
||||
"145": 145,
|
||||
"146": 146,
|
||||
"147": 147,
|
||||
"148": 148,
|
||||
"149": 149,
|
||||
"150": 150,
|
||||
"151": 151,
|
||||
"152": 152,
|
||||
"153": 153,
|
||||
"154": 154,
|
||||
"155": 155,
|
||||
"156": 156,
|
||||
"157": 157,
|
||||
"158": 158,
|
||||
"159": 159,
|
||||
"160": 160,
|
||||
"161": 161,
|
||||
"162": 162,
|
||||
"163": 163,
|
||||
"164": 164,
|
||||
"165": 165,
|
||||
"166": 166,
|
||||
"167": 167,
|
||||
"168": 168,
|
||||
"169": 169,
|
||||
"170": 170,
|
||||
"171": 171,
|
||||
"172": 172,
|
||||
"173": 173,
|
||||
"174": 174,
|
||||
"175": 175,
|
||||
"176": 176,
|
||||
"177": 177,
|
||||
"178": 178,
|
||||
"179": 179,
|
||||
"180": 180,
|
||||
"181": 181,
|
||||
"182": 182,
|
||||
"183": 183,
|
||||
"184": 184,
|
||||
"185": 185,
|
||||
"186": 186,
|
||||
"187": 187,
|
||||
"188": 188,
|
||||
"189": 189,
|
||||
"190": 190,
|
||||
"191": 191,
|
||||
"192": 192,
|
||||
"193": 193,
|
||||
"194": 194,
|
||||
"195": 195,
|
||||
"196": 196,
|
||||
"197": 197,
|
||||
"198": 198,
|
||||
"199": 199,
|
||||
"200": 200,
|
||||
"201": 201,
|
||||
"202": 202,
|
||||
"203": 203,
|
||||
"204": 204,
|
||||
"205": 205,
|
||||
"206": 206,
|
||||
"207": 207,
|
||||
"208": 208,
|
||||
"209": 209,
|
||||
"213": 213,
|
||||
"214": 214,
|
||||
"215": 215,
|
||||
"216": 216,
|
||||
"218": 218,
|
||||
"219": 219,
|
||||
"220": 220,
|
||||
"221": 221,
|
||||
"222": 222,
|
||||
"223": 223,
|
||||
"224": 224,
|
||||
"225": 225,
|
||||
"226": 226,
|
||||
"227": 227,
|
||||
"228": 228,
|
||||
"229": 229,
|
||||
"231": 231,
|
||||
"232": 232,
|
||||
"233": 233,
|
||||
"234": 234,
|
||||
"235": 235,
|
||||
"236": 236,
|
||||
"237": 237,
|
||||
"238": 238,
|
||||
"239": 239,
|
||||
"240": 240,
|
||||
"241": 241,
|
||||
"243": 243,
|
||||
"244": 244,
|
||||
"245": 245,
|
||||
"246": 246,
|
||||
"247": 247,
|
||||
"248": 248,
|
||||
"249": 249,
|
||||
"250": 250,
|
||||
"251": 251,
|
||||
"252": 252,
|
||||
"253": 253,
|
||||
"254": 254,
|
||||
"255": 255,
|
||||
"256": 256,
|
||||
"257": 257,
|
||||
"258": 258,
|
||||
"259": 259,
|
||||
"260": 260,
|
||||
"261": 261,
|
||||
"262": 262,
|
||||
"263": 263,
|
||||
"264": 264,
|
||||
"265": 265,
|
||||
"266": 266,
|
||||
"267": 267,
|
||||
"268": 268,
|
||||
"269": 269,
|
||||
"270": 270,
|
||||
"271": 271,
|
||||
"272": 272,
|
||||
"273": 273,
|
||||
"274": 274,
|
||||
"275": 275,
|
||||
"276": 276,
|
||||
"277": 277,
|
||||
"278": 278,
|
||||
"279": 279,
|
||||
"280": 280,
|
||||
"281": 281,
|
||||
"282": 282,
|
||||
"283": 283,
|
||||
"284": 284,
|
||||
"285": 285,
|
||||
"286": 286,
|
||||
"287": 287,
|
||||
"288": 288,
|
||||
"289": 289,
|
||||
"290": 290,
|
||||
"291": 291,
|
||||
"292": 292,
|
||||
"293": 293,
|
||||
"294": 294,
|
||||
"295": 295,
|
||||
"296": 296,
|
||||
"297": 297,
|
||||
"298": 298,
|
||||
"299": 299,
|
||||
"300": 300,
|
||||
"301": 301,
|
||||
"302": 302,
|
||||
"303": 303,
|
||||
"304": 304,
|
||||
"305": 305,
|
||||
"306": 306,
|
||||
"307": 307,
|
||||
"308": 308,
|
||||
"309": 309,
|
||||
"310": 310,
|
||||
"311": 311,
|
||||
"312": 312,
|
||||
"313": 313,
|
||||
"314": 314,
|
||||
"315": 315,
|
||||
"316": 316,
|
||||
"317": 317,
|
||||
"318": 318,
|
||||
"319": 319,
|
||||
"320": 320,
|
||||
"321": 321,
|
||||
"322": 322,
|
||||
"323": 323,
|
||||
"324": 324,
|
||||
"325": 325,
|
||||
"328": 328,
|
||||
"329": 329,
|
||||
"330": 330,
|
||||
"331": 331,
|
||||
"332": 332,
|
||||
"333": 333,
|
||||
"334": 334,
|
||||
"335": 335,
|
||||
"336": 336,
|
||||
"337": 337,
|
||||
"338": 338,
|
||||
"339": 339,
|
||||
"340": 340,
|
||||
"341": 341,
|
||||
"342": 342,
|
||||
"344": 344,
|
||||
"345": 345,
|
||||
"346": 346,
|
||||
"347": 347,
|
||||
"348": 348,
|
||||
"349": 349,
|
||||
"350": 350,
|
||||
"351": 351,
|
||||
"352": 352,
|
||||
"353": 353,
|
||||
"354": 354,
|
||||
"355": 355,
|
||||
"356": 356,
|
||||
"357": 357,
|
||||
"358": 358,
|
||||
"359": 359,
|
||||
"360": 360,
|
||||
"361": 361,
|
||||
"362": 362,
|
||||
"363": 363,
|
||||
"364": 364,
|
||||
"365": 365,
|
||||
"366": 366,
|
||||
"367": 367,
|
||||
"368": 368,
|
||||
"369": 369,
|
||||
"370": 370,
|
||||
"371": 371,
|
||||
"372": 372,
|
||||
"373": 373,
|
||||
"374": 374,
|
||||
"375": 375,
|
||||
"376": 376,
|
||||
"377": 377,
|
||||
"378": 378,
|
||||
"379": 379,
|
||||
"380": 380,
|
||||
"381": 381,
|
||||
"382": 382,
|
||||
"383": 383,
|
||||
"384": 384,
|
||||
"385": 385,
|
||||
"386": 386,
|
||||
"387": 387,
|
||||
"388": 388,
|
||||
"389": 389,
|
||||
"390": 390,
|
||||
"391": 391,
|
||||
"392": 392,
|
||||
"393": 393,
|
||||
"394": 394,
|
||||
"395": 395,
|
||||
"396": 396,
|
||||
"397": 397,
|
||||
"398": 398,
|
||||
"399": 399,
|
||||
"400": 400,
|
||||
"401": 401,
|
||||
"402": 402,
|
||||
"403": 403,
|
||||
"404": 404,
|
||||
"405": 405,
|
||||
"406": 406,
|
||||
"407": 407,
|
||||
"408": 408,
|
||||
"409": 409,
|
||||
"410": 410,
|
||||
"411": 411,
|
||||
"412": 412,
|
||||
"413": 413,
|
||||
"414": 414,
|
||||
"415": 415,
|
||||
"416": 416,
|
||||
"417": 417,
|
||||
"418": 418,
|
||||
"419": 419,
|
||||
"420": 420,
|
||||
"421": 421,
|
||||
"422": 422,
|
||||
"423": 423,
|
||||
"424": 424,
|
||||
"425": 425,
|
||||
"426": 426,
|
||||
"427": 427,
|
||||
"428": 428,
|
||||
"429": 429,
|
||||
"430": 430,
|
||||
"431": 431,
|
||||
"432": 432,
|
||||
"433": 433,
|
||||
"434": 434,
|
||||
"437": 437,
|
||||
"438": 438,
|
||||
"441": 441,
|
||||
"442": 442,
|
||||
"443": 443,
|
||||
"444": 444,
|
||||
"445": 445,
|
||||
"446": 446,
|
||||
"447": 447,
|
||||
"448": 448,
|
||||
"449": 449,
|
||||
"450": 450,
|
||||
"451": 451,
|
||||
"452": 452,
|
||||
"453": 453,
|
||||
"455": 455,
|
||||
"457": 457,
|
||||
"458": 458,
|
||||
"459": 459,
|
||||
"460": 460,
|
||||
"461": 461,
|
||||
"462": 462,
|
||||
"463": 463,
|
||||
"464": 464,
|
||||
"465": 465,
|
||||
"466": 466,
|
||||
"467": 467,
|
||||
"468": 468,
|
||||
"469": 469,
|
||||
"470": 470,
|
||||
"471": 471,
|
||||
"472": 472,
|
||||
"473": 473,
|
||||
"474": 474,
|
||||
"475": 475,
|
||||
"476": 476,
|
||||
"477": 477,
|
||||
"499": 499,
|
||||
"500": 500,
|
||||
"501": 501,
|
||||
"502": 502,
|
||||
"503": 503,
|
||||
"504": 504,
|
||||
"505": 505,
|
||||
"506": 506,
|
||||
"507": 507,
|
||||
"508": 508,
|
||||
"509": 509,
|
||||
"510": 510,
|
||||
"511": 511,
|
||||
"513": 513,
|
||||
"acacia_button": -140,
|
||||
"acacia_door": 430,
|
||||
"acacia_door_block": 196,
|
||||
@ -226,27 +913,16 @@
|
||||
"egg": 344,
|
||||
"element_0": 36,
|
||||
"element_1": -12,
|
||||
"element_2": -13,
|
||||
"element_3": -14,
|
||||
"element_4": -15,
|
||||
"element_5": -16,
|
||||
"element_6": -17,
|
||||
"element_7": -18,
|
||||
"element_8": -19,
|
||||
"element_9": -20,
|
||||
"element_10": -21,
|
||||
"element_100": -111,
|
||||
"element_101": -112,
|
||||
"element_102": -113,
|
||||
"element_103": -114,
|
||||
"element_104": -115,
|
||||
"element_105": -116,
|
||||
"element_106": -117,
|
||||
"element_107": -118,
|
||||
"element_108": -119,
|
||||
"element_109": -120,
|
||||
"element_11": -22,
|
||||
"element_110": -121,
|
||||
"element_111": -122,
|
||||
"element_112": -123,
|
||||
"element_113": -124,
|
||||
"element_114": -125,
|
||||
"element_115": -126,
|
||||
"element_116": -127,
|
||||
"element_117": -128,
|
||||
"element_118": -129,
|
||||
"element_12": -23,
|
||||
"element_13": -24,
|
||||
"element_14": -25,
|
||||
@ -255,7 +931,6 @@
|
||||
"element_17": -28,
|
||||
"element_18": -29,
|
||||
"element_19": -30,
|
||||
"element_2": -13,
|
||||
"element_20": -31,
|
||||
"element_21": -32,
|
||||
"element_22": -33,
|
||||
@ -266,7 +941,6 @@
|
||||
"element_27": -38,
|
||||
"element_28": -39,
|
||||
"element_29": -40,
|
||||
"element_3": -14,
|
||||
"element_30": -41,
|
||||
"element_31": -42,
|
||||
"element_32": -43,
|
||||
@ -277,7 +951,6 @@
|
||||
"element_37": -48,
|
||||
"element_38": -49,
|
||||
"element_39": -50,
|
||||
"element_4": -15,
|
||||
"element_40": -51,
|
||||
"element_41": -52,
|
||||
"element_42": -53,
|
||||
@ -288,7 +961,6 @@
|
||||
"element_47": -58,
|
||||
"element_48": -59,
|
||||
"element_49": -60,
|
||||
"element_5": -16,
|
||||
"element_50": -61,
|
||||
"element_51": -62,
|
||||
"element_52": -63,
|
||||
@ -299,7 +971,6 @@
|
||||
"element_57": -68,
|
||||
"element_58": -69,
|
||||
"element_59": -70,
|
||||
"element_6": -17,
|
||||
"element_60": -71,
|
||||
"element_61": -72,
|
||||
"element_62": -73,
|
||||
@ -310,7 +981,6 @@
|
||||
"element_67": -78,
|
||||
"element_68": -79,
|
||||
"element_69": -80,
|
||||
"element_7": -18,
|
||||
"element_70": -81,
|
||||
"element_71": -82,
|
||||
"element_72": -83,
|
||||
@ -321,7 +991,6 @@
|
||||
"element_77": -88,
|
||||
"element_78": -89,
|
||||
"element_79": -90,
|
||||
"element_8": -19,
|
||||
"element_80": -91,
|
||||
"element_81": -92,
|
||||
"element_82": -93,
|
||||
@ -332,7 +1001,6 @@
|
||||
"element_87": -98,
|
||||
"element_88": -99,
|
||||
"element_89": -100,
|
||||
"element_9": -20,
|
||||
"element_90": -101,
|
||||
"element_91": -102,
|
||||
"element_92": -103,
|
||||
@ -343,6 +1011,25 @@
|
||||
"element_97": -108,
|
||||
"element_98": -109,
|
||||
"element_99": -110,
|
||||
"element_100": -111,
|
||||
"element_101": -112,
|
||||
"element_102": -113,
|
||||
"element_103": -114,
|
||||
"element_104": -115,
|
||||
"element_105": -116,
|
||||
"element_106": -117,
|
||||
"element_107": -118,
|
||||
"element_108": -119,
|
||||
"element_109": -120,
|
||||
"element_110": -121,
|
||||
"element_111": -122,
|
||||
"element_112": -123,
|
||||
"element_113": -124,
|
||||
"element_114": -125,
|
||||
"element_115": -126,
|
||||
"element_116": -127,
|
||||
"element_117": -128,
|
||||
"element_118": -129,
|
||||
"elytra": 444,
|
||||
"emerald": 388,
|
||||
"emerald_block": 133,
|
||||
@ -532,8 +1219,8 @@
|
||||
"leather_pants": 300,
|
||||
"leather_tunic": 299,
|
||||
"leave": 18,
|
||||
"leaves": 18,
|
||||
"leave2": 161,
|
||||
"leaves": 18,
|
||||
"leaves2": 161,
|
||||
"lectern": -194,
|
||||
"lever": 69,
|
||||
|
Submodule resources/locale updated: c940b9f171...f9076e4a6e
Submodule resources/vanilla deleted from 19569dd729
@ -35,4 +35,5 @@ 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\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__) . '/vendor/autoload.php');
|
||||
|
@ -101,9 +101,12 @@ class CrashDump{
|
||||
/** @var string */
|
||||
private $path;
|
||||
|
||||
public function __construct(Server $server){
|
||||
private ?PluginManager $pluginManager;
|
||||
|
||||
public function __construct(Server $server, ?PluginManager $pluginManager){
|
||||
$this->time = microtime(true);
|
||||
$this->server = $server;
|
||||
$this->pluginManager = $pluginManager;
|
||||
|
||||
$crashPath = Path::join($this->server->getDataPath(), "crashdumps");
|
||||
if(!is_dir($crashPath)){
|
||||
@ -166,11 +169,11 @@ class CrashDump{
|
||||
}
|
||||
|
||||
private function pluginsData() : void{
|
||||
if($this->server->getPluginManager() instanceof PluginManager){
|
||||
if($this->pluginManager !== null){
|
||||
$plugins = $this->pluginManager->getPlugins();
|
||||
$this->addLine();
|
||||
$this->addLine("Loaded plugins:");
|
||||
$this->data["plugins"] = [];
|
||||
$plugins = $this->server->getPluginManager()->getPlugins();
|
||||
ksort($plugins, SORT_STRING);
|
||||
foreach($plugins as $p){
|
||||
$d = $p->getDescription();
|
||||
@ -356,7 +359,7 @@ class CrashDump{
|
||||
$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");
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\event\server\LowMemoryEvent;
|
||||
use pocketmine\network\mcpe\cache\ChunkCache;
|
||||
use pocketmine\scheduler\DumpWorkerMemoryTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\timings\Timings;
|
||||
@ -187,6 +188,7 @@ class MemoryManager{
|
||||
foreach($this->server->getWorldManager()->getWorlds() as $world){
|
||||
$world->clearCache(true);
|
||||
}
|
||||
ChunkCache::pruneCaches();
|
||||
}
|
||||
|
||||
if($this->lowMemChunkGC){
|
||||
|
@ -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.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +126,9 @@ namespace pocketmine {
|
||||
if(version_compare($leveldb_version, "0.2.1") < 0){
|
||||
$messages[] = "php-leveldb >= 0.2.1 is required, while you have $leveldb_version.";
|
||||
}
|
||||
if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
|
||||
$messages[] = "Given version of php-leveldb doesn't support ZLIB_RAW compression (use https://github.com/pmmp/php-leveldb)";
|
||||
}
|
||||
}
|
||||
|
||||
$chunkutils2_version = phpversion("chunkutils2");
|
||||
|
255
src/Server.php
255
src/Server.php
@ -64,6 +64,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 +82,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 +100,7 @@ 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;
|
||||
use pocketmine\utils\Utils;
|
||||
@ -106,24 +109,22 @@ use pocketmine\world\format\io\WorldProviderManager;
|
||||
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
||||
use pocketmine\world\generator\Generator;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
||||
use pocketmine\world\World;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
use pocketmine\world\WorldManager;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function array_shift;
|
||||
use function array_sum;
|
||||
use function base64_encode;
|
||||
use function cli_set_process_title;
|
||||
use function copy;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
@ -252,6 +253,8 @@ class Server{
|
||||
/** @var Player[] */
|
||||
private array $playerList = [];
|
||||
|
||||
private SignalHandler $signalHandler;
|
||||
|
||||
/**
|
||||
* @var CommandSender[][]
|
||||
* @phpstan-var array<string, array<int, CommandSender>>
|
||||
@ -547,11 +550,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;
|
||||
}
|
||||
|
||||
@ -571,16 +574,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -736,7 +739,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);
|
||||
@ -745,6 +748,11 @@ class Server{
|
||||
$this->autoloader = $autoloader;
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->signalHandler = new SignalHandler(function() : void{
|
||||
$this->logger->info("Received signal interrupt, stopping the server");
|
||||
$this->shutdown();
|
||||
});
|
||||
|
||||
try{
|
||||
foreach([
|
||||
$dataPath,
|
||||
@ -924,7 +932,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);
|
||||
|
||||
@ -967,91 +975,17 @@ class Server{
|
||||
$this->pluginManager->loadPlugins($this->pluginPath);
|
||||
$this->enablePlugins(PluginEnableOrder::STARTUP());
|
||||
|
||||
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
continue;
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($name, true)){
|
||||
$creationOptions = WorldCreationOptions::create();
|
||||
//TODO: error checking
|
||||
|
||||
if(isset($options["generator"])){
|
||||
$generatorOptions = explode(":", $options["generator"]);
|
||||
$creationOptions->setGeneratorClass(GeneratorManager::getInstance()->getGenerator(array_shift($generatorOptions)));
|
||||
if(count($generatorOptions) > 0){
|
||||
$creationOptions->setGeneratorOptions(implode(":", $generatorOptions));
|
||||
}
|
||||
}
|
||||
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
|
||||
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
|
||||
}
|
||||
if(isset($options["preset"]) && is_string($options["preset"])){
|
||||
$creationOptions->setGeneratorOptions($options["preset"]);
|
||||
}
|
||||
if(isset($options["seed"])){
|
||||
$convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? ""));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
}
|
||||
|
||||
$this->worldManager->generateWorld($name, $creationOptions);
|
||||
}
|
||||
if(!$this->startupPrepareWorlds()){
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->worldManager->getDefaultWorld() === null){
|
||||
$default = $this->configGroup->getConfigString("level-name", "world");
|
||||
if(trim($default) == ""){
|
||||
$this->getLogger()->warning("level-name cannot be null, using default");
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString("level-name", "world");
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($default, true)){
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
->setGeneratorClass(GeneratorManager::getInstance()->getGenerator($this->configGroup->getConfigString("level-type")))
|
||||
->setGeneratorOptions($this->configGroup->getConfigString("generator-settings"));
|
||||
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
$this->worldManager->generateWorld($default, $creationOptions);
|
||||
}
|
||||
|
||||
$world = $this->worldManager->getWorldByName($default);
|
||||
if($world === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->forceShutdown();
|
||||
|
||||
return;
|
||||
}
|
||||
$this->worldManager->setDefaultWorld($world);
|
||||
}
|
||||
|
||||
$this->enablePlugins(PluginEnableOrder::POSTWORLD());
|
||||
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
if(!$this->network->registerInterface(new RakLibInterface($this)) && $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")));
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($this->getIp(), (string) $this->getPort())));
|
||||
|
||||
if($useQuery){
|
||||
$this->network->registerRawPacketHandler(new QueryHandler($this));
|
||||
if(!$this->startupPrepareNetworkInterfaces()){
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($this->getIPBans()->getEntries() as $entry){
|
||||
$this->network->blockAddress($entry->getName(), -1);
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
|
||||
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("settings.send-usage", true)){
|
||||
if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){
|
||||
$this->sendUsageTicker = 6000;
|
||||
$this->sendUsage(SendUsageTask::TYPE_OPEN);
|
||||
}
|
||||
@ -1086,6 +1020,134 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
private function startupPrepareWorlds() : bool{
|
||||
$getGenerator = function(string $generatorName, string $generatorOptions, string $worldName) : ?string{
|
||||
$generatorEntry = GeneratorManager::getInstance()->getGenerator($generatorName);
|
||||
if($generatorEntry === null){
|
||||
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
|
||||
$worldName,
|
||||
KnownTranslationFactory::pocketmine_level_unknownGenerator($generatorName)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
$generatorEntry->validateGeneratorOptions($generatorOptions);
|
||||
}catch(InvalidGeneratorOptionsException $e){
|
||||
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_level_generationError(
|
||||
$worldName,
|
||||
KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions($generatorOptions, $generatorName, $e->getMessage())
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
return $generatorEntry->getGeneratorClass();
|
||||
};
|
||||
|
||||
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
continue;
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($name, true) && !$this->worldManager->isWorldGenerated($name)){
|
||||
$creationOptions = WorldCreationOptions::create();
|
||||
//TODO: error checking
|
||||
|
||||
$generatorName = $options["generator"] ?? "default";
|
||||
$generatorOptions = isset($options["preset"]) && is_string($options["preset"]) ? $options["preset"] : "";
|
||||
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $name);
|
||||
if($generatorClass === null){
|
||||
continue;
|
||||
}
|
||||
$creationOptions->setGeneratorClass($generatorClass);
|
||||
$creationOptions->setGeneratorOptions($generatorOptions);
|
||||
|
||||
if(isset($options["difficulty"]) && is_string($options["difficulty"])){
|
||||
$creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"]));
|
||||
}
|
||||
|
||||
if(isset($options["seed"])){
|
||||
$convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? ""));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
}
|
||||
|
||||
$this->worldManager->generateWorld($name, $creationOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->worldManager->getDefaultWorld() === null){
|
||||
$default = $this->configGroup->getConfigString("level-name", "world");
|
||||
if(trim($default) == ""){
|
||||
$this->getLogger()->warning("level-name cannot be null, using default");
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString("level-name", "world");
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($default, true) && !$this->worldManager->isWorldGenerated($default)){
|
||||
$generatorName = $this->configGroup->getConfigString("level-type");
|
||||
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
|
||||
if($generatorClass !== null){
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
->setGeneratorClass($generatorClass)
|
||||
->setGeneratorOptions($generatorOptions);
|
||||
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
$this->worldManager->generateWorld($default, $creationOptions);
|
||||
}
|
||||
}
|
||||
|
||||
$world = $this->worldManager->getWorldByName($default);
|
||||
if($world === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->forceShutdown();
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->worldManager->setDefaultWorld($world);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function startupPrepareNetworkInterfaces() : bool{
|
||||
$useQuery = $this->configGroup->getConfigBool("enable-query", true);
|
||||
|
||||
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")));
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($this->getIp(), (string) $this->getPort())));
|
||||
|
||||
if($useQuery){
|
||||
$this->network->registerRawPacketHandler(new QueryHandler($this));
|
||||
}
|
||||
|
||||
foreach($this->getIPBans()->getEntries() as $entry){
|
||||
$this->network->blockAddress($entry->getName(), -1);
|
||||
}
|
||||
|
||||
if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){
|
||||
$this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a particular message broadcast channel.
|
||||
* The channel ID can be any arbitrary string.
|
||||
@ -1301,7 +1363,10 @@ class Server{
|
||||
* Shuts the server down correctly
|
||||
*/
|
||||
public function shutdown() : void{
|
||||
$this->isRunning = false;
|
||||
if($this->isRunning){
|
||||
$this->isRunning = false;
|
||||
$this->signalHandler->unregister();
|
||||
}
|
||||
}
|
||||
|
||||
public function forceShutdown() : void{
|
||||
@ -1425,7 +1490,7 @@ class Server{
|
||||
ini_set("memory_limit", '-1'); //Fix error dump not dumped on memory problems
|
||||
try{
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_create()));
|
||||
$dump = new CrashDump($this);
|
||||
$dump = new CrashDump($this, $this->pluginManager ?? null);
|
||||
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($dump->getPath())));
|
||||
|
||||
|
@ -27,6 +27,8 @@ use pocketmine\utils\Config;
|
||||
use function array_key_exists;
|
||||
use function getopt;
|
||||
use function is_bool;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function strtolower;
|
||||
|
||||
final class ServerConfigGroup{
|
||||
@ -110,16 +112,20 @@ final class ServerConfigGroup{
|
||||
}else{
|
||||
$value = $this->serverProperties->exists($variable) ? $this->serverProperties->get($variable) : $defaultValue;
|
||||
}
|
||||
|
||||
if(is_bool($value)){
|
||||
return $value;
|
||||
}
|
||||
switch(strtolower($value)){
|
||||
case "on":
|
||||
case "true":
|
||||
case "1":
|
||||
case "yes":
|
||||
return true;
|
||||
if(is_int($value)){
|
||||
return $value !== 0;
|
||||
}
|
||||
if(is_string($value)){
|
||||
switch(strtolower($value)){
|
||||
case "on":
|
||||
case "true":
|
||||
case "1":
|
||||
case "yes":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -29,7 +29,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.0.0-BETA4";
|
||||
public const BASE_VERSION = "4.0.0-BETA10";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_NUMBER = 0;
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
|
@ -115,7 +115,7 @@ class Bamboo extends Transparent{
|
||||
return 12 + (self::getOffsetSeed($x, 0, $z) % 5);
|
||||
}
|
||||
|
||||
public function getPositionOffset() : ?Vector3{
|
||||
public function getModelPositionOffset() : ?Vector3{
|
||||
$seed = self::getOffsetSeed($this->position->getFloorX(), 0, $this->position->getFloorZ());
|
||||
$retX = (($seed % 12) + 1) / 16;
|
||||
$retZ = ((($seed >> 8) % 12) + 1) / 16;
|
||||
@ -145,12 +145,12 @@ class Bamboo extends Transparent{
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$top = $this->seekToTop();
|
||||
if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2))){
|
||||
if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
}elseif($item instanceof ItemBamboo){
|
||||
if($this->seekToTop()->grow(PHP_INT_MAX, 1)){
|
||||
if($this->seekToTop()->grow(PHP_INT_MAX, 1, $player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
@ -165,7 +165,7 @@ class Bamboo extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow(int $maxHeight, int $growAmount) : bool{
|
||||
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||
return false;
|
||||
@ -212,7 +212,7 @@ class Bamboo extends Transparent{
|
||||
$tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock);
|
||||
}
|
||||
|
||||
$ev = new StructureGrowEvent($this, $tx);
|
||||
$ev = new StructureGrowEvent($this, $tx, $player);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
@ -229,7 +229,7 @@ class Bamboo extends Transparent{
|
||||
$world = $this->position->getWorld();
|
||||
if($this->ready){
|
||||
$this->ready = false;
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1)){
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1, null)){
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}elseif($world->getBlock($this->position->up())->canBeReplaced()){
|
||||
|
@ -73,7 +73,7 @@ final class BambooSapling extends Flowable{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer || $item instanceof ItemBamboo){
|
||||
if($this->grow()){
|
||||
if($this->grow($player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
@ -87,7 +87,7 @@ final class BambooSapling extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow() : bool{
|
||||
private function grow(?Player $player) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||
return false;
|
||||
@ -98,7 +98,7 @@ final class BambooSapling extends Flowable{
|
||||
$tx->addBlock($this->position, $bamboo)
|
||||
->addBlock($this->position->up(), (clone $bamboo)->setLeafSize(Bamboo::SMALL_LEAVES));
|
||||
|
||||
$ev = new StructureGrowEvent($this, $tx);
|
||||
$ev = new StructureGrowEvent($this, $tx, $player);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
@ -115,7 +115,7 @@ final class BambooSapling extends Flowable{
|
||||
$world = $this->position->getWorld();
|
||||
if($this->ready){
|
||||
$this->ready = false;
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow()){
|
||||
if($world->getFullLight($this->position) < 9 || !$this->grow(null)){
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}elseif($world->getBlock($this->position->up())->canBeReplaced()){
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -577,7 +577,7 @@ class Block{
|
||||
final public function getCollisionBoxes() : array{
|
||||
if($this->collisionBoxes === null){
|
||||
$this->collisionBoxes = $this->recalculateCollisionBoxes();
|
||||
$extraOffset = $this->getPositionOffset();
|
||||
$extraOffset = $this->getModelPositionOffset();
|
||||
$offset = $extraOffset !== null ? $this->position->addVector($extraOffset) : $this->position;
|
||||
foreach($this->collisionBoxes as $bb){
|
||||
$bb->offset($offset->x, $offset->y, $offset->z);
|
||||
@ -588,10 +588,10 @@ class Block{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an additional fractional vector to shift the block's effective position by based on the current position.
|
||||
* Returns an additional fractional vector to shift the block model's position by based on the current position.
|
||||
* Used to randomize position of things like bamboo canes and tall grass.
|
||||
*/
|
||||
public function getPositionOffset() : ?Vector3{
|
||||
public function getModelPositionOffset() : ?Vector3{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -183,6 +183,7 @@ class BlockFactory{
|
||||
$this->register(new EnderChest(new BID(Ids::ENDER_CHEST, 0, null, TileEnderChest::class), "Ender Chest", new BlockBreakInfo(22.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 3000.0)));
|
||||
$this->register(new Farmland(new BID(Ids::FARMLAND, 0), "Farmland", new BlockBreakInfo(0.6, BlockToolType::SHOVEL)));
|
||||
$this->register(new Fire(new BID(Ids::FIRE, 0), "Fire Block", BlockBreakInfo::instant()));
|
||||
$this->register(new FletchingTable(new BID(Ids::FLETCHING_TABLE, 0), "Fletching Table", new BlockBreakInfo(2.5, BlockToolType::AXE, 0, 2.5)));
|
||||
$this->register(new Flower(new BID(Ids::DANDELION, 0), "Dandelion", BlockBreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_ALLIUM), "Allium", BlockBreakInfo::instant()));
|
||||
$this->register(new Flower(new BID(Ids::RED_FLOWER, Meta::FLOWER_AZURE_BLUET), "Azure Bluet", BlockBreakInfo::instant()));
|
||||
@ -402,6 +403,7 @@ class BlockFactory{
|
||||
$this->registerSlabWithDoubleHighBitsRemapping(new Slab(BlockLegacyIdHelper::getStoneSlabIdentifier(4, Meta::STONE_SLAB4_STONE), "Stone", $stoneSlabBreakInfo));
|
||||
$this->register(new Opaque(new BID(Ids::STONECUTTER, 0), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new Sugarcane(new BID(Ids::REEDS_BLOCK, 0, ItemIds::REEDS), "Sugarcane", BlockBreakInfo::instant()));
|
||||
$this->register(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
|
||||
$this->register(new TNT(new BID(Ids::TNT, 0), "TNT", BlockBreakInfo::instant()));
|
||||
|
||||
$fern = new TallGrass(new BID(Ids::TALLGRASS, Meta::TALLGRASS_FERN), "Fern", BlockBreakInfo::instant(BlockToolType::SHEARS, 1));
|
||||
@ -567,7 +569,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:dropper
|
||||
//TODO: minecraft:end_gateway
|
||||
//TODO: minecraft:end_portal
|
||||
//TODO: minecraft:fletching_table
|
||||
//TODO: minecraft:grindstone
|
||||
//TODO: minecraft:jigsaw
|
||||
//TODO: minecraft:kelp
|
||||
@ -584,7 +585,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:sticky_piston
|
||||
//TODO: minecraft:stonecutter_block
|
||||
//TODO: minecraft:structure_block
|
||||
$this->register(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
|
||||
//TODO: minecraft:turtle_egg
|
||||
//endregion
|
||||
|
||||
|
@ -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)
|
||||
|
30
src/block/FletchingTable.php
Normal file
30
src/block/FletchingTable.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
class FletchingTable extends Opaque{
|
||||
public function getFuelTime() : int{
|
||||
return 300;
|
||||
}
|
||||
}
|
@ -77,7 +77,7 @@ class Sapling extends Flowable{
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof Fertilizer){
|
||||
$this->grow();
|
||||
$this->grow($player);
|
||||
|
||||
$item->pop();
|
||||
|
||||
@ -100,7 +100,7 @@ class Sapling extends Flowable{
|
||||
public function onRandomTick() : void{
|
||||
if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 and mt_rand(1, 7) === 1){
|
||||
if($this->ready){
|
||||
$this->grow();
|
||||
$this->grow(null);
|
||||
}else{
|
||||
$this->ready = true;
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
@ -108,7 +108,7 @@ class Sapling extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
private function grow() : void{
|
||||
private function grow(?Player $player) : void{
|
||||
$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);
|
||||
@ -116,13 +116,11 @@ class Sapling extends Flowable{
|
||||
return;
|
||||
}
|
||||
|
||||
$ev = new StructureGrowEvent($this, $transaction);
|
||||
$ev = new StructureGrowEvent($this, $transaction, $player);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
if(!$ev->isCancelled()){
|
||||
$transaction->apply();
|
||||
}
|
||||
|
||||
$transaction->apply();
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
|
@ -300,6 +300,7 @@ use function assert;
|
||||
* @method static Farmland FARMLAND()
|
||||
* @method static TallGrass FERN()
|
||||
* @method static Fire FIRE()
|
||||
* @method static FletchingTable FLETCHING_TABLE()
|
||||
* @method static FlowerPot FLOWER_POT()
|
||||
* @method static FrostedIce FROSTED_ICE()
|
||||
* @method static Furnace FURNACE()
|
||||
@ -866,6 +867,7 @@ final class VanillaBlocks{
|
||||
self::register("farmland", $factory->get(60, 0));
|
||||
self::register("fern", $factory->get(31, 2));
|
||||
self::register("fire", $factory->get(51, 0));
|
||||
self::register("fletching_table", $factory->get(456, 0));
|
||||
self::register("flower_pot", $factory->get(140, 0));
|
||||
self::register("frosted_ice", $factory->get(207, 0));
|
||||
self::register("furnace", $factory->get(61, 2));
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block\inventory;
|
||||
|
||||
use pocketmine\inventory\SimpleInventory;
|
||||
use pocketmine\network\mcpe\protocol\BlockEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\ChestCloseSound;
|
||||
use pocketmine\world\sound\ChestOpenSound;
|
||||
@ -50,6 +51,6 @@ class ChestInventory extends SimpleInventory implements BlockInventory{
|
||||
$holder = $this->getHolder();
|
||||
|
||||
//event ID is always 1 for a chest
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\inventory\DelegateInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\PlayerEnderInventory;
|
||||
use pocketmine\network\mcpe\protocol\BlockEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\EnderChestCloseSound;
|
||||
@ -74,7 +75,7 @@ class EnderChestInventory extends DelegateInventory implements BlockInventory{
|
||||
$holder = $this->getHolder();
|
||||
|
||||
//event ID is always 1 for a chest
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
|
||||
}
|
||||
|
||||
public function onClose(Player $who) : void{
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\BlockLegacyIds;
|
||||
use pocketmine\inventory\SimpleInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\protocol\BlockEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\ShulkerBoxCloseSound;
|
||||
use pocketmine\world\sound\ShulkerBoxOpenSound;
|
||||
@ -59,6 +60,6 @@ class ShulkerBoxInventory extends SimpleInventory implements BlockInventory{
|
||||
$holder = $this->getHolder();
|
||||
|
||||
//event ID is always 1 for a chest
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
|
||||
|
||||
final class Bell extends Spawnable{
|
||||
@ -81,6 +82,6 @@ final class Bell extends Spawnable{
|
||||
$nbt->setByte(self::TAG_RINGING, 1);
|
||||
$nbt->setInt(self::TAG_DIRECTION, BlockDataSerializer::writeLegacyHorizontalFacing($bellHitFace));
|
||||
$nbt->setInt(self::TAG_TICKS, 0);
|
||||
return BlockActorDataPacket::create($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), new CacheableNbt($nbt));
|
||||
return BlockActorDataPacket::create(BlockPosition::fromVector3($this->position), new CacheableNbt($nbt));
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\block\utils;
|
||||
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
use pocketmine\utils\EnumTrait;
|
||||
|
||||
/**
|
||||
@ -54,18 +54,18 @@ final class RecordType{
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new RecordType("disk_13", "C418 - 13", LevelSoundEventPacket::SOUND_RECORD_13, KnownTranslationFactory::item_record_13_desc()),
|
||||
new RecordType("disk_cat", "C418 - cat", LevelSoundEventPacket::SOUND_RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()),
|
||||
new RecordType("disk_blocks", "C418 - blocks", LevelSoundEventPacket::SOUND_RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()),
|
||||
new RecordType("disk_chirp", "C418 - chirp", LevelSoundEventPacket::SOUND_RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()),
|
||||
new RecordType("disk_far", "C418 - far", LevelSoundEventPacket::SOUND_RECORD_FAR, KnownTranslationFactory::item_record_far_desc()),
|
||||
new RecordType("disk_mall", "C418 - mall", LevelSoundEventPacket::SOUND_RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()),
|
||||
new RecordType("disk_mellohi", "C418 - mellohi", LevelSoundEventPacket::SOUND_RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()),
|
||||
new RecordType("disk_stal", "C418 - stal", LevelSoundEventPacket::SOUND_RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()),
|
||||
new RecordType("disk_strad", "C418 - strad", LevelSoundEventPacket::SOUND_RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()),
|
||||
new RecordType("disk_ward", "C418 - ward", LevelSoundEventPacket::SOUND_RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()),
|
||||
new RecordType("disk_11", "C418 - 11", LevelSoundEventPacket::SOUND_RECORD_11, KnownTranslationFactory::item_record_11_desc()),
|
||||
new RecordType("disk_wait", "C418 - wait", LevelSoundEventPacket::SOUND_RECORD_WAIT, KnownTranslationFactory::item_record_wait_desc())
|
||||
new RecordType("disk_13", "C418 - 13", LevelSoundEvent::RECORD_13, KnownTranslationFactory::item_record_13_desc()),
|
||||
new RecordType("disk_cat", "C418 - cat", LevelSoundEvent::RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()),
|
||||
new RecordType("disk_blocks", "C418 - blocks", LevelSoundEvent::RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()),
|
||||
new RecordType("disk_chirp", "C418 - chirp", LevelSoundEvent::RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()),
|
||||
new RecordType("disk_far", "C418 - far", LevelSoundEvent::RECORD_FAR, KnownTranslationFactory::item_record_far_desc()),
|
||||
new RecordType("disk_mall", "C418 - mall", LevelSoundEvent::RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()),
|
||||
new RecordType("disk_mellohi", "C418 - mellohi", LevelSoundEvent::RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()),
|
||||
new RecordType("disk_stal", "C418 - stal", LevelSoundEvent::RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()),
|
||||
new RecordType("disk_strad", "C418 - strad", LevelSoundEvent::RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()),
|
||||
new RecordType("disk_ward", "C418 - ward", LevelSoundEvent::RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()),
|
||||
new RecordType("disk_11", "C418 - 11", LevelSoundEvent::RECORD_11, KnownTranslationFactory::item_record_11_desc()),
|
||||
new RecordType("disk_wait", "C418 - wait", LevelSoundEvent::RECORD_WAIT, KnownTranslationFactory::item_record_wait_desc())
|
||||
//TODO: Lena Raine - Pigstep
|
||||
);
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ abstract class Command{
|
||||
}
|
||||
|
||||
if($this->permissionMessage === null){
|
||||
$target->sendMessage(KnownTranslationFactory::commands_generic_permission()->prefix(TextFormat::RED));
|
||||
$target->sendMessage(KnownTranslationFactory::pocketmine_command_error_permission($this->name)->prefix(TextFormat::RED));
|
||||
}elseif($this->permissionMessage !== ""){
|
||||
$target->sendMessage(str_replace("<permission>", $permission ?? $this->permission, $this->permissionMessage));
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class GarbageCollectorCommand extends VanillaCommand{
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_entities(TextFormat::RED . number_format($entitiesCollected))->prefix(TextFormat::GOLD));
|
||||
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_cycles(TextFormat::RED . number_format($cyclesCollected))->prefix(TextFormat::GOLD));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_memoryFreed(TextFormat::RED . number_format(round((($memory - memory_get_usage()) / 1024) / 1024, 2), 2) . " MB")->prefix(TextFormat::GOLD));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_memoryFreed(TextFormat::RED . number_format(round((($memory - memory_get_usage()) / 1024) / 1024, 2), 2))->prefix(TextFormat::GOLD));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class TeleportCommand extends VanillaCommand{
|
||||
$x = $this->getRelativeDouble($base->x, $sender, $targetArgs[0]);
|
||||
$y = $this->getRelativeDouble($base->y, $sender, $targetArgs[1], 0, 256);
|
||||
$z = $this->getRelativeDouble($base->z, $sender, $targetArgs[2]);
|
||||
$targetLocation = new Location($x, $y, $z, $yaw, $pitch, $base->getWorld());
|
||||
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
|
||||
|
||||
$subject->teleport($targetLocation);
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_tp_success_coordinates(
|
||||
|
@ -39,6 +39,7 @@ use function stream_socket_accept;
|
||||
use function stream_socket_get_name;
|
||||
use function stream_socket_server;
|
||||
use function stream_socket_shutdown;
|
||||
use function trim;
|
||||
use const PHP_BINARY;
|
||||
use const STREAM_SHUT_RDWR;
|
||||
|
||||
@ -113,7 +114,9 @@ final class ConsoleReaderThread extends Thread{
|
||||
break;
|
||||
}
|
||||
|
||||
$buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
@ -681,10 +681,12 @@ abstract class Entity{
|
||||
throw new \InvalidArgumentException("Fire ticks must be in range 0 ... " . 0x7fff . ", got $fireTicks");
|
||||
}
|
||||
$this->fireTicks = $fireTicks;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function extinguish() : void{
|
||||
$this->fireTicks = 0;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function isFireProof() : bool{
|
||||
@ -978,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;
|
||||
}
|
||||
@ -1183,9 +1185,9 @@ abstract class Entity{
|
||||
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
|
||||
$this->boundingBox->minY - $this->ySize,
|
||||
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2,
|
||||
$this->location->world,
|
||||
$this->location->yaw,
|
||||
$this->location->pitch,
|
||||
$this->location->world
|
||||
$this->location->pitch
|
||||
);
|
||||
|
||||
$this->getWorld()->onEntityMoved($this);
|
||||
@ -1416,20 +1418,21 @@ abstract class Entity{
|
||||
* Called by spawnTo() to send whatever packets needed to spawn the entity to the client.
|
||||
*/
|
||||
protected function sendSpawnPacket(Player $player) : void{
|
||||
$pk = new AddActorPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->type = static::getNetworkTypeId();
|
||||
$pk->position = $this->location->asVector3();
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->yaw = $this->location->yaw;
|
||||
$pk->headYaw = $this->location->yaw; //TODO
|
||||
$pk->pitch = $this->location->pitch;
|
||||
$pk->attributes = array_map(function(Attribute $attr) : NetworkAttribute{
|
||||
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
|
||||
}, $this->attributeMap->getAll());
|
||||
$pk->metadata = $this->getAllNetworkData();
|
||||
|
||||
$player->getNetworkSession()->sendDataPacket($pk);
|
||||
$player->getNetworkSession()->sendDataPacket(AddActorPacket::create(
|
||||
$this->getId(), //TODO: actor unique ID
|
||||
$this->getId(),
|
||||
static::getNetworkTypeId(),
|
||||
$this->location->asVector3(),
|
||||
$this->getMotion(),
|
||||
$this->location->pitch,
|
||||
$this->location->yaw,
|
||||
$this->location->yaw, //TODO: head yaw
|
||||
array_map(function(Attribute $attr) : NetworkAttribute{
|
||||
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue());
|
||||
}, $this->attributeMap->getAll()),
|
||||
$this->getAllNetworkData(),
|
||||
[] //TODO: entity links
|
||||
));
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player) : void{
|
||||
@ -1437,7 +1440,7 @@ abstract class Entity{
|
||||
//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);
|
||||
|
@ -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;
|
||||
@ -223,8 +224,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;
|
||||
|
@ -47,8 +47,10 @@ use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\DeviceOS;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
|
||||
@ -145,7 +147,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
*/
|
||||
public function sendSkin(?array $targets = null) : void{
|
||||
$this->server->broadcastPackets($targets ?? $this->hasSpawned, [
|
||||
PlayerSkinPacket::create($this->getUniqueId(), SkinAdapterSingleton::get()->toSkinData($this->skin))
|
||||
PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin))
|
||||
]);
|
||||
}
|
||||
|
||||
@ -444,17 +446,24 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
|
||||
}
|
||||
|
||||
$pk = new AddPlayerPacket();
|
||||
$pk->uuid = $this->getUniqueId();
|
||||
$pk->username = $this->getName();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = $this->location->asVector3();
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->yaw = $this->location->yaw;
|
||||
$pk->pitch = $this->location->pitch;
|
||||
$pk->item = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand()));
|
||||
$pk->metadata = $this->getAllNetworkData();
|
||||
$player->getNetworkSession()->sendDataPacket($pk);
|
||||
$player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create(
|
||||
$this->getUniqueId(),
|
||||
$this->getName(),
|
||||
$this->getId(), //TODO: actor unique ID
|
||||
$this->getId(),
|
||||
"",
|
||||
$this->location->asVector3(),
|
||||
$this->getMotion(),
|
||||
$this->location->pitch,
|
||||
$this->location->yaw,
|
||||
$this->location->yaw, //TODO: head yaw
|
||||
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
|
||||
$this->getAllNetworkData(),
|
||||
AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->getId()), //TODO
|
||||
[], //TODO: entity links
|
||||
"", //device ID (we intentionally don't send this - secvuln)
|
||||
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)
|
||||
));
|
||||
|
||||
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately
|
||||
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
|
||||
|
@ -34,7 +34,7 @@ class Location extends Position{
|
||||
/** @var float */
|
||||
public $pitch;
|
||||
|
||||
public function __construct(float $x, float $y, float $z, float $yaw = 0.0, float $pitch = 0.0, ?World $world = null){
|
||||
public function __construct(float $x, float $y, float $z, ?World $world, float $yaw, float $pitch){
|
||||
$this->yaw = $yaw;
|
||||
$this->pitch = $pitch;
|
||||
parent::__construct($x, $y, $z, $world);
|
||||
@ -44,14 +44,14 @@ class Location extends Position{
|
||||
* @return Location
|
||||
*/
|
||||
public static function fromObject(Vector3 $pos, ?World $world, float $yaw = 0.0, float $pitch = 0.0){
|
||||
return new Location($pos->x, $pos->y, $pos->z, $yaw, $pitch, $world ?? (($pos instanceof Position) ? $pos->world : null));
|
||||
return new Location($pos->x, $pos->y, $pos->z, $world ?? (($pos instanceof Position) ? $pos->world : null), $yaw, $pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Location instance
|
||||
*/
|
||||
public function asLocation() : Location{
|
||||
return new Location($this->x, $this->y, $this->z, $this->yaw, $this->pitch, $this->world);
|
||||
return new Location($this->x, $this->y, $this->z, $this->world, $this->yaw, $this->pitch);
|
||||
}
|
||||
|
||||
public function getYaw() : float{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class ArmSwingAnimation implements Animation{
|
||||
|
||||
@ -37,7 +38,7 @@ final class ArmSwingAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::ARM_SWING, 0)
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEvent::ARM_SWING, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
class ArrowShakeAnimation implements Animation{
|
||||
|
||||
@ -40,7 +41,7 @@ class ArrowShakeAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->arrow->getId(), ActorEventPacket::ARROW_SHAKE, $this->durationInTicks)
|
||||
ActorEventPacket::create($this->arrow->getId(), ActorEvent::ARROW_SHAKE, $this->durationInTicks)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\entity\Human;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class ConsumingItemAnimation implements Animation{
|
||||
|
||||
@ -45,7 +46,7 @@ final class ConsumingItemAnimation implements Animation{
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($this->item->getId(), $this->item->getMeta());
|
||||
return [
|
||||
//TODO: need to check the data values
|
||||
ActorEventPacket::create($this->human->getId(), ActorEventPacket::EATING_ITEM, ($netId << 16) | $netData)
|
||||
ActorEventPacket::create($this->human->getId(), ActorEvent::EATING_ITEM, ($netId << 16) | $netData)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class DeathAnimation implements Animation{
|
||||
|
||||
@ -37,7 +38,7 @@ final class DeathAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::DEATH_ANIMATION, 0)
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEvent::DEATH_ANIMATION, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class HurtAnimation implements Animation{
|
||||
|
||||
@ -37,7 +38,7 @@ final class HurtAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::HURT_ANIMATION, 0)
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEvent::HURT_ANIMATION, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class RespawnAnimation implements Animation{
|
||||
|
||||
@ -37,7 +38,7 @@ final class RespawnAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEventPacket::RESPAWN, 0)
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEvent::RESPAWN, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Squid;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class SquidInkCloudAnimation implements Animation{
|
||||
|
||||
@ -37,7 +38,7 @@ final class SquidInkCloudAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->squid->getId(), ActorEventPacket::SQUID_INK_CLOUD, 0)
|
||||
ActorEventPacket::create($this->squid->getId(), ActorEvent::SQUID_INK_CLOUD, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Human;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
|
||||
final class TotemUseAnimation implements Animation{
|
||||
|
||||
@ -38,7 +39,7 @@ final class TotemUseAnimation implements Animation{
|
||||
|
||||
public function encode() : array{
|
||||
return [
|
||||
ActorEventPacket::create($this->human->getId(), ActorEventPacket::CONSUME_TOTEM, 0)
|
||||
ActorEventPacket::create($this->human->getId(), ActorEvent::CONSUME_TOTEM, 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -206,14 +206,15 @@ class ItemEntity extends Entity{
|
||||
}
|
||||
|
||||
protected function sendSpawnPacket(Player $player) : void{
|
||||
$pk = new AddItemActorPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = $this->location->asVector3();
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->item = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getItem()));
|
||||
$pk->metadata = $this->getAllNetworkData();
|
||||
|
||||
$player->getNetworkSession()->sendDataPacket($pk);
|
||||
$player->getNetworkSession()->sendDataPacket(AddItemActorPacket::create(
|
||||
$this->getId(), //TODO: entity unique ID
|
||||
$this->getId(),
|
||||
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getItem())),
|
||||
$this->location->asVector3(),
|
||||
$this->getMotion(),
|
||||
$this->getAllNetworkData(),
|
||||
false //TODO: I have no idea what this is needed for, but right now we don't support fishing anyway
|
||||
));
|
||||
}
|
||||
|
||||
public function getOffsetPosition(Vector3 $vector3) : Vector3{
|
||||
@ -227,8 +228,8 @@ class ItemEntity extends Entity{
|
||||
|
||||
$item = $this->getItem();
|
||||
$playerInventory = match(true){
|
||||
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->canAddItem($item) => $player->getOffHandInventory(),
|
||||
$player->getInventory()->canAddItem($item) => $player->getInventory(),
|
||||
$player->getOffHandInventory()->getItem(0)->canStackWith($item) and $player->getOffHandInventory()->getAddableItemQuantity($item) > 0 => $player->getOffHandInventory(),
|
||||
$player->getInventory()->getAddableItemQuantity($item) > 0 => $player->getInventory(),
|
||||
default => null
|
||||
};
|
||||
|
||||
@ -246,7 +247,12 @@ class ItemEntity extends Entity{
|
||||
$viewer->getNetworkSession()->onPlayerPickUpItem($player, $this);
|
||||
}
|
||||
|
||||
$ev->getInventory()?->addItem($ev->getItem());
|
||||
$inventory = $ev->getInventory();
|
||||
if($inventory !== null){
|
||||
foreach($inventory->addItem($ev->getItem()) as $remains){
|
||||
$this->getWorld()->dropItem($this->location, $remains, new Vector3(0, 0, 0));
|
||||
}
|
||||
}
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -149,17 +149,17 @@ class Painting extends Entity{
|
||||
}
|
||||
|
||||
protected function sendSpawnPacket(Player $player) : void{
|
||||
$pk = new AddPaintingPacket();
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = new Vector3(
|
||||
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
|
||||
($this->boundingBox->minY + $this->boundingBox->maxY) / 2,
|
||||
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
|
||||
);
|
||||
$pk->direction = self::FACING_TO_DATA[$this->facing];
|
||||
$pk->title = $this->motive->getName();
|
||||
|
||||
$player->getNetworkSession()->sendDataPacket($pk);
|
||||
$player->getNetworkSession()->sendDataPacket(AddPaintingPacket::create(
|
||||
$this->getId(), //TODO: entity unique ID
|
||||
$this->getId(),
|
||||
new Vector3(
|
||||
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
|
||||
($this->boundingBox->minY + $this->boundingBox->maxY) / 2,
|
||||
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
|
||||
),
|
||||
self::FACING_TO_DATA[$this->facing],
|
||||
$this->motive->getName()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ namespace pocketmine\event\block;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
/**
|
||||
@ -17,13 +18,23 @@ class StructureGrowEvent extends BlockEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
private BlockTransaction $transaction;
|
||||
private ?Player $player;
|
||||
|
||||
public function __construct(Block $block, BlockTransaction $transaction){
|
||||
public function __construct(Block $block, BlockTransaction $transaction, ?Player $player){
|
||||
parent::__construct($block);
|
||||
$this->transaction = $transaction;
|
||||
$this->player = $player;
|
||||
}
|
||||
|
||||
public function getTransaction() : BlockTransaction{
|
||||
return $this->transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns the player which grows the structure.
|
||||
* It returns null when the structure grows by itself.
|
||||
*/
|
||||
public function getPlayer() : ?Player{
|
||||
return $this->player;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\event\player;
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use function is_a;
|
||||
|
||||
/**
|
||||
@ -96,10 +97,7 @@ class PlayerCreationEvent extends Event{
|
||||
* @phpstan-param class-string<Player> $class
|
||||
*/
|
||||
public function setPlayerClass($class) : void{
|
||||
if(!is_a($class, $this->baseClass, true)){
|
||||
throw new \RuntimeException("Class $class must extend " . $this->baseClass);
|
||||
}
|
||||
|
||||
Utils::testValidInstance($class, $this->baseClass);
|
||||
$this->playerClass = $class;
|
||||
}
|
||||
}
|
||||
|
@ -169,6 +169,10 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
public function canAddItem(Item $item) : bool{
|
||||
return $this->getAddableItemQuantity($item) === $item->getCount();
|
||||
}
|
||||
|
||||
public function getAddableItemQuantity(Item $item) : int{
|
||||
$count = $item->getCount();
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$slot = $this->getItem($i);
|
||||
@ -181,11 +185,11 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
if($count <= 0){
|
||||
return true;
|
||||
return $item->getCount();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $item->getCount() - $count;
|
||||
}
|
||||
|
||||
public function addItem(Item ...$slots) : array{
|
||||
|
@ -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);
|
||||
|
@ -63,6 +63,11 @@ interface Inventory{
|
||||
*/
|
||||
public function canAddItem(Item $item) : bool;
|
||||
|
||||
/**
|
||||
* Returns how many items from the given itemstack can be added to this inventory.
|
||||
*/
|
||||
public function getAddableItemQuantity(Item $item) : int;
|
||||
|
||||
/**
|
||||
* Removes the given Item from the inventory.
|
||||
* It will return the Items that couldn't be removed.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ use function file_get_contents;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_numeric;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function str_replace;
|
||||
use function strtolower;
|
||||
@ -63,8 +62,8 @@ final class LegacyStringToItemParser{
|
||||
if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array");
|
||||
|
||||
foreach($mappings as $name => $id){
|
||||
if(!is_string($name) or !is_int($id)) throw new AssumptionFailedError("Invalid mappings format, expected string keys and int values");
|
||||
$result->addMapping($name, $id);
|
||||
if(!is_int($id)) throw new AssumptionFailedError("Invalid mappings format, expected int values");
|
||||
$result->addMapping((string) $name, $id);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@ -84,6 +83,14 @@ final class LegacyStringToItemParser{
|
||||
$this->map[$alias] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<string, int>
|
||||
*/
|
||||
public function getMappings() : array{
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse the specified string into Item types.
|
||||
*
|
||||
@ -106,9 +113,7 @@ final class LegacyStringToItemParser{
|
||||
throw new LegacyStringToItemParserException("Unable to parse \"" . $b[1] . "\" from \"" . $input . "\" as a valid meta value");
|
||||
}
|
||||
|
||||
if(is_numeric($b[0])){
|
||||
$item = $this->itemFactory->get((int) $b[0], $meta);
|
||||
}elseif(isset($this->map[strtolower($b[0])])){
|
||||
if(isset($this->map[strtolower($b[0])])){
|
||||
$item = $this->itemFactory->get($this->map[strtolower($b[0])], $meta);
|
||||
}else{
|
||||
throw new LegacyStringToItemParserException("Unable to resolve \"" . $input . "\" to a valid item");
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -530,6 +530,7 @@ final class StringToItemParser{
|
||||
$result->registerBlock("fence_gate_spruce", fn() => VanillaBlocks::SPRUCE_FENCE_GATE());
|
||||
$result->registerBlock("fern", fn() => VanillaBlocks::FERN());
|
||||
$result->registerBlock("fire", fn() => VanillaBlocks::FIRE());
|
||||
$result->registerBlock("fletching_table", fn() => VanillaBlocks::FLETCHING_TABLE());
|
||||
$result->registerBlock("flower_pot", fn() => VanillaBlocks::FLOWER_POT());
|
||||
$result->registerBlock("flower_pot_block", fn() => VanillaBlocks::FLOWER_POT());
|
||||
$result->registerBlock("flowing_lava", fn() => VanillaBlocks::LAVA());
|
||||
|
@ -1109,6 +1109,12 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ENCHANT_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_error_permission(Translatable|string $commandName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ERROR_PERMISSION, [
|
||||
"commandName" => $commandName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_error_playerNotFound(Translatable|string $playerName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_ERROR_PLAYERNOTFOUND, [
|
||||
"playerName" => $playerName,
|
||||
@ -1569,6 +1575,14 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_invalidGeneratorOptions(Translatable|string $preset, Translatable|string $generatorName, Translatable|string $details) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_INVALIDGENERATOROPTIONS, [
|
||||
"preset" => $preset,
|
||||
"generatorName" => $generatorName,
|
||||
"details" => $details,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_level_loadError(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_LEVEL_LOADERROR, [
|
||||
0 => $param0,
|
||||
@ -1665,6 +1679,12 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_badDataFolder(Translatable|string $dataFolder) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_BADDATAFOLDER, [
|
||||
"dataFolder" => $dataFolder,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_circularDependency() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_CIRCULARDEPENDENCY, []);
|
||||
}
|
||||
@ -1690,18 +1710,39 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_disallowedByBlacklist() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_DISALLOWEDBYBLACKLIST, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_disallowedByWhitelist() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_DISALLOWEDBYWHITELIST, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_duplicateError(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_DUPLICATEERROR, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_emptyExtensionVersionConstraint(Translatable|string $constraintIndex, Translatable|string $extensionName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_EMPTYEXTENSIONVERSIONCONSTRAINT, [
|
||||
"constraintIndex" => $constraintIndex,
|
||||
"extensionName" => $extensionName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_enable(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_ENABLE, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_extensionNotLoaded(Translatable|string $extensionName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_EXTENSIONNOTLOADED, [
|
||||
"extensionName" => $extensionName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_genericLoadError(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_GENERICLOADERROR, [
|
||||
0 => $param0,
|
||||
@ -1714,6 +1755,14 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_incompatibleExtensionVersion(Translatable|string $extensionVersion, Translatable|string $extensionName, Translatable|string $pluginRequirement) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INCOMPATIBLEEXTENSIONVERSION, [
|
||||
"extensionVersion" => $extensionVersion,
|
||||
"extensionName" => $extensionName,
|
||||
"pluginRequirement" => $pluginRequirement,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_incompatibleOS(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INCOMPATIBLEOS, [
|
||||
0 => $param0,
|
||||
@ -1738,6 +1787,13 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_invalidExtensionVersionConstraint(Translatable|string $versionConstraint, Translatable|string $extensionName) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INVALIDEXTENSIONVERSIONCONSTRAINT, [
|
||||
"versionConstraint" => $versionConstraint,
|
||||
"extensionName" => $extensionName,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_invalidManifest(Translatable|string $details) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_INVALIDMANIFEST, [
|
||||
"details" => $details,
|
||||
@ -1757,6 +1813,16 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_mainClassNotFound() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_MAINCLASSNOTFOUND, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_mainClassWrongType(Translatable|string $pluginInterface) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_MAINCLASSWRONGTYPE, [
|
||||
"pluginInterface" => $pluginInterface,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pocketmine_plugin_restrictedName() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_RESTRICTEDNAME, []);
|
||||
}
|
||||
@ -1884,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,
|
||||
@ -2009,6 +2083,10 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POTION_SATURATION, []);
|
||||
}
|
||||
|
||||
public static function potion_slowFalling() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POTION_SLOWFALLING, []);
|
||||
}
|
||||
|
||||
public static function potion_waterBreathing() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POTION_WATERBREATHING, []);
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION = "pocketmine.command.difficulty.description";
|
||||
public const POCKETMINE_COMMAND_EFFECT_DESCRIPTION = "pocketmine.command.effect.description";
|
||||
public const POCKETMINE_COMMAND_ENCHANT_DESCRIPTION = "pocketmine.command.enchant.description";
|
||||
public const POCKETMINE_COMMAND_ERROR_PERMISSION = "pocketmine.command.error.permission";
|
||||
public const POCKETMINE_COMMAND_ERROR_PLAYERNOTFOUND = "pocketmine.command.error.playerNotFound";
|
||||
public const POCKETMINE_COMMAND_EXCEPTION = "pocketmine.command.exception";
|
||||
public const POCKETMINE_COMMAND_GAMEMODE_DESCRIPTION = "pocketmine.command.gamemode.description";
|
||||
@ -337,6 +338,7 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_LEVEL_CORRUPTED = "pocketmine.level.corrupted";
|
||||
public const POCKETMINE_LEVEL_DEFAULTERROR = "pocketmine.level.defaultError";
|
||||
public const POCKETMINE_LEVEL_GENERATIONERROR = "pocketmine.level.generationError";
|
||||
public const POCKETMINE_LEVEL_INVALIDGENERATOROPTIONS = "pocketmine.level.invalidGeneratorOptions";
|
||||
public const POCKETMINE_LEVEL_LOADERROR = "pocketmine.level.loadError";
|
||||
public const POCKETMINE_LEVEL_NOTFOUND = "pocketmine.level.notFound";
|
||||
public const POCKETMINE_LEVEL_PREPARING = "pocketmine.level.preparing";
|
||||
@ -351,21 +353,30 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_PLAYER_LOGOUT = "pocketmine.player.logOut";
|
||||
public const POCKETMINE_PLUGIN_ALIASERROR = "pocketmine.plugin.aliasError";
|
||||
public const POCKETMINE_PLUGIN_AMBIGUOUSMINAPI = "pocketmine.plugin.ambiguousMinAPI";
|
||||
public const POCKETMINE_PLUGIN_BADDATAFOLDER = "pocketmine.plugin.badDataFolder";
|
||||
public const POCKETMINE_PLUGIN_CIRCULARDEPENDENCY = "pocketmine.plugin.circularDependency";
|
||||
public const POCKETMINE_PLUGIN_COMMANDERROR = "pocketmine.plugin.commandError";
|
||||
public const POCKETMINE_PLUGIN_DEPRECATEDEVENT = "pocketmine.plugin.deprecatedEvent";
|
||||
public const POCKETMINE_PLUGIN_DISABLE = "pocketmine.plugin.disable";
|
||||
public const POCKETMINE_PLUGIN_DISALLOWEDBYBLACKLIST = "pocketmine.plugin.disallowedByBlacklist";
|
||||
public const POCKETMINE_PLUGIN_DISALLOWEDBYWHITELIST = "pocketmine.plugin.disallowedByWhitelist";
|
||||
public const POCKETMINE_PLUGIN_DUPLICATEERROR = "pocketmine.plugin.duplicateError";
|
||||
public const POCKETMINE_PLUGIN_EMPTYEXTENSIONVERSIONCONSTRAINT = "pocketmine.plugin.emptyExtensionVersionConstraint";
|
||||
public const POCKETMINE_PLUGIN_ENABLE = "pocketmine.plugin.enable";
|
||||
public const POCKETMINE_PLUGIN_EXTENSIONNOTLOADED = "pocketmine.plugin.extensionNotLoaded";
|
||||
public const POCKETMINE_PLUGIN_GENERICLOADERROR = "pocketmine.plugin.genericLoadError";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEAPI = "pocketmine.plugin.incompatibleAPI";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEEXTENSIONVERSION = "pocketmine.plugin.incompatibleExtensionVersion";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEOS = "pocketmine.plugin.incompatibleOS";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEPHPVERSION = "pocketmine.plugin.incompatiblePhpVersion";
|
||||
public const POCKETMINE_PLUGIN_INCOMPATIBLEPROTOCOL = "pocketmine.plugin.incompatibleProtocol";
|
||||
public const POCKETMINE_PLUGIN_INVALIDAPI = "pocketmine.plugin.invalidAPI";
|
||||
public const POCKETMINE_PLUGIN_INVALIDEXTENSIONVERSIONCONSTRAINT = "pocketmine.plugin.invalidExtensionVersionConstraint";
|
||||
public const POCKETMINE_PLUGIN_INVALIDMANIFEST = "pocketmine.plugin.invalidManifest";
|
||||
public const POCKETMINE_PLUGIN_LOAD = "pocketmine.plugin.load";
|
||||
public const POCKETMINE_PLUGIN_LOADERROR = "pocketmine.plugin.loadError";
|
||||
public const POCKETMINE_PLUGIN_MAINCLASSNOTFOUND = "pocketmine.plugin.mainClassNotFound";
|
||||
public const POCKETMINE_PLUGIN_MAINCLASSWRONGTYPE = "pocketmine.plugin.mainClassWrongType";
|
||||
public const POCKETMINE_PLUGIN_RESTRICTEDNAME = "pocketmine.plugin.restrictedName";
|
||||
public const POCKETMINE_PLUGIN_SPACESDISCOURAGED = "pocketmine.plugin.spacesDiscouraged";
|
||||
public const POCKETMINE_PLUGIN_UNKNOWNDEPENDENCY = "pocketmine.plugin.unknownDependency";
|
||||
@ -390,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";
|
||||
@ -419,6 +431,7 @@ final class KnownTranslationKeys{
|
||||
public const POTION_REGENERATION = "potion.regeneration";
|
||||
public const POTION_RESISTANCE = "potion.resistance";
|
||||
public const POTION_SATURATION = "potion.saturation";
|
||||
public const POTION_SLOWFALLING = "potion.slowFalling";
|
||||
public const POTION_WATERBREATHING = "potion.waterBreathing";
|
||||
public const POTION_WEAKNESS = "potion.weakness";
|
||||
public const POTION_WITHER = "potion.wither";
|
||||
|
@ -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{
|
||||
|
@ -45,6 +45,7 @@ use pocketmine\network\mcpe\protocol\CreativeContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
@ -150,7 +151,7 @@ class InventoryManager{
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new \UnsupportedOperationException("Unsupported inventory type");
|
||||
throw new \LogicException("Unsupported inventory type");
|
||||
}
|
||||
|
||||
/** @phpstan-return ObjectSet<ContainerOpenClosure> */
|
||||
@ -164,27 +165,22 @@ class InventoryManager{
|
||||
//TODO: we should be using some kind of tagging system to identify the types. Instanceof is flaky especially
|
||||
//if the class isn't final, not to mention being inflexible.
|
||||
if($inv instanceof BlockInventory){
|
||||
switch(true){
|
||||
case $inv instanceof LoomInventory:
|
||||
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::LOOM, $inv->getHolder())];
|
||||
case $inv instanceof FurnaceInventory:
|
||||
return match($inv->getFurnaceType()->id()){
|
||||
FurnaceType::FURNACE()->id() => [ContainerOpenPacket::blockInvVec3($id, WindowTypes::FURNACE, $inv->getHolder())],
|
||||
FurnaceType::BLAST_FURNACE()->id() => [ContainerOpenPacket::blockInvVec3($id, WindowTypes::BLAST_FURNACE, $inv->getHolder())],
|
||||
FurnaceType::SMOKER()->id() => [ContainerOpenPacket::blockInvVec3($id, WindowTypes::SMOKER, $inv->getHolder())],
|
||||
$blockPosition = BlockPosition::fromVector3($inv->getHolder());
|
||||
$windowType = match(true){
|
||||
$inv instanceof LoomInventory => WindowTypes::LOOM,
|
||||
$inv instanceof FurnaceInventory => match($inv->getFurnaceType()->id()){
|
||||
FurnaceType::FURNACE()->id() => WindowTypes::FURNACE,
|
||||
FurnaceType::BLAST_FURNACE()->id() => WindowTypes::BLAST_FURNACE,
|
||||
FurnaceType::SMOKER()->id() => WindowTypes::SMOKER,
|
||||
default => throw new AssumptionFailedError("Unreachable")
|
||||
};
|
||||
case $inv instanceof EnchantInventory:
|
||||
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::ENCHANTMENT, $inv->getHolder())];
|
||||
case $inv instanceof BrewingStandInventory:
|
||||
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::BREWING_STAND, $inv->getHolder())];
|
||||
case $inv instanceof AnvilInventory:
|
||||
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::ANVIL, $inv->getHolder())];
|
||||
case $inv instanceof HopperInventory:
|
||||
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::HOPPER, $inv->getHolder())];
|
||||
default:
|
||||
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::CONTAINER, $inv->getHolder())];
|
||||
}
|
||||
},
|
||||
$inv instanceof EnchantInventory => WindowTypes::ENCHANTMENT,
|
||||
$inv instanceof BrewingStandInventory => WindowTypes::BREWING_STAND,
|
||||
$inv instanceof AnvilInventory => WindowTypes::ANVIL,
|
||||
$inv instanceof HopperInventory => WindowTypes::HOPPER,
|
||||
default => WindowTypes::CONTAINER
|
||||
};
|
||||
return [ContainerOpenPacket::blockInv($id, $windowType, $blockPosition)];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -279,6 +275,7 @@ class InventoryManager{
|
||||
$this->player->getId(),
|
||||
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->player->getInventory()->getItemInHand())),
|
||||
$selected,
|
||||
$selected,
|
||||
ContainerIds::INVENTORY
|
||||
));
|
||||
$this->clientSelectedHotbarSlot = $selected;
|
||||
|
@ -88,6 +88,7 @@ use pocketmine\network\mcpe\protocol\SetTitlePacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\TransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandData;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
|
||||
@ -347,6 +348,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){
|
||||
@ -446,6 +451,8 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
public function getPacketSerializerContext() : PacketSerializerContext{ return $this->packetSerializerContext; }
|
||||
|
||||
public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; }
|
||||
|
||||
public function getCompressor() : Compressor{
|
||||
@ -529,8 +536,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{
|
||||
@ -556,7 +561,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 : "");
|
||||
@ -726,16 +731,17 @@ class NetworkSession{
|
||||
$yaw = $yaw ?? $location->getYaw();
|
||||
$pitch = $pitch ?? $location->getPitch();
|
||||
|
||||
$pk = new MovePlayerPacket();
|
||||
$pk->entityRuntimeId = $this->player->getId();
|
||||
$pk->position = $this->player->getOffsetPosition($pos);
|
||||
$pk->pitch = $pitch;
|
||||
$pk->headYaw = $yaw;
|
||||
$pk->yaw = $yaw;
|
||||
$pk->mode = $mode;
|
||||
$pk->onGround = $this->player->onGround;
|
||||
|
||||
$this->sendDataPacket($pk);
|
||||
$this->sendDataPacket(MovePlayerPacket::simple(
|
||||
$this->player->getId(),
|
||||
$this->player->getOffsetPosition($pos),
|
||||
$pitch,
|
||||
$yaw,
|
||||
$yaw, //TODO: head yaw
|
||||
$mode,
|
||||
$this->player->onGround,
|
||||
0, //TODO: riding entity ID
|
||||
0 //TODO: tick
|
||||
));
|
||||
|
||||
if($this->handler instanceof InGamePacketHandler){
|
||||
$this->handler->forceMoveSync = true;
|
||||
@ -748,16 +754,17 @@ class NetworkSession{
|
||||
}
|
||||
|
||||
public function syncViewAreaCenterPoint(Vector3 $newPos, int $viewDistance) : void{
|
||||
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create($newPos->getFloorX(), $newPos->getFloorY(), $newPos->getFloorZ(), $viewDistance * 16)); //blocks, not chunks >.>
|
||||
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create(BlockPosition::fromVector3($newPos), $viewDistance * 16)); //blocks, not chunks >.>
|
||||
}
|
||||
|
||||
public function syncPlayerSpawnPoint(Position $newSpawn) : void{
|
||||
[$x, $y, $z] = [$newSpawn->getFloorX(), $newSpawn->getFloorY(), $newSpawn->getFloorZ()];
|
||||
$this->sendDataPacket(SetSpawnPositionPacket::playerSpawn($x, $y, $z, DimensionIds::OVERWORLD, $x, $y, $z));
|
||||
$newSpawnBlockPosition = BlockPosition::fromVector3($newSpawn);
|
||||
//TODO: respawn causing block position (bed, respawn anchor)
|
||||
$this->sendDataPacket(SetSpawnPositionPacket::playerSpawn($newSpawnBlockPosition, DimensionIds::OVERWORLD, $newSpawnBlockPosition));
|
||||
}
|
||||
|
||||
public function syncWorldSpawnPoint(Position $newSpawn) : void{
|
||||
$this->sendDataPacket(SetSpawnPositionPacket::worldSpawn($newSpawn->getFloorX(), $newSpawn->getFloorY(), $newSpawn->getFloorZ(), DimensionIds::OVERWORLD));
|
||||
$this->sendDataPacket(SetSpawnPositionPacket::worldSpawn(BlockPosition::fromVector3($newSpawn), DimensionIds::OVERWORLD));
|
||||
}
|
||||
|
||||
public function syncGameMode(GameMode $mode, bool $isRollback = false) : void{
|
||||
@ -774,7 +781,15 @@ class NetworkSession{
|
||||
* TODO: make this less specialized
|
||||
*/
|
||||
public function syncAdventureSettings(Player $for) : void{
|
||||
$pk = new AdventureSettingsPacket();
|
||||
$isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
$pk = AdventureSettingsPacket::create(
|
||||
0,
|
||||
$isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL,
|
||||
0,
|
||||
$isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER,
|
||||
0,
|
||||
$for->getId()
|
||||
);
|
||||
|
||||
$pk->setFlag(AdventureSettingsPacket::WORLD_IMMUTABLE, $for->isSpectator());
|
||||
$pk->setFlag(AdventureSettingsPacket::NO_PVP, $for->isSpectator());
|
||||
@ -785,11 +800,6 @@ class NetworkSession{
|
||||
|
||||
//TODO: permission flags
|
||||
|
||||
$isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
$pk->commandPermission = ($isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL);
|
||||
$pk->playerPermission = ($isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER);
|
||||
$pk->entityUniqueId = $for->getId();
|
||||
|
||||
$this->sendDataPacket($pk);
|
||||
}
|
||||
|
||||
@ -826,9 +836,9 @@ class NetworkSession{
|
||||
}
|
||||
|
||||
public function syncAvailableCommands() : void{
|
||||
$pk = new AvailableCommandsPacket();
|
||||
$commandData = [];
|
||||
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
|
||||
if(isset($pk->commandData[$command->getName()]) or $command->getName() === "help" or !$command->testPermissionSilent($this->player)){
|
||||
if(isset($commandData[$command->getName()]) or $command->getName() === "help" or !$command->testPermissionSilent($this->player)){
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -855,10 +865,10 @@ class NetworkSession{
|
||||
]
|
||||
);
|
||||
|
||||
$pk->commandData[$command->getName()] = $data;
|
||||
$commandData[$command->getName()] = $data;
|
||||
}
|
||||
|
||||
$this->sendDataPacket($pk);
|
||||
$this->sendDataPacket(AvailableCommandsPacket::create($commandData, [], [], []));
|
||||
}
|
||||
|
||||
public function onRawChatMessage(string $message) : void{
|
||||
@ -916,7 +926,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
|
||||
@ -966,12 +976,12 @@ class NetworkSession{
|
||||
public function onMobMainHandItemChange(Human $mob) : void{
|
||||
//TODO: we could send zero for slot here because remote players don't need to know which slot was selected
|
||||
$inv = $mob->getInventory();
|
||||
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
|
||||
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
|
||||
}
|
||||
|
||||
public function onMobOffHandItemChange(Human $mob) : void{
|
||||
$inv = $mob->getOffHandInventory();
|
||||
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, ContainerIds::OFFHAND));
|
||||
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, 0, ContainerIds::OFFHAND));
|
||||
}
|
||||
|
||||
public function onMobArmorChange(Living $mob) : void{
|
||||
|
@ -23,10 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\network\mcpe\compression\Compressor;
|
||||
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\Server;
|
||||
use function spl_object_id;
|
||||
|
||||
@ -40,35 +37,40 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
}
|
||||
|
||||
public function broadcastPackets(array $recipients, array $packets) : void{
|
||||
//TODO: we should be using session-specific serializer contexts for this
|
||||
$stream = PacketBatch::fromPackets(new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()), ...$packets);
|
||||
|
||||
/** @var Compressor[] $compressors */
|
||||
$buffers = [];
|
||||
$compressors = [];
|
||||
/** @var NetworkSession[][] $compressorTargets */
|
||||
$compressorTargets = [];
|
||||
$targetMap = [];
|
||||
foreach($recipients as $recipient){
|
||||
$compressor = $recipient->getCompressor();
|
||||
$compressorId = spl_object_id($compressor);
|
||||
$serializerContext = $recipient->getPacketSerializerContext();
|
||||
$bufferId = spl_object_id($serializerContext);
|
||||
if(!isset($buffers[$bufferId])){
|
||||
$buffers[$bufferId] = PacketBatch::fromPackets($serializerContext, ...$packets);
|
||||
}
|
||||
|
||||
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
|
||||
$compressors[$compressorId] = $compressor;
|
||||
$compressorTargets[$compressorId][] = $recipient;
|
||||
$compressor = $recipient->getCompressor();
|
||||
$compressors[spl_object_id($compressor)] = $compressor;
|
||||
|
||||
$targetMap[$bufferId][spl_object_id($compressor)][] = $recipient;
|
||||
}
|
||||
|
||||
foreach($compressors as $compressorId => $compressor){
|
||||
if(!$compressor->willCompress($stream->getBuffer())){
|
||||
foreach($compressorTargets[$compressorId] as $target){
|
||||
foreach($packets as $pk){
|
||||
$target->addToSendBuffer($pk);
|
||||
foreach($targetMap as $bufferId => $compressorMap){
|
||||
$buffer = $buffers[$bufferId];
|
||||
foreach($compressorMap as $compressorId => $compressorTargets){
|
||||
$compressor = $compressors[$compressorId];
|
||||
if(!$compressor->willCompress($buffer->getBuffer())){
|
||||
foreach($compressorTargets as $target){
|
||||
foreach($packets as $pk){
|
||||
$target->addToSendBuffer($pk);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$promise = $this->server->prepareBatch($buffer, $compressor);
|
||||
foreach($compressorTargets as $target){
|
||||
$target->queueCompressed($promise);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$promise = $this->server->prepareBatch($stream, $compressor);
|
||||
foreach($compressorTargets[$compressorId] as $target){
|
||||
$target->queueCompressed($promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
16
src/network/mcpe/cache/ChunkCache.php
vendored
16
src/network/mcpe/cache/ChunkCache.php
vendored
@ -36,9 +36,6 @@ use function strlen;
|
||||
|
||||
/**
|
||||
* This class is used by the current MCPE protocol system to store cached chunk packets for fast resending.
|
||||
*
|
||||
* TODO: make MemoryManager aware of this so the cache can be destroyed when memory is low
|
||||
* TODO: this needs a hook for world unloading
|
||||
*/
|
||||
class ChunkCache implements ChunkListener{
|
||||
/** @var self[][] */
|
||||
@ -69,6 +66,19 @@ class ChunkCache implements ChunkListener{
|
||||
return self::$instances[$worldId][$compressorId];
|
||||
}
|
||||
|
||||
public static function pruneCaches() : void{
|
||||
foreach(self::$instances as $compressorMap){
|
||||
foreach($compressorMap as $chunkCache){
|
||||
foreach($chunkCache->caches as $chunkHash => $promise){
|
||||
if($promise->hasResult()){
|
||||
//Do not clear promises that are not yet fulfilled; they will have requesters waiting on them
|
||||
unset($chunkCache->caches[$chunkHash]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var World */
|
||||
private $world;
|
||||
/** @var Compressor */
|
||||
|
11
src/network/mcpe/cache/CraftingDataCache.php
vendored
11
src/network/mcpe/cache/CraftingDataCache.php
vendored
@ -71,15 +71,14 @@ final class CraftingDataCache{
|
||||
*/
|
||||
private function buildCraftingDataCache(CraftingManager $manager) : CraftingDataPacket{
|
||||
Timings::$craftingDataCacheRebuild->startTiming();
|
||||
$pk = new CraftingDataPacket();
|
||||
$pk->cleanRecipes = true;
|
||||
|
||||
$counter = 0;
|
||||
$nullUUID = Uuid::fromString(Uuid::NIL);
|
||||
$converter = TypeConverter::getInstance();
|
||||
$recipesWithTypeIds = [];
|
||||
foreach($manager->getShapelessRecipes() as $list){
|
||||
foreach($list as $recipe){
|
||||
$pk->entries[] = new ProtocolShapelessRecipe(
|
||||
$recipesWithTypeIds[] = new ProtocolShapelessRecipe(
|
||||
CraftingDataPacket::ENTRY_SHAPELESS,
|
||||
Binary::writeInt(++$counter),
|
||||
array_map(function(Item $item) use ($converter) : RecipeIngredient{
|
||||
@ -104,7 +103,7 @@ final class CraftingDataCache{
|
||||
$inputs[$row][$column] = $converter->coreItemStackToRecipeIngredient($recipe->getIngredient($column, $row));
|
||||
}
|
||||
}
|
||||
$pk->entries[] = $r = new ProtocolShapedRecipe(
|
||||
$recipesWithTypeIds[] = $r = new ProtocolShapedRecipe(
|
||||
CraftingDataPacket::ENTRY_SHAPED,
|
||||
Binary::writeInt(++$counter),
|
||||
$inputs,
|
||||
@ -128,7 +127,7 @@ final class CraftingDataCache{
|
||||
};
|
||||
foreach($manager->getFurnaceRecipeManager($furnaceType)->getAll() as $recipe){
|
||||
$input = $converter->coreItemStackToNet($recipe->getInput());
|
||||
$pk->entries[] = new ProtocolFurnaceRecipe(
|
||||
$recipesWithTypeIds[] = new ProtocolFurnaceRecipe(
|
||||
CraftingDataPacket::ENTRY_FURNACE_DATA,
|
||||
$input->getId(),
|
||||
$input->getMeta(),
|
||||
@ -139,6 +138,6 @@ final class CraftingDataCache{
|
||||
}
|
||||
|
||||
Timings::$craftingDataCacheRebuild->stopTiming();
|
||||
return $pk;
|
||||
return CraftingDataPacket::create($recipesWithTypeIds, [], [], [], true);
|
||||
}
|
||||
}
|
||||
|
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)){
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\convert;
|
||||
|
||||
use pocketmine\data\bedrock\LegacyItemIdToStringIdMap;
|
||||
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
@ -65,21 +66,18 @@ 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");
|
||||
}
|
||||
$legacyStringToIntMap = json_decode($legacyStringToIntMapRaw, true);
|
||||
if(!is_array($legacyStringToIntMap)){
|
||||
throw new AssumptionFailedError("Invalid mapping table format");
|
||||
}
|
||||
$legacyStringToIntMap = LegacyItemIdToStringIdMap::getInstance();
|
||||
|
||||
/** @phpstan-var array<string, int> $simpleMappings */
|
||||
$simpleMappings = [];
|
||||
@ -87,13 +85,14 @@ final class ItemTranslator{
|
||||
if(!is_string($oldId) || !is_string($newId)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
if(!isset($legacyStringToIntMap[$oldId])){
|
||||
$intId = $legacyStringToIntMap->stringToLegacy($oldId);
|
||||
if($intId === null){
|
||||
//new item without a fixed legacy ID - we can't handle this right now
|
||||
continue;
|
||||
}
|
||||
$simpleMappings[$newId] = $legacyStringToIntMap[$oldId];
|
||||
$simpleMappings[$newId] = $intId;
|
||||
}
|
||||
foreach($legacyStringToIntMap as $stringId => $intId){
|
||||
foreach($legacyStringToIntMap->getStringToLegacyMap() as $stringId => $intId){
|
||||
if(isset($simpleMappings[$stringId])){
|
||||
throw new \UnexpectedValueException("Old ID $stringId collides with new ID");
|
||||
}
|
||||
@ -110,7 +109,12 @@ final class ItemTranslator{
|
||||
if(!is_numeric($meta) || !is_string($newId)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
$complexMappings[$newId] = [$legacyStringToIntMap[$oldId], (int) $meta];
|
||||
$intId = $legacyStringToIntMap->stringToLegacy($oldId);
|
||||
if($intId === null){
|
||||
//new item without a fixed legacy ID - we can't handle this right now
|
||||
continue;
|
||||
}
|
||||
$complexMappings[$newId] = [$intId, (int) $meta];
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,10 +146,10 @@ final class ItemTranslator{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
* @return int[]|null
|
||||
* @phpstan-return array{int, int}|null
|
||||
*/
|
||||
public function toNetworkId(int $internalId, int $internalMeta) : array{
|
||||
public function toNetworkIdQuiet(int $internalId, int $internalMeta) : ?array{
|
||||
if($internalMeta === -1){
|
||||
$internalMeta = 0x7fff;
|
||||
}
|
||||
@ -156,17 +160,27 @@ final class ItemTranslator{
|
||||
return [$this->simpleCoreToNetMapping[$internalId], $internalMeta];
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
public function toNetworkId(int $internalId, int $internalMeta) : array{
|
||||
return $this->toNetworkIdQuiet($internalId, $internalMeta) ??
|
||||
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
* @throws TypeConversionException
|
||||
*/
|
||||
public function fromNetworkId(int $networkId, int $networkMeta, ?bool &$isComplexMapping = null) : array{
|
||||
if(isset($this->complexNetToCoreMapping[$networkId])){
|
||||
if($networkMeta !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected non-zero network meta on complex item mapping");
|
||||
throw new TypeConversionException("Unexpected non-zero network meta on complex item mapping");
|
||||
}
|
||||
$isComplexMapping = true;
|
||||
return $this->complexNetToCoreMapping[$networkId];
|
||||
@ -175,12 +189,13 @@ final class ItemTranslator{
|
||||
if(isset($this->simpleNetToCoreMapping[$networkId])){
|
||||
return [$this->simpleNetToCoreMapping[$networkId], $networkMeta];
|
||||
}
|
||||
throw new \UnexpectedValueException("Unmapped network ID/metadata combination $networkId:$networkMeta");
|
||||
throw new TypeConversionException("Unmapped network ID/metadata combination $networkId:$networkMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
* @throws TypeConversionException
|
||||
*/
|
||||
public function fromNetworkIdWithWildcardHandling(int $networkId, int $networkMeta) : array{
|
||||
$isComplexMapping = false;
|
||||
|
@ -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();
|
||||
|
@ -58,6 +58,7 @@ class TypeConverter{
|
||||
|
||||
private const DAMAGE_TAG = "Damage"; //TAG_Int
|
||||
private const DAMAGE_TAG_CONFLICT_RESOLUTION = "___Damage_ProtocolCollisionResolution___";
|
||||
private const PM_ID_TAG = "___Id___";
|
||||
private const PM_META_TAG = "___Meta___";
|
||||
|
||||
/** @var int */
|
||||
@ -143,26 +144,40 @@ class TypeConverter{
|
||||
}
|
||||
|
||||
$isBlockItem = $itemStack->getId() < 256;
|
||||
if($itemStack instanceof Durable and $itemStack->getDamage() > 0){
|
||||
if($nbt !== null){
|
||||
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
$nbt->setTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION, $existing);
|
||||
}
|
||||
}else{
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
|
||||
}elseif($isBlockItem && $itemStack->getMeta() !== 0){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
|
||||
$idMeta = ItemTranslator::getInstance()->toNetworkIdQuiet($itemStack->getId(), $itemStack->getMeta());
|
||||
if($idMeta === null){
|
||||
//Display unmapped items as INFO_UPDATE, but stick something in their NBT to make sure they don't stack with
|
||||
//other unmapped items.
|
||||
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId(ItemIds::INFO_UPDATE, 0);
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_ID_TAG, $itemStack->getId());
|
||||
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
|
||||
}else{
|
||||
[$id, $meta] = $idMeta;
|
||||
|
||||
if($itemStack instanceof Durable and $itemStack->getDamage() > 0){
|
||||
if($nbt !== null){
|
||||
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
$nbt->setTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION, $existing);
|
||||
}
|
||||
}else{
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
|
||||
}elseif($isBlockItem && $itemStack->getMeta() !== 0){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
|
||||
}
|
||||
}
|
||||
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId($itemStack->getId(), $itemStack->getMeta());
|
||||
|
||||
$blockRuntimeId = 0;
|
||||
if($isBlockItem){
|
||||
@ -197,14 +212,16 @@ class TypeConverter{
|
||||
|
||||
if($compound !== null){
|
||||
$compound = clone $compound;
|
||||
if(($idTag = $compound->getTag(self::PM_ID_TAG)) instanceof IntTag){
|
||||
$id = $idTag->getValue();
|
||||
$compound->removeTag(self::PM_ID_TAG);
|
||||
}
|
||||
if(($damageTag = $compound->getTag(self::DAMAGE_TAG)) instanceof IntTag){
|
||||
$meta = $damageTag->getValue();
|
||||
$compound->removeTag(self::DAMAGE_TAG);
|
||||
if(($conflicted = $compound->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$compound->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$compound->setTag(self::DAMAGE_TAG, $conflicted);
|
||||
}elseif($compound->count() === 0){
|
||||
$compound = null;
|
||||
}
|
||||
}elseif(($metaTag = $compound->getTag(self::PM_META_TAG)) instanceof IntTag){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
@ -212,9 +229,9 @@ class TypeConverter{
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
$meta = $metaTag->getValue();
|
||||
$compound->removeTag(self::PM_META_TAG);
|
||||
if($compound->count() === 0){
|
||||
$compound = null;
|
||||
}
|
||||
}
|
||||
if($compound->count() === 0){
|
||||
$compound = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\handler;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\RespawnPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerAction;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class DeathPacketHandler extends PacketHandler{
|
||||
@ -49,7 +50,7 @@ class DeathPacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
if($packet->action === PlayerActionPacket::ACTION_RESPAWN){
|
||||
if($packet->action === PlayerAction::RESPAWN){
|
||||
$this->player->respawn();
|
||||
return true;
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
|
||||
@ -92,6 +93,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerAction;
|
||||
use pocketmine\network\PacketHandlingException;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
@ -199,14 +201,14 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleActorEvent(ActorEventPacket $packet) : bool{
|
||||
if($packet->entityRuntimeId !== $this->player->getId()){
|
||||
if($packet->actorRuntimeId !== $this->player->getId()){
|
||||
//TODO HACK: EATING_ITEM is sent back to the server when the server sends it for other players (1.14 bug, maybe earlier)
|
||||
return $packet->event === ActorEventPacket::EATING_ITEM;
|
||||
return $packet->actorRuntimeId === ActorEvent::EATING_ITEM;
|
||||
}
|
||||
$this->player->doCloseInventory();
|
||||
|
||||
switch($packet->event){
|
||||
case ActorEventPacket::EATING_ITEM: //TODO: ignore this and handle it server-side
|
||||
switch($packet->eventId){
|
||||
case ActorEvent::EATING_ITEM: //TODO: ignore this and handle it server-side
|
||||
$item = $this->player->getInventory()->getItemInHand();
|
||||
if($item->isNull()){
|
||||
return false;
|
||||
@ -295,6 +297,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();
|
||||
@ -330,6 +333,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->player->setUsingItem(false);
|
||||
$transaction = new InventoryTransaction($this->player, $actions);
|
||||
$this->inventoryManager->onTransactionStart($transaction);
|
||||
try{
|
||||
@ -356,12 +360,12 @@ class InGamePacketHandler extends PacketHandler{
|
||||
switch($data->getActionType()){
|
||||
case UseItemTransactionData::ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$clickPos = $data->getClickPos();
|
||||
$clickPos = $data->getClickPosition();
|
||||
$spamBug = ($this->lastRightClickData !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickData->getPlayerPos()->distanceSquared($data->getPlayerPos()) < 0.00001 and
|
||||
$this->lastRightClickData->getBlockPos()->equals($data->getBlockPos()) and
|
||||
$this->lastRightClickData->getClickPos()->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
$this->lastRightClickData->getPlayerPosition()->distanceSquared($data->getPlayerPosition()) < 0.00001 and
|
||||
$this->lastRightClickData->getBlockPosition()->equals($data->getBlockPosition()) and
|
||||
$this->lastRightClickData->getClickPosition()->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickData = $data;
|
||||
@ -371,9 +375,10 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
|
||||
$blockPos = $data->getBlockPos();
|
||||
if(!$this->player->interactBlock($blockPos, $data->getFace(), $clickPos)){
|
||||
$this->onFailedBlockAction($blockPos, $data->getFace());
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){
|
||||
$this->onFailedBlockAction($vBlockPos, $data->getFace());
|
||||
}elseif(
|
||||
!array_key_exists($windowId = InventoryManager::HARDCODED_CRAFTING_GRID_WINDOW_ID, $this->openHardcodedWindows) &&
|
||||
$this->player->getCraftingGrid()->getGridWidth() === CraftingGrid::SIZE_BIG
|
||||
@ -381,7 +386,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
//TODO: HACK! crafting grid doesn't fit very well into the current PM container system, so this hack
|
||||
//allows it to carry on working approximately the same way as it did in 1.14
|
||||
$this->openHardcodedWindows[$windowId] = true;
|
||||
$this->session->sendDataPacket(ContainerOpenPacket::blockInvVec3(
|
||||
$this->session->sendDataPacket(ContainerOpenPacket::blockInv(
|
||||
InventoryManager::HARDCODED_CRAFTING_GRID_WINDOW_ID,
|
||||
WindowTypes::WORKBENCH,
|
||||
$blockPos
|
||||
@ -389,9 +394,10 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$blockPos = $data->getBlockPos();
|
||||
if(!$this->player->breakBlock($blockPos)){
|
||||
$this->onFailedBlockAction($blockPos, null);
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->breakBlock($vBlockPos)){
|
||||
$this->onFailedBlockAction($vBlockPos, null);
|
||||
}
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
@ -432,7 +438,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
private function handleUseItemOnEntityTransaction(UseItemOnEntityTransactionData $data) : bool{
|
||||
$target = $this->player->getWorld()->getEntity($data->getEntityRuntimeId());
|
||||
$target = $this->player->getWorld()->getEntity($data->getActorRuntimeId());
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
@ -442,7 +448,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
//TODO: use transactiondata for rollbacks here
|
||||
switch($data->getActionType()){
|
||||
case UseItemOnEntityTransactionData::ACTION_INTERACT:
|
||||
if(!$this->player->interactEntity($target, $data->getClickPos())){
|
||||
if(!$this->player->interactEntity($target, $data->getClickPosition())){
|
||||
$this->inventoryManager->syncSlot($this->player->getInventory(), $this->player->getInventory()->getHeldItemIndex());
|
||||
}
|
||||
return true;
|
||||
@ -498,7 +504,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
//TODO: implement handling for this where it matters
|
||||
return true;
|
||||
}
|
||||
$target = $this->player->getWorld()->getEntity($packet->target);
|
||||
$target = $this->player->getWorld()->getEntity($packet->targetActorRuntimeId);
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
@ -521,7 +527,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleBlockPickRequest(BlockPickRequestPacket $packet) : bool{
|
||||
return $this->player->pickBlock(new Vector3($packet->blockX, $packet->blockY, $packet->blockZ), $packet->addUserData);
|
||||
return $this->player->pickBlock(new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ()), $packet->addUserData);
|
||||
}
|
||||
|
||||
public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{
|
||||
@ -529,63 +535,63 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
$pos = new Vector3($packet->x, $packet->y, $packet->z);
|
||||
$pos = new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ());
|
||||
|
||||
switch($packet->action){
|
||||
case PlayerActionPacket::ACTION_START_BREAK:
|
||||
case PlayerAction::START_BREAK:
|
||||
if(!$this->player->attackBlock($pos, $packet->face)){
|
||||
$this->onFailedBlockAction($pos, $packet->face);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PlayerActionPacket::ACTION_ABORT_BREAK:
|
||||
case PlayerActionPacket::ACTION_STOP_BREAK:
|
||||
case PlayerAction::ABORT_BREAK:
|
||||
case PlayerAction::STOP_BREAK:
|
||||
$this->player->stopBreakBlock($pos);
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_START_SLEEPING:
|
||||
case PlayerAction::START_SLEEPING:
|
||||
//unused
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_STOP_SLEEPING:
|
||||
case PlayerAction::STOP_SLEEPING:
|
||||
$this->player->stopSleep();
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_JUMP:
|
||||
case PlayerAction::JUMP:
|
||||
$this->player->jump();
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_START_SPRINT:
|
||||
case PlayerAction::START_SPRINT:
|
||||
if(!$this->player->toggleSprint(true)){
|
||||
$this->player->sendData([$this->player]);
|
||||
}
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_STOP_SPRINT:
|
||||
case PlayerAction::STOP_SPRINT:
|
||||
if(!$this->player->toggleSprint(false)){
|
||||
$this->player->sendData([$this->player]);
|
||||
}
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_START_SNEAK:
|
||||
case PlayerAction::START_SNEAK:
|
||||
if(!$this->player->toggleSneak(true)){
|
||||
$this->player->sendData([$this->player]);
|
||||
}
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_STOP_SNEAK:
|
||||
case PlayerAction::STOP_SNEAK:
|
||||
if(!$this->player->toggleSneak(false)){
|
||||
$this->player->sendData([$this->player]);
|
||||
}
|
||||
return true;
|
||||
case PlayerActionPacket::ACTION_START_GLIDE:
|
||||
case PlayerActionPacket::ACTION_STOP_GLIDE:
|
||||
case PlayerAction::START_GLIDE:
|
||||
case PlayerAction::STOP_GLIDE:
|
||||
break; //TODO
|
||||
case PlayerActionPacket::ACTION_CRACK_BREAK:
|
||||
case PlayerAction::CRACK_BREAK:
|
||||
$this->player->continueBreakBlock($pos, $packet->face);
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_START_SWIMMING:
|
||||
case PlayerAction::START_SWIMMING:
|
||||
break; //TODO
|
||||
case PlayerActionPacket::ACTION_STOP_SWIMMING:
|
||||
case PlayerAction::STOP_SWIMMING:
|
||||
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //TODO: ignored (for now)
|
||||
case PlayerAction::INTERACT_BLOCK: //TODO: ignored (for now)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_CREATIVE_PLAYER_DESTROY_BLOCK:
|
||||
case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK:
|
||||
//TODO: do we need to handle this?
|
||||
break;
|
||||
default:
|
||||
@ -628,7 +634,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{
|
||||
if($packet->entityUniqueId !== $this->player->getId()){
|
||||
if($packet->targetActorUniqueId !== $this->player->getId()){
|
||||
return false; //TODO: operators can change other people's permissions using this
|
||||
}
|
||||
|
||||
@ -648,13 +654,13 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleBlockActorData(BlockActorDataPacket $packet) : bool{
|
||||
$pos = new Vector3($packet->x, $packet->y, $packet->z);
|
||||
$pos = new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ());
|
||||
if($pos->distanceSquared($this->player->getLocation()) > 10000){
|
||||
return false;
|
||||
}
|
||||
|
||||
$block = $this->player->getLocation()->getWorld()->getBlock($pos);
|
||||
$nbt = $packet->namedtag->getRoot();
|
||||
$nbt = $packet->nbt->getRoot();
|
||||
if(!($nbt instanceof CompoundTag)) throw new AssumptionFailedError("PHPStan should ensure this is a CompoundTag"); //for phpstorm's benefit
|
||||
|
||||
if($block instanceof BaseSign){
|
||||
@ -678,7 +684,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->session->getLogger()->debug("Invalid sign update data: " . base64_encode($packet->namedtag->getEncodedNbt()));
|
||||
$this->session->getLogger()->debug("Invalid sign update data: " . base64_encode($packet->nbt->getEncodedNbt()));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -712,9 +718,10 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{
|
||||
$block = $this->player->getWorld()->getBlockAt($packet->x, $packet->y, $packet->z);
|
||||
$blockPosition = $packet->blockPosition;
|
||||
$block = $this->player->getWorld()->getBlockAt($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ());
|
||||
if($block instanceof ItemFrame and $block->getFramedItem() !== null){
|
||||
return $this->player->attackBlock(new Vector3($packet->x, $packet->y, $packet->z), $block->getFacing());
|
||||
return $this->player->attackBlock(new Vector3($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ()), $block->getFacing());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -31,9 +31,11 @@ use pocketmine\network\mcpe\InventoryManager;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
|
||||
use pocketmine\network\mcpe\protocol\StartGamePacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\BoolGameRule;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSettings;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
|
||||
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
|
||||
@ -64,39 +66,46 @@ class PreSpawnPacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function setUp() : void{
|
||||
$spawnPosition = $this->player->getSpawn();
|
||||
$location = $this->player->getLocation();
|
||||
|
||||
$pk = new StartGamePacket();
|
||||
$pk->entityUniqueId = $this->player->getId();
|
||||
$pk->entityRuntimeId = $this->player->getId();
|
||||
$pk->playerGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->player->getGamemode());
|
||||
$pk->playerPosition = $this->player->getOffsetPosition($location);
|
||||
$pk->pitch = $location->pitch;
|
||||
$pk->yaw = $location->yaw;
|
||||
$pk->seed = -1;
|
||||
$pk->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly
|
||||
$pk->worldGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->server->getGamemode());
|
||||
$pk->difficulty = $location->getWorld()->getDifficulty();
|
||||
$pk->spawnX = $spawnPosition->getFloorX();
|
||||
$pk->spawnY = $spawnPosition->getFloorY();
|
||||
$pk->spawnZ = $spawnPosition->getFloorZ();
|
||||
$pk->hasAchievementsDisabled = true;
|
||||
$pk->time = $location->getWorld()->getTime();
|
||||
$pk->eduEditionOffer = 0;
|
||||
$pk->rainLevel = 0; //TODO: implement these properly
|
||||
$pk->lightningLevel = 0;
|
||||
$pk->commandsEnabled = true;
|
||||
$pk->gameRules = [
|
||||
$levelSettings = new LevelSettings();
|
||||
$levelSettings->seed = -1;
|
||||
$levelSettings->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly
|
||||
$levelSettings->worldGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->server->getGamemode());
|
||||
$levelSettings->difficulty = $location->getWorld()->getDifficulty();
|
||||
$levelSettings->spawnPosition = BlockPosition::fromVector3($location->getWorld()->getSpawnLocation());
|
||||
$levelSettings->hasAchievementsDisabled = true;
|
||||
$levelSettings->time = $location->getWorld()->getTime();
|
||||
$levelSettings->eduEditionOffer = 0;
|
||||
$levelSettings->rainLevel = 0; //TODO: implement these properly
|
||||
$levelSettings->lightningLevel = 0;
|
||||
$levelSettings->commandsEnabled = true;
|
||||
$levelSettings->gameRules = [
|
||||
"naturalregeneration" => new BoolGameRule(false, false) //Hack for client side regeneration
|
||||
];
|
||||
$pk->experiments = new Experiments([], false);
|
||||
$pk->levelId = "";
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$pk->itemTable = GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries(); //TODO: check if this is actually needed
|
||||
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
|
||||
$pk->serverSoftwareVersion = sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true));
|
||||
$this->session->sendDataPacket($pk);
|
||||
$levelSettings->experiments = new Experiments([], false);
|
||||
|
||||
$this->session->sendDataPacket(StartGamePacket::create(
|
||||
$this->player->getId(),
|
||||
$this->player->getId(),
|
||||
TypeConverter::getInstance()->coreGameModeToProtocol($this->player->getGamemode()),
|
||||
$this->player->getOffsetPosition($location),
|
||||
$location->pitch,
|
||||
$location->yaw,
|
||||
$levelSettings,
|
||||
"",
|
||||
$this->server->getMotd(),
|
||||
"",
|
||||
false,
|
||||
new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
|
||||
[],
|
||||
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries()
|
||||
));
|
||||
|
||||
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getAvailableActorIdentifiers());
|
||||
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs());
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe\handler;
|
||||
|
||||
use pocketmine\lang\KnownTranslationKeys;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
@ -34,6 +35,7 @@ use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
|
||||
use pocketmine\resourcepacks\ResourcePack;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use function array_map;
|
||||
@ -113,7 +115,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
self::PACK_CHUNK_SIZE,
|
||||
(int) ceil($pack->getPackSize() / self::PACK_CHUNK_SIZE),
|
||||
$pack->getPackSize(),
|
||||
$pack->getSha256()
|
||||
$pack->getSha256(),
|
||||
false,
|
||||
ResourcePackType::ADDON //TODO: this might be an addon (not behaviour pack), needed to properly support client-side custom items
|
||||
));
|
||||
}
|
||||
$this->session->getLogger()->debug("Player requested download of " . count($packet->packIds) . " resource packs");
|
||||
@ -130,7 +134,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
//we don't force here, because it doesn't have user-facing effects
|
||||
//but it does have an annoying side-effect when true: it makes
|
||||
//the client remove its own non-server-supplied resource packs.
|
||||
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, new Experiments([], false)));
|
||||
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, ProtocolInfo::MINECRAFT_VERSION_NETWORK, new Experiments([], false)));
|
||||
$this->session->getLogger()->debug("Applying resource pack stack");
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
|
@ -32,11 +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\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use raklib\generic\SocketException;
|
||||
use raklib\protocol\EncapsulatedPacket;
|
||||
use raklib\protocol\PacketReliability;
|
||||
use raklib\server\ipc\RakLibToUserThreadMessageReceiver;
|
||||
@ -121,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");
|
||||
}
|
||||
|
||||
@ -133,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");
|
||||
}
|
||||
@ -192,10 +197,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$logger->error("Bad packet (error ID $errorId): " . $e->getMessage());
|
||||
|
||||
//intentionally doesn't use logException, we don't want spammy packet error traces to appear in release mode
|
||||
$logger->debug("Origin: " . Filesystem::cleanPath($e->getFile()) . "(" . $e->getLine() . ")");
|
||||
foreach(Utils::printableTrace($e->getTrace()) as $frame){
|
||||
$logger->debug($frame);
|
||||
}
|
||||
$logger->debug(implode("\n", Utils::printableExceptionInfo($e)));
|
||||
$session->disconnect("Packet processing error (Error ID: $errorId)");
|
||||
$this->interface->blockAddress($address, 5);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user