mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-13 12:55:06 +00:00
Compare commits
119 Commits
Author | SHA1 | Date | |
---|---|---|---|
5066d5225b | |||
aefaf73685 | |||
15401d740f | |||
5920b0ba40 | |||
dea75a0687 | |||
873e8740e0 | |||
260c55f23a | |||
9ed430acb9 | |||
f0241043de | |||
135f1c95e4 | |||
5431807e43 | |||
d49ae832e8 | |||
ff9d013005 | |||
b0e1317818 | |||
8653afb0fb | |||
995b56aaa0 | |||
3ecddf312d | |||
470243ca6f | |||
3f21e59917 | |||
fdd74a4f46 | |||
a43b46a93c | |||
0604dfc9e5 | |||
dd2c3db285 | |||
c95e283507 | |||
6afbd1f55c | |||
0682c93f5a | |||
da90ae85da | |||
e87127f309 | |||
0237a50d90 | |||
8b53e4150e | |||
1c43538238 | |||
68887105b2 | |||
104e90b794 | |||
994062f6dc | |||
69a41a5ed4 | |||
3903b70ef5 | |||
692e63ad7c | |||
4d1be4d41d | |||
5f0310a8b6 | |||
9b01fb3d89 | |||
f28405fcfb | |||
9c07c206f6 | |||
d0d701f232 | |||
07cae8a129 | |||
6869ee1c2d | |||
26155acff2 | |||
b550cf5163 | |||
48fa19fdcd | |||
bac986d0b2 | |||
215bac8dd7 | |||
3709ba172b | |||
ef034f2d68 | |||
ab18332572 | |||
48595630fc | |||
4102205ba6 | |||
9e85ee4a7a | |||
e8e6b9304c | |||
23849b7f63 | |||
d2f68836c6 | |||
d19db5d2e4 | |||
98cdc80d37 | |||
8273f789ee | |||
29eccba5f0 | |||
9984b15de6 | |||
6ea01e0dd4 | |||
46331df7db | |||
691c49fb32 | |||
db815360d1 | |||
6e297168c2 | |||
95dbb00d4c | |||
50e29a5ed8 | |||
9f3fb935b5 | |||
e30b1ee2c7 | |||
574b7f6343 | |||
e8b6b56330 | |||
c368ebb5e7 | |||
fa920aa868 | |||
a421d32273 | |||
6c21c23444 | |||
55e0d9c520 | |||
37ee3f2775 | |||
bfdcc12e81 | |||
b2299e08e0 | |||
d7741050c5 | |||
6cff08cd65 | |||
fec42f16ba | |||
deb0cee8a0 | |||
c0dafe7872 | |||
340881d590 | |||
e2e960e43d | |||
500fd2d842 | |||
0b550b346b | |||
1424114cf2 | |||
a8980a0f67 | |||
69aa7c5ac1 | |||
11b74868ee | |||
9a53de0903 | |||
0f8101d4a6 | |||
55ecac4c80 | |||
2a1d1e90a2 | |||
4444a79468 | |||
4cbeee3ab8 | |||
a251960c1c | |||
52f734799e | |||
42171f6e06 | |||
1fe4fdc67c | |||
af4f30d1c8 | |||
3e2926441d | |||
0b33762be0 | |||
e6f89213dc | |||
f8d249b240 | |||
b39afa20d1 | |||
7027a9b972 | |||
b02f3f4090 | |||
8564912149 | |||
873535f719 | |||
78f4fcf6ab | |||
90b749c260 | |||
d5398b2781 |
1
.github/ISSUE_TEMPLATE/crash.md
vendored
1
.github/ISSUE_TEMPLATE/crash.md
vendored
@ -9,6 +9,7 @@ assignees: ''
|
||||
|
||||
<!--- submit crashdump files to https://crash.pmmp.io -->
|
||||
<!--- or, copy the data between ===BEGIN CRASH DUMP=== and ===END CRASH DUMP and paste it on a site like https://pastebin.com -->
|
||||
<!--- DON'T JUST PASTE the crashdump into an issue -->
|
||||
Link to crashdump:
|
||||
|
||||
<!--- write additional information about the crash to help us find the problem -->
|
||||
|
147
.github/workflows/main.yml
vendored
Normal file
147
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-php:
|
||||
name: Prepare PHP
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2 #needed for build.sh
|
||||
- name: Check for PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Compile PHP
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: ./tests/gh-actions/build.sh
|
||||
|
||||
phpstan:
|
||||
name: PHPStan analysis
|
||||
needs: build-php
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
|
||||
|
||||
phpunit:
|
||||
name: PHPUnit tests
|
||||
needs: build-php
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPUnit tests
|
||||
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
|
||||
integration:
|
||||
name: Integration tests
|
||||
needs: build-php
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run integration tests
|
||||
run: ./tests/travis.sh -t4
|
21
.travis.yml
21
.travis.yml
@ -1,21 +0,0 @@
|
||||
import:
|
||||
source: ./tests/travis/setup-php.yml
|
||||
|
||||
script:
|
||||
- composer install --prefer-dist
|
||||
- ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
|
||||
- ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
- composer install --no-dev --prefer-dist
|
||||
- ./tests/travis.sh -t4
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache/files
|
||||
- $HOME/.composer/cache/vcs
|
||||
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- team@pmmp.io
|
||||
on_success: change
|
||||
on_failure: always
|
@ -30,7 +30,7 @@ If you use a custom binary, you'll need to replace `composer` usages in this gui
|
||||
Preprocessor requires that the `cpp` (c preprocessor) is available in your PATH.
|
||||
|
||||
## Building `PocketMine-MP.phar`
|
||||
Run `build/server-phar.php` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
|
||||
Run `composer make-server` using your preferred PHP binary. It'll drop a `PocketMine-MP.phar` into the current working directory.
|
||||
|
||||
You can also use the `--out` option to change the output filename.
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<b>A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP</b>
|
||||
</p>
|
||||
|
||||
[](https://travis-ci.com/pmmp/PocketMine-MP)
|
||||

|
||||
|
||||
## Getting started
|
||||
- [Documentation](http://pmmp.readthedocs.org/)
|
||||
|
@ -37,7 +37,6 @@ use const STDIN;
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
|
||||
function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev) : void{
|
||||
$versionInfo = file_get_contents($versionInfoPath);
|
||||
$versionInfo = preg_replace(
|
||||
@ -86,6 +85,4 @@ function main(array $argv) : void{
|
||||
system('git push origin HEAD ' . $currentVer->getBaseVersion());
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
main($argv);
|
||||
}
|
||||
main($argv);
|
||||
|
Submodule build/php updated: e45cfc1ece...a42c7df20a
@ -26,7 +26,6 @@ namespace pocketmine\build\server_phar;
|
||||
use pocketmine\utils\Git;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function getcwd;
|
||||
@ -41,6 +40,7 @@ use function rtrim;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function unlink;
|
||||
use const PHP_EOL;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
@ -129,6 +129,10 @@ function main() : void{
|
||||
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
if(file_exists(dirname(__DIR__) . '/vendor/phpunit')){
|
||||
echo "Remove Composer dev dependencies before building (composer install --no-dev)" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$opts = getopt("", ["out:", "git:"]);
|
||||
if(isset($opts["git"])){
|
||||
@ -169,6 +173,4 @@ STUB
|
||||
}
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
main();
|
||||
}
|
||||
main();
|
||||
|
@ -32,3 +32,23 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Fixed issues with preloading `SubChunk`.
|
||||
- `/gc` and automatic garbage collection will now release unused heap blocks back to the OS. Previously, the PHP process might hold onto these blocks indefinitely even when not used, causing elevated real memory usage.
|
||||
- Added some documentation to `FurnaceBurnEvent`.
|
||||
|
||||
# 3.15.3
|
||||
- Fixed fall damage accumulation over continuous knockbacks (e.g. combo attacks in PvP).
|
||||
- Fixed a bug in `Human->addXp()` that would cause a crash when saving player data.
|
||||
- `Human->addXp()` will no longer modify the target's total XP if `PlayerExperienceChangeEvent` was cancelled.
|
||||
- `AsyncPool->getTaskQueueSizes()` has been added to allow external detection of async pool overload. This is planned to be implemented as a core feature in the future, but it hasn't been done yet.
|
||||
- `BaseInventory->canAddItem()` behaviour now matches `addItem()` by considering the max stack size of the given item.
|
||||
- Fixed a bug in generator options handling for worlds loaded via `pocketmine.yml`. This fix has the following side effects:
|
||||
- It's now possible to provide generator options as an `options` key when loading a world via `pocketmine.yml`.
|
||||
- If generator options are not provided, the options from `server.properties` will be used, instead of using an empty preset. (It's not clear whether this is desired behaviour, but it was clearly intended, since there is code to do this which was broken until this release. As such, this behaviour is subject to change in the future.)
|
||||
- Fixed a bug in region-based world loading where some files without filename extensions and names containing a region filename extension (e.g a file named `amca` in a McRegion world) would cause the world not to load. These files are now ignored.
|
||||
- Default network compression level has been lowered to 6, due to level 7 being 25% more expensive for only a marginal improvement in bandwidth.
|
||||
- Fixed a performance issue with chunk requesting when players trigger chunk generation on first join.
|
||||
- Setup wizard will now always show IP information, even if the user chose to skip the setup wizard when prompted. (This doesn't affect `--no-wizard` in any way.)
|
||||
- `Maximum memory (system)` is no longer reported in `/status` due to having a misleading output (it was the same as the current memory usage).
|
||||
- The `Player Chunk Send` timer on timings reports now actually reports measurements of chunk sending, not chunk loading.
|
||||
- A new parent timer `World Load` has been added to timings reports, which aggregates timings from `syncChunkLoad` and subtimings from all worlds.
|
||||
|
||||
# 3.15.4
|
||||
- Fixed a bug in the inventory transaction system that caused the server to freeze under some circumstances.
|
||||
|
24
changelogs/3.16.md
Normal file
24
changelogs/3.16.md
Normal file
@ -0,0 +1,24 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.100**
|
||||
|
||||
### 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.16.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.100.
|
||||
- Removed compatibility with earlier versions.
|
||||
- Added new custom composer commands `make-server` and `make-devtools` to ease setting up a development environment and building the server.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.16.1
|
||||
- Fixed incorrect encoding of skins in the protocol.
|
||||
- `/version` no longer crashes when a plugin provides `string[]` for the `author` field in `plugin.yml`.
|
||||
- `author` in `plugin.yml` now accepts arrays, just like `authors`.
|
||||
- Fixed `HellBiome` never being registered.
|
28
changelogs/3.17.md
Normal file
28
changelogs/3.17.md
Normal file
@ -0,0 +1,28 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.200**
|
||||
|
||||
### 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.17.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.200.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.17.1
|
||||
- Fixed some instances of plugin-caused crashes not being detected (eval()'d code, custom plugin paths).
|
||||
- Server uptime is now included in crash reports.
|
||||
- Hoes now take damage when used to break sponges.
|
||||
- Using lava as fuel in a furnace now leaves behind an empty bucket.
|
||||
|
||||
# 3.17.2
|
||||
- Fixed region header corruption when chunks with larger-than-expected lengths are found. These chunks are now treated as corrupted, instead of automatically attempting to salvage them (which usually fails anyway).
|
||||
- `RegionLoader->removeChunk()` now allows the space used by the removed chunk to be reused by future region saves.
|
||||
- Extracted `Living->applyConsumptionResults()` from `Living->consumeObject()` (preparation for a future bug fix).
|
@ -7,7 +7,6 @@
|
||||
"require": {
|
||||
"php": ">=7.3.0",
|
||||
"php-64bit": "*",
|
||||
"ext-bcmath": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-date": "*",
|
||||
@ -35,10 +34,10 @@
|
||||
"pocketmine/log-pthreads": "^0.1.0",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"adhocore/json-comment": "^0.1.0",
|
||||
"ocramius/package-versions": "^1.5"
|
||||
"composer-runtime-api": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.48",
|
||||
"phpstan/phpstan": "0.12.64",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
@ -62,5 +61,12 @@
|
||||
"platform": {
|
||||
"php": "7.3.0"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/DevTools/ConsoleScript.php --make tests/plugins/DevTools --out plugins/DevTools.phar",
|
||||
"make-server": [
|
||||
"@composer install --no-dev --classmap-authoritative",
|
||||
"@php -dphar.readonly=0 build/server-phar.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
588
composer.lock
generated
588
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,6 @@ includes:
|
||||
- tests/phpstan/configs/actual-problems.neon
|
||||
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
|
||||
- tests/phpstan/configs/com-dotnet-magic.neon
|
||||
- tests/phpstan/configs/custom-leveldb.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/l7-baseline.neon
|
||||
- tests/phpstan/configs/l8-baseline.neon
|
||||
|
@ -23,11 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
use PackageVersions\Versions;
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\plugin\PluginLoadOrder;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\utils\VersionString;
|
||||
use function base64_encode;
|
||||
@ -46,6 +47,7 @@ use function is_resource;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function max;
|
||||
use function microtime;
|
||||
use function mkdir;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
@ -54,10 +56,10 @@ use function php_uname;
|
||||
use function phpinfo;
|
||||
use function phpversion;
|
||||
use function preg_replace;
|
||||
use function sprintf;
|
||||
use function str_split;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function time;
|
||||
use function zend_version;
|
||||
use function zlib_encode;
|
||||
use const E_COMPILE_ERROR;
|
||||
@ -88,7 +90,7 @@ class CrashDump{
|
||||
* having their content changed, version format changing, etc.
|
||||
* It is not necessary to increase this when adding new fields.
|
||||
*/
|
||||
private const FORMAT_VERSION = 3;
|
||||
private const FORMAT_VERSION = 4;
|
||||
|
||||
private const PLUGIN_INVOLVEMENT_NONE = "none";
|
||||
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
|
||||
@ -98,7 +100,7 @@ class CrashDump{
|
||||
private $server;
|
||||
/** @var resource */
|
||||
private $fp;
|
||||
/** @var int */
|
||||
/** @var float */
|
||||
private $time;
|
||||
/**
|
||||
* @var mixed[]
|
||||
@ -111,12 +113,12 @@ class CrashDump{
|
||||
private $path;
|
||||
|
||||
public function __construct(Server $server){
|
||||
$this->time = time();
|
||||
$this->time = microtime(true);
|
||||
$this->server = $server;
|
||||
if(!is_dir($this->server->getDataPath() . "crashdumps")){
|
||||
mkdir($this->server->getDataPath() . "crashdumps");
|
||||
}
|
||||
$this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log";
|
||||
$this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", (int) $this->time) . ".log";
|
||||
$fp = @fopen($this->path, "wb");
|
||||
if(!is_resource($fp)){
|
||||
throw new \RuntimeException("Could not create Crash Dump");
|
||||
@ -124,7 +126,8 @@ class CrashDump{
|
||||
$this->fp = $fp;
|
||||
$this->data["format_version"] = self::FORMAT_VERSION;
|
||||
$this->data["time"] = $this->time;
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time));
|
||||
$this->data["uptime"] = $this->time - \pocketmine\START_TIME;
|
||||
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->time));
|
||||
$this->addLine();
|
||||
$this->baseCrash();
|
||||
$this->generalData();
|
||||
@ -165,7 +168,9 @@ class CrashDump{
|
||||
if($json === false){
|
||||
throw new \RuntimeException("Failed to encode crashdump JSON: " . json_last_error_msg());
|
||||
}
|
||||
$this->encodedData = zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9);
|
||||
$zlibEncoded = zlib_encode($json, ZLIB_ENCODING_DEFLATE, 9);
|
||||
if($zlibEncoded === false) throw new AssumptionFailedError("ZLIB compression failed");
|
||||
$this->encodedData = $zlibEncoded;
|
||||
foreach(str_split(base64_encode($this->encodedData), 76) as $line){
|
||||
$this->addLine($line);
|
||||
}
|
||||
@ -309,8 +314,8 @@ class CrashDump{
|
||||
}
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
$frameCleanPath = Utils::cleanPath($filePath); //this will be empty in phar stub
|
||||
if(strpos($frameCleanPath, "plugins") === 0 and file_exists($filePath)){
|
||||
$frameCleanPath = Utils::cleanPath($filePath);
|
||||
if(strpos($frameCleanPath, Utils::CLEAN_PATH_SRC_PREFIX) !== 0){
|
||||
$this->addLine();
|
||||
if($crashFrame){
|
||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||
@ -320,15 +325,17 @@ class CrashDump{
|
||||
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
if(file_exists($filePath)){
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||
if(strpos($frameCleanPath, $filePath) === 0){
|
||||
$this->data["plugin"] = $plugin->getName();
|
||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -338,6 +345,15 @@ class CrashDump{
|
||||
|
||||
private function generalData() : void{
|
||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||
$composerLibraries = [];
|
||||
foreach(InstalledVersions::getInstalledPackages() as $package){
|
||||
$composerLibraries[$package] = sprintf(
|
||||
"%s@%s",
|
||||
InstalledVersions::getPrettyVersion($package) ?? "unknown",
|
||||
InstalledVersions::getReference($package) ?? "unknown"
|
||||
);
|
||||
}
|
||||
|
||||
$this->data["general"] = [];
|
||||
$this->data["general"]["name"] = $this->server->getName();
|
||||
$this->data["general"]["base_version"] = \pocketmine\BASE_VERSION;
|
||||
@ -350,7 +366,7 @@ class CrashDump{
|
||||
$this->data["general"]["zend"] = zend_version();
|
||||
$this->data["general"]["php_os"] = PHP_OS;
|
||||
$this->data["general"]["os"] = Utils::getOS();
|
||||
$this->data["general"]["composer_libraries"] = Versions::VERSIONS;
|
||||
$this->data["general"]["composer_libraries"] = $composerLibraries;
|
||||
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
|
||||
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
|
||||
$this->addLine("uname -a: " . php_uname("a"));
|
||||
@ -358,7 +374,7 @@ class CrashDump{
|
||||
$this->addLine("Zend version: " . zend_version());
|
||||
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
|
||||
$this->addLine("Composer libraries: ");
|
||||
foreach(Versions::VERSIONS as $library => $libraryVersion){
|
||||
foreach($composerLibraries as $library => $libraryVersion){
|
||||
$this->addLine("- $library $libraryVersion");
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
@ -148,6 +149,7 @@ use pocketmine\network\mcpe\protocol\types\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandParameter;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\network\mcpe\protocol\types\GameMode;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
@ -1939,7 +1941,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$animation["ImageWidth"],
|
||||
base64_decode($animation["Image"], true)),
|
||||
$animation["Type"],
|
||||
$animation["Frames"]
|
||||
$animation["Frames"],
|
||||
$animation["AnimationExpression"]
|
||||
);
|
||||
}
|
||||
|
||||
@ -2179,6 +2182,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
//but it does have an annoying side-effect when true: it makes
|
||||
//the client remove its own non-server-supplied resource packs.
|
||||
$pk->mustAccept = false;
|
||||
$pk->experiments = new Experiments([], false);
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
@ -2244,6 +2248,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->commandsEnabled = true;
|
||||
$pk->levelId = "";
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$pk->experiments = new Experiments([], false);
|
||||
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
|
||||
@ -2870,7 +2876,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE_REQUEST)){
|
||||
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN)){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2957,7 +2963,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case PlayerActionPacket::ACTION_STOP_SWIMMING:
|
||||
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //ignored (for now)
|
||||
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //TODO: ignored (for now)
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_CREATIVE_PLAYER_DESTROY_BLOCK:
|
||||
//TODO: do we need to handle this?
|
||||
break;
|
||||
default:
|
||||
$this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName());
|
||||
@ -3039,6 +3048,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var int|null */
|
||||
private $closingWindowId = null;
|
||||
|
||||
/** @internal */
|
||||
public function getClosingWindowId() : ?int{ return $this->closingWindowId; }
|
||||
|
||||
public function handleContainerClose(ContainerClosePacket $packet) : bool{
|
||||
if(!$this->spawned){
|
||||
return true;
|
||||
@ -3050,12 +3065,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
unset($this->openHardcodedWindows[$packet->windowId]);
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowId = $packet->windowId;
|
||||
$pk->server = false;
|
||||
$this->sendDataPacket($pk);
|
||||
return true;
|
||||
}
|
||||
if(isset($this->windowIndex[$packet->windowId])){
|
||||
$this->closingWindowId = $packet->windowId;
|
||||
(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call();
|
||||
$this->removeWindow($this->windowIndex[$packet->windowId]);
|
||||
$this->closingWindowId = null;
|
||||
//removeWindow handles sending the appropriate
|
||||
return true;
|
||||
}
|
||||
@ -4122,7 +4140,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function onChunkChanged(Chunk $chunk){
|
||||
if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
$hasSent = $this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())] ?? false;
|
||||
if($hasSent){
|
||||
$this->usedChunks[$hash] = false;
|
||||
$this->nextChunkOrderRun = 0;
|
||||
}
|
||||
|
@ -73,7 +73,6 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
$extensions = [
|
||||
"bcmath" => "BC Math",
|
||||
"curl" => "cURL",
|
||||
"ctype" => "ctype",
|
||||
"date" => "Date",
|
||||
@ -280,7 +279,7 @@ namespace pocketmine {
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
$logger->debug("Some threads could not be stopped, performing a force-kill");
|
||||
Process::kill(getmypid());
|
||||
Process::kill(Process::pid());
|
||||
}
|
||||
}while(false);
|
||||
|
||||
@ -300,7 +299,5 @@ namespace pocketmine {
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){
|
||||
\pocketmine\server();
|
||||
}
|
||||
\pocketmine\server();
|
||||
}
|
||||
|
@ -126,7 +126,6 @@ use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function getmypid;
|
||||
use function getopt;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
@ -1400,10 +1399,10 @@ class Server{
|
||||
Network::$BATCH_THRESHOLD = -1;
|
||||
}
|
||||
|
||||
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 7);
|
||||
$this->networkCompressionLevel = (int) $this->getProperty("network.compression-level", 6);
|
||||
if($this->networkCompressionLevel < 1 or $this->networkCompressionLevel > 9){
|
||||
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 7");
|
||||
$this->networkCompressionLevel = 7;
|
||||
$this->logger->warning("Invalid network compression level $this->networkCompressionLevel set, setting to default 6");
|
||||
$this->networkCompressionLevel = 6;
|
||||
}
|
||||
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
|
||||
|
||||
@ -1543,7 +1542,7 @@ class Server{
|
||||
if(isset($options["generator"])){
|
||||
$generatorOptions = explode(":", $options["generator"]);
|
||||
$generator = GeneratorManager::getGenerator(array_shift($generatorOptions));
|
||||
if(count($options) > 0){
|
||||
if(count($generatorOptions) > 0){
|
||||
$options["preset"] = implode(":", $generatorOptions);
|
||||
}
|
||||
}else{
|
||||
@ -1939,7 +1938,7 @@ class Server{
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
$this->logger->emergency("Crashed while crashing, killing process");
|
||||
@Process::kill(getmypid());
|
||||
@Process::kill(Process::pid());
|
||||
}
|
||||
|
||||
}
|
||||
@ -2132,7 +2131,7 @@ class Server{
|
||||
echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
|
||||
sleep($spacing);
|
||||
}
|
||||
@Process::kill(getmypid());
|
||||
@Process::kill(Process::pid());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.15.2";
|
||||
const BASE_VERSION = "3.17.2";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
@ -35,5 +35,6 @@ interface BlockToolType{
|
||||
public const TYPE_PICKAXE = 1 << 2;
|
||||
public const TYPE_AXE = 1 << 3;
|
||||
public const TYPE_SHEARS = 1 << 4;
|
||||
public const TYPE_HOE = 1 << 5;
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ class Sponge extends Solid{
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_HOE;
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0.6;
|
||||
}
|
||||
|
@ -100,7 +100,6 @@ class StatusCommand extends VanillaCommand{
|
||||
$sender->sendMessage(TextFormat::GOLD . "Total memory: " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2), 2) . " MB.");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Total virtual memory: " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2), 2) . " MB.");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Heap memory: " . TextFormat::RED . number_format(round(($rUsage[0] / 1024) / 1024, 2), 2) . " MB.");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (system): " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2), 2) . " MB.");
|
||||
|
||||
if($server->getProperty("memory.global-limit") > 0){
|
||||
$sender->sendMessage(TextFormat::GOLD . "Maximum memory (manager): " . TextFormat::RED . number_format(round($server->getProperty("memory.global-limit"), 2), 2) . " MB.");
|
||||
|
@ -77,7 +77,6 @@ use function abs;
|
||||
use function assert;
|
||||
use function cos;
|
||||
use function count;
|
||||
use function current;
|
||||
use function deg2rad;
|
||||
use function floor;
|
||||
use function fmod;
|
||||
@ -334,8 +333,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
*/
|
||||
private static $knownEntities = [];
|
||||
/**
|
||||
* @var string[][]
|
||||
* @phpstan-var array<class-string<Entity>, list<string>>
|
||||
* @var string[]
|
||||
* @phpstan-var array<class-string<Entity>, string>
|
||||
*/
|
||||
private static $saveNames = [];
|
||||
|
||||
@ -414,7 +413,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
self::$knownEntities[$name] = $className;
|
||||
}
|
||||
|
||||
self::$saveNames[$className] = $saveNames;
|
||||
self::$saveNames[$className] = reset($saveNames);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -872,8 +871,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
if(!isset(self::$saveNames[static::class])){
|
||||
throw new \InvalidStateException("Entity " . static::class . " is not registered");
|
||||
}
|
||||
reset(self::$saveNames[static::class]);
|
||||
return current(self::$saveNames[static::class]);
|
||||
return self::$saveNames[static::class];
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
@ -1451,8 +1449,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->fall($this->fallDistance);
|
||||
$this->resetFallDistance();
|
||||
}
|
||||
}elseif($distanceThisTick < 0){
|
||||
}elseif($distanceThisTick < $this->fallDistance){
|
||||
//we've fallen some distance (distanceThisTick is negative)
|
||||
//or we ascended back towards where fall distance was measured from initially (distanceThisTick is positive but less than existing fallDistance)
|
||||
$this->fallDistance -= $distanceThisTick;
|
||||
}else{
|
||||
//we ascended past the apex where fall distance was originally being measured from
|
||||
//reset it so it will be measured starting from the new, higher position
|
||||
$this->fallDistance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,17 +300,20 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
if($consumable instanceof MaybeConsumable and !$consumable->canBeConsumed()){
|
||||
return false;
|
||||
}
|
||||
if($consumable instanceof FoodSource && $consumable->requiresHunger() and !$this->isHungry()){
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::consumeObject($consumable);
|
||||
}
|
||||
|
||||
protected function applyConsumptionResults(Consumable $consumable) : void{
|
||||
if($consumable instanceof FoodSource){
|
||||
if($consumable->requiresHunger() and !$this->isHungry()){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addFood($consumable->getFoodRestore());
|
||||
$this->addSaturation($consumable->getSaturationRestore());
|
||||
}
|
||||
|
||||
return parent::consumeObject($consumable);
|
||||
parent::applyConsumptionResults($consumable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,12 +403,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
* @param bool $playSound Whether to play level-up and XP gained sounds.
|
||||
*/
|
||||
public function addXp(int $amount, bool $playSound = true) : bool{
|
||||
$this->totalXp += $amount;
|
||||
|
||||
$oldLevel = $this->getXpLevel();
|
||||
$oldTotal = $this->getCurrentTotalXp();
|
||||
|
||||
if($this->setCurrentTotalXp($oldTotal + $amount)){
|
||||
if($amount > 0){
|
||||
$this->totalXp += $amount;
|
||||
}
|
||||
|
||||
if($playSound){
|
||||
$newLevel = $this->getXpLevel();
|
||||
if((int) ($newLevel / 5) > (int) ($oldLevel / 5)){
|
||||
|
@ -363,13 +363,20 @@ abstract class Living extends Entity implements Damageable{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->applyConsumptionResults($consumable);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies effects from consuming the object. This shouldn't do any can-consume checks (those are expected to be
|
||||
* handled by the caller).
|
||||
*/
|
||||
protected function applyConsumptionResults(Consumable $consumable) : void{
|
||||
foreach($consumable->getAdditionalEffects() as $effect){
|
||||
$this->addEffect($effect);
|
||||
}
|
||||
|
||||
$consumable->onConsume($this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\entity\utils;
|
||||
|
||||
use pocketmine\math\Math;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function count;
|
||||
use function max;
|
||||
|
||||
abstract class ExperienceUtils{
|
||||
|
@ -39,14 +39,22 @@ class PlayerCreationEvent extends Event{
|
||||
/** @var int */
|
||||
private $port;
|
||||
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<Player>
|
||||
*/
|
||||
private $baseClass;
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<Player>
|
||||
*/
|
||||
private $playerClass;
|
||||
|
||||
/**
|
||||
* @param string $baseClass
|
||||
* @param string $playerClass
|
||||
* @phpstan-param class-string<Player> $baseClass
|
||||
* @phpstan-param class-string<Player> $playerClass
|
||||
*/
|
||||
public function __construct(SourceInterface $interface, $baseClass, $playerClass, string $address, int $port){
|
||||
$this->interface = $interface;
|
||||
@ -80,6 +88,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @phpstan-return class-string<Player>
|
||||
*/
|
||||
public function getBaseClass(){
|
||||
return $this->baseClass;
|
||||
@ -87,6 +96,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @phpstan-param class-string<Player> $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -100,6 +110,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @phpstan-return class-string<Player>
|
||||
*/
|
||||
public function getPlayerClass(){
|
||||
return $this->playerClass;
|
||||
@ -107,6 +118,7 @@ class PlayerCreationEvent extends Event{
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @phpstan-param class-string<Player> $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -252,11 +252,11 @@ abstract class BaseInventory implements Inventory{
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$slot = $this->getItem($i);
|
||||
if($item->equals($slot)){
|
||||
if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){
|
||||
if(($diff = min($slot->getMaxStackSize(), $item->getMaxStackSize()) - $slot->getCount()) > 0){
|
||||
$count -= $diff;
|
||||
}
|
||||
}elseif($slot->isNull()){
|
||||
$count -= $this->getMaxStackSize();
|
||||
$count -= min($this->getMaxStackSize(), $item->getMaxStackSize());
|
||||
}
|
||||
|
||||
if($count <= 0){
|
||||
|
@ -64,6 +64,7 @@ abstract class ContainerInventory extends BaseInventory{
|
||||
public function onClose(Player $who) : void{
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowId = $who->getWindowId($this);
|
||||
$pk->server = $who->getClosingWindowId() !== $pk->windowId;
|
||||
$who->dataPacket($pk);
|
||||
parent::onClose($who);
|
||||
}
|
||||
|
@ -108,9 +108,6 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
}
|
||||
}
|
||||
|
||||
if($iterations < 1){
|
||||
throw new TransactionValidationException("Tried to craft zero times");
|
||||
}
|
||||
if(count($txItems) > 0){
|
||||
//all items should be destroyed in this process
|
||||
throw new TransactionValidationException("Expected 0 ingredients left over, have " . count($txItems));
|
||||
@ -166,6 +163,7 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
*/
|
||||
$pk = new ContainerClosePacket();
|
||||
$pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID;
|
||||
$pk->server = true;
|
||||
$this->source->dataPacket($pk);
|
||||
}
|
||||
|
||||
|
@ -230,21 +230,34 @@ class InventoryTransaction{
|
||||
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
|
||||
assert(count($possibleActions) > 0);
|
||||
|
||||
$candidate = null;
|
||||
$newList = $possibleActions;
|
||||
foreach($possibleActions as $i => $action){
|
||||
if($action->getSourceItem()->equalsExact($needOrigin)){
|
||||
$newList = $possibleActions;
|
||||
if($candidate !== null){
|
||||
/*
|
||||
* we found multiple possible actions that match the origin action
|
||||
* this means that there are multiple ways that this chain could play out
|
||||
* if we cared so much about this, we could build all the possible chains in parallel and see which
|
||||
* variation managed to complete the chain, but this has an extremely high complexity which is not
|
||||
* worth the trouble for this scenario (we don't usually expect to see chains longer than a couple
|
||||
* of actions in here anyway), and might still result in multiple possible results.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
$candidate = $action;
|
||||
unset($newList[$i]);
|
||||
if(count($newList) === 0){
|
||||
return $action->getTargetItem();
|
||||
}
|
||||
$result = $this->findResultItem($action->getTargetItem(), $newList);
|
||||
if($result !== null){
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($candidate === null){
|
||||
//chaining is not possible with this origin, none of the actions are valid
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
if(count($newList) === 0){
|
||||
return $candidate->getTargetItem();
|
||||
}
|
||||
return $this->findResultItem($candidate->getTargetItem(), $newList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,6 +50,14 @@ class Bucket extends Item implements MaybeConsumable{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getFuelResidue() : Item{
|
||||
if($this->meta === Block::LAVA or $this->meta === Block::FLOWING_LAVA){
|
||||
return ItemFactory::get(Item::BUCKET);
|
||||
}
|
||||
|
||||
return parent::getFuelResidue();
|
||||
}
|
||||
|
||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
||||
$resultBlock = BlockFactory::get($this->meta);
|
||||
|
||||
|
@ -23,11 +23,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockToolType;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class Hoe extends TieredTool{
|
||||
|
||||
public function getBlockToolType() : int{
|
||||
return BlockToolType::TYPE_HOE;
|
||||
}
|
||||
|
||||
public function onAttackEntity(Entity $victim) : bool{
|
||||
return $this->applyDamage(1);
|
||||
}
|
||||
|
||||
public function onDestroyBlock(Block $block) : bool{
|
||||
if($block->getHardness() > 0){
|
||||
return $this->applyDamage(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -655,6 +655,16 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an item after burning fuel
|
||||
*/
|
||||
public function getFuelResidue() : Item{
|
||||
$item = clone $this;
|
||||
$item->pop();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many points of damage this item will deal to an entity when used as a weapon.
|
||||
*/
|
||||
|
@ -154,7 +154,6 @@ class Explosion{
|
||||
* and creating sounds and particles.
|
||||
*/
|
||||
public function explodeB() : bool{
|
||||
$send = [];
|
||||
$updateBlocks = [];
|
||||
|
||||
$source = (new Vector3($this->source->x, $this->source->y, $this->source->z))->floor();
|
||||
@ -254,7 +253,6 @@ class Explosion{
|
||||
$updateBlocks[$index] = true;
|
||||
}
|
||||
}
|
||||
$send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z);
|
||||
}
|
||||
|
||||
$this->level->addParticle(new HugeExplodeSeedParticle($source));
|
||||
|
@ -53,7 +53,6 @@ use pocketmine\level\format\io\ChunkRequestTask;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\level\format\io\LevelProvider;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\GeneratorManager;
|
||||
use pocketmine\level\generator\GeneratorRegisterTask;
|
||||
use pocketmine\level\generator\GeneratorUnregisterTask;
|
||||
@ -289,7 +288,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var bool */
|
||||
private $doingTick = false;
|
||||
|
||||
/** @var string|Generator */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<\pocketmine\level\generator\Generator>
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/** @var bool */
|
||||
|
@ -80,15 +80,14 @@ class LevelTimings{
|
||||
$this->entityTick = new TimingsHandler("** " . $name . "entityTick");
|
||||
$this->tileEntityTick = new TimingsHandler("** " . $name . "tileEntityTick");
|
||||
|
||||
$this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend");
|
||||
$this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare");
|
||||
Timings::init(); //make sure the timers we want are available
|
||||
$this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend", Timings::$playerChunkSendTimer);
|
||||
$this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare", Timings::$playerChunkSendTimer);
|
||||
|
||||
$this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad");
|
||||
$this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad", Timings::$worldLoadTimer);
|
||||
$this->syncChunkLoadDataTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Data");
|
||||
$this->syncChunkLoadEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Entities");
|
||||
$this->syncChunkLoadTileEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - TileEntities");
|
||||
|
||||
Timings::init(); //make sure the timer we want is available
|
||||
$this->syncChunkSaveTimer = new TimingsHandler("** " . $name . "syncChunkSave", Timings::$worldSaveTimer);
|
||||
|
||||
$this->doTick = new TimingsHandler($name . "doTick");
|
||||
|
@ -99,6 +99,8 @@ abstract class Biome{
|
||||
self::register(self::SWAMP, new SwampBiome());
|
||||
self::register(self::RIVER, new RiverBiome());
|
||||
|
||||
self::register(self::HELL, new HellBiome());
|
||||
|
||||
self::register(self::ICE_PLAINS, new IcePlainsBiome());
|
||||
|
||||
self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome());
|
||||
|
@ -921,7 +921,9 @@ class Chunk{
|
||||
|
||||
$biomeIds = $stream->get(256);
|
||||
if($lightPopulated){
|
||||
$heightMap = array_values(unpack("v*", $stream->get(512)));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("v*", $stream->get(512)); //unpack() will never fail here
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -929,6 +931,7 @@ class Chunk{
|
||||
$chunk->setGenerated($terrainGenerated);
|
||||
$chunk->setPopulated($terrainPopulated);
|
||||
$chunk->setLightPopulated($lightPopulated);
|
||||
$chunk->setChanged(false);
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
@ -368,7 +368,9 @@ class LevelDB extends BaseLevelProvider{
|
||||
if(($maps2d = $this->db->get($index . self::TAG_DATA_2D)) !== false){
|
||||
$binaryStream->setBuffer($maps2d, 0);
|
||||
|
||||
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("v*", $binaryStream->get(512)); //unpack() will never fail here
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
$biomeIds = $binaryStream->get(256);
|
||||
}
|
||||
break;
|
||||
@ -411,8 +413,13 @@ class LevelDB extends BaseLevelProvider{
|
||||
$subChunks[$yy] = new SubChunk($ids, $data, $skyLight, $blockLight);
|
||||
}
|
||||
|
||||
$heightMap = array_values(unpack("C*", $binaryStream->get(256)));
|
||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("C*", $binaryStream->get(256)); //unpack() will never fail here, but static analysers don't know that
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
|
||||
/** @var int[] $unpackedBiomeIds */
|
||||
$unpackedBiomeIds = unpack("N*", $binaryStream->get(1024)); //nor here
|
||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values($unpackedBiomeIds));
|
||||
break;
|
||||
default:
|
||||
//TODO: set chunks read-only so the version on disk doesn't get overwritten
|
||||
|
@ -182,7 +182,9 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
$heightMap = [];
|
||||
if($chunk->hasTag("HeightMap", ByteArrayTag::class)){
|
||||
$heightMap = array_values(unpack("C*", $chunk->getByteArray("HeightMap")));
|
||||
/** @var int[] $unpackedHeightMap */
|
||||
$unpackedHeightMap = unpack("C*", $chunk->getByteArray("HeightMap")); //unpack() will never fail here
|
||||
$heightMap = array_values($unpackedHeightMap);
|
||||
}elseif($chunk->hasTag("HeightMap", IntArrayTag::class)){
|
||||
$heightMap = $chunk->getIntArray("HeightMap"); #blameshoghicp
|
||||
}
|
||||
@ -245,11 +247,13 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
if($isValid){
|
||||
$files = array_filter(scandir($path . "/region/", SCANDIR_SORT_NONE), function(string $file) : bool{
|
||||
return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file
|
||||
$extPos = strrpos($file, ".");
|
||||
return $extPos !== false && substr($file, $extPos + 1, 2) === "mc"; //region file
|
||||
});
|
||||
|
||||
foreach($files as $f){
|
||||
if(substr($f, strrpos($f, ".") + 1) !== static::REGION_FILE_EXTENSION){
|
||||
$extPos = strrpos($f, ".");
|
||||
if($extPos !== false && substr($f, $extPos + 1) !== static::REGION_FILE_EXTENSION){
|
||||
$isValid = false;
|
||||
break;
|
||||
}
|
||||
@ -389,6 +393,9 @@ class McRegion extends BaseLevelProvider{
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
assert(is_int($regionX) and is_int($regionZ));
|
||||
|
||||
if(!file_exists($this->pathToRegion($regionX, $regionZ))){
|
||||
return null;
|
||||
}
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$chunkData = $this->getRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f);
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function assert;
|
||||
use function ceil;
|
||||
use function chr;
|
||||
@ -160,10 +159,7 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index]->getSectorCount() << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index]->getSectorCount() << 12) . " sectors, got $length sectors)");
|
||||
$old = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($old->getFirstSector(), $length >> 12, time());
|
||||
$this->writeLocationIndex($index);
|
||||
throw new CorruptedChunkException("Chunk length mismatch (expected " . ($this->locationTable[$index]->getSectorCount() << 12) . " sectors, got $length sectors)");
|
||||
}
|
||||
|
||||
$chunkData = fread($this->filePointer, $length);
|
||||
@ -186,6 +182,23 @@ class RegionLoader{
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
private function disposeGarbageArea(RegionLocationTableEntry $oldLocation) : void{
|
||||
/* release the area containing the old copy to the garbage pool */
|
||||
$this->garbageTable->add($oldLocation);
|
||||
|
||||
$endGarbage = $this->garbageTable->end();
|
||||
$nextSector = $this->nextSector;
|
||||
for(; $endGarbage !== null and $endGarbage->getLastSector() + 1 === $nextSector; $endGarbage = $this->garbageTable->end()){
|
||||
$nextSector = $endGarbage->getFirstSector();
|
||||
$this->garbageTable->remove($endGarbage);
|
||||
}
|
||||
|
||||
if($nextSector !== $this->nextSector){
|
||||
$this->nextSector = $nextSector;
|
||||
ftruncate($this->filePointer, $this->nextSector << 12);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws ChunkException
|
||||
@ -230,20 +243,7 @@ class RegionLoader{
|
||||
$this->writeLocationIndex($index);
|
||||
|
||||
if($oldLocation !== null){
|
||||
/* release the area containing the old copy to the garbage pool */
|
||||
$this->garbageTable->add($oldLocation);
|
||||
|
||||
$endGarbage = $this->garbageTable->end();
|
||||
$nextSector = $this->nextSector;
|
||||
for(; $endGarbage !== null and $endGarbage->getLastSector() + 1 === $nextSector; $endGarbage = $this->garbageTable->end()){
|
||||
$nextSector = $endGarbage->getFirstSector();
|
||||
$this->garbageTable->remove($endGarbage);
|
||||
}
|
||||
|
||||
if($nextSector !== $this->nextSector){
|
||||
$this->nextSector = $nextSector;
|
||||
ftruncate($this->filePointer, $this->nextSector << 12);
|
||||
}
|
||||
$this->disposeGarbageArea($oldLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,8 +253,12 @@ class RegionLoader{
|
||||
*/
|
||||
public function removeChunk(int $x, int $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$oldLocation = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = null;
|
||||
$this->writeLocationIndex($index);
|
||||
if($oldLocation !== null){
|
||||
$this->disposeGarbageArea($oldLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,10 @@ use function unserialize;
|
||||
|
||||
class GeneratorRegisterTask extends AsyncTask{
|
||||
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @phpstan-var class-string<Generator>
|
||||
*/
|
||||
public $generatorClass;
|
||||
/** @var string */
|
||||
public $settings;
|
||||
@ -48,6 +51,7 @@ class GeneratorRegisterTask extends AsyncTask{
|
||||
|
||||
/**
|
||||
* @param mixed[] $generatorSettings
|
||||
* @phpstan-param class-string<Generator> $generatorClass
|
||||
* @phpstan-param array<string, mixed> $generatorSettings
|
||||
*/
|
||||
public function __construct(Level $level, string $generatorClass, array $generatorSettings = []){
|
||||
|
@ -35,6 +35,9 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\NamedTag;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\GameRuleType;
|
||||
@ -47,6 +50,7 @@ use pocketmine\network\mcpe\protocol\types\StructureEditorData;
|
||||
use pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\UUID;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function strlen;
|
||||
|
||||
@ -91,7 +95,8 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$skinImage = $this->getSkinImage();
|
||||
$animationType = $this->getLInt();
|
||||
$animationFrames = $this->getLFloat();
|
||||
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames);
|
||||
$expressionType = $this->getLInt();
|
||||
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
|
||||
}
|
||||
$capeData = $this->getSkinImage();
|
||||
$geometryData = $this->getString();
|
||||
@ -143,6 +148,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putSkinImage($animation->getImage());
|
||||
$this->putLInt($animation->getType());
|
||||
$this->putLFloat($animation->getFrames());
|
||||
$this->putLInt($animation->getExpressionType());
|
||||
}
|
||||
$this->putSkinImage($skin->getCapeImage());
|
||||
$this->putString($skin->getGeometryData());
|
||||
@ -186,15 +192,17 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
}
|
||||
|
||||
public function getSlot() : Item{
|
||||
$id = $this->getVarInt();
|
||||
if($id === 0){
|
||||
$netId = $this->getVarInt();
|
||||
if($netId === 0){
|
||||
return ItemFactory::get(0, 0, 0);
|
||||
}
|
||||
|
||||
$auxValue = $this->getVarInt();
|
||||
$data = $auxValue >> 8;
|
||||
$netData = $auxValue >> 8;
|
||||
$cnt = $auxValue & 0xff;
|
||||
|
||||
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkId($netId, $netData);
|
||||
|
||||
$nbtLen = $this->getLShort();
|
||||
|
||||
/** @var CompoundTag|null $nbt */
|
||||
@ -223,26 +231,23 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->getString();
|
||||
}
|
||||
|
||||
if($id === ItemIds::SHIELD){
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$this->getVarLong(); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
if($nbt !== null){
|
||||
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
|
||||
$data = $nbt->getInt(self::DAMAGE_TAG);
|
||||
$meta = $nbt->getInt(self::DAMAGE_TAG);
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
if($nbt->count() === 0){
|
||||
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$conflicted->setName(self::DAMAGE_TAG);
|
||||
$nbt->setTag($conflicted);
|
||||
}elseif($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$conflicted->setName(self::DAMAGE_TAG);
|
||||
$nbt->setTag($conflicted);
|
||||
}
|
||||
}
|
||||
end:
|
||||
return ItemFactory::get($id, $data, $cnt, $nbt);
|
||||
return ItemFactory::get($id, $meta, $cnt, $nbt);
|
||||
}
|
||||
|
||||
public function putSlot(Item $item) : void{
|
||||
@ -252,8 +257,10 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->putVarInt($item->getId());
|
||||
$auxValue = (($item->getDamage() & 0x7fff) << 8) | $item->getCount();
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
|
||||
|
||||
$this->putVarInt($netId);
|
||||
$auxValue = (($netData & 0x7fff) << 8) | $item->getCount();
|
||||
$this->putVarInt($auxValue);
|
||||
|
||||
$nbt = null;
|
||||
@ -284,20 +291,18 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putVarInt(0); //CanPlaceOn entry count (TODO)
|
||||
$this->putVarInt(0); //CanDestroy entry count (TODO)
|
||||
|
||||
if($item->getId() === ItemIds::SHIELD){
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$this->putVarLong(0); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
}
|
||||
|
||||
public function getRecipeIngredient() : Item{
|
||||
$id = $this->getVarInt();
|
||||
if($id === 0){
|
||||
$netId = $this->getVarInt();
|
||||
if($netId === 0){
|
||||
return ItemFactory::get(ItemIds::AIR, 0, 0);
|
||||
}
|
||||
$meta = $this->getVarInt();
|
||||
if($meta === 0x7fff){
|
||||
$meta = -1;
|
||||
}
|
||||
$netData = $this->getVarInt();
|
||||
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($netId, $netData);
|
||||
$count = $this->getVarInt();
|
||||
return ItemFactory::get($id, $meta, $count);
|
||||
}
|
||||
@ -306,8 +311,14 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
if($item->isNull()){
|
||||
$this->putVarInt(0);
|
||||
}else{
|
||||
$this->putVarInt($item->getId());
|
||||
$this->putVarInt($item->getDamage() & 0x7fff);
|
||||
if($item->hasAnyDamageValue()){
|
||||
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($item->getId(), 0);
|
||||
$netData = 0x7fff;
|
||||
}else{
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
|
||||
}
|
||||
$this->putVarInt($netId);
|
||||
$this->putVarInt($netData);
|
||||
$this->putVarInt($item->getCount());
|
||||
}
|
||||
}
|
||||
@ -750,6 +761,25 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putVarInt($structureEditorData->structureRedstoneSaveMove);
|
||||
}
|
||||
|
||||
public function getNbtRoot() : NamedTag{
|
||||
$offset = $this->getOffset();
|
||||
try{
|
||||
$result = (new NetworkLittleEndianNBTStream())->read($this->getBuffer(), false, $offset, 512);
|
||||
assert($result instanceof NamedTag, "doMultiple is false so we should definitely have a NamedTag here");
|
||||
return $result;
|
||||
}finally{
|
||||
$this->setOffset($offset);
|
||||
}
|
||||
}
|
||||
|
||||
public function getNbtCompoundRoot() : CompoundTag{
|
||||
$root = $this->getNbtRoot();
|
||||
if(!($root instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Expected TAG_Compound root");
|
||||
}
|
||||
return $root;
|
||||
}
|
||||
|
||||
public function readGenericTypeNetworkId() : int{
|
||||
return $this->getVarInt();
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorFallPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddBehaviorTreePacket;
|
||||
@ -33,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPaintingPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimateEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AnvilDamagePacket;
|
||||
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
|
||||
@ -45,6 +45,7 @@ use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\BookEditPacket;
|
||||
use pocketmine\network\mcpe\protocol\BossEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\CameraPacket;
|
||||
use pocketmine\network\mcpe\protocol\CameraShakePacket;
|
||||
use pocketmine\network\mcpe\protocol\ChangeDimensionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket;
|
||||
@ -60,6 +61,7 @@ use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\CorrectPlayerMovePredictionPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
|
||||
@ -70,6 +72,7 @@ use pocketmine\network\mcpe\protocol\EducationSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmoteListPacket;
|
||||
use pocketmine\network\mcpe\protocol\EmotePacket;
|
||||
use pocketmine\network\mcpe\protocol\EventPacket;
|
||||
use pocketmine\network\mcpe\protocol\FilterTextPacket;
|
||||
use pocketmine\network\mcpe\protocol\GameRulesChangedPacket;
|
||||
use pocketmine\network\mcpe\protocol\GuiDataPickItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\HurtArmorPacket;
|
||||
@ -77,6 +80,7 @@ use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemComponentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
|
||||
@ -96,6 +100,7 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\MotionPredictionHintsPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
@ -111,6 +116,7 @@ use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerArmorDamagePacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerFogPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
@ -171,7 +177,6 @@ use pocketmine\network\mcpe\protocol\TickSyncPacket;
|
||||
use pocketmine\network\mcpe\protocol\TransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPropertiesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockSyncedPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateEquipPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdatePlayerGameTypePacket;
|
||||
@ -325,10 +330,6 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleActorFall(ActorFallPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleHurtArmor(HurtArmorPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
@ -705,10 +706,6 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleUpdateBlockProperties(UpdateBlockPropertiesPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleClientCacheBlobStatus(ClientCacheBlobStatusPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
@ -796,4 +793,32 @@ abstract class NetworkSession{
|
||||
public function handlePacketViolationWarning(PacketViolationWarningPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleMotionPredictionHints(MotionPredictionHintsPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleAnimateEntity(AnimateEntityPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleCameraShake(CameraShakePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handlePlayerFog(PlayerFogPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleCorrectPlayerMovePrediction(CorrectPlayerMovePredictionPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleItemComponent(ItemComponentPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleFilterText(FilterTextPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorFallPacket;
|
||||
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
@ -174,10 +173,6 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
return $this->player->handlePlayerAction($packet);
|
||||
}
|
||||
|
||||
public function handleActorFall(ActorFallPacket $packet) : bool{
|
||||
return true; //Not used
|
||||
}
|
||||
|
||||
public function handleAnimate(AnimatePacket $packet) : bool{
|
||||
return $this->player->handleAnimate($packet);
|
||||
}
|
||||
|
183
src/pocketmine/network/mcpe/convert/ItemTranslator.php
Normal file
183
src/pocketmine/network/mcpe/convert/ItemTranslator.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?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\convert;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use function array_key_exists;
|
||||
use function file_get_contents;
|
||||
use function is_array;
|
||||
use function is_numeric;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
|
||||
/**
|
||||
* This class handles translation between network item ID+metadata to PocketMine-MP internal ID+metadata and vice versa.
|
||||
*/
|
||||
final class ItemTranslator{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private $simpleCoreToNetMapping = [];
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private $simpleNetToCoreMapping = [];
|
||||
|
||||
/**
|
||||
* runtimeId = array[internalId][metadata]
|
||||
* @var int[][]
|
||||
* @phpstan-var array<int, array<int, int>>
|
||||
*/
|
||||
private $complexCoreToNetMapping = [];
|
||||
/**
|
||||
* [internalId, metadata] = array[runtimeId]
|
||||
* @var int[][]
|
||||
* @phpstan-var array<int, array{int, int}>
|
||||
*/
|
||||
private $complexNetToCoreMapping = [];
|
||||
|
||||
private static function make() : self{
|
||||
$data = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/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(\pocketmine\RESOURCE_PATH . '/vanilla/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");
|
||||
}
|
||||
|
||||
/** @phpstan-var array<string, int> $simpleMappings */
|
||||
$simpleMappings = [];
|
||||
foreach($json["simple"] as $oldId => $newId){
|
||||
if(!is_string($oldId) || !is_string($newId)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
$simpleMappings[$newId] = $legacyStringToIntMap[$oldId];
|
||||
}
|
||||
foreach($legacyStringToIntMap as $stringId => $intId){
|
||||
if(isset($simpleMappings[$stringId])){
|
||||
throw new \UnexpectedValueException("Old ID $stringId collides with new ID");
|
||||
}
|
||||
$simpleMappings[$stringId] = $intId;
|
||||
}
|
||||
|
||||
/** @phpstan-var array<string, array{int, int}> $complexMappings */
|
||||
$complexMappings = [];
|
||||
foreach($json["complex"] as $oldId => $map){
|
||||
if(!is_string($oldId) || !is_array($map)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
foreach($map as $meta => $newId){
|
||||
if(!is_numeric($meta) || !is_string($newId)){
|
||||
throw new AssumptionFailedError("Invalid item table format");
|
||||
}
|
||||
$complexMappings[$newId] = [$legacyStringToIntMap[$oldId], (int) $meta];
|
||||
}
|
||||
}
|
||||
|
||||
return new self(ItemTypeDictionary::getInstance(), $simpleMappings, $complexMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $simpleMappings
|
||||
* @param int[][] $complexMappings
|
||||
* @phpstan-param array<string, int> $simpleMappings
|
||||
* @phpstan-param array<string, array<int, int>> $complexMappings
|
||||
*/
|
||||
public function __construct(ItemTypeDictionary $dictionary, array $simpleMappings, array $complexMappings){
|
||||
foreach($dictionary->getEntries() as $entry){
|
||||
$stringId = $entry->getStringId();
|
||||
$netId = $entry->getNumericId();
|
||||
if(isset($complexMappings[$stringId])){
|
||||
[$id, $meta] = $complexMappings[$stringId];
|
||||
$this->complexCoreToNetMapping[$id][$meta] = $netId;
|
||||
$this->complexNetToCoreMapping[$netId] = [$id, $meta];
|
||||
}elseif(isset($simpleMappings[$stringId])){
|
||||
$this->simpleCoreToNetMapping[$simpleMappings[$stringId]] = $netId;
|
||||
$this->simpleNetToCoreMapping[$netId] = $simpleMappings[$stringId];
|
||||
}elseif($stringId !== "minecraft:unknown"){
|
||||
throw new \InvalidArgumentException("Unmapped entry " . $stringId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
public function toNetworkId(int $internalId, int $internalMeta) : array{
|
||||
if(isset($this->complexCoreToNetMapping[$internalId][$internalMeta])){
|
||||
return [$this->complexCoreToNetMapping[$internalId][$internalMeta], 0];
|
||||
}
|
||||
if(array_key_exists($internalId, $this->simpleCoreToNetMapping)){
|
||||
return [$this->simpleCoreToNetMapping[$internalId], $internalMeta];
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
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");
|
||||
}
|
||||
$isComplexMapping = true;
|
||||
return $this->complexNetToCoreMapping[$networkId];
|
||||
}
|
||||
$isComplexMapping = false;
|
||||
if(isset($this->simpleNetToCoreMapping[$networkId])){
|
||||
return [$this->simpleNetToCoreMapping[$networkId], $networkMeta];
|
||||
}
|
||||
throw new \UnexpectedValueException("Unmapped network ID/metadata combination $networkId:$networkMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
public function fromNetworkIdWithWildcardHandling(int $networkId, int $networkMeta) : array{
|
||||
$isComplexMapping = false;
|
||||
if($networkMeta !== 0x7fff){
|
||||
return $this->fromNetworkId($networkId, $networkMeta);
|
||||
}
|
||||
[$id, $meta] = $this->fromNetworkId($networkId, 0, $isComplexMapping);
|
||||
return [$id, $isComplexMapping ? $meta : -1];
|
||||
}
|
||||
}
|
106
src/pocketmine/network/mcpe/convert/ItemTypeDictionary.php
Normal file
106
src/pocketmine/network/mcpe/convert/ItemTypeDictionary.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?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\convert;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use function array_key_exists;
|
||||
use function file_get_contents;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
|
||||
final class ItemTypeDictionary{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var ItemTypeEntry[]
|
||||
* @phpstan-var list<ItemTypeEntry>
|
||||
*/
|
||||
private $itemTypes;
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array<int, string>
|
||||
*/
|
||||
private $intToStringIdMap = [];
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<string, int>
|
||||
*/
|
||||
private $stringToIntMap = [];
|
||||
|
||||
private static function make() : self{
|
||||
$data = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/required_item_list.json');
|
||||
if($data === false) throw new AssumptionFailedError("Missing required resource file");
|
||||
$table = json_decode($data, true);
|
||||
if(!is_array($table)){
|
||||
throw new AssumptionFailedError("Invalid item list format");
|
||||
}
|
||||
|
||||
$params = [];
|
||||
foreach($table as $name => $entry){
|
||||
if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){
|
||||
throw new AssumptionFailedError("Invalid item list format");
|
||||
}
|
||||
$params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"]);
|
||||
}
|
||||
return new self($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ItemTypeEntry[] $itemTypes
|
||||
*/
|
||||
public function __construct(array $itemTypes){
|
||||
$this->itemTypes = $itemTypes;
|
||||
foreach($this->itemTypes as $type){
|
||||
$this->stringToIntMap[$type->getStringId()] = $type->getNumericId();
|
||||
$this->intToStringIdMap[$type->getNumericId()] = $type->getStringId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ItemTypeEntry[]
|
||||
* @phpstan-return list<ItemTypeEntry>
|
||||
*/
|
||||
public function getEntries() : array{
|
||||
return $this->itemTypes;
|
||||
}
|
||||
|
||||
public function fromStringId(string $stringId) : int{
|
||||
if(!array_key_exists($stringId, $this->stringToIntMap)){
|
||||
throw new \InvalidArgumentException("Unmapped string ID \"$stringId\"");
|
||||
}
|
||||
return $this->stringToIntMap[$stringId];
|
||||
}
|
||||
|
||||
public function fromIntId(int $intId) : string{
|
||||
if(!array_key_exists($intId, $this->intToStringIdMap)){
|
||||
throw new \InvalidArgumentException("Unmapped int ID $intId");
|
||||
}
|
||||
return $this->intToStringIdMap[$intId];
|
||||
}
|
||||
}
|
@ -27,14 +27,10 @@ use pocketmine\block\BlockIds;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function file_get_contents;
|
||||
use function getmypid;
|
||||
use function json_decode;
|
||||
use function mt_rand;
|
||||
use function mt_srand;
|
||||
use function shuffle;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -53,14 +49,16 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
|
||||
public static function init() : void{
|
||||
$tag = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"));
|
||||
if(!($tag instanceof ListTag) or $tag->getTagType() !== NBT::TAG_Compound){ //this is a little redundant currently, but good for auto complete and makes phpstan happy
|
||||
throw new \RuntimeException("Invalid blockstates table, expected TAG_List<TAG_Compound> root");
|
||||
$canonicalBlockStatesFile = file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/canonical_block_states.nbt");
|
||||
if($canonicalBlockStatesFile === false){
|
||||
throw new AssumptionFailedError("Missing required resource file");
|
||||
}
|
||||
|
||||
/** @var CompoundTag[] $list */
|
||||
$list = $tag->getValue();
|
||||
self::$bedrockKnownStates = self::randomizeTable($list);
|
||||
$stream = new NetworkBinaryStream($canonicalBlockStatesFile);
|
||||
$list = [];
|
||||
while(!$stream->feof()){
|
||||
$list[] = $stream->getNbtCompoundRoot();
|
||||
}
|
||||
self::$bedrockKnownStates = $list;
|
||||
|
||||
self::setupLegacyMappings();
|
||||
}
|
||||
@ -90,7 +88,7 @@ final class RuntimeBlockMapping{
|
||||
*/
|
||||
$idToStatesMap = [];
|
||||
foreach(self::$bedrockKnownStates as $k => $state){
|
||||
$idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
|
||||
$idToStatesMap[$state->getString("name")][] = $k;
|
||||
}
|
||||
foreach($legacyStateMap as $pair){
|
||||
$id = $legacyIdMap[$pair->getId()] ?? null;
|
||||
@ -105,14 +103,14 @@ final class RuntimeBlockMapping{
|
||||
$mappedState = $pair->getBlockState();
|
||||
|
||||
//TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values
|
||||
$mappedState->setName("block");
|
||||
$mappedState->setName("");
|
||||
$mappedName = $mappedState->getString("name");
|
||||
if(!isset($idToStatesMap[$mappedName])){
|
||||
throw new \RuntimeException("Mapped new state does not appear in network table");
|
||||
}
|
||||
foreach($idToStatesMap[$mappedName] as $k){
|
||||
$networkState = self::$bedrockKnownStates[$k];
|
||||
if($mappedState->equals($networkState->getCompoundTag("block"))){
|
||||
if($mappedState->equals($networkState)){
|
||||
self::registerMapping($k, $id, $data);
|
||||
continue 2;
|
||||
}
|
||||
@ -127,23 +125,6 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomizes the order of the runtimeID table to prevent plugins relying on them.
|
||||
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
|
||||
* aren't supposed to do, so we have to deliberately break it to make them stop.
|
||||
*
|
||||
* @param CompoundTag[] $table
|
||||
*
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
private static function randomizeTable(array $table) : array{
|
||||
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
|
||||
mt_srand(getmypid()); //Use a seed which is the same on all threads. This isn't a secure seed, but we don't care.
|
||||
shuffle($table);
|
||||
mt_srand($postSeed); //restore a good quality seed that isn't dependent on PID
|
||||
return $table;
|
||||
}
|
||||
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
self::lazyInit();
|
||||
/*
|
||||
|
108
src/pocketmine/network/mcpe/protocol/AnimateEntityPacket.php
Normal file
108
src/pocketmine/network/mcpe/protocol/AnimateEntityPacket.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?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\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use function count;
|
||||
|
||||
class AnimateEntityPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::ANIMATE_ENTITY_PACKET;
|
||||
|
||||
/** @var string */
|
||||
private $animation;
|
||||
/** @var string */
|
||||
private $nextState;
|
||||
/** @var string */
|
||||
private $stopExpression;
|
||||
/** @var string */
|
||||
private $controller;
|
||||
/** @var float */
|
||||
private $blendOutTime;
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var list<int>
|
||||
*/
|
||||
private $actorRuntimeIds;
|
||||
|
||||
/**
|
||||
* @param int[] $actorRuntimeIds
|
||||
* @phpstan-param list<int> $actorRuntimeIds
|
||||
*/
|
||||
public static function create(string $animation, string $nextState, string $stopExpression, string $controller, float $blendOutTime, array $actorRuntimeIds) : self{
|
||||
$result = new self;
|
||||
$result->animation = $animation;
|
||||
$result->nextState = $nextState;
|
||||
$result->stopExpression = $stopExpression;
|
||||
$result->controller = $controller;
|
||||
$result->blendOutTime = $blendOutTime;
|
||||
$result->actorRuntimeIds = $actorRuntimeIds;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getAnimation() : string{ return $this->animation; }
|
||||
|
||||
public function getNextState() : string{ return $this->nextState; }
|
||||
|
||||
public function getStopExpression() : string{ return $this->stopExpression; }
|
||||
|
||||
public function getController() : string{ return $this->controller; }
|
||||
|
||||
public function getBlendOutTime() : float{ return $this->blendOutTime; }
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return list<int>
|
||||
*/
|
||||
public function getActorRuntimeIds() : array{ return $this->actorRuntimeIds; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->animation = $this->getString();
|
||||
$this->nextState = $this->getString();
|
||||
$this->stopExpression = $this->getString();
|
||||
$this->controller = $this->getString();
|
||||
$this->blendOutTime = $this->getLFloat();
|
||||
$this->actorRuntimeIds = [];
|
||||
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$this->actorRuntimeIds[] = $this->getEntityRuntimeId();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putString($this->animation);
|
||||
$this->putString($this->nextState);
|
||||
$this->putString($this->stopExpression);
|
||||
$this->putString($this->controller);
|
||||
$this->putLFloat($this->blendOutTime);
|
||||
$this->putUnsignedVarInt(count($this->actorRuntimeIds));
|
||||
foreach($this->actorRuntimeIds as $id){
|
||||
$this->putEntityRuntimeId($id);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleAnimateEntity($this);
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function assert;
|
||||
use function get_class;
|
||||
use function strlen;
|
||||
@ -72,7 +73,9 @@ class BatchPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->put(zlib_encode($this->payload, ZLIB_ENCODING_RAW, $this->compressionLevel));
|
||||
$encoded = zlib_encode($this->payload, ZLIB_ENCODING_RAW, $this->compressionLevel);
|
||||
if($encoded === false) throw new AssumptionFailedError("ZLIB compression failed");
|
||||
$this->put($encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
|
72
src/pocketmine/network/mcpe/protocol/CameraShakePacket.php
Normal file
72
src/pocketmine/network/mcpe/protocol/CameraShakePacket.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?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\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class CameraShakePacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::CAMERA_SHAKE_PACKET;
|
||||
|
||||
public const TYPE_POSITIONAL = 0;
|
||||
public const TYPE_ROTATIONAL = 1;
|
||||
|
||||
/** @var float */
|
||||
private $intensity;
|
||||
/** @var float */
|
||||
private $duration;
|
||||
/** @var int */
|
||||
private $shakeType;
|
||||
|
||||
public static function create(float $intensity, float $duration, int $shakeType) : self{
|
||||
$result = new self;
|
||||
$result->intensity = $intensity;
|
||||
$result->duration = $duration;
|
||||
$result->shakeType = $shakeType;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getIntensity() : float{ return $this->intensity; }
|
||||
|
||||
public function getDuration() : float{ return $this->duration; }
|
||||
|
||||
public function getShakeType() : int{ return $this->shakeType; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->intensity = $this->getLFloat();
|
||||
$this->duration = $this->getLFloat();
|
||||
$this->shakeType = $this->getByte();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putLFloat($this->intensity);
|
||||
$this->putLFloat($this->duration);
|
||||
$this->putByte($this->shakeType);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleCameraShake($this);
|
||||
}
|
||||
}
|
@ -32,13 +32,17 @@ class ContainerClosePacket extends DataPacket{
|
||||
|
||||
/** @var int */
|
||||
public $windowId;
|
||||
/** @var bool */
|
||||
public $server = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->windowId = $this->getByte();
|
||||
$this->server = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->windowId);
|
||||
$this->putBool($this->server);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -0,0 +1,77 @@
|
||||
<?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\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class CorrectPlayerMovePredictionPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::CORRECT_PLAYER_MOVE_PREDICTION_PACKET;
|
||||
|
||||
/** @var Vector3 */
|
||||
private $position;
|
||||
/** @var Vector3 */
|
||||
private $delta;
|
||||
/** @var bool */
|
||||
private $onGround;
|
||||
/** @var int */
|
||||
private $tick;
|
||||
|
||||
public static function create(Vector3 $position, Vector3 $delta, bool $onGround, int $tick) : self{
|
||||
$result = new self;
|
||||
$result->position = $position;
|
||||
$result->delta = $delta;
|
||||
$result->onGround = $onGround;
|
||||
$result->tick = $tick;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getPosition() : Vector3{ return $this->position; }
|
||||
|
||||
public function getDelta() : Vector3{ return $this->delta; }
|
||||
|
||||
public function isOnGround() : bool{ return $this->onGround; }
|
||||
|
||||
public function getTick() : int{ return $this->tick; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->position = $this->getVector3();
|
||||
$this->delta = $this->getVector3();
|
||||
$this->onGround = $this->getBool();
|
||||
$this->tick = $this->getUnsignedVarLong();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putVector3($this->position);
|
||||
$this->putVector3($this->delta);
|
||||
$this->putBool($this->onGround);
|
||||
$this->putUnsignedVarLong($this->tick);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleCorrectPlayerMovePrediction($this);
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ use pocketmine\inventory\ShapedRecipe;
|
||||
use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe;
|
||||
@ -124,13 +125,12 @@ class CraftingDataPacket extends DataPacket{
|
||||
break;
|
||||
case self::ENTRY_FURNACE:
|
||||
case self::ENTRY_FURNACE_DATA:
|
||||
$inputId = $this->getVarInt();
|
||||
$inputData = -1;
|
||||
if($recipeType === self::ENTRY_FURNACE_DATA){
|
||||
$inputData = $this->getVarInt();
|
||||
if($inputData === 0x7fff){
|
||||
$inputData = -1;
|
||||
}
|
||||
$inputIdNet = $this->getVarInt();
|
||||
if($recipeType === self::ENTRY_FURNACE){
|
||||
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, 0x7fff);
|
||||
}else{
|
||||
$inputMetaNet = $this->getVarInt();
|
||||
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, $inputMetaNet);
|
||||
}
|
||||
$entry["input"] = ItemFactory::get($inputId, $inputData);
|
||||
$entry["output"] = $out = $this->getSlot();
|
||||
@ -150,18 +150,25 @@ class CraftingDataPacket extends DataPacket{
|
||||
$this->decodedEntries[] = $entry;
|
||||
}
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$input = $this->getVarInt();
|
||||
$inputMeta = $this->getVarInt();
|
||||
$ingredient = $this->getVarInt();
|
||||
$ingredientMeta = $this->getVarInt();
|
||||
$output = $this->getVarInt();
|
||||
$outputMeta = $this->getVarInt();
|
||||
$inputIdNet = $this->getVarInt();
|
||||
$inputMetaNet = $this->getVarInt();
|
||||
[$input, $inputMeta] = ItemTranslator::getInstance()->fromNetworkId($inputIdNet, $inputMetaNet);
|
||||
$ingredientIdNet = $this->getVarInt();
|
||||
$ingredientMetaNet = $this->getVarInt();
|
||||
[$ingredient, $ingredientMeta] = ItemTranslator::getInstance()->fromNetworkId($ingredientIdNet, $ingredientMetaNet);
|
||||
$outputIdNet = $this->getVarInt();
|
||||
$outputMetaNet = $this->getVarInt();
|
||||
[$output, $outputMeta] = ItemTranslator::getInstance()->fromNetworkId($outputIdNet, $outputMetaNet);
|
||||
$this->potionTypeRecipes[] = new PotionTypeRecipe($input, $inputMeta, $ingredient, $ingredientMeta, $output, $outputMeta);
|
||||
}
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$input = $this->getVarInt();
|
||||
$ingredient = $this->getVarInt();
|
||||
$output = $this->getVarInt();
|
||||
//TODO: we discard inbound ID here, not safe because netID on its own might map to internalID+internalMeta for us
|
||||
$inputIdNet = $this->getVarInt();
|
||||
[$input, ] = ItemTranslator::getInstance()->fromNetworkId($inputIdNet, 0);
|
||||
$ingredientIdNet = $this->getVarInt();
|
||||
[$ingredient, ] = ItemTranslator::getInstance()->fromNetworkId($ingredientIdNet, 0);
|
||||
$outputIdNet = $this->getVarInt();
|
||||
[$output, ] = ItemTranslator::getInstance()->fromNetworkId($outputIdNet, 0);
|
||||
$this->potionContainerRecipes[] = new PotionContainerChangeRecipe($input, $ingredient, $output);
|
||||
}
|
||||
$this->cleanRecipes = $this->getBool();
|
||||
@ -230,15 +237,18 @@ class CraftingDataPacket extends DataPacket{
|
||||
}
|
||||
|
||||
private static function writeFurnaceRecipe(FurnaceRecipe $recipe, NetworkBinaryStream $stream) : int{
|
||||
$stream->putVarInt($recipe->getInput()->getId());
|
||||
$result = CraftingDataPacket::ENTRY_FURNACE;
|
||||
if(!$recipe->getInput()->hasAnyDamageValue()){ //Data recipe
|
||||
$stream->putVarInt($recipe->getInput()->getDamage());
|
||||
$result = CraftingDataPacket::ENTRY_FURNACE_DATA;
|
||||
$input = $recipe->getInput();
|
||||
if($input->hasAnyDamageValue()){
|
||||
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($input->getId(), 0);
|
||||
$netData = 0x7fff;
|
||||
}else{
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($input->getId(), $input->getDamage());
|
||||
}
|
||||
$stream->putVarInt($netId);
|
||||
$stream->putVarInt($netData);
|
||||
$stream->putSlot($recipe->getResult());
|
||||
$stream->putString("furnace"); //TODO: blocktype (no prefix) (this might require internal API breaks)
|
||||
return $result;
|
||||
return CraftingDataPacket::ENTRY_FURNACE_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,7 +174,7 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
public function __debugInfo(){
|
||||
$data = [];
|
||||
foreach((array) $this as $k => $v){
|
||||
if($k === "buffer" and is_string($v)){
|
||||
if($k === "buffer"){
|
||||
$data[$k] = bin2hex($v);
|
||||
}elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){
|
||||
$data[$k] = Utils::printable((string) $v);
|
||||
|
@ -25,31 +25,38 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class UpdateBlockPropertiesPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::UPDATE_BLOCK_PROPERTIES_PACKET;
|
||||
class FilterTextPacket extends DataPacket/* implements ClientboundPacket, ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::FILTER_TEXT_PACKET;
|
||||
|
||||
/** @var string */
|
||||
private $nbt;
|
||||
private $text;
|
||||
/** @var bool */
|
||||
private $fromServer;
|
||||
|
||||
public static function create(CompoundTag $data) : self{
|
||||
public static function create(string $text, bool $server) : self{
|
||||
$result = new self;
|
||||
$result->nbt = (new NetworkLittleEndianNBTStream())->write($data);
|
||||
$result->text = $text;
|
||||
$result->fromServer = $server;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getText() : string{ return $this->text; }
|
||||
|
||||
public function isFromServer() : bool{ return $this->fromServer; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->nbt = $this->getRemaining();
|
||||
$this->text = $this->getString();
|
||||
$this->fromServer = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->put($this->nbt);
|
||||
$this->putString($this->text);
|
||||
$this->putBool($this->fromServer);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleUpdateBlockProperties($this);
|
||||
return $handler->handleFilterText($this);
|
||||
}
|
||||
}
|
78
src/pocketmine/network/mcpe/protocol/ItemComponentPacket.php
Normal file
78
src/pocketmine/network/mcpe/protocol/ItemComponentPacket.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\ItemComponentPacketEntry;
|
||||
use function count;
|
||||
|
||||
class ItemComponentPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::ITEM_COMPONENT_PACKET;
|
||||
|
||||
/**
|
||||
* @var ItemComponentPacketEntry[]
|
||||
* @phpstan-var list<ItemComponentPacketEntry>
|
||||
*/
|
||||
private $entries;
|
||||
|
||||
/**
|
||||
* @param ItemComponentPacketEntry[] $entries
|
||||
* @phpstan-param list<ItemComponentPacketEntry> $entries
|
||||
*/
|
||||
public static function create(array $entries) : self{
|
||||
$result = new self;
|
||||
$result->entries = $entries;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ItemComponentPacketEntry[]
|
||||
* @phpstan-return list<ItemComponentPacketEntry>
|
||||
*/
|
||||
public function getEntries() : array{ return $this->entries; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->entries = [];
|
||||
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$name = $this->getString();
|
||||
$nbt = $this->getNbtCompoundRoot();
|
||||
$this->entries[] = new ItemComponentPacketEntry($name, $nbt);
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putUnsignedVarInt(count($this->entries));
|
||||
foreach($this->entries as $entry){
|
||||
$this->putString($entry->getName());
|
||||
$this->put((new NetworkLittleEndianNBTStream())->write($entry->getComponentNbt()));
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleItemComponent($this);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?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\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class MotionPredictionHintsPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::MOTION_PREDICTION_HINTS_PACKET;
|
||||
|
||||
/** @var int */
|
||||
private $entityRuntimeId;
|
||||
/** @var Vector3 */
|
||||
private $motion;
|
||||
/** @var bool */
|
||||
private $onGround;
|
||||
|
||||
public static function create(int $entityRuntimeId, Vector3 $motion, bool $onGround) : self{
|
||||
$result = new self;
|
||||
$result->entityRuntimeId = $entityRuntimeId;
|
||||
$result->motion = $motion;
|
||||
$result->onGround = $onGround;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getEntityRuntimeIdField() : int{ return $this->entityRuntimeId; } //TODO: rename this on PM4 (crap architecture, thanks shoghi)
|
||||
|
||||
public function getMotion() : Vector3{ return $this->motion; }
|
||||
|
||||
public function isOnGround() : bool{ return $this->onGround; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->motion = $this->getVector3();
|
||||
$this->onGround = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putVector3($this->motion);
|
||||
$this->putBool($this->onGround);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleMotionPredictionHints($this);
|
||||
}
|
||||
}
|
@ -44,12 +44,12 @@ class MoveActorDeltaPacket extends DataPacket{
|
||||
public $entityRuntimeId;
|
||||
/** @var int */
|
||||
public $flags;
|
||||
/** @var int */
|
||||
public $xDiff = 0;
|
||||
/** @var int */
|
||||
public $yDiff = 0;
|
||||
/** @var int */
|
||||
public $zDiff = 0;
|
||||
/** @var float */
|
||||
public $xPos = 0;
|
||||
/** @var float */
|
||||
public $yPos = 0;
|
||||
/** @var float */
|
||||
public $zPos = 0;
|
||||
/** @var float */
|
||||
public $xRot = 0.0;
|
||||
/** @var float */
|
||||
@ -57,9 +57,9 @@ class MoveActorDeltaPacket extends DataPacket{
|
||||
/** @var float */
|
||||
public $zRot = 0.0;
|
||||
|
||||
private function maybeReadCoord(int $flag) : int{
|
||||
private function maybeReadCoord(int $flag) : float{
|
||||
if(($this->flags & $flag) !== 0){
|
||||
return $this->getVarInt();
|
||||
return $this->getLFloat();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -74,17 +74,17 @@ class MoveActorDeltaPacket extends DataPacket{
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->flags = $this->getLShort();
|
||||
$this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X);
|
||||
$this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y);
|
||||
$this->zDiff = $this->maybeReadCoord(self::FLAG_HAS_Z);
|
||||
$this->xPos = $this->maybeReadCoord(self::FLAG_HAS_X);
|
||||
$this->yPos = $this->maybeReadCoord(self::FLAG_HAS_Y);
|
||||
$this->zPos = $this->maybeReadCoord(self::FLAG_HAS_Z);
|
||||
$this->xRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_X);
|
||||
$this->yRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Y);
|
||||
$this->zRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Z);
|
||||
}
|
||||
|
||||
private function maybeWriteCoord(int $flag, int $val) : void{
|
||||
private function maybeWriteCoord(int $flag, float $val) : void{
|
||||
if(($this->flags & $flag) !== 0){
|
||||
$this->putVarInt($val);
|
||||
$this->putLFloat($val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,9 +97,9 @@ class MoveActorDeltaPacket extends DataPacket{
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putLShort($this->flags);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zDiff);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xPos);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yPos);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zPos);
|
||||
$this->maybeWriteRotation(self::FLAG_HAS_ROT_X, $this->xRot);
|
||||
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Y, $this->yRot);
|
||||
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Z, $this->zRot);
|
||||
|
@ -56,6 +56,8 @@ class MovePlayerPacket extends DataPacket{
|
||||
public $teleportCause = 0;
|
||||
/** @var int */
|
||||
public $teleportItem = 0;
|
||||
/** @var int */
|
||||
public $tick = 0;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
@ -70,6 +72,7 @@ class MovePlayerPacket extends DataPacket{
|
||||
$this->teleportCause = $this->getLInt();
|
||||
$this->teleportItem = $this->getLInt();
|
||||
}
|
||||
$this->tick = $this->getUnsignedVarLong();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -85,6 +88,7 @@ class MovePlayerPacket extends DataPacket{
|
||||
$this->putLInt($this->teleportCause);
|
||||
$this->putLInt($this->teleportItem);
|
||||
}
|
||||
$this->putUnsignedVarLong($this->tick);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -71,7 +71,6 @@ class PacketPool{
|
||||
static::registerPacket(new BlockPickRequestPacket());
|
||||
static::registerPacket(new ActorPickRequestPacket());
|
||||
static::registerPacket(new PlayerActionPacket());
|
||||
static::registerPacket(new ActorFallPacket());
|
||||
static::registerPacket(new HurtArmorPacket());
|
||||
static::registerPacket(new SetActorDataPacket());
|
||||
static::registerPacket(new SetActorMotionPacket());
|
||||
@ -166,7 +165,6 @@ class PacketPool{
|
||||
static::registerPacket(new MapCreateLockedCopyPacket());
|
||||
static::registerPacket(new StructureTemplateDataRequestPacket());
|
||||
static::registerPacket(new StructureTemplateDataResponsePacket());
|
||||
static::registerPacket(new UpdateBlockPropertiesPacket());
|
||||
static::registerPacket(new ClientCacheBlobStatusPacket());
|
||||
static::registerPacket(new ClientCacheMissResponsePacket());
|
||||
static::registerPacket(new EducationSettingsPacket());
|
||||
@ -189,6 +187,13 @@ class PacketPool{
|
||||
static::registerPacket(new PositionTrackingDBClientRequestPacket());
|
||||
static::registerPacket(new DebugInfoPacket());
|
||||
static::registerPacket(new PacketViolationWarningPacket());
|
||||
static::registerPacket(new MotionPredictionHintsPacket());
|
||||
static::registerPacket(new AnimateEntityPacket());
|
||||
static::registerPacket(new CameraShakePacket());
|
||||
static::registerPacket(new PlayerFogPacket());
|
||||
static::registerPacket(new CorrectPlayerMovePredictionPacket());
|
||||
static::registerPacket(new ItemComponentPacket());
|
||||
static::registerPacket(new FilterTextPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ class PlayerActionPacket extends DataPacket{
|
||||
public const ACTION_STOP_SPRINT = 10;
|
||||
public const ACTION_START_SNEAK = 11;
|
||||
public const ACTION_STOP_SNEAK = 12;
|
||||
public const ACTION_DIMENSION_CHANGE_REQUEST = 13; //sent when dying in different dimension
|
||||
public const ACTION_CREATIVE_PLAYER_DESTROY_BLOCK = 13;
|
||||
public const ACTION_DIMENSION_CHANGE_ACK = 14; //sent when spawning in a different dimension to tell the server we spawned
|
||||
public const ACTION_START_GLIDE = 15;
|
||||
public const ACTION_STOP_GLIDE = 16;
|
||||
|
@ -54,13 +54,17 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
private $playMode;
|
||||
/** @var Vector3|null */
|
||||
private $vrGazeDirection = null;
|
||||
/** @var int */
|
||||
private $tick;
|
||||
/** @var Vector3 */
|
||||
private $delta;
|
||||
|
||||
/**
|
||||
* @param int $inputMode @see InputMode
|
||||
* @param int $playMode @see PlayMode
|
||||
* @param Vector3|null $vrGazeDirection only used when PlayMode::VR
|
||||
*/
|
||||
public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection = null) : self{
|
||||
public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection, int $tick, Vector3 $delta) : self{
|
||||
if($playMode === PlayMode::VR and $vrGazeDirection === null){
|
||||
//yuck, can we get a properly written packet just once? ...
|
||||
throw new \InvalidArgumentException("Gaze direction must be provided for VR play mode");
|
||||
@ -78,6 +82,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
if($vrGazeDirection !== null){
|
||||
$result->vrGazeDirection = $vrGazeDirection->asVector3();
|
||||
}
|
||||
$result->tick = $tick;
|
||||
$result->delta = $delta;
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -127,6 +133,10 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
return $this->vrGazeDirection;
|
||||
}
|
||||
|
||||
public function getTick() : int{ return $this->tick; }
|
||||
|
||||
public function getDelta() : Vector3{ return $this->delta; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->pitch = $this->getLFloat();
|
||||
$this->yaw = $this->getLFloat();
|
||||
@ -140,6 +150,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
if($this->playMode === PlayMode::VR){
|
||||
$this->vrGazeDirection = $this->getVector3();
|
||||
}
|
||||
$this->tick = $this->getUnsignedVarLong();
|
||||
$this->delta = $this->getVector3();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
@ -156,6 +168,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
assert($this->vrGazeDirection !== null);
|
||||
$this->putVector3($this->vrGazeDirection);
|
||||
}
|
||||
$this->putUnsignedVarLong($this->tick);
|
||||
$this->putVector3($this->delta);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
|
73
src/pocketmine/network/mcpe/protocol/PlayerFogPacket.php
Normal file
73
src/pocketmine/network/mcpe/protocol/PlayerFogPacket.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use function count;
|
||||
|
||||
class PlayerFogPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::PLAYER_FOG_PACKET;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private $fogLayers;
|
||||
|
||||
/**
|
||||
* @param string[] $fogLayers
|
||||
* @phpstan-param list<string> $fogLayers
|
||||
*/
|
||||
public static function create(array $fogLayers) : self{
|
||||
$result = new self;
|
||||
$result->fogLayers = $fogLayers;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getFogLayers() : array{ return $this->fogLayers; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->fogLayers = [];
|
||||
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$this->fogLayers[] = $this->getString();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putUnsignedVarInt(count($this->fogLayers));
|
||||
foreach($this->fogLayers as $fogLayer){
|
||||
$this->putString($fogLayer);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handlePlayerFog($this);
|
||||
}
|
||||
}
|
@ -37,11 +37,11 @@ interface ProtocolInfo{
|
||||
*/
|
||||
|
||||
/** Actual Minecraft: PE protocol version */
|
||||
public const CURRENT_PROTOCOL = 408;
|
||||
public const CURRENT_PROTOCOL = 422;
|
||||
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
|
||||
public const MINECRAFT_VERSION = 'v1.16.20';
|
||||
public const MINECRAFT_VERSION = 'v1.16.200';
|
||||
/** Version number sent to clients in ping responses. */
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.16.20';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.16.200';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
@ -79,7 +79,7 @@ interface ProtocolInfo{
|
||||
public const BLOCK_PICK_REQUEST_PACKET = 0x22;
|
||||
public const ACTOR_PICK_REQUEST_PACKET = 0x23;
|
||||
public const PLAYER_ACTION_PACKET = 0x24;
|
||||
public const ACTOR_FALL_PACKET = 0x25;
|
||||
|
||||
public const HURT_ARMOR_PACKET = 0x26;
|
||||
public const SET_ACTOR_DATA_PACKET = 0x27;
|
||||
public const SET_ACTOR_MOTION_PACKET = 0x28;
|
||||
@ -176,7 +176,7 @@ interface ProtocolInfo{
|
||||
public const MAP_CREATE_LOCKED_COPY_PACKET = 0x83;
|
||||
public const STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET = 0x84;
|
||||
public const STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET = 0x85;
|
||||
public const UPDATE_BLOCK_PROPERTIES_PACKET = 0x86;
|
||||
|
||||
public const CLIENT_CACHE_BLOB_STATUS_PACKET = 0x87;
|
||||
public const CLIENT_CACHE_MISS_RESPONSE_PACKET = 0x88;
|
||||
public const EDUCATION_SETTINGS_PACKET = 0x89;
|
||||
@ -199,5 +199,12 @@ interface ProtocolInfo{
|
||||
public const POSITION_TRACKING_D_B_CLIENT_REQUEST_PACKET = 0x9a;
|
||||
public const DEBUG_INFO_PACKET = 0x9b;
|
||||
public const PACKET_VIOLATION_WARNING_PACKET = 0x9c;
|
||||
public const MOTION_PREDICTION_HINTS_PACKET = 0x9d;
|
||||
public const ANIMATE_ENTITY_PACKET = 0x9e;
|
||||
public const CAMERA_SHAKE_PACKET = 0x9f;
|
||||
public const PLAYER_FOG_PACKET = 0xa0;
|
||||
public const CORRECT_PLAYER_MOVE_PREDICTION_PACKET = 0xa1;
|
||||
public const ITEM_COMPONENT_PACKET = 0xa2;
|
||||
public const FILTER_TEXT_PACKET = 0xa3;
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\resourcepacks\ResourcePack;
|
||||
use function count;
|
||||
|
||||
@ -40,11 +41,12 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
/** @var ResourcePack[] */
|
||||
public $resourcePackStack = [];
|
||||
|
||||
/** @var bool */
|
||||
public $isExperimental = false;
|
||||
/** @var string */
|
||||
public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
|
||||
|
||||
/** @var Experiments */
|
||||
public $experiments;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->mustAccept = $this->getBool();
|
||||
$behaviorPackCount = $this->getUnsignedVarInt();
|
||||
@ -61,8 +63,8 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
$this->getString();
|
||||
}
|
||||
|
||||
$this->isExperimental = $this->getBool();
|
||||
$this->baseGameVersion = $this->getString();
|
||||
$this->experiments = Experiments::read($this);
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -82,8 +84,8 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
$this->putString(""); //TODO: subpack name
|
||||
}
|
||||
|
||||
$this->putBool($this->isExperimental);
|
||||
$this->putString($this->baseGameVersion);
|
||||
$this->experiments->write($this);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -64,6 +64,7 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
$this->getString();
|
||||
$this->getString();
|
||||
$this->getBool();
|
||||
$this->getBool();
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +90,7 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
$this->putString(""); //TODO: subpack name
|
||||
$this->putString(""); //TODO: content identity
|
||||
$this->putBool(false); //TODO: seems useless for resource packs
|
||||
$this->putBool(false); //TODO: supports RTX
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,14 +38,19 @@ class SetActorDataPacket extends DataPacket{
|
||||
*/
|
||||
public $metadata;
|
||||
|
||||
/** @var int */
|
||||
public $tick = 0;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->metadata = $this->getEntityMetadata();
|
||||
$this->tick = $this->getUnsignedVarLong();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putEntityMetadata($this->metadata);
|
||||
$this->putUnsignedVarLong($this->tick);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -27,29 +27,22 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPaletteEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\EducationEditionOffer;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\network\mcpe\protocol\types\GameRuleType;
|
||||
use pocketmine\network\mcpe\protocol\types\GeneratorType;
|
||||
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\MultiplayerGameVisibility;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
|
||||
use function count;
|
||||
use function file_get_contents;
|
||||
use function json_decode;
|
||||
use const pocketmine\RESOURCE_PATH;
|
||||
|
||||
class StartGamePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::START_GAME_PACKET;
|
||||
|
||||
/** @var string|null */
|
||||
private static $blockTableCache = null;
|
||||
/** @var string|null */
|
||||
private static $itemTableCache = null;
|
||||
|
||||
/** @var int */
|
||||
public $entityUniqueId;
|
||||
/** @var int */
|
||||
@ -116,6 +109,8 @@ class StartGamePacket extends DataPacket{
|
||||
public $gameRules = [ //TODO: implement this
|
||||
"naturalregeneration" => [GameRuleType::BOOL, false] //Hack for client side regeneration
|
||||
];
|
||||
/** @var Experiments */
|
||||
public $experiments;
|
||||
/** @var bool */
|
||||
public $hasBonusChestEnabled = false;
|
||||
/** @var bool */
|
||||
@ -159,8 +154,8 @@ class StartGamePacket extends DataPacket{
|
||||
public $premiumWorldTemplateId = "";
|
||||
/** @var bool */
|
||||
public $isTrial = false;
|
||||
/** @var bool */
|
||||
public $isMovementServerAuthoritative = false;
|
||||
/** @var int */
|
||||
public $playerMovementType = PlayerMovementType::LEGACY;
|
||||
/** @var int */
|
||||
public $currentTick = 0; //only used if isTrial is true
|
||||
/** @var int */
|
||||
@ -168,13 +163,17 @@ class StartGamePacket extends DataPacket{
|
||||
/** @var string */
|
||||
public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort
|
||||
|
||||
/** @var ListTag|null */
|
||||
public $blockTable = null;
|
||||
/**
|
||||
* @var int[]|null string (name) => int16 (legacyID)
|
||||
* @phpstan-var array<string, int>|null
|
||||
* @var BlockPaletteEntry[]
|
||||
* @phpstan-var list<BlockPaletteEntry>
|
||||
*/
|
||||
public $itemTable = null;
|
||||
public $blockPalette = [];
|
||||
|
||||
/**
|
||||
* @var ItemTypeEntry[]
|
||||
* @phpstan-var list<ItemTypeEntry>
|
||||
*/
|
||||
public $itemTable;
|
||||
/** @var bool */
|
||||
public $enableNewInventorySystem = false; //TODO
|
||||
|
||||
@ -210,6 +209,7 @@ class StartGamePacket extends DataPacket{
|
||||
$this->commandsEnabled = $this->getBool();
|
||||
$this->isTexturePacksRequired = $this->getBool();
|
||||
$this->gameRules = $this->getGameRules();
|
||||
$this->experiments = Experiments::read($this);
|
||||
$this->hasBonusChestEnabled = $this->getBool();
|
||||
$this->hasStartWithMapEnabled = $this->getBool();
|
||||
$this->defaultPlayerPermission = $this->getVarInt();
|
||||
@ -235,23 +235,25 @@ class StartGamePacket extends DataPacket{
|
||||
$this->worldName = $this->getString();
|
||||
$this->premiumWorldTemplateId = $this->getString();
|
||||
$this->isTrial = $this->getBool();
|
||||
$this->isMovementServerAuthoritative = $this->getBool();
|
||||
$this->playerMovementType = $this->getVarInt();
|
||||
$this->currentTick = $this->getLLong();
|
||||
|
||||
$this->enchantmentSeed = $this->getVarInt();
|
||||
|
||||
$blockTable = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
if(!($blockTable instanceof ListTag)){
|
||||
throw new \UnexpectedValueException("Wrong block table root NBT tag type");
|
||||
$this->blockPalette = [];
|
||||
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$blockName = $this->getString();
|
||||
$state = $this->getNbtCompoundRoot();
|
||||
$this->blockPalette[] = new BlockPaletteEntry($blockName, $state);
|
||||
}
|
||||
$this->blockTable = $blockTable;
|
||||
|
||||
$this->itemTable = [];
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$id = $this->getString();
|
||||
$legacyId = $this->getSignedLShort();
|
||||
$stringId = $this->getString();
|
||||
$numericId = $this->getSignedLShort();
|
||||
$isComponentBased = $this->getBool();
|
||||
|
||||
$this->itemTable[$id] = $legacyId;
|
||||
$this->itemTable[] = new ItemTypeEntry($stringId, $numericId, $isComponentBased);
|
||||
}
|
||||
|
||||
$this->multiplayerCorrelationId = $this->getString();
|
||||
@ -290,6 +292,7 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putBool($this->commandsEnabled);
|
||||
$this->putBool($this->isTexturePacksRequired);
|
||||
$this->putGameRules($this->gameRules);
|
||||
$this->experiments->write($this);
|
||||
$this->putBool($this->hasBonusChestEnabled);
|
||||
$this->putBool($this->hasStartWithMapEnabled);
|
||||
$this->putVarInt($this->defaultPlayerPermission);
|
||||
@ -314,47 +317,28 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putString($this->worldName);
|
||||
$this->putString($this->premiumWorldTemplateId);
|
||||
$this->putBool($this->isTrial);
|
||||
$this->putBool($this->isMovementServerAuthoritative);
|
||||
$this->putVarInt($this->playerMovementType);
|
||||
$this->putLLong($this->currentTick);
|
||||
|
||||
$this->putVarInt($this->enchantmentSeed);
|
||||
|
||||
if($this->blockTable === null){
|
||||
if(self::$blockTableCache === null){
|
||||
//this is a really nasty hack, but it'll do for now
|
||||
self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(new ListTag("", RuntimeBlockMapping::getBedrockKnownStates()));
|
||||
}
|
||||
$this->put(self::$blockTableCache);
|
||||
}else{
|
||||
$this->put((new NetworkLittleEndianNBTStream())->write($this->blockTable));
|
||||
$this->putUnsignedVarInt(count($this->blockPalette));
|
||||
$nbtWriter = new NetworkLittleEndianNBTStream();
|
||||
foreach($this->blockPalette as $entry){
|
||||
$this->putString($entry->getName());
|
||||
$this->put($nbtWriter->write($entry->getStates()));
|
||||
}
|
||||
if($this->itemTable === null){
|
||||
if(self::$itemTableCache === null){
|
||||
self::$itemTableCache = self::serializeItemTable(json_decode(file_get_contents(RESOURCE_PATH . '/vanilla/item_id_map.json'), true));
|
||||
}
|
||||
$this->put(self::$itemTableCache);
|
||||
}else{
|
||||
$this->put(self::serializeItemTable($this->itemTable));
|
||||
$this->putUnsignedVarInt(count($this->itemTable));
|
||||
foreach($this->itemTable as $entry){
|
||||
$this->putString($entry->getStringId());
|
||||
$this->putLShort($entry->getNumericId());
|
||||
$this->putBool($entry->isComponentBased());
|
||||
}
|
||||
|
||||
$this->putString($this->multiplayerCorrelationId);
|
||||
$this->putBool($this->enableNewInventorySystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $table
|
||||
* @phpstan-param array<string, int> $table
|
||||
*/
|
||||
private static function serializeItemTable(array $table) : string{
|
||||
$stream = new NetworkBinaryStream();
|
||||
$stream->putUnsignedVarInt(count($table));
|
||||
foreach($table as $name => $legacyId){
|
||||
$stream->putString($name);
|
||||
$stream->putLShort($legacyId);
|
||||
}
|
||||
return $stream->getBuffer();
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleStartGame($this);
|
||||
}
|
||||
|
@ -35,15 +35,19 @@ class UpdateAttributesPacket extends DataPacket{
|
||||
public $entityRuntimeId;
|
||||
/** @var Attribute[] */
|
||||
public $entries = [];
|
||||
/** @var int */
|
||||
public $tick = 0;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->entries = $this->getAttributeList();
|
||||
$this->tick = $this->getUnsignedVarLong();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putAttributeList(...$this->entries);
|
||||
$this->putUnsignedVarLong($this->tick);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -0,0 +1,43 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
|
||||
final class BlockPaletteEntry{
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
/** @var CompoundTag */
|
||||
private $states;
|
||||
|
||||
public function __construct(string $name, CompoundTag $states){
|
||||
$this->name = $name;
|
||||
$this->states = $states;
|
||||
}
|
||||
|
||||
public function getName() : string{ return $this->name; }
|
||||
|
||||
public function getStates() : CompoundTag{ return $this->states; }
|
||||
}
|
72
src/pocketmine/network/mcpe/protocol/types/Experiments.php
Normal file
72
src/pocketmine/network/mcpe/protocol/types/Experiments.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use function count;
|
||||
|
||||
final class Experiments{
|
||||
|
||||
/**
|
||||
* @var bool[]
|
||||
* @phpstan-var array<string, bool>
|
||||
*/
|
||||
private $experiments;
|
||||
/** @var bool */
|
||||
private $hasPreviouslyUsedExperiments;
|
||||
|
||||
/**
|
||||
* @param bool[] $experiments
|
||||
* @phpstan-param array<string, bool> $experiments
|
||||
*/
|
||||
public function __construct(array $experiments, bool $hasPreviouslyUsedExperiments){
|
||||
$this->experiments = $experiments;
|
||||
$this->hasPreviouslyUsedExperiments = $hasPreviouslyUsedExperiments;
|
||||
}
|
||||
|
||||
/** @return bool[] */
|
||||
public function getExperiments() : array{ return $this->experiments; }
|
||||
|
||||
public function hasPreviouslyUsedExperiments() : bool{ return $this->hasPreviouslyUsedExperiments; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$experiments = [];
|
||||
for($i = 0, $len = $in->getLInt(); $i < $len; ++$i){
|
||||
$experimentName = $in->getString();
|
||||
$enabled = $in->getBool();
|
||||
$experiments[$experimentName] = $enabled;
|
||||
}
|
||||
$hasPreviouslyUsedExperiments = $in->getBool();
|
||||
return new self($experiments, $hasPreviouslyUsedExperiments);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->putLInt(count($this->experiments));
|
||||
foreach($this->experiments as $experimentName => $enabled){
|
||||
$out->putString($experimentName);
|
||||
$out->putBool($enabled);
|
||||
}
|
||||
$out->putBool($this->hasPreviouslyUsedExperiments);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
|
||||
final class ItemComponentPacketEntry{
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
/** @var CompoundTag */
|
||||
private $componentNbt;
|
||||
|
||||
public function __construct(string $name, CompoundTag $componentNbt){
|
||||
$this->name = $name;
|
||||
$this->componentNbt = $componentNbt;
|
||||
}
|
||||
|
||||
public function getName() : string{ return $this->name; }
|
||||
|
||||
public function getComponentNbt() : CompoundTag{ return $this->componentNbt; }
|
||||
}
|
@ -21,35 +21,26 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class ActorFallPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::ACTOR_FALL_PACKET;
|
||||
final class ItemTypeEntry{
|
||||
|
||||
/** @var string */
|
||||
private $stringId;
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
/** @var float */
|
||||
public $fallDistance;
|
||||
private $numericId;
|
||||
/** @var bool */
|
||||
public $isInVoid;
|
||||
private $componentBased;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->fallDistance = $this->getLFloat();
|
||||
$this->isInVoid = $this->getBool();
|
||||
public function __construct(string $stringId, int $numericId, bool $componentBased){
|
||||
$this->stringId = $stringId;
|
||||
$this->numericId = $numericId;
|
||||
$this->componentBased = $componentBased;
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putLFloat($this->fallDistance);
|
||||
$this->putBool($this->isInVoid);
|
||||
}
|
||||
public function getStringId() : string{ return $this->stringId; }
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleActorFall($this);
|
||||
}
|
||||
public function getNumericId() : int{ return $this->numericId; }
|
||||
|
||||
public function isComponentBased() : bool{ return $this->componentBased; }
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
final class PlayerMovementType{
|
||||
|
||||
public const LEGACY = 0; //MovePlayerPacket
|
||||
public const SERVER_AUTHORITATIVE_V1 = 1; //PlayerAuthInputPacket
|
||||
public const SERVER_AUTHORITATIVE_V2_REWIND = 2; //PlayerAuthInputPacket + a bunch of junk that solves a nonexisting problem
|
||||
}
|
@ -29,17 +29,23 @@ class SkinAnimation{
|
||||
public const TYPE_BODY_32 = 2;
|
||||
public const TYPE_BODY_64 = 3;
|
||||
|
||||
public const EXPRESSION_LINEAR = 0; //???
|
||||
public const EXPRESSION_BLINKING = 1;
|
||||
|
||||
/** @var SkinImage */
|
||||
private $image;
|
||||
/** @var int */
|
||||
private $type;
|
||||
/** @var float */
|
||||
private $frames;
|
||||
/** @var int */
|
||||
private $expressionType;
|
||||
|
||||
public function __construct(SkinImage $image, int $type, float $frames){
|
||||
public function __construct(SkinImage $image, int $type, float $frames, int $expressionType){
|
||||
$this->image = $image;
|
||||
$this->type = $type;
|
||||
$this->frames = $frames;
|
||||
$this->expressionType = $expressionType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,4 +68,6 @@ class SkinAnimation{
|
||||
public function getFrames() : float{
|
||||
return $this->frames;
|
||||
}
|
||||
|
||||
public function getExpressionType() : int{ return $this->expressionType; }
|
||||
}
|
||||
|
@ -28,8 +28,13 @@ use function count;
|
||||
|
||||
final class ItemStackResponse{
|
||||
|
||||
/** @var bool */
|
||||
private $ok;
|
||||
public const RESULT_OK = 0;
|
||||
public const RESULT_ERROR = 1;
|
||||
//TODO: there are a ton more possible result types but we don't need them yet and they are wayyyyyy too many for me
|
||||
//to waste my time on right now...
|
||||
|
||||
/** @var int */
|
||||
private $result;
|
||||
/** @var int */
|
||||
private $requestId;
|
||||
/** @var ItemStackResponseContainerInfo[] */
|
||||
@ -38,13 +43,13 @@ final class ItemStackResponse{
|
||||
/**
|
||||
* @param ItemStackResponseContainerInfo[] $containerInfos
|
||||
*/
|
||||
public function __construct(bool $ok, int $requestId, array $containerInfos){
|
||||
$this->ok = $ok;
|
||||
public function __construct(int $result, int $requestId, array $containerInfos){
|
||||
$this->result = $result;
|
||||
$this->requestId = $requestId;
|
||||
$this->containerInfos = $containerInfos;
|
||||
}
|
||||
|
||||
public function isOk() : bool{ return $this->ok; }
|
||||
public function getResult() : int{ return $this->result; }
|
||||
|
||||
public function getRequestId() : int{ return $this->requestId; }
|
||||
|
||||
@ -52,17 +57,17 @@ final class ItemStackResponse{
|
||||
public function getContainerInfos() : array{ return $this->containerInfos; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$ok = $in->getBool();
|
||||
$result = $in->getByte();
|
||||
$requestId = $in->readGenericTypeNetworkId();
|
||||
$containerInfos = [];
|
||||
for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$containerInfos[] = ItemStackResponseContainerInfo::read($in);
|
||||
}
|
||||
return new self($ok, $requestId, $containerInfos);
|
||||
return new self($result, $requestId, $containerInfos);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->putBool($this->ok);
|
||||
$out->putByte($this->result);
|
||||
$out->writeGenericTypeNetworkId($this->requestId);
|
||||
$out->putUnsignedVarInt(count($this->containerInfos));
|
||||
foreach($this->containerInfos as $containerInfo){
|
||||
|
@ -35,12 +35,15 @@ final class ItemStackResponseSlotInfo{
|
||||
private $count;
|
||||
/** @var int */
|
||||
private $itemStackId;
|
||||
/** @var string */
|
||||
private $customName;
|
||||
|
||||
public function __construct(int $slot, int $hotbarSlot, int $count, int $itemStackId){
|
||||
public function __construct(int $slot, int $hotbarSlot, int $count, int $itemStackId, string $customName){
|
||||
$this->slot = $slot;
|
||||
$this->hotbarSlot = $hotbarSlot;
|
||||
$this->count = $count;
|
||||
$this->itemStackId = $itemStackId;
|
||||
$this->customName = $customName;
|
||||
}
|
||||
|
||||
public function getSlot() : int{ return $this->slot; }
|
||||
@ -51,12 +54,15 @@ final class ItemStackResponseSlotInfo{
|
||||
|
||||
public function getItemStackId() : int{ return $this->itemStackId; }
|
||||
|
||||
public function getCustomName() : string{ return $this->customName; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$slot = $in->getByte();
|
||||
$hotbarSlot = $in->getByte();
|
||||
$count = $in->getByte();
|
||||
$itemStackId = $in->readGenericTypeNetworkId();
|
||||
return new self($slot, $hotbarSlot, $count, $itemStackId);
|
||||
$customName = $in->getString();
|
||||
return new self($slot, $hotbarSlot, $count, $itemStackId, $customName);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
@ -64,5 +70,6 @@ final class ItemStackResponseSlotInfo{
|
||||
$out->putByte($this->hotbarSlot);
|
||||
$out->putByte($this->count);
|
||||
$out->writeGenericTypeNetworkId($this->itemStackId);
|
||||
$out->putString($this->customName);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\permission;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function explode;
|
||||
@ -145,7 +146,9 @@ class BanEntry{
|
||||
private static function parseDate(string $date) : \DateTime{
|
||||
$datetime = \DateTime::createFromFormat(self::$format, $date);
|
||||
if(!($datetime instanceof \DateTime)){
|
||||
throw new \RuntimeException("Error parsing date for BanEntry: " . implode(", ", \DateTime::getLastErrors()["errors"]));
|
||||
$lastErrors = \DateTime::getLastErrors();
|
||||
if($lastErrors === false) throw new AssumptionFailedError("DateTime::getLastErrors() should not be returning false in here");
|
||||
throw new \RuntimeException("Error parsing date for BanEntry: " . implode(", ", $lastErrors["errors"]));
|
||||
}
|
||||
|
||||
return $datetime;
|
||||
|
@ -123,7 +123,16 @@ class PermissionAttachment{
|
||||
if($this->permissions[$name] === $value){
|
||||
return;
|
||||
}
|
||||
unset($this->permissions[$name]); //Fixes children getting overwritten
|
||||
/* Because of the way child permissions are calculated, permissions which were set later in time are
|
||||
* preferred over earlier ones when conflicts in inherited permission values occur.
|
||||
* Here's the kicker: This behaviour depends on PHP's internal array ordering, which maintains insertion
|
||||
* order -- BUT -- assigning to an existing index replaces the old value WITHOUT changing the order.
|
||||
* (what crazy person thought relying on this this was a good idea?!?!?!?!?!)
|
||||
*
|
||||
* This removes the old value so that the new value will be added at the end of the array's internal order
|
||||
* instead of directly taking the place of the older value.
|
||||
*/
|
||||
unset($this->permissions[$name]);
|
||||
}
|
||||
$this->permissions[$name] = $value;
|
||||
$this->permissible->recalculatePermissions();
|
||||
|
@ -36,9 +36,6 @@ class PermissionAttachmentInfo{
|
||||
/** @var bool */
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @throws \InvalidStateException
|
||||
*/
|
||||
public function __construct(Permissible $permissible, string $permission, PermissionAttachment $attachment = null, bool $value){
|
||||
$this->permissible = $permissible;
|
||||
$this->permission = $permission;
|
||||
|
@ -157,7 +157,7 @@ class PermissionManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|Permissible[]
|
||||
* @return Permissible[]
|
||||
*/
|
||||
public function getPermissionSubscriptions(string $permission) : array{
|
||||
return $this->permSubs[$permission] ?? [];
|
||||
|
@ -58,7 +58,7 @@ class PluginDescription{
|
||||
private $compatibleOperatingSystems = [];
|
||||
/**
|
||||
* @var string[][]
|
||||
* @phpstan-var array<string, list<mixed>>
|
||||
* @phpstan-var array<string, list<string>>
|
||||
*/
|
||||
private $extensions = [];
|
||||
/** @var string[] */
|
||||
@ -104,13 +104,13 @@ class PluginDescription{
|
||||
|
||||
$this->name = $plugin["name"];
|
||||
if(preg_match('/^[A-Za-z0-9 _.-]+$/', $this->name) === 0){
|
||||
throw new PluginException("Invalid PluginDescription name");
|
||||
throw new PluginException("Invalid Plugin name");
|
||||
}
|
||||
$this->name = str_replace(" ", "_", $this->name);
|
||||
$this->version = (string) $plugin["version"];
|
||||
$this->main = $plugin["main"];
|
||||
if(stripos($this->main, "pocketmine\\") === 0){
|
||||
throw new PluginException("Invalid PluginDescription main, cannot start within the PocketMine namespace");
|
||||
throw new PluginException("Invalid Plugin main, cannot start within the PocketMine namespace");
|
||||
}
|
||||
|
||||
$this->api = array_map("\strval", (array) ($plugin["api"] ?? []));
|
||||
@ -132,7 +132,7 @@ class PluginDescription{
|
||||
$k = $v;
|
||||
$v = "*";
|
||||
}
|
||||
$this->extensions[$k] = is_array($v) ? $v : [$v];
|
||||
$this->extensions[$k] = array_map('strval', is_array($v) ? $v : [$v]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,14 +149,18 @@ class PluginDescription{
|
||||
if(isset($plugin["load"])){
|
||||
$order = mb_strtoupper($plugin["load"]);
|
||||
if(!defined(PluginLoadOrder::class . "::" . $order)){
|
||||
throw new PluginException("Invalid PluginDescription load");
|
||||
throw new PluginException("Invalid Plugin load");
|
||||
}else{
|
||||
$this->order = constant(PluginLoadOrder::class . "::" . $order);
|
||||
}
|
||||
}
|
||||
$this->authors = [];
|
||||
if(isset($plugin["author"])){
|
||||
$this->authors[] = $plugin["author"];
|
||||
if(is_array($plugin["author"])){
|
||||
$this->authors = $plugin["author"];
|
||||
}else{
|
||||
$this->authors[] = $plugin["author"];
|
||||
}
|
||||
}
|
||||
if(isset($plugin["authors"])){
|
||||
foreach($plugin["authors"] as $author){
|
||||
|
@ -84,7 +84,7 @@ network:
|
||||
#Set to 0 to compress everything, -1 to disable.
|
||||
batch-threshold: 256
|
||||
#Compression level used when sending batched packets. Higher = more CPU, less bandwidth usage
|
||||
compression-level: 7
|
||||
compression-level: 6
|
||||
#Use AsyncTasks for compression. Adds half/one tick delay, less CPU load on main thread
|
||||
async-compression: false
|
||||
#Experimental, only for Windows. Tries to use UPnP to automatically port forward
|
||||
|
Submodule src/pocketmine/resources/vanilla updated: afc885ccca...4e58a3c67d
@ -329,6 +329,16 @@ class AsyncPool{
|
||||
$this->collectWorkers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of worker ID => task queue size
|
||||
*
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*/
|
||||
public function getTaskQueueSizes() : array{
|
||||
return $this->workerUsage;
|
||||
}
|
||||
|
||||
public function shutdownUnusedWorkers() : int{
|
||||
$ret = 0;
|
||||
$time = time();
|
||||
|
@ -152,8 +152,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
if($this->burnTime > 0 and $ev->isBurning()){
|
||||
$fuel->pop();
|
||||
$this->inventory->setFuel($fuel);
|
||||
$this->inventory->setFuel($fuel->getFuelResidue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,6 @@ use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use function current;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
@ -74,8 +73,8 @@ abstract class Tile extends Position{
|
||||
*/
|
||||
private static $knownTiles = [];
|
||||
/**
|
||||
* @var string[][]
|
||||
* @phpstan-var array<class-string<Tile>, list<string>>
|
||||
* @var string[]
|
||||
* @phpstan-var array<class-string<Tile>, string>
|
||||
*/
|
||||
private static $saveNames = [];
|
||||
|
||||
@ -138,7 +137,7 @@ abstract class Tile extends Position{
|
||||
self::$knownTiles[$name] = $className;
|
||||
}
|
||||
|
||||
self::$saveNames[$className] = $saveNames;
|
||||
self::$saveNames[$className] = reset($saveNames);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -154,8 +153,7 @@ abstract class Tile extends Position{
|
||||
throw new \InvalidStateException("Tile is not registered");
|
||||
}
|
||||
|
||||
reset(self::$saveNames[static::class]);
|
||||
return current(self::$saveNames[static::class]);
|
||||
return self::$saveNames[static::class];
|
||||
}
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
|
@ -59,6 +59,8 @@ abstract class Timings{
|
||||
/** @var TimingsHandler */
|
||||
public static $serverCommandTimer;
|
||||
/** @var TimingsHandler */
|
||||
public static $worldLoadTimer;
|
||||
/** @var TimingsHandler */
|
||||
public static $worldSaveTimer;
|
||||
/** @var TimingsHandler */
|
||||
public static $populationTimer;
|
||||
@ -126,6 +128,7 @@ abstract class Timings{
|
||||
self::$connectionTimer = new TimingsHandler("Connection Handler");
|
||||
self::$schedulerTimer = new TimingsHandler("Scheduler");
|
||||
self::$serverCommandTimer = new TimingsHandler("Server Command");
|
||||
self::$worldLoadTimer = new TimingsHandler("World Load");
|
||||
self::$worldSaveTimer = new TimingsHandler("World Save");
|
||||
self::$populationTimer = new TimingsHandler("World Population");
|
||||
self::$generationCallbackTimer = new TimingsHandler("World Generation Callback");
|
||||
|
@ -30,6 +30,8 @@ use function fclose;
|
||||
use function file;
|
||||
use function file_get_contents;
|
||||
use function function_exists;
|
||||
use function getmypid;
|
||||
use function getmyuid;
|
||||
use function hexdec;
|
||||
use function memory_get_usage;
|
||||
use function posix_kill;
|
||||
@ -175,4 +177,20 @@ final class Process{
|
||||
|
||||
return proc_close($process);
|
||||
}
|
||||
|
||||
public static function pid() : int{
|
||||
$result = getmypid();
|
||||
if($result === false){
|
||||
throw new \LogicException("getmypid() doesn't work on this platform");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function uid() : int{
|
||||
$result = getmyuid();
|
||||
if($result === false){
|
||||
throw new \LogicException("getmyuid() doesn't work on this platform");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use pocketmine\Thread;
|
||||
use function getmypid;
|
||||
use function time;
|
||||
|
||||
class ServerKiller extends Thread{
|
||||
@ -55,7 +54,7 @@ class ServerKiller extends Thread{
|
||||
});
|
||||
if(time() - $start >= $this->time){
|
||||
echo "\nTook too long to stop, server was killed forcefully!\n";
|
||||
@Process::kill(getmypid());
|
||||
@Process::kill(Process::pid());
|
||||
}
|
||||
}
|
||||
|
||||
|
48
src/pocketmine/utils/SingletonTrait.php
Normal file
48
src/pocketmine/utils/SingletonTrait.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
trait SingletonTrait{
|
||||
/** @var self|null */
|
||||
private static $instance = null;
|
||||
|
||||
private static function make() : self{
|
||||
return new self;
|
||||
}
|
||||
|
||||
public static function getInstance() : self{
|
||||
if(self::$instance === null){
|
||||
self::$instance = self::make();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function setInstance(self $instance) : void{
|
||||
self::$instance = $instance;
|
||||
}
|
||||
|
||||
public static function reset() : void{
|
||||
self::$instance = null;
|
||||
}
|
||||
}
|
@ -88,7 +88,7 @@ abstract class Timezone{
|
||||
break;
|
||||
}
|
||||
|
||||
if($response = Internet::getURL("http://ip-api.com/json") //If system timezone detection fails or timezone is an invalid value.
|
||||
if(($response = Internet::getURL("http://ip-api.com/json")) !== false //If system timezone detection fails or timezone is an invalid value.
|
||||
and $ip_geolocation_data = json_decode($response, true)
|
||||
and $ip_geolocation_data['status'] !== 'fail'
|
||||
and date_default_timezone_set($ip_geolocation_data['timezone'])
|
||||
|
@ -94,7 +94,7 @@ class UUID{
|
||||
}
|
||||
|
||||
public static function fromRandom() : UUID{
|
||||
return self::fromData(Binary::writeInt(time()), Binary::writeShort(getmypid()), Binary::writeShort(getmyuid()), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)));
|
||||
return self::fromData(Binary::writeInt(time()), Binary::writeShort(($pid = getmypid()) !== false ? $pid : 0), Binary::writeShort(($uid = getmyuid()) !== false ? $uid : 0), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)));
|
||||
}
|
||||
|
||||
public function toBinary() : string{
|
||||
|
@ -106,6 +106,9 @@ class Utils{
|
||||
public const OS_BSD = "bsd";
|
||||
public const OS_UNKNOWN = "other";
|
||||
|
||||
public const CLEAN_PATH_SRC_PREFIX = "pmsrc";
|
||||
public const CLEAN_PATH_PLUGINS_PREFIX = "plugins";
|
||||
|
||||
/** @var string|null */
|
||||
public static $os;
|
||||
/** @var UUID|null */
|
||||
@ -189,7 +192,14 @@ class Utils{
|
||||
}
|
||||
|
||||
$machine = php_uname("a");
|
||||
$machine .= ($cpuinfo = @file("/proc/cpuinfo")) !== false ? implode(preg_grep("/(model name|Processor|Serial)/", $cpuinfo)) : "";
|
||||
$cpuinfo = @file("/proc/cpuinfo");
|
||||
if($cpuinfo !== false){
|
||||
$cpuinfoLines = preg_grep("/(model name|Processor|Serial)/", $cpuinfo);
|
||||
if($cpuinfoLines === false){
|
||||
throw new AssumptionFailedError("Pattern is valid, so this shouldn't fail ...");
|
||||
}
|
||||
$machine .= implode("", $cpuinfoLines);
|
||||
}
|
||||
$machine .= sys_get_temp_dir();
|
||||
$machine .= $extra;
|
||||
$os = Utils::getOS();
|
||||
@ -527,7 +537,7 @@ class Utils{
|
||||
$ret = explode("\n", $contents);
|
||||
ob_end_clean();
|
||||
|
||||
if(count($ret) >= 1 and preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
|
||||
if(preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
|
||||
return ((int) $m[1]) - ($includeCurrent ? 3 : 4); //$value + zval call + extra call
|
||||
}
|
||||
return -1;
|
||||
@ -604,8 +614,8 @@ class Utils{
|
||||
//remove relative paths
|
||||
//TODO: make these paths dynamic so they can be unit-tested against
|
||||
static $cleanPaths = [
|
||||
\pocketmine\PLUGIN_PATH => "plugins", //this has to come BEFORE \pocketmine\PATH because it's inside that by default on src installations
|
||||
\pocketmine\PATH => ""
|
||||
\pocketmine\PLUGIN_PATH => self::CLEAN_PATH_PLUGINS_PREFIX, //this has to come BEFORE \pocketmine\PATH because it's inside that by default on src installations
|
||||
\pocketmine\PATH => self::CLEAN_PATH_SRC_PREFIX
|
||||
];
|
||||
foreach($cleanPaths as $cleanPath => $replacement){
|
||||
$cleanPath = rtrim(str_replace([DIRECTORY_SEPARATOR, "phar://"], ["/", ""], $cleanPath), "/");
|
||||
|
@ -92,6 +92,7 @@ class SetupWizard{
|
||||
$config->save();
|
||||
|
||||
if(strtolower($this->getInput($this->lang->get("skip_installer"), "n", "y/N")) === "y"){
|
||||
$this->printIpDetails();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -101,6 +102,7 @@ class SetupWizard{
|
||||
$this->generateUserFiles();
|
||||
|
||||
$this->networkFunctions();
|
||||
$this->printIpDetails();
|
||||
|
||||
$this->endWizard();
|
||||
|
||||
@ -218,7 +220,9 @@ LICENSE;
|
||||
}
|
||||
|
||||
$config->save();
|
||||
|
||||
}
|
||||
|
||||
private function printIpDetails() : void{
|
||||
$this->message($this->lang->get("ip_get"));
|
||||
|
||||
$externalIP = Internet::getIP();
|
||||
|
54
tests/gh-actions/build.sh
Executable file
54
tests/gh-actions/build.sh
Executable file
@ -0,0 +1,54 @@
|
||||
VERSION=7.4.13
|
||||
sudo apt update && sudo apt install -y \
|
||||
re2c \
|
||||
libtool \
|
||||
libtool-bin \
|
||||
zlib1g-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libxml2-dev \
|
||||
libyaml-dev \
|
||||
libgmp-dev \
|
||||
libzip-dev \
|
||||
libssl-dev
|
||||
|
||||
curl -sSL https://www.php.net/distributions/php-$VERSION.tar.gz | tar -xz
|
||||
|
||||
INSTALL_DIR="$(pwd)/bin/php7"
|
||||
cd php-$VERSION
|
||||
cd ext/
|
||||
curl -sSL https://github.com/pmmp/pthreads/archive/2bcd8b8c10395d58b8a9bc013e3a5328080c867f.tar.gz | tar -xz
|
||||
curl -sSL https://github.com/php/pecl-file_formats-yaml/archive/2.2.0.tar.gz | tar -xz
|
||||
cd ..
|
||||
|
||||
CFLAGS="$CFLAGS -march=x86-64"
|
||||
CXXFLAGS="$CXXFLAGS -march=x86-64"
|
||||
|
||||
./buildconf --force
|
||||
./configure \
|
||||
--prefix="$INSTALL_DIR" \
|
||||
--exec-prefix="$INSTALL_DIR" \
|
||||
--enable-maintainer-zts \
|
||||
--enable-cli \
|
||||
--disable-cgi \
|
||||
--disable-phpdbg \
|
||||
--disable-mbregex \
|
||||
--disable-pdo \
|
||||
--disable-session \
|
||||
--enable-mbstring \
|
||||
--enable-pthreads \
|
||||
--enable-simplexml \
|
||||
--enable-sockets \
|
||||
--enable-xml \
|
||||
--enable-xmlreader \
|
||||
--enable-xmlwriter \
|
||||
--with-curl \
|
||||
--with-gmp \
|
||||
--with-libxml \
|
||||
--with-openssl \
|
||||
--with-yaml \
|
||||
--with-zip \
|
||||
--with-zlib \
|
||||
--without-pear \
|
||||
--without-sqlite3
|
||||
|
||||
make -j8 install
|
@ -21,7 +21,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
define('pocketmine\_PHPSTAN_ANALYSIS', true);
|
||||
if(!defined('LEVELDB_ZLIB_RAW_COMPRESSION')){
|
||||
//leveldb might not be loaded
|
||||
define('LEVELDB_ZLIB_RAW_COMPRESSION', 4);
|
||||
}
|
||||
|
||||
//TODO: these need to be defined properly or removed
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 2) . '/vendor/autoload.php');
|
||||
|
@ -185,11 +185,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Effect.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\entity\\\\Entity\\:\\:getSaveId\\(\\) should return string but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Entity.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to int\\.$#"
|
||||
count: 2
|
||||
@ -530,21 +525,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\plugin\\\\PluginDescription\\:\\:getRequiredExtensions\\(\\) should return array\\<string, array\\<int, string\\>\\> but returns array\\<string, array\\<int, mixed\\>\\>\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of function substr expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Part \\$constr \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/plugin/PluginDescription.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$description of method pocketmine\\\\command\\\\Command\\:\\:setDescription\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
@ -575,11 +555,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/scheduler/AsyncTask.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\tile\\\\Tile\\:\\:getSaveId\\(\\) should return string but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/tile/Tile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$timestamp of function date expects int, mixed given\\.$#"
|
||||
count: 1
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user