mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 11:16:57 +00:00
Compare commits
110 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
d7a66ad755 | |||
b3f88e7b73 | |||
55adc1ef63 | |||
868d236ddc | |||
59e9c84806 | |||
a110317d1b | |||
b169d89291 | |||
74bef7f423 | |||
8db7867881 | |||
d8f8afe531 | |||
7dabf305f8 | |||
ed0053d0ee | |||
28255e35d1 | |||
0fc9170bbf | |||
7e2efae024 | |||
e9fa07b550 | |||
8ac32824a2 | |||
1322defead | |||
d084b7a34b | |||
c2d0605b1e | |||
0ff0b33047 | |||
114df07622 | |||
4a88db7f43 | |||
d3ea29d527 | |||
d2f1a3cf5b | |||
f9c2ed6200 | |||
e47a711494 | |||
2ea7a9e216 | |||
9f60484212 | |||
3031d89ec5 | |||
883e135bc0 | |||
4448f603a6 | |||
9365525efa | |||
17bee5e349 | |||
c6e0753c3e | |||
2ae7ba275b | |||
6aa0a82341 | |||
0af08a7375 | |||
81c1613e5d | |||
9cf8f608d8 | |||
dd4f26a9cf | |||
f976545f56 | |||
9929fb0abd | |||
37e453b875 | |||
b7578fef9c | |||
09eb904f6b | |||
b47d6bbc22 | |||
aa26ddf8b1 | |||
119c72980f | |||
eba888449d | |||
dac76f0e0f | |||
89fe8f7f10 | |||
2d77b1e364 | |||
e59a4296f8 | |||
6856761946 | |||
4fe3401182 | |||
e80ad22702 | |||
c22ab37372 | |||
1f9d672cfc | |||
974cbae725 | |||
b53f88027e | |||
9a0f723dff | |||
ab2003a85d | |||
4befd9095a | |||
06623d788a | |||
730ee74a65 | |||
700e0afee0 | |||
4b9712fdee | |||
dbd015b866 |
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Help & support on Discord
|
||||
url: https://discord.gg/bmSAZBG
|
||||
about: We don't accept support requests on the issue tracker. Please try asking on Discord instead.
|
||||
- name: Help & support on forums
|
||||
url: https://forums.pmmp.io
|
||||
about: We don't accept support requests on the issue tracker. Please try asking on forums instead.
|
||||
- name: Documentation
|
||||
url: https://pmmp.rtfd.io
|
||||
about: PocketMine-MP documentation
|
14
.github/ISSUE_TEMPLATE/help---support.md
vendored
14
.github/ISSUE_TEMPLATE/help---support.md
vendored
@ -1,14 +0,0 @@
|
||||
---
|
||||
name: Help & support
|
||||
about: We don't accept support requests here. Try the links on the README.
|
||||
title: ''
|
||||
labels: Support request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
We don't accept support requests on the issue tracker. Please try the following links instead:
|
||||
|
||||
Documentation: http://pmmp.rtfd.io
|
||||
Forums: https://forums.pmmp.io
|
||||
Discord: https://discord.gg/bmSAZBG
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
name: Security/DoS vulnerability
|
||||
about: 'Bug or exploit that can be used to attack servers (hint: don''t report it
|
||||
on a public issue tracker)'
|
||||
title: ''
|
||||
labels: 'Auto: Spam'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please DO NOT report security vulnerabilities here.
|
||||
Instead, send an email to team@pmmp.io or contact a developer directly, IN PRIVATE.
|
25
.travis.yml
25
.travis.yml
@ -1,20 +1,5 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.3
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.1.0
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout 45579e1e622acd80f9c880f3a025ba3b98b8ebef
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
- make install
|
||||
- cd ..
|
||||
- echo "extension=pthreads.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
import:
|
||||
source: ./tests/travis/setup-php.yml
|
||||
|
||||
script:
|
||||
- composer install --prefer-dist
|
||||
@ -29,4 +14,8 @@ cache:
|
||||
- $HOME/.composer/cache/vcs
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
email:
|
||||
recipients:
|
||||
- team@pmmp.io
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
33
SECURITY.md
Normal file
33
SECURITY.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
The following release lines are currently receiving active security updates and bug fixes:
|
||||
|
||||
| Version | Supported |
|
||||
| -------- | ------------------ |
|
||||
| 3.15.x | :white_check_mark: |
|
||||
| < 3.15.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**DO NOT report vulnerabilities on the GitHub issue tracker.**
|
||||
GitHub is public and anyone can see the issues you post on the issue tracker, including people who would exploit vulnerabilities for their own gain.
|
||||
|
||||
**WARNING: You may put live servers at risk by reporting a vulnerability on the GitHub issue tracker.**
|
||||
|
||||
**Contact us** by sending an email to [**team@pmmp.io**](mailto:team@pmmp.io?subject=Security%20Vulnerability%20in%20PocketMine-MP). Include the following information:
|
||||
|
||||
- Version of PocketMine-MP
|
||||
- Detailed description of the vulnerability (e.g. how to exploit it, what the effects are)
|
||||
|
||||
Please note that we can't guarantee a reply to every email.
|
||||
|
||||
## FAQ
|
||||
### Do you offer a bug bounty?
|
||||
No.
|
||||
|
||||
### How soon can I expect a fix for a vulnerability I've reported?
|
||||
This depends on the nature of the problem. We can't provide any general ETA (nor would it be wise to provide one).
|
||||
In general, it depends on when developers have time to look into the problem, how complex the problem is to fix, and how many users it impacts.
|
||||
|
||||
When a fix for a severe vulnerability is pushed, a patch release for the target version will usually be released within 24 hours so that users can update.
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\build\make_release;
|
||||
|
||||
use pocketmine\utils\VersionString;
|
||||
use function defined;
|
||||
use function dirname;
|
||||
use function fgets;
|
||||
use function file_get_contents;
|
||||
@ -37,7 +38,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(
|
||||
|
Submodule build/php updated: cec63c3093...e45cfc1ece
@ -6,7 +6,7 @@ Plugin developers should **only** update their required API to this version if y
|
||||
|
||||
**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.14.0
|
||||
# 3.15.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.20.
|
||||
- Removed compatibility with 1.16.0.
|
||||
|
||||
@ -15,3 +15,20 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.15.1
|
||||
- Fixed various PHP 7.4 compatibility issues in Composer dependencies (primarily callback-validator).
|
||||
- Fixed LevelDB worlds with corrupted `level.dat` crashing the server instead of failing gracefully.
|
||||
- Fixed error spam when using strings for layers in flatworld presets (`e.g. bedrock,3xdirt,grass`).
|
||||
- Fixed blocks not getting updated properly on explosions.
|
||||
- Fixed `BlockGrowEvent` not being called when sugarcane grows.
|
||||
- Potato crops now drop poisonous potatoes when harvested.
|
||||
- Fixed the wrong number of potatoes being dropped when harvesting potato crops.
|
||||
- Players no longer get pullbacks when sprinting on slabs, stairs and various other blocks when `player.anti-cheat.allow-movement-cheats` is set to `false`. (This bug has been around for over 5 years, so many of you will be used to its existence.)
|
||||
- Fixed entity collision box calculation not taking clip distance into account.
|
||||
- Entities now step up the correct height of the target block, instead of jumping into the air 0.6 blocks and falling back down.
|
||||
|
||||
# 3.15.2
|
||||
- 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`.
|
||||
|
@ -33,12 +33,12 @@
|
||||
"pocketmine/classloader": "^0.1.0",
|
||||
"pocketmine/log": "^0.2.0",
|
||||
"pocketmine/log-pthreads": "^0.1.0",
|
||||
"pocketmine/callback-validator": "^1.0.1",
|
||||
"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.37",
|
||||
"phpstan/phpstan": "0.12.54",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
@ -57,5 +57,10 @@
|
||||
"psr-4": {
|
||||
"pocketmine\\": "tests/phpunit/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
814
composer.lock
generated
814
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
|
||||
@ -20,6 +19,8 @@ parameters:
|
||||
checkExplicitMixed: true
|
||||
bootstrapFiles:
|
||||
- tests/phpstan/bootstrap.php
|
||||
scanDirectories:
|
||||
- tests/plugins/TesterPlugin
|
||||
scanFiles:
|
||||
- src/pocketmine/PocketMine.php
|
||||
- build/make-release.php
|
||||
@ -29,6 +30,7 @@ parameters:
|
||||
- build/make-release.php
|
||||
- build/server-phar.php
|
||||
- tests/phpunit
|
||||
- tests/plugins/TesterPlugin
|
||||
dynamicConstantNames:
|
||||
- pocketmine\IS_DEVELOPMENT_BUILD
|
||||
- pocketmine\DEBUG
|
||||
|
@ -23,7 +23,7 @@ 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;
|
||||
@ -54,6 +54,7 @@ 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;
|
||||
@ -338,6 +339,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 +360,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 +368,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");
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ use function fwrite;
|
||||
use function gc_collect_cycles;
|
||||
use function gc_disable;
|
||||
use function gc_enable;
|
||||
use function gc_mem_caches;
|
||||
use function get_class;
|
||||
use function get_declared_classes;
|
||||
use function implode;
|
||||
@ -273,6 +274,7 @@ class MemoryManager{
|
||||
}
|
||||
|
||||
$cycles = gc_collect_cycles();
|
||||
gc_mem_caches();
|
||||
|
||||
Timings::$garbageCollectorTimer->stopTiming();
|
||||
|
||||
|
@ -1601,10 +1601,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$dy = $newPos->y - $this->y;
|
||||
$dz = $newPos->z - $this->z;
|
||||
|
||||
//the client likes to clip into blocks like stairs, but we do full server-side prediction of that without
|
||||
//help from the client's position changes, so we deduct the expected clip height from the moved distance.
|
||||
$expectedClipDistance = $this->ySize * (1 - self::STEP_CLIP_MULTIPLIER);
|
||||
$dy -= $expectedClipDistance;
|
||||
$this->move($dx, $dy, $dz);
|
||||
|
||||
$diff = $this->distanceSquared($newPos);
|
||||
|
||||
//TODO: Explore lowering this threshold now that stairs work properly.
|
||||
if($this->isSurvival() and $diff > 0.0625){
|
||||
$ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ));
|
||||
$ev->setCancelled($this->allowMovementCheats);
|
||||
@ -1614,7 +1619,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$ev->isCancelled()){
|
||||
$revert = true;
|
||||
$this->server->getLogger()->debug($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()]));
|
||||
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos);
|
||||
$this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos . ", expected clip distance: $expectedClipDistance");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2881,7 +2886,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$target = $this->level->getBlock($pos);
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $packet->face, PlayerInteractEvent::LEFT_CLICK_BLOCK);
|
||||
if($this->level->checkSpawnProtection($this, $target)){
|
||||
if($this->isSpectator() || $this->level->checkSpawnProtection($this, $target)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
@ -3903,10 +3908,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if($targets !== null){
|
||||
if(in_array($this, $targets, true)){
|
||||
$this->forceMoveSync = $pos->asVector3();
|
||||
$this->ySize = 0;
|
||||
}
|
||||
$this->server->broadcastPacket($targets, $pk);
|
||||
}else{
|
||||
$this->forceMoveSync = $pos->asVector3();
|
||||
$this->ySize = 0;
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
@ -4115,7 +4122,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;
|
||||
}
|
||||
|
@ -1400,10 +1400,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 +1543,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{
|
||||
|
@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.15.0";
|
||||
const BASE_VERSION = "3.15.3";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
@ -40,9 +40,13 @@ class Potato extends Crops{
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
ItemFactory::get(Item::POTATO, 0, $this->getDamage() >= 0x07 ? mt_rand(1, 4) : 1)
|
||||
$result = [
|
||||
ItemFactory::get(Item::POTATO, 0, $this->getDamage() >= 0x07 ? mt_rand(1, 5) : 1)
|
||||
];
|
||||
if($this->getDamage() >= 7 && mt_rand(0, 49) === 0){
|
||||
$result[] = ItemFactory::get(Item::POISONOUS_POTATO);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
|
@ -87,7 +87,12 @@ class Sugarcane extends Flowable{
|
||||
for($y = 1; $y < 3; ++$y){
|
||||
$b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z);
|
||||
if($b->getId() === self::AIR){
|
||||
$this->getLevelNonNull()->setBlock($b, BlockFactory::get(Block::SUGARCANE_BLOCK), true);
|
||||
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK));
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
break;
|
||||
}
|
||||
$this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -54,9 +54,9 @@ class StatusCommand extends VanillaCommand{
|
||||
$server = $sender->getServer();
|
||||
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
|
||||
|
||||
$time = microtime(true) - \pocketmine\START_TIME;
|
||||
$time = (int) (microtime(true) - \pocketmine\START_TIME);
|
||||
|
||||
$seconds = floor($time % 60);
|
||||
$seconds = $time % 60;
|
||||
$minutes = null;
|
||||
$hours = null;
|
||||
$days = null;
|
||||
@ -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.");
|
||||
|
@ -80,6 +80,7 @@ use function count;
|
||||
use function current;
|
||||
use function deg2rad;
|
||||
use function floor;
|
||||
use function fmod;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
@ -94,6 +95,7 @@ use const M_PI_2;
|
||||
abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
public const MOTION_THRESHOLD = 0.00001;
|
||||
protected const STEP_CLIP_MULTIPLIER = 0.4;
|
||||
|
||||
public const NETWORK_ID = -1;
|
||||
|
||||
@ -707,10 +709,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
$this->boundingBox->setBounds(
|
||||
$this->x - $halfWidth,
|
||||
$this->y,
|
||||
$this->y + $this->ySize,
|
||||
$this->z - $halfWidth,
|
||||
$this->x + $halfWidth,
|
||||
$this->y + $this->height,
|
||||
$this->y + $this->height + $this->ySize,
|
||||
$this->z + $halfWidth
|
||||
);
|
||||
}
|
||||
@ -1311,7 +1313,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
public function getDirection() : ?int{
|
||||
$rotation = ($this->yaw - 90) % 360;
|
||||
$rotation = fmod($this->yaw - 90, 360);
|
||||
if($rotation < 0){
|
||||
$rotation += 360.0;
|
||||
}
|
||||
@ -1449,8 +1451,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1538,7 +1546,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
if($this->keepMovement){
|
||||
$this->boundingBox->offset($dx, $dy, $dz);
|
||||
}else{
|
||||
$this->ySize *= 0.4;
|
||||
$this->ySize *= self::STEP_CLIP_MULTIPLIER;
|
||||
|
||||
/*
|
||||
if($this->isColliding){ //With cobweb?
|
||||
@ -1605,7 +1613,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
$this->boundingBox->offset(0, 0, $dz);
|
||||
|
||||
if($this->stepHeight > 0 and $fallingFlag and $this->ySize < 0.05 and ($movX != $dx or $movZ != $dz)){
|
||||
if($this->stepHeight > 0 and $fallingFlag and ($movX != $dx or $movZ != $dz)){
|
||||
$cx = $dx;
|
||||
$cy = $dy;
|
||||
$cz = $dz;
|
||||
@ -1637,13 +1645,20 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
$this->boundingBox->offset(0, 0, $dz);
|
||||
|
||||
$reverseDY = -$dy;
|
||||
foreach($list as $bb){
|
||||
$reverseDY = $bb->calculateYOffset($this->boundingBox, $reverseDY);
|
||||
}
|
||||
$dy += $reverseDY;
|
||||
$this->boundingBox->offset(0, $reverseDY, 0);
|
||||
|
||||
if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){
|
||||
$dx = $cx;
|
||||
$dy = $cy;
|
||||
$dz = $cz;
|
||||
$this->boundingBox->setBB($axisalignedbb1);
|
||||
}else{
|
||||
$this->ySize += 0.5; //FIXME: this should be the height of the block it walked up, not fixed 0.5
|
||||
$this->ySize += $dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,12 +400,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)){
|
||||
|
@ -45,6 +45,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
@ -97,17 +98,15 @@ abstract class Living extends Entity implements Damageable{
|
||||
//TODO: load/save armor inventory contents
|
||||
$this->armorInventory->setEventProcessor(new ArmorInventoryEventProcessor($this));
|
||||
|
||||
$health = $this->getMaxHealth();
|
||||
|
||||
if($this->namedtag->hasTag("HealF", FloatTag::class)){
|
||||
$health = $this->namedtag->getFloat("HealF");
|
||||
$this->namedtag->removeTag("HealF");
|
||||
}elseif($this->namedtag->hasTag("Health")){
|
||||
$healthTag = $this->namedtag->getTag("Health");
|
||||
$health = (float) $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
|
||||
if(!($healthTag instanceof FloatTag)){
|
||||
$this->namedtag->removeTag("Health");
|
||||
}
|
||||
}elseif($this->namedtag->hasTag("Health", ShortTag::class)){
|
||||
//Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
|
||||
$health = $this->namedtag->getShort("Health");
|
||||
$this->namedtag->removeTag("Health");
|
||||
}else{
|
||||
$health = $this->namedtag->getFloat("Health", $this->getMaxHealth());
|
||||
}
|
||||
|
||||
$this->setHealth($health);
|
||||
|
@ -24,6 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\entity\utils;
|
||||
|
||||
use pocketmine\math\Math;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function count;
|
||||
use function max;
|
||||
|
||||
abstract class ExperienceUtils{
|
||||
@ -59,6 +61,9 @@ abstract class ExperienceUtils{
|
||||
* This returns a floating-point number, the decimal part being the progress through the resulting level.
|
||||
*/
|
||||
public static function getLevelFromXp(int $xp) : float{
|
||||
if($xp < 0){
|
||||
throw new \InvalidArgumentException("XP must be at least 0");
|
||||
}
|
||||
if($xp <= self::getXpToReachLevel(16)){
|
||||
$a = 1;
|
||||
$b = 6;
|
||||
@ -74,6 +79,9 @@ abstract class ExperienceUtils{
|
||||
}
|
||||
|
||||
$x = Math::solveQuadratic($a, $b, $c - $xp);
|
||||
if(count($x) === 0){
|
||||
throw new AssumptionFailedError("Expected at least 1 solution");
|
||||
}
|
||||
|
||||
return max($x); //we're only interested in the positive solution
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ use pocketmine\event\Cancellable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\tile\Furnace;
|
||||
|
||||
/**
|
||||
* Called when a furnace is about to consume a new fuel item.
|
||||
*/
|
||||
class FurnaceBurnEvent extends BlockEvent implements Cancellable{
|
||||
/** @var Furnace */
|
||||
private $furnace;
|
||||
@ -53,18 +56,31 @@ class FurnaceBurnEvent extends BlockEvent implements Cancellable{
|
||||
return $this->fuel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ticks that the furnace will be powered for.
|
||||
*/
|
||||
public function getBurnTime() : int{
|
||||
return $this->burnTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ticks that the given fuel will power the furnace for.
|
||||
*/
|
||||
public function setBurnTime(int $burnTime) : void{
|
||||
$this->burnTime = $burnTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the fuel item will be consumed.
|
||||
*/
|
||||
public function isBurning() : bool{
|
||||
return $this->burning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the fuel will be consumed. If false, the furnace will smelt as if it consumed fuel, but no fuel
|
||||
* will be deducted.
|
||||
*/
|
||||
public function setBurning(bool $burning) : void{
|
||||
$this->burning = $burning;
|
||||
}
|
||||
|
@ -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){
|
||||
|
@ -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));
|
||||
|
@ -42,6 +42,7 @@ use pocketmine\nbt\tag\NamedTag;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Binary;
|
||||
use function array_map;
|
||||
use function base64_decode;
|
||||
@ -475,7 +476,12 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
public function getLore() : array{
|
||||
$display = $this->getNamedTagEntry(self::TAG_DISPLAY);
|
||||
if($display instanceof CompoundTag and ($lore = $display->getListTag(self::TAG_DISPLAY_LORE)) !== null){
|
||||
return $lore->getAllValues();
|
||||
return array_map(function(NamedTag $line) : string{
|
||||
if(!($line instanceof StringTag)){
|
||||
throw new AssumptionFailedError("Nobody bothered to handle this error case and we can't fix it until PM4, oops ... #blameshoghi");
|
||||
}
|
||||
return $line->getValue();
|
||||
}, $lore->getValue());
|
||||
}
|
||||
|
||||
return [];
|
||||
|
Submodule src/pocketmine/lang/locale updated: 85343cfb7f...c85a7b79f3
@ -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();
|
||||
@ -232,7 +231,9 @@ class Explosion{
|
||||
|
||||
$t->close();
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->affectedBlocks as $block){
|
||||
$pos = new Vector3($block->x, $block->y, $block->z);
|
||||
|
||||
for($side = 0; $side <= 5; $side++){
|
||||
@ -252,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");
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\block\BlockFactory;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Spawnable;
|
||||
@ -691,13 +692,14 @@ class Chunk{
|
||||
|
||||
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
|
||||
$idTag = $nbt->getTag("id");
|
||||
if(!($idTag instanceof IntTag) && !($idTag instanceof StringTag)){ //allow mixed types (because of leveldb)
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
try{
|
||||
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
|
||||
$entity = Entity::createEntity($idTag->getValue(), $level, $nbt);
|
||||
if(!($entity instanceof Entity)){
|
||||
$changed = true;
|
||||
continue;
|
||||
@ -927,6 +929,7 @@ class Chunk{
|
||||
$chunk->setGenerated($terrainGenerated);
|
||||
$chunk->setPopulated($terrainPopulated);
|
||||
$chunk->setLightPopulated($lightPopulated);
|
||||
$chunk->setChanged(false);
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ if(!defined(__NAMESPACE__ . '\ZERO_NIBBLE_ARRAY')){
|
||||
}
|
||||
|
||||
class SubChunk implements SubChunkInterface{
|
||||
private const ZERO_NIBBLE_ARRAY = ZERO_NIBBLE_ARRAY;
|
||||
|
||||
/** @var string */
|
||||
protected $ids;
|
||||
/** @var string */
|
||||
@ -68,7 +70,7 @@ class SubChunk implements SubChunkInterface{
|
||||
substr_count($this->ids, "\x00") === 4096 and
|
||||
(!$checkLight or (
|
||||
substr_count($this->skyLight, "\xff") === 2048 and
|
||||
$this->blockLight === ZERO_NIBBLE_ARRAY
|
||||
$this->blockLight === self::ZERO_NIBBLE_ARRAY
|
||||
))
|
||||
);
|
||||
}
|
||||
@ -230,14 +232,14 @@ class SubChunk implements SubChunkInterface{
|
||||
* reference to the const instead of duplicating the whole string. The string will only be duplicated when
|
||||
* modified, which is perfect for this purpose.
|
||||
*/
|
||||
if($this->data === ZERO_NIBBLE_ARRAY){
|
||||
$this->data = ZERO_NIBBLE_ARRAY;
|
||||
if($this->data === self::ZERO_NIBBLE_ARRAY){
|
||||
$this->data = self::ZERO_NIBBLE_ARRAY;
|
||||
}
|
||||
if($this->skyLight === ZERO_NIBBLE_ARRAY){
|
||||
$this->skyLight = ZERO_NIBBLE_ARRAY;
|
||||
if($this->skyLight === self::ZERO_NIBBLE_ARRAY){
|
||||
$this->skyLight = self::ZERO_NIBBLE_ARRAY;
|
||||
}
|
||||
if($this->blockLight === ZERO_NIBBLE_ARRAY){
|
||||
$this->blockLight = ZERO_NIBBLE_ARRAY;
|
||||
if($this->blockLight === self::ZERO_NIBBLE_ARRAY){
|
||||
$this->blockLight = self::ZERO_NIBBLE_ARRAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +131,11 @@ class LevelDB extends BaseLevelProvider{
|
||||
throw new LevelException("Truncated level.dat");
|
||||
}
|
||||
$nbt = new LittleEndianNBTStream();
|
||||
$levelData = $nbt->read(substr($rawLevelData, 8));
|
||||
try{
|
||||
$levelData = $nbt->read(substr($rawLevelData, 8));
|
||||
}catch(\UnexpectedValueException $e){
|
||||
throw new LevelException("Invalid level.dat (" . $e->getMessage() . ")", 0, $e);
|
||||
}
|
||||
if($levelData instanceof CompoundTag){
|
||||
$this->levelData = $levelData;
|
||||
}else{
|
||||
|
@ -245,11 +245,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;
|
||||
}
|
||||
|
@ -69,11 +69,14 @@ final class RegionGarbageMap{
|
||||
/** @var RegionLocationTableEntry|null $prevEntry */
|
||||
$prevEntry = null;
|
||||
foreach($usedMap as $firstSector => $entry){
|
||||
$expectedStart = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
|
||||
$actualStart = $entry->getFirstSector();
|
||||
if($expectedStart < $actualStart){
|
||||
$prevEndPlusOne = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
|
||||
$currentStart = $entry->getFirstSector();
|
||||
if($prevEndPlusOne < $currentStart){
|
||||
//found a gap in the table
|
||||
$garbageMap[$expectedStart] = new RegionLocationTableEntry($expectedStart, $actualStart - $expectedStart, 0);
|
||||
$garbageMap[$prevEndPlusOne] = new RegionLocationTableEntry($prevEndPlusOne, $currentStart - $prevEndPlusOne, 0);
|
||||
}elseif($prevEndPlusOne > $currentStart){
|
||||
//current entry starts inside the previous. This would be a bug since RegionLoader should prevent this
|
||||
throw new AssumptionFailedError("Overlapping entries detected");
|
||||
}
|
||||
$prevEntry = $entry;
|
||||
}
|
||||
|
@ -303,6 +303,7 @@ class RegionLoader{
|
||||
throw new CorruptedRegionException("Corrupted region header (unexpected end of file)");
|
||||
}
|
||||
|
||||
/** @var int[] $data */
|
||||
$data = unpack("N*", $headerRaw);
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\level\biome\Biome;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\SimpleChunkManager;
|
||||
@ -34,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;
|
||||
@ -47,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 = []){
|
||||
@ -59,6 +64,7 @@ class GeneratorRegisterTask extends AsyncTask{
|
||||
|
||||
public function onRun(){
|
||||
BlockFactory::init();
|
||||
ItemFactory::init();
|
||||
Biome::init();
|
||||
$manager = new SimpleChunkManager($this->seed, $this->worldHeight);
|
||||
$this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager);
|
||||
|
@ -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,7 +149,7 @@ 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -25,11 +25,13 @@ namespace pocketmine\scheduler;
|
||||
|
||||
use function gc_collect_cycles;
|
||||
use function gc_enable;
|
||||
use function gc_mem_caches;
|
||||
|
||||
class GarbageCollectionTask extends AsyncTask{
|
||||
|
||||
public function onRun(){
|
||||
gc_enable();
|
||||
gc_collect_cycles();
|
||||
gc_mem_caches();
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -141,8 +141,8 @@ abstract class TextFormat{
|
||||
if(!is_array($string)){
|
||||
$string = self::tokenize($string);
|
||||
}
|
||||
$newString = [];
|
||||
$pointer =& $newString;
|
||||
$newString = new TextFormatJsonObject();
|
||||
$pointer = $newString;
|
||||
$color = "white";
|
||||
$bold = false;
|
||||
$italic = false;
|
||||
@ -152,165 +152,164 @@ abstract class TextFormat{
|
||||
$index = 0;
|
||||
|
||||
foreach($string as $token){
|
||||
if(isset($pointer["text"])){
|
||||
if(!isset($newString["extra"])){
|
||||
$newString["extra"] = [];
|
||||
if($pointer->text !== null){
|
||||
if($newString->extra === null){
|
||||
$newString->extra = [];
|
||||
}
|
||||
$newString["extra"][$index] = [];
|
||||
$pointer =& $newString["extra"][$index];
|
||||
$newString->extra[$index] = $pointer = new TextFormatJsonObject();
|
||||
if($color !== "white"){
|
||||
$pointer["color"] = $color;
|
||||
$pointer->color = $color;
|
||||
}
|
||||
if($bold){
|
||||
$pointer["bold"] = true;
|
||||
$pointer->bold = true;
|
||||
}
|
||||
if($italic){
|
||||
$pointer["italic"] = true;
|
||||
$pointer->italic = true;
|
||||
}
|
||||
if($underlined){
|
||||
$pointer["underlined"] = true;
|
||||
$pointer->underlined = true;
|
||||
}
|
||||
if($strikethrough){
|
||||
$pointer["strikethrough"] = true;
|
||||
$pointer->strikethrough = true;
|
||||
}
|
||||
if($obfuscated){
|
||||
$pointer["obfuscated"] = true;
|
||||
$pointer->obfuscated = true;
|
||||
}
|
||||
++$index;
|
||||
}
|
||||
switch($token){
|
||||
case TextFormat::BOLD:
|
||||
if(!$bold){
|
||||
$pointer["bold"] = true;
|
||||
$pointer->bold = true;
|
||||
$bold = true;
|
||||
}
|
||||
break;
|
||||
case TextFormat::OBFUSCATED:
|
||||
if(!$obfuscated){
|
||||
$pointer["obfuscated"] = true;
|
||||
$pointer->obfuscated = true;
|
||||
$obfuscated = true;
|
||||
}
|
||||
break;
|
||||
case TextFormat::ITALIC:
|
||||
if(!$italic){
|
||||
$pointer["italic"] = true;
|
||||
$pointer->italic = true;
|
||||
$italic = true;
|
||||
}
|
||||
break;
|
||||
case TextFormat::UNDERLINE:
|
||||
if(!$underlined){
|
||||
$pointer["underlined"] = true;
|
||||
$pointer->underlined = true;
|
||||
$underlined = true;
|
||||
}
|
||||
break;
|
||||
case TextFormat::STRIKETHROUGH:
|
||||
if(!$strikethrough){
|
||||
$pointer["strikethrough"] = true;
|
||||
$pointer->strikethrough = true;
|
||||
$strikethrough = true;
|
||||
}
|
||||
break;
|
||||
case TextFormat::RESET:
|
||||
if($color !== "white"){
|
||||
$pointer["color"] = "white";
|
||||
$pointer->color = "white";
|
||||
$color = "white";
|
||||
}
|
||||
if($bold){
|
||||
$pointer["bold"] = false;
|
||||
$pointer->bold = false;
|
||||
$bold = false;
|
||||
}
|
||||
if($italic){
|
||||
$pointer["italic"] = false;
|
||||
$pointer->italic = false;
|
||||
$italic = false;
|
||||
}
|
||||
if($underlined){
|
||||
$pointer["underlined"] = false;
|
||||
$pointer->underlined = false;
|
||||
$underlined = false;
|
||||
}
|
||||
if($strikethrough){
|
||||
$pointer["strikethrough"] = false;
|
||||
$pointer->strikethrough = false;
|
||||
$strikethrough = false;
|
||||
}
|
||||
if($obfuscated){
|
||||
$pointer["obfuscated"] = false;
|
||||
$pointer->obfuscated = false;
|
||||
$obfuscated = false;
|
||||
}
|
||||
break;
|
||||
|
||||
//Colors
|
||||
case TextFormat::BLACK:
|
||||
$pointer["color"] = "black";
|
||||
$pointer->color = "black";
|
||||
$color = "black";
|
||||
break;
|
||||
case TextFormat::DARK_BLUE:
|
||||
$pointer["color"] = "dark_blue";
|
||||
$pointer->color = "dark_blue";
|
||||
$color = "dark_blue";
|
||||
break;
|
||||
case TextFormat::DARK_GREEN:
|
||||
$pointer["color"] = "dark_green";
|
||||
$pointer->color = "dark_green";
|
||||
$color = "dark_green";
|
||||
break;
|
||||
case TextFormat::DARK_AQUA:
|
||||
$pointer["color"] = "dark_aqua";
|
||||
$pointer->color = "dark_aqua";
|
||||
$color = "dark_aqua";
|
||||
break;
|
||||
case TextFormat::DARK_RED:
|
||||
$pointer["color"] = "dark_red";
|
||||
$pointer->color = "dark_red";
|
||||
$color = "dark_red";
|
||||
break;
|
||||
case TextFormat::DARK_PURPLE:
|
||||
$pointer["color"] = "dark_purple";
|
||||
$pointer->color = "dark_purple";
|
||||
$color = "dark_purple";
|
||||
break;
|
||||
case TextFormat::GOLD:
|
||||
$pointer["color"] = "gold";
|
||||
$pointer->color = "gold";
|
||||
$color = "gold";
|
||||
break;
|
||||
case TextFormat::GRAY:
|
||||
$pointer["color"] = "gray";
|
||||
$pointer->color = "gray";
|
||||
$color = "gray";
|
||||
break;
|
||||
case TextFormat::DARK_GRAY:
|
||||
$pointer["color"] = "dark_gray";
|
||||
$pointer->color = "dark_gray";
|
||||
$color = "dark_gray";
|
||||
break;
|
||||
case TextFormat::BLUE:
|
||||
$pointer["color"] = "blue";
|
||||
$pointer->color = "blue";
|
||||
$color = "blue";
|
||||
break;
|
||||
case TextFormat::GREEN:
|
||||
$pointer["color"] = "green";
|
||||
$pointer->color = "green";
|
||||
$color = "green";
|
||||
break;
|
||||
case TextFormat::AQUA:
|
||||
$pointer["color"] = "aqua";
|
||||
$pointer->color = "aqua";
|
||||
$color = "aqua";
|
||||
break;
|
||||
case TextFormat::RED:
|
||||
$pointer["color"] = "red";
|
||||
$pointer->color = "red";
|
||||
$color = "red";
|
||||
break;
|
||||
case TextFormat::LIGHT_PURPLE:
|
||||
$pointer["color"] = "light_purple";
|
||||
$pointer->color = "light_purple";
|
||||
$color = "light_purple";
|
||||
break;
|
||||
case TextFormat::YELLOW:
|
||||
$pointer["color"] = "yellow";
|
||||
$pointer->color = "yellow";
|
||||
$color = "yellow";
|
||||
break;
|
||||
case TextFormat::WHITE:
|
||||
$pointer["color"] = "white";
|
||||
$pointer->color = "white";
|
||||
$color = "white";
|
||||
break;
|
||||
default:
|
||||
$pointer["text"] = $token;
|
||||
$pointer->text = $token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($newString["extra"])){
|
||||
foreach($newString["extra"] as $k => $d){
|
||||
if(!isset($d["text"])){
|
||||
unset($newString["extra"][$k]);
|
||||
if($newString->extra !== null){
|
||||
foreach($newString->extra as $k => $d){
|
||||
if($d->text === null){
|
||||
unset($newString->extra[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
src/pocketmine/utils/TextFormatJsonObject.php
Normal file
60
src/pocketmine/utils/TextFormatJsonObject.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @see TextFormat::toJSON()
|
||||
*/
|
||||
final class TextFormatJsonObject implements \JsonSerializable{
|
||||
/** @var string|null */
|
||||
public $text = null;
|
||||
/** @var string|null */
|
||||
public $color = null;
|
||||
/** @var bool|null */
|
||||
public $bold = null;
|
||||
/** @var bool|null */
|
||||
public $italic = null;
|
||||
/** @var bool|null */
|
||||
public $underlined = null;
|
||||
/** @var bool|null */
|
||||
public $strikethrough = null;
|
||||
/** @var bool|null */
|
||||
public $obfuscated = null;
|
||||
/**
|
||||
* @var TextFormatJsonObject[]|null
|
||||
* @phpstan-var array<int, TextFormatJsonObject>|null
|
||||
*/
|
||||
public $extra = null;
|
||||
|
||||
public function jsonSerialize(){
|
||||
$result = (array) $this;
|
||||
foreach($result as $k => $v){
|
||||
if($v === null){
|
||||
unset($result[$k]);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -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'])
|
||||
|
@ -527,7 +527,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;
|
||||
|
@ -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();
|
||||
|
@ -23,6 +23,11 @@ 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');
|
||||
define('pocketmine\DATA', '');
|
||||
|
@ -530,21 +530,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
|
||||
@ -560,11 +545,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/plugin/PluginManager.php
|
||||
|
||||
-
|
||||
message: "#^Part \\$openResult \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$code of class pocketmine\\\\resourcepacks\\\\ResourcePackException constructor expects int, mixed given\\.$#"
|
||||
count: 1
|
||||
|
@ -1,12 +0,0 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
#TODO: use custom stubs
|
||||
-
|
||||
message: "#^Used constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
||||
|
||||
-
|
||||
message: "#^Constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/format/io/leveldb/LevelDB.php
|
@ -355,21 +355,11 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/CommandReader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$params of class pocketmine\\\\lang\\\\TranslationContainer constructor expects array\\<float\\|int\\|string\\>, array\\<int, string\\|null\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanCommand.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$params of class pocketmine\\\\lang\\\\TranslationContainer constructor expects array\\<float\\|int\\|string\\>, array\\<int, string\\|null\\> given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
|
||||
count: 1
|
||||
@ -575,11 +565,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/Explosion.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$generatorClass of class pocketmine\\\\level\\\\generator\\\\GeneratorRegisterTask constructor expects string, pocketmine\\\\level\\\\generator\\\\Generator\\|string given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/Level.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'priority' on array\\('priority' \\=\\> int, 'data' \\=\\> pocketmine\\\\math\\\\Vector3\\)\\|int\\|pocketmine\\\\math\\\\Vector3\\.$#"
|
||||
count: 1
|
||||
@ -751,12 +736,7 @@ parameters:
|
||||
path: ../../../src/pocketmine/level/format/io/region/McRegion.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in \\+, int\\|false given on the left side\\.$#"
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/level/format/io/region/McRegion.php
|
||||
|
||||
-
|
||||
message: "#^Only numeric types are allowed in %, int\\|false given on the left side\\.$#"
|
||||
message: "#^Only numeric types are allowed in %%, int\\|false given on the left side\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php
|
||||
|
||||
|
@ -240,11 +240,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/Server.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of static method pocketmine\\\\level\\\\generator\\\\GeneratorManager\\:\\:getGenerator\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/Server.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$uuid of method pocketmine\\\\Server\\:\\:updatePlayerListData\\(\\) expects pocketmine\\\\utils\\\\UUID, pocketmine\\\\utils\\\\UUID\\|null given\\.$#"
|
||||
count: 1
|
||||
@ -535,46 +530,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/SimpleCommandMap.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$target of method pocketmine\\\\permission\\\\BanList\\:\\:addBan\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayerExact\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$ip of method pocketmine\\\\command\\\\defaults\\\\BanIpCommand\\:\\:processIPBan\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/BanIpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getOfflinePlayer\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/DeopCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/KickCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getOfflinePlayer\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/OpCommand.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method addParticle\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#"
|
||||
count: 1
|
||||
@ -590,11 +545,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/SetWorldSpawnCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/command/defaults/TellCommand.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getTime\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#"
|
||||
count: 1
|
||||
@ -775,11 +725,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Human.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getValue\\(\\) on pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/entity/Living.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$attribute of method pocketmine\\\\entity\\\\AttributeMap\\:\\:addAttribute\\(\\) expects pocketmine\\\\entity\\\\Attribute, pocketmine\\\\entity\\\\Attribute\\|null given\\.$#"
|
||||
count: 6
|
||||
@ -1225,11 +1170,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getValue\\(\\) on pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/format/Chunk.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\level\\\\format\\\\Chunk\\:\\:getSubChunk\\(\\) should return pocketmine\\\\level\\\\format\\\\SubChunkInterface but returns pocketmine\\\\level\\\\format\\\\SubChunkInterface\\|null\\.$#"
|
||||
count: 1
|
||||
@ -1550,11 +1490,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/network/rcon/RCONInstance.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/permission/BanEntry.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\permission\\\\DefaultPermissions\\:\\:registerPermission\\(\\) should return pocketmine\\\\permission\\\\Permission but returns pocketmine\\\\permission\\\\Permission\\|null\\.$#"
|
||||
count: 1
|
||||
@ -1645,16 +1580,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/tile/Spawnable.php
|
||||
|
||||
-
|
||||
message: "#^Cannot clone non\\-object variable \\$customBlockDataTag of type pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/tile/Tile.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$tag of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setTag\\(\\) expects pocketmine\\\\nbt\\\\tag\\\\NamedTag, pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/tile/Tile.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getBlockAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#"
|
||||
count: 1
|
||||
@ -1695,11 +1620,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/utils/Config.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/utils/Config.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\utils\\\\MainLogger\\:\\:getLogger\\(\\) should return pocketmine\\\\utils\\\\MainLogger but returns pocketmine\\\\utils\\\\MainLogger\\|null\\.$#"
|
||||
count: 1
|
||||
@ -1711,7 +1631,7 @@ parameters:
|
||||
path: ../../../src/pocketmine/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\StupidJsonDecodeTest\\:\\:\\$stupidJsonDecodeFunc \\(Closure\\) does not accept Closure\\|null\\.$#"
|
||||
message: "#^Cannot call method cancel\\(\\) on pocketmine\\\\scheduler\\\\TaskHandler\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/network/mcpe/StupidJsonDecodeTest.php
|
||||
path: ../../plugins/TesterPlugin/src/pmmp/TesterPlugin/CheckTestCompletionTask.php
|
||||
|
||||
|
@ -5,3 +5,8 @@ parameters:
|
||||
count: 1
|
||||
path: ../../phpunit/network/mcpe/StupidJsonDecodeTest.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\StupidJsonDecodeTest\\:\\:\\$stupidJsonDecodeFunc \\(Closure\\(string, bool\\)\\: mixed\\) does not accept Closure\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/network/mcpe/StupidJsonDecodeTest.php
|
||||
|
||||
|
@ -60,11 +60,6 @@ parameters:
|
||||
count: 2
|
||||
path: ../../../src/pocketmine/level/Level.php
|
||||
|
||||
-
|
||||
message: "#^Call to function assert\\(\\) with bool will always evaluate to true\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/pocketmine/level/Level.php
|
||||
|
||||
-
|
||||
message: "#^Call to function is_subclass_of\\(\\) with class\\-string\\<pocketmine\\\\level\\\\generator\\\\Generator\\> and 'pocketmine\\\\\\\\level\\\\\\\\generator\\\\\\\\Generator' will always evaluate to true\\.$#"
|
||||
count: 1
|
||||
|
@ -110,8 +110,6 @@ class BlockTest extends TestCase{
|
||||
|
||||
/**
|
||||
* @dataProvider blockGetProvider
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
*/
|
||||
public function testBlockGet(int $id, int $meta) : void{
|
||||
$block = BlockFactory::get($id, $meta);
|
||||
|
@ -79,9 +79,6 @@ class ItemTest extends TestCase{
|
||||
|
||||
/**
|
||||
* @dataProvider itemFromStringProvider
|
||||
* @param string $string
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
*/
|
||||
public function testFromStringSingle(string $string, int $id, int $meta) : void{
|
||||
$item = ItemFactory::fromStringSingle($string);
|
||||
|
@ -82,8 +82,6 @@ class RegionLoaderTest extends TestCase{
|
||||
|
||||
/**
|
||||
* @dataProvider outOfBoundsCoordsProvider
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @throws ChunkException
|
||||
* @throws \InvalidArgumentException
|
||||
@ -109,8 +107,6 @@ class RegionLoaderTest extends TestCase{
|
||||
|
||||
/**
|
||||
* @dataProvider outOfBoundsCoordsProvider
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \pocketmine\level\format\io\exception\CorruptedChunkException
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\format\io\region;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function sprintf;
|
||||
|
||||
class RegionLocationTableEntryTest extends TestCase{
|
||||
|
||||
|
@ -26,7 +26,10 @@ namespace pocketmine\network\mcpe;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class StupidJsonDecodeTest extends TestCase{
|
||||
/** @var \Closure */
|
||||
/**
|
||||
* @var \Closure
|
||||
* @phpstan-var \Closure(string $json, bool $assoc=) : mixed
|
||||
*/
|
||||
private $stupidJsonDecodeFunc;
|
||||
|
||||
public function setUp() : void{
|
||||
@ -54,7 +57,6 @@ class StupidJsonDecodeTest extends TestCase{
|
||||
/**
|
||||
* @dataProvider stupidJsonDecodeProvider
|
||||
*
|
||||
* @param string $brokenJson
|
||||
* @param mixed $expect
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function yaml_parse;
|
||||
|
||||
class ConfigTest extends TestCase{
|
||||
|
||||
@ -61,7 +62,6 @@ class ConfigTest extends TestCase{
|
||||
/**
|
||||
* @dataProvider fixYamlIndexesProvider
|
||||
*
|
||||
* @param string $test
|
||||
* @param mixed[] $expected
|
||||
*/
|
||||
public function testFixYamlIndexes(string $test, array $expected) : void{
|
||||
|
@ -51,7 +51,6 @@ class UtilsTest extends TestCase{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $docComment
|
||||
* @dataProvider parseDocCommentNewlineProvider
|
||||
*/
|
||||
public function testParseDocCommentNewlines(string $docComment) : void{
|
||||
|
Submodule tests/plugins/DevTools updated: 8f5f3009f9...9a43721a2f
@ -26,6 +26,7 @@ namespace pmmp\TesterPlugin;
|
||||
use pocketmine\event\Listener;
|
||||
use pocketmine\event\server\CommandEvent;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use function array_shift;
|
||||
|
||||
class Main extends PluginBase implements Listener{
|
||||
|
||||
@ -38,7 +39,7 @@ class Main extends PluginBase implements Listener{
|
||||
/** @var int */
|
||||
protected $currentTestNumber = 0;
|
||||
|
||||
public function onEnable(){
|
||||
public function onEnable() : void{
|
||||
$this->getServer()->getPluginManager()->registerEvents($this, $this);
|
||||
$this->getScheduler()->scheduleRepeatingTask(new CheckTestCompletionTask($this), 10);
|
||||
|
||||
@ -49,7 +50,7 @@ class Main extends PluginBase implements Listener{
|
||||
];
|
||||
}
|
||||
|
||||
public function onServerCommand(CommandEvent $event){
|
||||
public function onServerCommand(CommandEvent $event) : void{
|
||||
//The CI will send this command as a failsafe to prevent the build from hanging if the tester plugin failed to
|
||||
//run. However, if the plugin loaded successfully we don't want to allow this to stop the server as there may
|
||||
//be asynchronous tests running. Instead we cancel this and stop the server of our own accord once all tests
|
||||
@ -59,10 +60,7 @@ class Main extends PluginBase implements Listener{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Test|null
|
||||
*/
|
||||
public function getCurrentTest(){
|
||||
public function getCurrentTest() : ?Test{
|
||||
return $this->currentTest;
|
||||
}
|
||||
|
||||
@ -77,7 +75,7 @@ class Main extends PluginBase implements Listener{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onTestCompleted(Test $test){
|
||||
public function onTestCompleted(Test $test) : void{
|
||||
$message = "Finished test #" . $this->currentTestNumber . " (" . $test->getName() . "): ";
|
||||
switch($test->getResult()){
|
||||
case Test::RESULT_OK:
|
||||
@ -103,7 +101,7 @@ class Main extends PluginBase implements Listener{
|
||||
$this->currentTest = null;
|
||||
}
|
||||
|
||||
public function onAllTestsCompleted(){
|
||||
public function onAllTestsCompleted() : void{
|
||||
$this->getLogger()->notice("All tests finished, stopping the server");
|
||||
$this->getServer()->shutdown();
|
||||
}
|
||||
|
@ -23,15 +23,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace pmmp\TesterPlugin;
|
||||
|
||||
use function time;
|
||||
|
||||
abstract class Test{
|
||||
const RESULT_WAITING = -1;
|
||||
const RESULT_OK = 0;
|
||||
const RESULT_FAILED = 1;
|
||||
const RESULT_ERROR = 2;
|
||||
|
||||
/** @var Main */
|
||||
private $plugin;
|
||||
/** @var int */
|
||||
private $result = Test::RESULT_WAITING;
|
||||
/** @var int */
|
||||
private $startTime;
|
||||
/** @var int */
|
||||
private $timeout = 60; //seconds
|
||||
|
||||
public function __construct(Main $plugin){
|
||||
@ -42,7 +48,7 @@ abstract class Test{
|
||||
return $this->plugin;
|
||||
}
|
||||
|
||||
final public function start(){
|
||||
final public function start() : void{
|
||||
$this->startTime = time();
|
||||
try{
|
||||
$this->run();
|
||||
@ -55,11 +61,11 @@ abstract class Test{
|
||||
}
|
||||
}
|
||||
|
||||
public function tick(){
|
||||
public function tick() : void{
|
||||
|
||||
}
|
||||
|
||||
abstract public function run();
|
||||
abstract public function run() : void;
|
||||
|
||||
public function isFinished() : bool{
|
||||
return $this->result !== Test::RESULT_WAITING;
|
||||
@ -69,7 +75,7 @@ abstract class Test{
|
||||
return !$this->isFinished() and time() - $this->timeout > $this->startTime;
|
||||
}
|
||||
|
||||
protected function setTimeout(int $timeout){
|
||||
protected function setTimeout(int $timeout) : void{
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
@ -77,7 +83,7 @@ abstract class Test{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function setResult(int $result){
|
||||
public function setResult(int $result) : void{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,16 @@ namespace pmmp\TesterPlugin\tests;
|
||||
use pmmp\TesterPlugin\Test;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function ob_end_flush;
|
||||
use function ob_get_contents;
|
||||
use function ob_start;
|
||||
use function strpos;
|
||||
|
||||
class AsyncTaskMainLoggerTest extends Test{
|
||||
|
||||
public function run(){
|
||||
public function run() : void{
|
||||
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
|
||||
|
||||
/** @var bool */
|
||||
@ -43,7 +48,9 @@ class AsyncTaskMainLoggerTest extends Test{
|
||||
public function onRun(){
|
||||
ob_start();
|
||||
MainLogger::getLogger()->info("Testing");
|
||||
if(strpos(ob_get_contents(), "Testing") !== false){
|
||||
$contents = ob_get_contents();
|
||||
if($contents === false) throw new AssumptionFailedError("ob_get_contents() should not return false here");
|
||||
if(strpos($contents, "Testing") !== false){
|
||||
$this->success = true;
|
||||
}
|
||||
ob_end_flush();
|
||||
@ -65,5 +72,4 @@ class AsyncTaskMainLoggerTest extends Test{
|
||||
return "Verifies that the MainLogger is accessible by MainLogger::getLogger() in an AsyncTask";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -25,14 +25,15 @@ namespace pmmp\TesterPlugin\tests;
|
||||
|
||||
use pmmp\TesterPlugin\Test;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use function usleep;
|
||||
|
||||
class AsyncTaskMemoryLeakTest extends Test{
|
||||
|
||||
public function run(){
|
||||
public function run() : void{
|
||||
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new TestAsyncTask());
|
||||
}
|
||||
|
||||
public function tick(){
|
||||
public function tick() : void{
|
||||
if(TestAsyncTask::$destroyed === true){
|
||||
$this->setResult(Test::RESULT_OK);
|
||||
}
|
||||
@ -48,6 +49,7 @@ class AsyncTaskMemoryLeakTest extends Test{
|
||||
}
|
||||
|
||||
class TestAsyncTask extends AsyncTask{
|
||||
/** @var bool */
|
||||
public static $destroyed = false;
|
||||
|
||||
public function onRun(){
|
||||
|
@ -41,6 +41,7 @@ class AsyncTaskPublishProgressRaceTest extends Test{
|
||||
//this test is racy, but it should fail often enough to be a pest if something is broken
|
||||
|
||||
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
|
||||
/** @var bool */
|
||||
private static $success = false;
|
||||
|
||||
public function __construct(AsyncTaskPublishProgressRaceTest $t){
|
||||
|
18
tests/travis/setup-php.yml
Normal file
18
tests/travis/setup-php.yml
Normal file
@ -0,0 +1,18 @@
|
||||
language: php
|
||||
php:
|
||||
- 7.3
|
||||
- 7.4
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.1.0
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout b81ab29df58fa0fb239a9d5ca1c2380a0d087feb
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
- make install
|
||||
- cd ..
|
||||
- echo "extension=pthreads.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- composer self-update --2
|
Reference in New Issue
Block a user