mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
f2df702c67 | |||
481270e6aa | |||
a897bdfaa0 | |||
09668a37d6 | |||
ea92a23d0d | |||
691e67018d | |||
fe2140a716 | |||
57330a7186 | |||
9ddac21de0 | |||
c91aa24daa | |||
6186fc0bfe | |||
ef40934d24 | |||
69b668355f | |||
0547383296 | |||
c7dff9ea40 | |||
043350753b | |||
5ad8016b99 | |||
2e5b2eed6e | |||
5a0cde49cc | |||
008a022ec1 | |||
5c85a7c306 | |||
599c4284f5 | |||
9499e2e595 | |||
a4fea1444a | |||
1ba47802a8 | |||
9d111e13f1 | |||
44bc4d8c7c | |||
d317347a9b | |||
077fac84bf | |||
fdb3a5b121 | |||
e3bc36ab5b | |||
283ff28aa9 | |||
bb60a9057f | |||
02cf5ed388 | |||
6cad559dbe | |||
84a943bcec | |||
73bf5d4b29 | |||
eb136e60c8 | |||
4228880509 | |||
709d874204 | |||
07dc10d6e6 | |||
7f6269c432 | |||
194714b448 | |||
4def4d52d9 | |||
9bfcd39f2a | |||
8102616ff4 | |||
eb130f2906 | |||
eb4679fefd | |||
71b78b02d3 | |||
84cb070d56 | |||
8102586ee0 | |||
0336394098 | |||
1569bed37a | |||
a6e79c5004 |
4
.github/workflows/discord-release-notify.yml
vendored
4
.github/workflows/discord-release-notify.yml
vendored
@ -13,9 +13,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
uses: shivammathur/setup-php@2.25.2
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-version: 8.1
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
|
4
.github/workflows/draft-release.yml
vendored
4
.github/workflows/draft-release.yml
vendored
@ -18,9 +18,9 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
uses: shivammathur/setup-php@2.25.2
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-version: 8.1
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
|
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
php: [8.1.19, 8.2.6]
|
||||
|
||||
steps:
|
||||
- name: Build and prepare PHP cache
|
||||
@ -32,7 +32,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
php: [8.1.19, 8.2.6]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -71,7 +71,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
php: [8.1.19, 8.2.6]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -110,7 +110,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
php: [8.1.19, 8.2.6]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -151,7 +151,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.0.28, 8.1.18, 8.2.5]
|
||||
php: [8.1.19, 8.2.6]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -203,9 +203,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
uses: shivammathur/setup-php@2.25.2
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-version: 8.1
|
||||
tools: php-cs-fixer:3.16
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
1
.github/workflows/update-php-versions.php
vendored
1
.github/workflows/update-php-versions.php
vendored
@ -22,7 +22,6 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const VERSIONS = [
|
||||
"8.0",
|
||||
"8.1",
|
||||
"8.2"
|
||||
];
|
||||
|
10
BUILDING.md
10
BUILDING.md
@ -2,13 +2,13 @@
|
||||
## Pre-requisites
|
||||
- A bash shell (git bash is sufficient for Windows)
|
||||
- [`git`](https://git-scm.com) available in your shell
|
||||
- PHP 8.0 or newer available in your shell
|
||||
- PHP 8.1 or newer available in your shell
|
||||
- [`composer`](https://getcomposer.org) available in your shell
|
||||
|
||||
## Custom PHP binaries
|
||||
Because PocketMine-MP requires several non-standard PHP extensions and configuration, PMMP provides scripts to build custom binaries for running PocketMine-MP, as well as prebuilt binaries.
|
||||
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-8.0-Aggregate)
|
||||
- [Prebuilt binaries](https://github.com/pmmp/PHP-Binaries/releases)
|
||||
- [Compile scripts](https://github.com/pmmp/php-build-scripts) are provided as a submodule in the path `build/php`
|
||||
|
||||
If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`.
|
||||
@ -29,11 +29,5 @@ Run `composer make-server` using your preferred PHP binary. It'll drop a `Pocket
|
||||
|
||||
You can also use the `--out` option to change the output filename.
|
||||
|
||||
There is a bug in PHP that might cause an error which looks like this:
|
||||
```
|
||||
Fatal error: Uncaught BadMethodCallException: unable to create temporary file in PocketMine-MP/build/server-phar.php:119
|
||||
```
|
||||
You can work around it by setting `ulimit -n` to some bigger number, e.g. `8192`, or by updating your PHP version to at least 8.0.3.
|
||||
|
||||
## Running PocketMine-MP from source code
|
||||
Run `src/PocketMine.php` using your preferred PHP binary.
|
||||
|
@ -18,6 +18,30 @@ Larger contributions like feature additions should be preceded by a [Change Prop
|
||||
## Other things you'll need
|
||||
- [git](https://git-scm.com/)
|
||||
|
||||
## List of `pocketmine` namespaces which are in other repos
|
||||
PocketMine-MP has several dependencies which are independent from the main server code. Most of them use the `pocketmine` namespace.
|
||||
Some of these add extra classes to packages which already exist in PocketMine-MP.
|
||||
|
||||
Take a look at the table below if you can't find the class or function you're looking for.
|
||||
|
||||
| Source URL | Namespace, class or function |
|
||||
|:----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [pmmp/BedrockProtocol](https://github.com/pmmp/BedrockProtocol) | `pocketmine\network\mcpe\protocol` |
|
||||
| [pmmp/BinaryUtils](https://github.com/pmmp/BinaryUtils) | `pocketmine\utils\BinaryDataException`</br>`pocketmine\utils\BinaryStream`</br>`pocketmine\utils\Binary` |
|
||||
| [pmmp/ClassLoader](https://github.com/pmmp/`ClassLoader`) | `BaseClassLoader`</br>`ClassLoader`</br>`DynamicClassLoader` |
|
||||
| [pmmp/Color](https://github.com/pmmp/Color) | `pocketmine\color` |
|
||||
| [pmmp/ErrorHandler](https://github.com/pmmp/ErrorHandler) | `pocketmine\errorhandler` |
|
||||
| [pmmp/LogPthreads](https://github.com/pmmp/LogPthreads) | `ThreadedLoggerAttachment`</br>`ThreadedLogger`</br>`AttachableThreadedLogger` |
|
||||
| [pmmp/Log](https://github.com/pmmp/Log) | `AttachableLogger`</br>`BufferedLogger`</br>`GlobalLogger`</br>`LogLevel`</br>`Logger`</br>`PrefixedLogger`</br>`SimpleLogger` |
|
||||
| [pmmp/Math](https://github.com/pmmp/Math) | `pocketmine\math` |
|
||||
| [pmmp/NBT](https://github.com/pmmp/NBT) | `pocketmine\nbt` |
|
||||
| [pmmp/RakLibIpc](https://github.com/pmmp/RakLibIpc) | `raklib\server\ipc` |
|
||||
| [pmmp/RakLib](https://github.com/pmmp/RakLib) | `raklib` |
|
||||
| [pmmp/Snooze](https://github.com/pmmp/Snooze) | `pocketmine\snooze` |
|
||||
| [pmmp/ext-chunkutils2](https://github.com/pmmp/ext-chunkutils2) | `pocketmine\world\format\LightArray`</br>`pocketmine\world\format\PalettedBlockArray`</br>`pocketmine\world\format\io\SubChunkConverter` |
|
||||
| [pmmp/ext-morton](https://github.com/pmmp/ext-morton) | `morton2d_decode`</br>`morton2d_encode`</br>`morton3d_decode`</br>`morton3d_encode` |
|
||||
| [pmmp/ext-libdeflate](https://github.com/pmmp/ext-libdeflate) | `libdeflate_deflate_compress`</br>`libdeflate_gzip_compress`</br>`libdeflate_zlib_compress` |
|
||||
|
||||
## Choosing a target branch
|
||||
PocketMine-MP has three primary branches of development.
|
||||
|
||||
|
Submodule build/php updated: 9d8807be82...f2ece7b30d
77
changelogs/4.21.md
Normal file
77
changelogs/4.21.md
Normal file
@ -0,0 +1,77 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.80**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the `pocketmine\network\mcpe` namespace, and don't use reflection or any internal methods,
|
||||
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 `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
# 4.21.0
|
||||
Released 17th May 2023.
|
||||
|
||||
## General
|
||||
- PHP 8.1 is now required. Most plugins should run without changes, but some might need to be updated due to language-level deprecations.
|
||||
- Ticking chunk count is now shown separately from loaded chunk count in the `/status` command, providing useful performance information.
|
||||
- Further improved performance of ticking chunk selection.
|
||||
- Improved performance of some inventory functions.
|
||||
- Reduced server memory footprint in most cases by ~9 MB per thread.
|
||||
- Due to large overhead, async network compression is now only used for packets larger than 10 KB by default.
|
||||
|
||||
## Configuration
|
||||
- Added the following new `pocketmine.yml` configuration options:
|
||||
- `network.async-compression-threshold` - minimum size of packet which will be compressed using `AsyncTask`
|
||||
- Default is 10 KB, which means that very few packets will use async compression in practice. This is because the overhead of compressing async is currently so high that it's not worth it for smaller packets.
|
||||
|
||||
## Timings
|
||||
- Timings reports no longer include the unused metadata fields `Entities` and `LivingEntities`.
|
||||
- Timings reports now correctly calculate the peak time of a timer.
|
||||
- Previously it was incorrectly recorded as the longest time spent in a single tick, rather than the longest time spent in a single activation.
|
||||
- Timings report version has been bumped to `2` to reflect this change.
|
||||
- All world-specific timers now have generic aggregate timings, making it much easier to locate performance patterns across all worlds.
|
||||
|
||||
## Gameplay
|
||||
- Players in spectator mode are no longer able to pick blocks, and now have finite resources similar to survival mode.
|
||||
|
||||
## API
|
||||
### `pocketmine\world`
|
||||
- The following API methods have been added:
|
||||
- `public World->getTickingChunks() : list<int>` - returns a list of chunk position hashes (a la `World::chunkHash()`) which are currently valid for random ticking
|
||||
|
||||
### `pocketmine\inventory`
|
||||
- The following API methods have been added:
|
||||
- `protected BaseInventory->getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int` - returns the number of items in the given stack if the content of the slot matches the test item, or zero otherwise
|
||||
- This should be overridden if directly extending `BaseInventory` to provide a performance-optimised version. A slow default implementation is provided, but it will be removed in the future.
|
||||
|
||||
## Internals
|
||||
### Entity
|
||||
- Unused `NameTag` tag is no longer saved for `Human` entities.
|
||||
|
||||
### Inventory
|
||||
- `BaseInventory` now uses a new internal method `getMatchingItemCount()` to locate items in the inventory without useless cloning. This improves performance of various API methods, such as `addItem()`, `contains()`, and more.
|
||||
- Specialization of `Inventory->isSlotEmpty()` in `BaseInventory` subclasses has been added to improve performance of some API methods.
|
||||
|
||||
### Network
|
||||
- `RuntimeBlockMapping` no longer keeps all block palette NBT data in memory.
|
||||
- This significantly reduces server idle memory footprint.
|
||||
- For multi-version implementations, this will have a significant impact on memory usage, since a different block palette is often required to support each version.
|
||||
- NBT will be lazy-loaded into memory and cached if `getBedrockKnownStates()` is called. However, this is not used by PocketMine-MP under normal circumstances.
|
||||
- Removed unnecessary usage of `Utils::validateCallableSignature()` from some internal network pathways, improving performance.
|
||||
|
||||
### Scheduler
|
||||
- `AsyncPool` no longer double-checks progress updates on completed tasks.
|
||||
|
||||
### World
|
||||
- Ticking chunks are now tracked in `World->validTickingChunks` and `World->recheckTickingChunks`.
|
||||
- This allows avoiding rechecking every ticking chunk for validity during ticking chunk selection, improving performance.
|
||||
- In some cases, this allows bypassing chunk selection entirely, reducing selection cost to zero.
|
||||
- Registered but ineligible ticking chunks are no longer rechecked every tick.
|
||||
- This was causing wasted cycles during async worker backlog.
|
||||
- The internal system must call `markTickingChunkForRecheck()` whenever a ticking chunk's eligibility for ticking has potentially changed, rather than just when it has changed from a yes to a no.
|
||||
|
||||
# 4.21.1
|
||||
Released 30th May 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash due to a bug in upstream dependency [`netresearch/jsonmapper`](https://github.com/cweiske/JsonMapper).
|
@ -5,7 +5,7 @@
|
||||
"homepage": "https://pmmp.io",
|
||||
"license": "LGPL-3.0",
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php": "^8.1",
|
||||
"php-64bit": "*",
|
||||
"ext-chunkutils2": "^0.3.1",
|
||||
"ext-crypto": "^0.3.1",
|
||||
@ -56,10 +56,10 @@
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.14",
|
||||
"phpstan/phpstan": "1.10.15",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
"phpunit/phpunit": "^10.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -77,7 +77,7 @@
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "8.0.0"
|
||||
"php": "8.1.0"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
|
702
composer.lock
generated
702
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -85,8 +85,11 @@ network:
|
||||
batch-threshold: 256
|
||||
#Compression level used when sending batched packets. Higher = more CPU, less bandwidth usage
|
||||
compression-level: 6
|
||||
#Use AsyncTasks for compression. Adds half/one tick delay, less CPU load on main thread
|
||||
#Use AsyncTasks for compression during the main game session. Increases latency, but may reduce main thread load
|
||||
async-compression: false
|
||||
#Threshold for async compression, in bytes. Only packets larger than this will be compressed asynchronously
|
||||
#Due to large overhead of AsyncTask, async compression isn't worth it except for large packets
|
||||
async-compression-threshold: 10000
|
||||
#Experimental. Use UPnP to automatically port forward
|
||||
upnp-forwarding: false
|
||||
#Maximum size in bytes of packets sent over the network (default 1492 bytes). Packets larger than this will be
|
||||
|
@ -50,7 +50,7 @@ namespace pocketmine {
|
||||
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
|
||||
const MIN_PHP_VERSION = "8.0.0";
|
||||
const MIN_PHP_VERSION = "8.1.0";
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
@ -265,9 +265,6 @@ JIT_WARNING
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if(extension_loaded('parallel')){
|
||||
\parallel\bootstrap(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}
|
||||
|
||||
ErrorToExceptionHandler::set();
|
||||
|
||||
|
@ -208,6 +208,8 @@ class Server{
|
||||
private const TICKS_PER_TPS_OVERLOAD_WARNING = 5 * self::TARGET_TICKS_PER_SECOND;
|
||||
private const TICKS_PER_STATS_REPORT = 300 * self::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
private const DEFAULT_ASYNC_COMPRESSION_THRESHOLD = 10_000;
|
||||
|
||||
private static ?Server $instance = null;
|
||||
|
||||
private TimeTrackingSleeperHandler $tickSleeper;
|
||||
@ -266,6 +268,7 @@ class Server{
|
||||
|
||||
private Network $network;
|
||||
private bool $networkCompressionAsync = true;
|
||||
private int $networkCompressionAsyncThreshold = self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD;
|
||||
|
||||
private Language $language;
|
||||
private bool $forceLanguage = false;
|
||||
@ -908,6 +911,10 @@ class Server{
|
||||
ZlibCompressor::setInstance(new ZlibCompressor($netCompressionLevel, $netCompressionThreshold, ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE));
|
||||
|
||||
$this->networkCompressionAsync = $this->configGroup->getPropertyBool("network.async-compression", true);
|
||||
$this->networkCompressionAsyncThreshold = max(
|
||||
$this->configGroup->getPropertyInt("network.async-compression-threshold", self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD),
|
||||
$netCompressionThreshold ?? self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD
|
||||
);
|
||||
|
||||
EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool("network.enable-encryption", true);
|
||||
|
||||
@ -1375,7 +1382,7 @@ class Server{
|
||||
}
|
||||
|
||||
$promise = new CompressBatchPromise();
|
||||
if(!$sync){
|
||||
if(!$sync && strlen($buffer) >= $this->networkCompressionAsyncThreshold){
|
||||
$task = new CompressBatchTask($buffer, $promise, $compressor);
|
||||
$this->asyncPool->submitTask($task);
|
||||
}else{
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.20.5";
|
||||
public const BASE_VERSION = "4.21.1";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
|
@ -85,6 +85,20 @@ class DoubleChestInventory extends BaseInventory implements BlockInventory, Inve
|
||||
$this->right->setContents($rightContents);
|
||||
}
|
||||
|
||||
protected function getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int{
|
||||
$leftSize = $this->left->getSize();
|
||||
return $slot < $leftSize ?
|
||||
$this->left->getMatchingItemCount($slot, $test, $checkDamage, $checkTags) :
|
||||
$this->right->getMatchingItemCount($slot - $leftSize, $test, $checkDamage, $checkTags);
|
||||
}
|
||||
|
||||
public function isSlotEmpty(int $index) : bool{
|
||||
$leftSize = $this->left->getSize();
|
||||
return $index < $leftSize ?
|
||||
$this->left->isSlotEmpty($index) :
|
||||
$this->right->isSlotEmpty($index - $leftSize);
|
||||
}
|
||||
|
||||
protected function getOpenSound() : Sound{ return new ChestOpenSound(); }
|
||||
|
||||
protected function getCloseSound() : Sound{ return new ChestCloseSound(); }
|
||||
|
@ -110,7 +110,8 @@ class StatusCommand extends VanillaCommand{
|
||||
$worldName = $world->getFolderName() !== $world->getDisplayName() ? " (" . $world->getDisplayName() . ")" : "";
|
||||
$timeColor = $world->getTickRateTime() > 40 ? TextFormat::RED : TextFormat::YELLOW;
|
||||
$sender->sendMessage(TextFormat::GOLD . "World \"{$world->getFolderName()}\"$worldName: " .
|
||||
TextFormat::RED . number_format(count($world->getLoadedChunks())) . TextFormat::GREEN . " chunks, " .
|
||||
TextFormat::RED . number_format(count($world->getLoadedChunks())) . TextFormat::GREEN . " loaded chunks, " .
|
||||
TextFormat::RED . number_format(count($world->getTickingChunks())) . TextFormat::GREEN . " ticking chunks, " .
|
||||
TextFormat::RED . number_format(count($world->getEntities())) . TextFormat::GREEN . " entities. " .
|
||||
"Time $timeColor" . round($world->getTickRateTime(), 2) . "ms"
|
||||
);
|
||||
|
@ -92,7 +92,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
private const TAG_XP_PROGRESS = "XpP"; //TAG_Float
|
||||
private const TAG_LIFETIME_XP_TOTAL = "XpTotal"; //TAG_Int
|
||||
private const TAG_XP_SEED = "XpSeed"; //TAG_Int
|
||||
private const TAG_NAME_TAG = "NameTag"; //TAG_String
|
||||
private const TAG_SKIN = "Skin"; //TAG_Compound
|
||||
private const TAG_SKIN_NAME = "Name"; //TAG_String
|
||||
private const TAG_SKIN_DATA = "Data"; //TAG_ByteArray
|
||||
@ -245,10 +244,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
|
||||
*/
|
||||
protected function initHumanData(CompoundTag $nbt) : void{
|
||||
if(($nameTagTag = $nbt->getTag(self::TAG_NAME_TAG)) instanceof StringTag){
|
||||
$this->setNameTag($nameTagTag->getValue());
|
||||
}
|
||||
|
||||
//TODO: use of NIL UUID for namespace is a hack; we should provide a proper UUID for the namespace
|
||||
$this->uuid = Uuid::uuid3(Uuid::NIL, ((string) $this->getId()) . $this->skin->getSkinData() . $this->getNameTag());
|
||||
}
|
||||
|
@ -108,13 +108,23 @@ abstract class BaseInventory implements Inventory{
|
||||
$this->onContentChange($oldContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for utility functions which search the inventory.
|
||||
* TODO: make this abstract instead of providing a slow default implementation (BC break)
|
||||
*/
|
||||
protected function getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int{
|
||||
$item = $this->getItem($slot);
|
||||
return $item->equals($test, $checkDamage, $checkTags) ? $item->getCount() : 0;
|
||||
}
|
||||
|
||||
public function contains(Item $item) : bool{
|
||||
$count = max(1, $item->getCount());
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
$checkTags = $item->hasNamedTag();
|
||||
foreach($this->getContents() as $i){
|
||||
if($item->equals($i, $checkDamage, $checkTags)){
|
||||
$count -= $i->getCount();
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
|
||||
$slotCount = $this->getMatchingItemCount($i, $item, $checkDamage, $checkTags);
|
||||
if($slotCount > 0){
|
||||
$count -= $slotCount;
|
||||
if($count <= 0){
|
||||
return true;
|
||||
}
|
||||
@ -128,9 +138,9 @@ abstract class BaseInventory implements Inventory{
|
||||
$slots = [];
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
$checkTags = $item->hasNamedTag();
|
||||
foreach($this->getContents() as $index => $i){
|
||||
if($item->equals($i, $checkDamage, $checkTags)){
|
||||
$slots[$index] = $i;
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
|
||||
if($this->getMatchingItemCount($i, $item, $checkDamage, $checkTags) > 0){
|
||||
$slots[$i] = $this->getItem($i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,18 +152,9 @@ abstract class BaseInventory implements Inventory{
|
||||
$checkDamage = $exact || !$item->hasAnyDamageValue();
|
||||
$checkTags = $exact || $item->hasNamedTag();
|
||||
|
||||
foreach($this->getContents() as $index => $i){
|
||||
if($item->equals($i, $checkDamage, $checkTags) && ($i->getCount() === $count || (!$exact && $i->getCount() > $count))){
|
||||
return $index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function firstEmpty() : int{
|
||||
foreach($this->getContents(true) as $i => $slot){
|
||||
if($slot->isNull()){
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
|
||||
$slotCount = $this->getMatchingItemCount($i, $item, $checkDamage, $checkTags);
|
||||
if($slotCount > 0 && ($slotCount === $count || (!$exact && $slotCount > $count))){
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
@ -161,6 +162,20 @@ abstract class BaseInventory implements Inventory{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function firstEmpty() : int{
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
|
||||
if($this->isSlotEmpty($i)){
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: make this abstract and force implementations to implement it properly (BC break)
|
||||
* This default implementation works, but is slow.
|
||||
*/
|
||||
public function isSlotEmpty(int $index) : bool{
|
||||
return $this->getItem($index)->isNull();
|
||||
}
|
||||
@ -171,14 +186,16 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
public function getAddableItemQuantity(Item $item) : int{
|
||||
$count = $item->getCount();
|
||||
$maxStackSize = min($this->getMaxStackSize(), $item->getMaxStackSize());
|
||||
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$slot = $this->getItem($i);
|
||||
if($item->canStackWith($slot)){
|
||||
if(($diff = min($slot->getMaxStackSize(), $item->getMaxStackSize()) - $slot->getCount()) > 0){
|
||||
if($this->isSlotEmpty($i)){
|
||||
$count -= $maxStackSize;
|
||||
}else{
|
||||
$slotCount = $this->getMatchingItemCount($i, $item, true, true);
|
||||
if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
|
||||
$count -= $diff;
|
||||
}
|
||||
}elseif($slot->isNull()){
|
||||
$count -= min($this->getMaxStackSize(), $item->getMaxStackSize());
|
||||
}
|
||||
|
||||
if($count <= 0){
|
||||
@ -212,23 +229,29 @@ abstract class BaseInventory implements Inventory{
|
||||
return $returnSlots;
|
||||
}
|
||||
|
||||
private function internalAddItem(Item $slot) : Item{
|
||||
private function internalAddItem(Item $newItem) : Item{
|
||||
$emptySlots = [];
|
||||
|
||||
$maxStackSize = min($this->getMaxStackSize(), $newItem->getMaxStackSize());
|
||||
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$item = $this->getItem($i);
|
||||
if($item->isNull()){
|
||||
if($this->isSlotEmpty($i)){
|
||||
$emptySlots[] = $i;
|
||||
continue;
|
||||
}
|
||||
$slotCount = $this->getMatchingItemCount($i, $newItem, true, true);
|
||||
if($slotCount === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($slot->canStackWith($item) && $item->getCount() < $item->getMaxStackSize()){
|
||||
$amount = min($item->getMaxStackSize() - $item->getCount(), $slot->getCount(), $this->getMaxStackSize());
|
||||
if($slotCount < $maxStackSize){
|
||||
$amount = min($maxStackSize - $slotCount, $newItem->getCount());
|
||||
if($amount > 0){
|
||||
$slot->setCount($slot->getCount() - $amount);
|
||||
$item->setCount($item->getCount() + $amount);
|
||||
$this->setItem($i, $item);
|
||||
if($slot->getCount() <= 0){
|
||||
$newItem->setCount($newItem->getCount() - $amount);
|
||||
$slotItem = $this->getItem($i);
|
||||
$slotItem->setCount($slotItem->getCount() + $amount);
|
||||
$this->setItem($i, $slotItem);
|
||||
if($newItem->getCount() <= 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -237,65 +260,67 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
if(count($emptySlots) > 0){
|
||||
foreach($emptySlots as $slotIndex){
|
||||
$amount = min($slot->getMaxStackSize(), $slot->getCount(), $this->getMaxStackSize());
|
||||
$slot->setCount($slot->getCount() - $amount);
|
||||
$item = clone $slot;
|
||||
$item->setCount($amount);
|
||||
$this->setItem($slotIndex, $item);
|
||||
if($slot->getCount() <= 0){
|
||||
$amount = min($maxStackSize, $newItem->getCount());
|
||||
$newItem->setCount($newItem->getCount() - $amount);
|
||||
$slotItem = clone $newItem;
|
||||
$slotItem->setCount($amount);
|
||||
$this->setItem($slotIndex, $slotItem);
|
||||
if($newItem->getCount() <= 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $slot;
|
||||
return $newItem;
|
||||
}
|
||||
|
||||
public function remove(Item $item) : void{
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
$checkTags = $item->hasNamedTag();
|
||||
|
||||
foreach($this->getContents() as $index => $i){
|
||||
if($item->equals($i, $checkDamage, $checkTags)){
|
||||
$this->clear($index);
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
|
||||
if($this->getMatchingItemCount($i, $item, $checkDamage, $checkTags) > 0){
|
||||
$this->clear($i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function removeItem(Item ...$slots) : array{
|
||||
/** @var Item[] $itemSlots */
|
||||
/** @var Item[] $searchItems */
|
||||
/** @var Item[] $slots */
|
||||
$itemSlots = [];
|
||||
$searchItems = [];
|
||||
foreach($slots as $slot){
|
||||
if(!$slot->isNull()){
|
||||
$itemSlots[] = clone $slot;
|
||||
$searchItems[] = clone $slot;
|
||||
}
|
||||
}
|
||||
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$item = $this->getItem($i);
|
||||
if($item->isNull()){
|
||||
if($this->isSlotEmpty($i)){
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach($itemSlots as $index => $slot){
|
||||
if($slot->equals($item, !$slot->hasAnyDamageValue(), $slot->hasNamedTag())){
|
||||
$amount = min($item->getCount(), $slot->getCount());
|
||||
$slot->setCount($slot->getCount() - $amount);
|
||||
$item->setCount($item->getCount() - $amount);
|
||||
$this->setItem($i, $item);
|
||||
if($slot->getCount() <= 0){
|
||||
unset($itemSlots[$index]);
|
||||
foreach($searchItems as $index => $search){
|
||||
$slotCount = $this->getMatchingItemCount($i, $search, !$search->hasAnyDamageValue(), $search->hasNamedTag());
|
||||
if($slotCount > 0){
|
||||
$amount = min($slotCount, $search->getCount());
|
||||
$search->setCount($search->getCount() - $amount);
|
||||
|
||||
$slotItem = $this->getItem($i);
|
||||
$slotItem->setCount($slotItem->getCount() - $amount);
|
||||
$this->setItem($i, $slotItem);
|
||||
if($search->getCount() <= 0){
|
||||
unset($searchItems[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count($itemSlots) === 0){
|
||||
if(count($searchItems) === 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $itemSlots;
|
||||
return $searchItems;
|
||||
}
|
||||
|
||||
public function clear(int $index) : void{
|
||||
|
@ -85,6 +85,10 @@ class DelegateInventory extends BaseInventory{
|
||||
$this->backingInventory->setContents($items);
|
||||
}
|
||||
|
||||
public function isSlotEmpty(int $index) : bool{
|
||||
return $this->backingInventory->isSlotEmpty($index);
|
||||
}
|
||||
|
||||
protected function onSlotChange(int $index, Item $before) : void{
|
||||
if($this->backingInventoryChanging){
|
||||
parent::onSlotChange($index, $before);
|
||||
|
@ -83,4 +83,13 @@ class SimpleInventory extends BaseInventory{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int{
|
||||
$slotItem = $this->slots[$slot];
|
||||
return $slotItem !== null && $slotItem->equals($test, $checkDamage, $checkTags) ? $slotItem->getCount() : 0;
|
||||
}
|
||||
|
||||
public function isSlotEmpty(int $index) : bool{
|
||||
return $this->slots[$index] === null || $this->slots[$index]->isNull();
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,6 @@ use pocketmine\utils\BinaryDataException;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\Position;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
@ -1000,8 +999,6 @@ class NetworkSession{
|
||||
* @phpstan-param \Closure() : void $onCompletion
|
||||
*/
|
||||
public function startUsingChunk(int $chunkX, int $chunkZ, \Closure $onCompletion) : void{
|
||||
Utils::validateCallableSignature(function() : void{}, $onCompletion);
|
||||
|
||||
$world = $this->player->getLocation()->getWorld();
|
||||
ChunkCache::getInstance($world, $this->compressor)->request($chunkX, $chunkZ)->onResolve(
|
||||
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\compression;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_push;
|
||||
|
||||
class CompressBatchPromise{
|
||||
@ -42,9 +41,6 @@ class CompressBatchPromise{
|
||||
*/
|
||||
public function onResolve(\Closure ...$callbacks) : void{
|
||||
$this->checkCancelled();
|
||||
foreach($callbacks as $callback){
|
||||
Utils::validateCallableSignature(function(CompressBatchPromise $promise) : void{}, $callback);
|
||||
}
|
||||
if($this->result !== null){
|
||||
foreach($callbacks as $callback){
|
||||
$callback($this);
|
||||
|
@ -48,8 +48,8 @@ final class RuntimeBlockMapping{
|
||||
private array $legacyToRuntimeMap = [];
|
||||
/** @var int[] */
|
||||
private array $runtimeToLegacyMap = [];
|
||||
/** @var CompoundTag[] */
|
||||
private array $bedrockKnownStates;
|
||||
/** @var CompoundTag[]|null */
|
||||
private ?array $bedrockKnownStates = null;
|
||||
|
||||
private static function make() : self{
|
||||
return new self(
|
||||
@ -85,25 +85,13 @@ final class RuntimeBlockMapping{
|
||||
return $newTag;
|
||||
}
|
||||
|
||||
public function __construct(string $canonicalBlockStatesFile, string $r12ToCurrentBlockMapFile){
|
||||
$stream = new BinaryStream(Filesystem::fileGetContents($canonicalBlockStatesFile));
|
||||
$list = [];
|
||||
$nbtReader = new NetworkNbtSerializer();
|
||||
public function __construct(
|
||||
private string $canonicalBlockStatesFile,
|
||||
string $r12ToCurrentBlockMapFile
|
||||
){
|
||||
//do not cache this - we only need it to set up mappings under normal circumstances
|
||||
$bedrockKnownStates = $this->loadBedrockKnownStates();
|
||||
|
||||
$keyIndex = [];
|
||||
$valueIndex = [];
|
||||
while(!$stream->feof()){
|
||||
$offset = $stream->getOffset();
|
||||
$blockState = $nbtReader->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$stream->setOffset($offset);
|
||||
$list[] = self::deduplicateCompound($blockState, $keyIndex, $valueIndex);
|
||||
}
|
||||
$this->bedrockKnownStates = $list;
|
||||
|
||||
$this->setupLegacyMappings($r12ToCurrentBlockMapFile);
|
||||
}
|
||||
|
||||
private function setupLegacyMappings(string $r12ToCurrentBlockMapFile) : void{
|
||||
$legacyIdMap = LegacyBlockIdToStringIdMap::getInstance();
|
||||
/** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */
|
||||
$legacyStateMap = [];
|
||||
@ -123,7 +111,7 @@ final class RuntimeBlockMapping{
|
||||
* @var int[][] $idToStatesMap string id -> int[] list of candidate state indices
|
||||
*/
|
||||
$idToStatesMap = [];
|
||||
foreach($this->bedrockKnownStates as $k => $state){
|
||||
foreach($bedrockKnownStates as $k => $state){
|
||||
$idToStatesMap[$state->getString("name")][] = $k;
|
||||
}
|
||||
foreach($legacyStateMap as $pair){
|
||||
@ -142,7 +130,7 @@ final class RuntimeBlockMapping{
|
||||
throw new \RuntimeException("Mapped new state does not appear in network table");
|
||||
}
|
||||
foreach($idToStatesMap[$mappedName] as $k){
|
||||
$networkState = $this->bedrockKnownStates[$k];
|
||||
$networkState = $bedrockKnownStates[$k];
|
||||
if($mappedState->equals($networkState)){
|
||||
$this->registerMapping($k, $id, $data);
|
||||
continue 2;
|
||||
@ -152,6 +140,25 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
private function loadBedrockKnownStates() : array{
|
||||
$stream = new BinaryStream(Filesystem::fileGetContents($this->canonicalBlockStatesFile));
|
||||
$list = [];
|
||||
$nbtReader = new NetworkNbtSerializer();
|
||||
|
||||
$keyIndex = [];
|
||||
$valueIndex = [];
|
||||
while(!$stream->feof()){
|
||||
$offset = $stream->getOffset();
|
||||
$blockState = $nbtReader->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$stream->setOffset($offset);
|
||||
$list[] = self::deduplicateCompound($blockState, $keyIndex, $valueIndex);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function toRuntimeId(int $internalStateId) : int{
|
||||
return $this->legacyToRuntimeMap[$internalStateId] ?? $this->legacyToRuntimeMap[BlockLegacyIds::INFO_UPDATE << Block::INTERNAL_METADATA_BITS];
|
||||
}
|
||||
@ -166,9 +173,14 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: This method may load the palette from disk, which is a slow operation.
|
||||
* Afterwards, it will cache the palette in memory, which requires (in some cases) tens of MB of memory.
|
||||
* Avoid using this where possible.
|
||||
*
|
||||
* @deprecated
|
||||
* @return CompoundTag[]
|
||||
*/
|
||||
public function getBedrockKnownStates() : array{
|
||||
return $this->bedrockKnownStates;
|
||||
return $this->bedrockKnownStates ??= $this->loadBedrockKnownStates();
|
||||
}
|
||||
}
|
||||
|
@ -1191,7 +1191,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
* TODO: make this a dynamic ability instead of being hardcoded
|
||||
*/
|
||||
public function hasFiniteResources() : bool{
|
||||
return $this->gamemode->equals(GameMode::SURVIVAL()) || $this->gamemode->equals(GameMode::ADVENTURE());
|
||||
return !$this->gamemode->equals(GameMode::CREATIVE());
|
||||
}
|
||||
|
||||
public function isFireProof() : bool{
|
||||
@ -1657,7 +1657,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
$ev = new PlayerBlockPickEvent($this, $block, $item);
|
||||
$existingSlot = $this->inventory->first($item);
|
||||
if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){
|
||||
if($existingSlot === -1 && $this->hasFiniteResources()){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
|
@ -247,7 +247,6 @@ class AsyncPool{
|
||||
while(!$queue->isEmpty()){
|
||||
/** @var AsyncTask $task */
|
||||
$task = $queue->bottom();
|
||||
$task->checkProgressUpdates();
|
||||
if($task->isFinished()){ //make sure the task actually executed before trying to collect
|
||||
$queue->dequeue();
|
||||
|
||||
@ -268,6 +267,7 @@ class AsyncPool{
|
||||
$task->onCompletion();
|
||||
}
|
||||
}else{
|
||||
$task->checkProgressUpdates();
|
||||
$more = true;
|
||||
break; //current task is still running, skip to next worker
|
||||
}
|
||||
|
@ -87,14 +87,6 @@ abstract class Timings{
|
||||
/** @var TimingsHandler */
|
||||
public static $serverCommand;
|
||||
/** @var TimingsHandler */
|
||||
public static $worldLoad;
|
||||
/** @var TimingsHandler */
|
||||
public static $worldSave;
|
||||
/** @var TimingsHandler */
|
||||
public static $population;
|
||||
/** @var TimingsHandler */
|
||||
public static $generationCallback;
|
||||
/** @var TimingsHandler */
|
||||
public static $permissibleCalculation;
|
||||
/** @var TimingsHandler */
|
||||
public static $permissibleCalculationDiff;
|
||||
@ -111,10 +103,6 @@ abstract class Timings{
|
||||
|
||||
/** @var TimingsHandler */
|
||||
public static $playerCheckNearEntities;
|
||||
/** @var TimingsHandler */
|
||||
public static $tickEntity;
|
||||
/** @var TimingsHandler */
|
||||
public static $tickTileEntity;
|
||||
|
||||
/** @var TimingsHandler */
|
||||
public static $entityBaseTick;
|
||||
@ -203,10 +191,6 @@ abstract class Timings{
|
||||
self::$playerChunkSend = new TimingsHandler("Player Network Send - Chunks", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$scheduler = new TimingsHandler("Scheduler");
|
||||
self::$serverCommand = new TimingsHandler("Server Command");
|
||||
self::$worldLoad = new TimingsHandler("World Load");
|
||||
self::$worldSave = new TimingsHandler("World Save");
|
||||
self::$population = new TimingsHandler("World Population");
|
||||
self::$generationCallback = new TimingsHandler("World Generation Callback");
|
||||
self::$permissibleCalculation = new TimingsHandler("Permissible Calculation");
|
||||
self::$permissibleCalculationDiff = new TimingsHandler("Permissible Calculation - Diff", self::$permissibleCalculation, group: self::GROUP_BREAKDOWN);
|
||||
self::$permissibleCalculationCallback = new TimingsHandler("Permissible Calculation - Callbacks", self::$permissibleCalculation, group: self::GROUP_BREAKDOWN);
|
||||
@ -221,9 +205,6 @@ abstract class Timings{
|
||||
self::$projectileMoveRayTrace = new TimingsHandler("Projectile Movement - Ray Tracing", self::$projectileMove, group: self::GROUP_BREAKDOWN);
|
||||
|
||||
self::$playerCheckNearEntities = new TimingsHandler("checkNearEntities", group: self::GROUP_BREAKDOWN);
|
||||
self::$tickEntity = new TimingsHandler("Entity Tick", group: self::GROUP_BREAKDOWN);
|
||||
self::$tickTileEntity = new TimingsHandler("Block Entity Tick", group: self::GROUP_BREAKDOWN);
|
||||
|
||||
self::$entityBaseTick = new TimingsHandler("Entity Base Tick", group: self::GROUP_BREAKDOWN);
|
||||
self::$livingEntityBaseTick = new TimingsHandler("Entity Base Tick - Living", group: self::GROUP_BREAKDOWN);
|
||||
self::$itemEntityBaseTick = new TimingsHandler("Entity Base Tick - ItemEntity", group: self::GROUP_BREAKDOWN);
|
||||
@ -272,7 +253,7 @@ abstract class Timings{
|
||||
}else{
|
||||
$displayName = self::shortenCoreClassName($entity::class, "pocketmine\\entity\\");
|
||||
}
|
||||
self::$entityTypeTimingMap[$entity::class] = new TimingsHandler("Entity Tick - " . $displayName, self::$tickEntity, group: self::GROUP_BREAKDOWN);
|
||||
self::$entityTypeTimingMap[$entity::class] = new TimingsHandler("Entity Tick - " . $displayName, group: self::GROUP_BREAKDOWN);
|
||||
}
|
||||
|
||||
return self::$entityTypeTimingMap[$entity::class];
|
||||
@ -282,7 +263,6 @@ abstract class Timings{
|
||||
if(!isset(self::$tileEntityTypeTimingMap[$tile::class])){
|
||||
self::$tileEntityTypeTimingMap[$tile::class] = new TimingsHandler(
|
||||
"Block Entity Tick - " . self::shortenCoreClassName($tile::class, "pocketmine\\block\\tile\\"),
|
||||
self::$tickTileEntity,
|
||||
group: self::GROUP_BREAKDOWN
|
||||
);
|
||||
}
|
||||
|
@ -23,16 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\timings;
|
||||
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function hrtime;
|
||||
use function implode;
|
||||
use function spl_object_id;
|
||||
|
||||
class TimingsHandler{
|
||||
private const FORMAT_VERSION = 1;
|
||||
private const FORMAT_VERSION = 2; //peak timings fix
|
||||
|
||||
private static bool $enabled = false;
|
||||
private static int $timingStart = 0;
|
||||
@ -77,19 +75,6 @@ class TimingsHandler{
|
||||
$result[] = "# Version " . Server::getInstance()->getVersion();
|
||||
$result[] = "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion();
|
||||
|
||||
$entities = 0;
|
||||
$livingEntities = 0;
|
||||
foreach(Server::getInstance()->getWorldManager()->getWorlds() as $world){
|
||||
$entities += count($world->getEntities());
|
||||
foreach($world->getEntities() as $e){
|
||||
if($e instanceof Living){
|
||||
++$livingEntities;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = "# Entities " . $entities;
|
||||
$result[] = "# LivingEntities " . $livingEntities;
|
||||
$result[] = "# FormatVersion " . self::FORMAT_VERSION;
|
||||
|
||||
$sampleTime = hrtime(true) - self::$timingStart;
|
||||
|
@ -66,9 +66,6 @@ final class TimingsRecord{
|
||||
if($record->curTickTotal > Server::TARGET_NANOSECONDS_PER_TICK){
|
||||
$record->violations += (int) floor($record->curTickTotal / Server::TARGET_NANOSECONDS_PER_TICK);
|
||||
}
|
||||
if($record->curTickTotal > $record->peakTime){
|
||||
$record->peakTime = $record->curTickTotal;
|
||||
}
|
||||
$record->curTickTotal = 0;
|
||||
$record->curCount = 0;
|
||||
$record->ticksActive++;
|
||||
@ -126,7 +123,7 @@ final class TimingsRecord{
|
||||
|
||||
public function getTicksActive() : int{ return $this->ticksActive; }
|
||||
|
||||
public function getPeakTime() : float{ return $this->peakTime; }
|
||||
public function getPeakTime() : int{ return $this->peakTime; }
|
||||
|
||||
public function startTiming(int $now) : void{
|
||||
$this->start = $now;
|
||||
@ -152,6 +149,9 @@ final class TimingsRecord{
|
||||
++$this->curCount;
|
||||
++$this->count;
|
||||
$this->start = 0;
|
||||
if($diff > $this->peakTime){
|
||||
$this->peakTime = $diff;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCurrentRecord() : ?self{
|
||||
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TickingChunkEntry{
|
||||
/**
|
||||
* @var ChunkTicker[] spl_object_id => ChunkTicker
|
||||
* @phpstan-var array<int, ChunkTicker>
|
||||
*/
|
||||
public array $tickers = [];
|
||||
|
||||
public bool $ready = false;
|
||||
}
|
@ -77,7 +77,6 @@ use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\ServerConfigGroup;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\ReversePriorityQueue;
|
||||
@ -104,6 +103,7 @@ use pocketmine\world\utils\SubChunkExplorer;
|
||||
use function abs;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function array_sum;
|
||||
@ -223,10 +223,25 @@ class World implements ChunkManager{
|
||||
private array $tickingLoaderCounter = [];
|
||||
|
||||
/**
|
||||
* @var TickingChunkEntry[] chunkHash => TickingChunkEntry
|
||||
* @phpstan-var array<ChunkPosHash, TickingChunkEntry>
|
||||
* @var ChunkTicker[][] chunkHash => [spl_object_id => ChunkTicker]
|
||||
* @phpstan-var array<ChunkPosHash, array<int, ChunkTicker>>
|
||||
*/
|
||||
private array $tickingChunks = [];
|
||||
private array $registeredTickingChunks = [];
|
||||
|
||||
/**
|
||||
* Set of chunks which are definitely ready for ticking.
|
||||
*
|
||||
* @var int[]
|
||||
* @phpstan-var array<ChunkPosHash, ChunkPosHash>
|
||||
*/
|
||||
private array $validTickingChunks = [];
|
||||
|
||||
/**
|
||||
* Set of chunks which might be ready for ticking. These will be checked at the next tick.
|
||||
* @var int[]
|
||||
* @phpstan-var array<ChunkPosHash, ChunkPosHash>
|
||||
*/
|
||||
private array $recheckTickingChunks = [];
|
||||
|
||||
/**
|
||||
* @var ChunkLoader[][] chunkHash => [spl_object_id => ChunkLoader]
|
||||
@ -983,7 +998,6 @@ class World implements ChunkManager{
|
||||
|
||||
$this->timings->entityTick->startTiming();
|
||||
//Update entities that need update
|
||||
Timings::$tickEntity->startTiming();
|
||||
foreach($this->updateEntities as $id => $entity){
|
||||
if($entity->isClosed() || $entity->isFlaggedForDespawn() || !$entity->onUpdate($currentTick)){
|
||||
unset($this->updateEntities[$id]);
|
||||
@ -992,7 +1006,6 @@ class World implements ChunkManager{
|
||||
$entity->close();
|
||||
}
|
||||
}
|
||||
Timings::$tickEntity->stopTiming();
|
||||
$this->timings->entityTick->stopTiming();
|
||||
|
||||
$this->timings->randomChunkUpdates->startTiming();
|
||||
@ -1155,17 +1168,25 @@ class World implements ChunkManager{
|
||||
$this->chunkTickRadius = $radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of chunk position hashes (as returned by World::chunkHash()) which are currently valid for
|
||||
* ticking.
|
||||
*
|
||||
* @return int[]
|
||||
* @phpstan-return list<ChunkPosHash>
|
||||
*/
|
||||
public function getTickingChunks() : array{
|
||||
return array_keys($this->validTickingChunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the World to tick the specified chunk, for as long as this chunk ticker (or any other chunk ticker) is
|
||||
* registered to it.
|
||||
*/
|
||||
public function registerTickingChunk(ChunkTicker $ticker, int $chunkX, int $chunkZ) : void{
|
||||
$chunkPosHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$entry = $this->tickingChunks[$chunkPosHash] ?? null;
|
||||
if($entry === null){
|
||||
$entry = $this->tickingChunks[$chunkPosHash] = new TickingChunkEntry();
|
||||
}
|
||||
$entry->tickers[spl_object_id($ticker)] = $ticker;
|
||||
$this->registeredTickingChunks[$chunkPosHash][spl_object_id($ticker)] = $ticker;
|
||||
$this->recheckTickingChunks[$chunkPosHash] = $chunkPosHash;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1175,10 +1196,14 @@ class World implements ChunkManager{
|
||||
public function unregisterTickingChunk(ChunkTicker $ticker, int $chunkX, int $chunkZ) : void{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$tickerId = spl_object_id($ticker);
|
||||
if(isset($this->tickingChunks[$chunkHash]->tickers[$tickerId])){
|
||||
unset($this->tickingChunks[$chunkHash]->tickers[$tickerId]);
|
||||
if(count($this->tickingChunks[$chunkHash]->tickers) === 0){
|
||||
unset($this->tickingChunks[$chunkHash]);
|
||||
if(isset($this->registeredTickingChunks[$chunkHash][$tickerId])){
|
||||
unset($this->registeredTickingChunks[$chunkHash][$tickerId]);
|
||||
if(count($this->registeredTickingChunks[$chunkHash]) === 0){
|
||||
unset(
|
||||
$this->registeredTickingChunks[$chunkHash],
|
||||
$this->recheckTickingChunks[$chunkHash],
|
||||
$this->validTickingChunks[$chunkHash]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1222,37 +1247,37 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
private function tickChunks() : void{
|
||||
if($this->chunkTickRadius <= 0 || (count($this->tickingChunks) === 0 && count($this->tickingLoaders) === 0)){
|
||||
if($this->chunkTickRadius <= 0 || (count($this->registeredTickingChunks) === 0 && count($this->tickingLoaders) === 0)){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->timings->randomChunkUpdatesChunkSelection->startTiming();
|
||||
if(count($this->recheckTickingChunks) > 0 || count($this->tickingLoaders) > 0){
|
||||
$this->timings->randomChunkUpdatesChunkSelection->startTiming();
|
||||
|
||||
/** @var bool[] $chunkTickList chunkhash => dummy */
|
||||
$chunkTickList = [];
|
||||
$chunkTickableCache = [];
|
||||
|
||||
$chunkTickableCache = [];
|
||||
|
||||
foreach($this->tickingChunks as $hash => $entry){
|
||||
if(!$entry->ready){
|
||||
foreach($this->recheckTickingChunks as $hash => $_){
|
||||
World::getXZ($hash, $chunkX, $chunkZ);
|
||||
if($this->isChunkTickable($chunkX, $chunkZ, $chunkTickableCache)){
|
||||
$entry->ready = true;
|
||||
}else{
|
||||
//the chunk has been flagged as temporarily not tickable, so we don't want to tick it this time
|
||||
continue;
|
||||
$this->validTickingChunks[$hash] = $hash;
|
||||
}
|
||||
}
|
||||
$chunkTickList[$hash] = true;
|
||||
}
|
||||
$this->recheckTickingChunks = [];
|
||||
|
||||
//TODO: REMOVE THIS
|
||||
//backwards compatibility for TickingChunkLoader, although I'm not sure this is really necessary in practice
|
||||
if(count($this->tickingLoaders) !== 0){
|
||||
$this->selectTickableChunksLegacy($chunkTickList, $chunkTickableCache);
|
||||
}
|
||||
//TODO: REMOVE THIS - we need a local var to add extra chunks to if we have legacy ticking loaders
|
||||
//this is copy-on-write, so it won't have any performance impact if there are no legacy ticking loaders
|
||||
$chunkTickList = $this->validTickingChunks;
|
||||
|
||||
$this->timings->randomChunkUpdatesChunkSelection->stopTiming();
|
||||
//TODO: REMOVE THIS
|
||||
//backwards compatibility for TickingChunkLoader, although I'm not sure this is really necessary in practice
|
||||
if(count($this->tickingLoaders) !== 0){
|
||||
$this->selectTickableChunksLegacy($chunkTickList, $chunkTickableCache);
|
||||
}
|
||||
|
||||
$this->timings->randomChunkUpdatesChunkSelection->stopTiming();
|
||||
}else{
|
||||
$chunkTickList = $this->validTickingChunks;
|
||||
}
|
||||
|
||||
foreach($chunkTickList as $index => $_){
|
||||
World::getXZ($index, $chunkX, $chunkZ);
|
||||
@ -1303,16 +1328,23 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the 3x3 chunks around the specified chunk as not ready to be ticked. This is used to prevent chunk ticking
|
||||
* while a chunk is being populated, light-populated, or unloaded.
|
||||
* Each chunk will be rechecked every tick until it is ready to be ticked again.
|
||||
* Marks the 3x3 square of chunks centered on the specified chunk for chunk ticking eligibility recheck.
|
||||
*
|
||||
* This should be used whenever the chunk's eligibility to be ticked is changed. This includes:
|
||||
* - Loading/unloading the chunk (the chunk may be registered for ticking before it is loaded)
|
||||
* - Locking/unlocking the chunk (e.g. world population)
|
||||
* - Light populated state change (i.e. scheduled for light population, or light population completed)
|
||||
* - Arbitrary chunk replacement (i.e. setChunk() or similar)
|
||||
*/
|
||||
private function markTickingChunkUnavailable(int $chunkX, int $chunkZ) : void{
|
||||
private function markTickingChunkForRecheck(int $chunkX, int $chunkZ) : void{
|
||||
for($cx = -1; $cx <= 1; ++$cx){
|
||||
for($cz = -1; $cz <= 1; ++$cz){
|
||||
$chunkHash = World::chunkHash($chunkX + $cx, $chunkZ + $cz);
|
||||
if(isset($this->tickingChunks[$chunkHash])){
|
||||
$this->tickingChunks[$chunkHash]->ready = false;
|
||||
unset($this->validTickingChunks[$chunkHash]);
|
||||
if(isset($this->registeredTickingChunks[$chunkHash])){
|
||||
$this->recheckTickingChunks[$chunkHash] = $chunkHash;
|
||||
}else{
|
||||
unset($this->recheckTickingChunks[$chunkHash]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1323,7 +1355,7 @@ class World implements ChunkManager{
|
||||
$lightPopulatedState = $this->chunks[$chunkHash]->isLightPopulated();
|
||||
if($lightPopulatedState === false){
|
||||
$this->chunks[$chunkHash]->setLightPopulated(null);
|
||||
$this->markTickingChunkUnavailable($chunkX, $chunkZ);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
|
||||
$this->workerPool->submitTask(new LightPopulationTask(
|
||||
$this->chunks[$chunkHash],
|
||||
@ -1347,6 +1379,7 @@ class World implements ChunkManager{
|
||||
$chunk->getSubChunk($y)->setBlockSkyLightArray($lightArray);
|
||||
}
|
||||
$chunk->setLightPopulated(true);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
}
|
||||
));
|
||||
}
|
||||
@ -1403,10 +1436,15 @@ class World implements ChunkManager{
|
||||
|
||||
(new WorldSaveEvent($this))->call();
|
||||
|
||||
$timings = $this->timings->syncDataSave;
|
||||
$timings->startTiming();
|
||||
|
||||
$this->provider->getWorldData()->setTime($this->time);
|
||||
$this->saveChunks();
|
||||
$this->provider->getWorldData()->save();
|
||||
|
||||
$timings->stopTiming();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2391,7 +2429,7 @@ class World implements ChunkManager{
|
||||
throw new \InvalidArgumentException("Chunk $chunkX $chunkZ is already locked");
|
||||
}
|
||||
$this->chunkLock[$chunkHash] = $lockId;
|
||||
$this->markTickingChunkUnavailable($chunkX, $chunkZ);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2406,6 +2444,7 @@ class World implements ChunkManager{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunkLock[$chunkHash]) && ($lockId === null || $this->chunkLock[$chunkHash] === $lockId)){
|
||||
unset($this->chunkLock[$chunkHash]);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -2457,7 +2496,7 @@ class World implements ChunkManager{
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
$chunk->setTerrainDirty();
|
||||
$this->markTickingChunkUnavailable($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking
|
||||
|
||||
if(!$this->isChunkInUse($chunkX, $chunkZ)){
|
||||
$this->unloadChunkRequest($chunkX, $chunkZ);
|
||||
@ -2739,6 +2778,7 @@ class World implements ChunkManager{
|
||||
foreach($this->getChunkListeners($x, $z) as $listener){
|
||||
$listener->onChunkLoaded($x, $z, $this->chunks[$chunkHash]);
|
||||
}
|
||||
$this->markTickingChunkForRecheck($x, $z); //tickers may have been registered before the chunk was loaded
|
||||
|
||||
$this->timings->syncChunkLoad->stopTiming();
|
||||
|
||||
@ -2900,8 +2940,8 @@ class World implements ChunkManager{
|
||||
unset($this->chunks[$chunkHash]);
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
unset($this->tickingChunks[$chunkHash]);
|
||||
$this->markTickingChunkUnavailable($x, $z);
|
||||
unset($this->registeredTickingChunks[$chunkHash]);
|
||||
$this->markTickingChunkForRecheck($x, $z);
|
||||
|
||||
if(array_key_exists($chunkHash, $this->chunkPopulationRequestMap)){
|
||||
$this->logger->debug("Rejecting population promise for chunk $x $z");
|
||||
@ -3210,7 +3250,8 @@ class World implements ChunkManager{
|
||||
private function internalOrderChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader, ?PromiseResolver $resolver) : Promise{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
|
||||
Timings::$population->startTiming();
|
||||
$timings = $this->timings->chunkPopulationOrder;
|
||||
$timings->startTiming();
|
||||
|
||||
try{
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
@ -3267,7 +3308,7 @@ class World implements ChunkManager{
|
||||
|
||||
return $resolver->getPromise();
|
||||
}finally{
|
||||
Timings::$population->stopTiming();
|
||||
$timings->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3276,7 +3317,8 @@ class World implements ChunkManager{
|
||||
* @phpstan-param array<int, Chunk> $adjacentChunks
|
||||
*/
|
||||
private function generateChunkCallback(ChunkLockId $chunkLockId, int $x, int $z, Chunk $chunk, array $adjacentChunks, ChunkLoader $temporaryChunkLoader) : void{
|
||||
Timings::$generationCallback->startTiming();
|
||||
$timings = $this->timings->chunkPopulationCompletion;
|
||||
$timings->startTiming();
|
||||
|
||||
$dirtyChunks = 0;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
@ -3345,7 +3387,7 @@ class World implements ChunkManager{
|
||||
|
||||
$this->drainPopulationRequestQueue();
|
||||
}
|
||||
Timings::$generationCallback->stopTiming();
|
||||
$timings->stopTiming();
|
||||
}
|
||||
|
||||
public function doChunkGarbageCollection() : void{
|
||||
|
@ -30,7 +30,6 @@ use pocketmine\event\world\WorldUnloadEvent;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\player\ChunkSelector;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\io\exception\CorruptedWorldException;
|
||||
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
|
||||
@ -391,7 +390,6 @@ class WorldManager{
|
||||
}
|
||||
|
||||
private function doAutoSave() : void{
|
||||
Timings::$worldSave->startTiming();
|
||||
foreach($this->worlds as $world){
|
||||
foreach($world->getPlayers() as $player){
|
||||
if($player->spawned){
|
||||
@ -400,6 +398,5 @@ class WorldManager{
|
||||
}
|
||||
$world->save(false);
|
||||
}
|
||||
Timings::$worldSave->stopTiming();
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ class WorldTimings{
|
||||
public TimingsHandler $randomChunkUpdatesChunkSelection;
|
||||
public TimingsHandler $doChunkGC;
|
||||
public TimingsHandler $entityTick;
|
||||
public TimingsHandler $tileTick;
|
||||
public TimingsHandler $doTick;
|
||||
|
||||
public TimingsHandler $syncChunkSend;
|
||||
@ -48,33 +49,54 @@ class WorldTimings{
|
||||
public TimingsHandler $syncChunkLoadFixInvalidBlocks;
|
||||
public TimingsHandler $syncChunkLoadEntities;
|
||||
public TimingsHandler $syncChunkLoadTileEntities;
|
||||
|
||||
public TimingsHandler $syncDataSave;
|
||||
public TimingsHandler $syncChunkSave;
|
||||
|
||||
public TimingsHandler $chunkPopulationOrder;
|
||||
public TimingsHandler $chunkPopulationCompletion;
|
||||
|
||||
/**
|
||||
* @var TimingsHandler[]
|
||||
* @phpstan-var array<string, TimingsHandler>
|
||||
*/
|
||||
private static array $aggregators = [];
|
||||
|
||||
private static function newTimer(string $worldName, string $timerName) : TimingsHandler{
|
||||
$aggregator = self::$aggregators[$timerName] ??= new TimingsHandler("Worlds - $timerName"); //displayed in Minecraft primary table
|
||||
|
||||
return new TimingsHandler("$worldName - $timerName", $aggregator, Timings::GROUP_BREAKDOWN);
|
||||
}
|
||||
|
||||
public function __construct(World $world){
|
||||
$name = $world->getFolderName() . " - ";
|
||||
$name = $world->getFolderName();
|
||||
|
||||
$this->setBlock = new TimingsHandler($name . "setBlock", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->doBlockLightUpdates = new TimingsHandler($name . "Block Light Updates", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->doBlockSkyLightUpdates = new TimingsHandler($name . "Sky Light Updates", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->setBlock = self::newTimer($name, "Set Blocks");
|
||||
$this->doBlockLightUpdates = self::newTimer($name, "Block Light Updates");
|
||||
$this->doBlockSkyLightUpdates = self::newTimer($name, "Sky Light Updates");
|
||||
|
||||
$this->doChunkUnload = new TimingsHandler($name . "Unload Chunks", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->scheduledBlockUpdates = new TimingsHandler($name . "Scheduled Block Updates", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->randomChunkUpdates = new TimingsHandler($name . "Random Chunk Updates", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->randomChunkUpdatesChunkSelection = new TimingsHandler($name . "Random Chunk Updates - Chunk Selection", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->doChunkGC = new TimingsHandler($name . "Garbage Collection", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->entityTick = new TimingsHandler($name . "Tick Entities", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->doChunkUnload = self::newTimer($name, "Unload Chunks");
|
||||
$this->scheduledBlockUpdates = self::newTimer($name, "Scheduled Block Updates");
|
||||
$this->randomChunkUpdates = self::newTimer($name, "Random Chunk Updates");
|
||||
$this->randomChunkUpdatesChunkSelection = self::newTimer($name, "Random Chunk Updates - Chunk Selection");
|
||||
$this->doChunkGC = self::newTimer($name, "Garbage Collection");
|
||||
$this->entityTick = self::newTimer($name, "Entity Tick");
|
||||
$this->tileTick = self::newTimer($name, "Block Entity Tick");
|
||||
$this->doTick = self::newTimer($name, "World Tick");
|
||||
|
||||
Timings::init(); //make sure the timers we want are available
|
||||
$this->syncChunkSend = new TimingsHandler($name . "Player Send Chunks", Timings::$playerChunkSend, group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkSendPrepare = new TimingsHandler($name . "Player Send Chunk Prepare", Timings::$playerChunkSend, group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkSend = self::newTimer($name, "Player Send Chunks");
|
||||
$this->syncChunkSendPrepare = self::newTimer($name, "Player Send Chunk Prepare");
|
||||
|
||||
$this->syncChunkLoad = new TimingsHandler($name . "Chunk Load", Timings::$worldLoad, group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkLoadData = new TimingsHandler($name . "Chunk Load - Data", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkLoadFixInvalidBlocks = new TimingsHandler($name . "Chunk Load - Fix Invalid Blocks", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkLoadEntities = new TimingsHandler($name . "Chunk Load - Entities", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkLoadTileEntities = new TimingsHandler($name . "Chunk Load - TileEntities", group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkSave = new TimingsHandler($name . "Chunk Save", Timings::$worldSave, group: Timings::GROUP_BREAKDOWN);
|
||||
$this->syncChunkLoad = self::newTimer($name, "Chunk Load");
|
||||
$this->syncChunkLoadData = self::newTimer($name, "Chunk Load - Data");
|
||||
$this->syncChunkLoadFixInvalidBlocks = self::newTimer($name, "Chunk Load - Fix Invalid Blocks");
|
||||
$this->syncChunkLoadEntities = self::newTimer($name, "Chunk Load - Entities");
|
||||
$this->syncChunkLoadTileEntities = self::newTimer($name, "Chunk Load - Block Entities");
|
||||
|
||||
$this->doTick = new TimingsHandler($name . "World Tick");
|
||||
$this->syncDataSave = self::newTimer($name, "Data Save");
|
||||
$this->syncChunkSave = self::newTimer($name, "Chunk Save");
|
||||
|
||||
$this->chunkPopulationOrder = self::newTimer($name, "Chunk Population - Order");
|
||||
$this->chunkPopulationCompletion = self::newTimer($name, "Chunk Population - Completion");
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user