Compare commits

..

24 Commits

Author SHA1 Message Date
5aa8b953a8 Release 5.0.0-ALPHA8 2023-01-23 20:38:35 +00:00
375ec8e00c Fix CS 2023-01-23 20:36:02 +00:00
002f7d6826 PlayerDuplicateLoginEvent: remove dead code 2023-01-23 20:21:06 +00:00
06ad1a2d2b Updated dependencies to release versions 2023-01-23 20:04:01 +00:00
222415859a Require pthreads ^5.1
This version of pthreads has a substantially improved API, improved
performance, improved memory usage, and much less magical and broken
behaviour.
2023-01-23 20:02:33 +00:00
14b250c63f Merge branch 'next-minor' into next-major 2023-01-23 19:37:18 +00:00
644881372d Merge branch 'stable' into next-minor 2023-01-23 19:37:02 +00:00
a12aac71fd Updated setup-php-action 2023-01-23 19:36:52 +00:00
f948cb0086 PocketMine.php: refuse pthreads 5.0 2023-01-21 15:30:36 +00:00
07a30ea1f9 Merge branch 'next-minor' into next-major 2023-01-20 15:42:30 +00:00
6c52723d97 Merge branch 'stable' into next-minor 2023-01-20 15:40:32 +00:00
d5b7bf77b0 Bump build/php from 6b605ed to b479ec4 (#5521)
Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `6b605ed` to `b479ec4`.
- [Release notes](https://github.com/pmmp/php-build-scripts/releases)
- [Commits](6b605ed7c4...b479ec438f)

---
updated-dependencies:
- dependency-name: build/php
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-20 09:44:19 +00:00
b1a5b02d3a Updated DevTools to 1.16.1 2023-01-20 01:55:52 +00:00
74e052de51 Terminal: fix deprecation error on PHP 8.2 2023-01-20 01:30:39 +00:00
a5397d55fe Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-01-20 01:13:27 +00:00
65ef929d22 Update Actions PHP versions 2023-01-20 01:13:15 +00:00
441919c5e3 Begin testing on PHP 8.2 2023-01-20 01:12:35 +00:00
448aeec780 Bump phpstan/phpstan from 1.9.12 to 1.9.13 (#5520)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.12 to 1.9.13.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.10.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.12...1.9.13)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-19 13:04:28 +00:00
b8f6b66e42 First look at separating disconnect reason and disconnect screen messages (#4512) 2023-01-18 20:57:17 +00:00
365cce9d0c Merge branch 'next-minor' into next-major 2023-01-18 20:46:22 +00:00
78aea5c34c Merge branch 'stable' into next-minor 2023-01-18 20:46:10 +00:00
d7f40f75d2 PlayerPreLoginEvent: fixed documentation errors 2023-01-18 20:45:49 +00:00
41e60cb62c NetworkSession: remove unnecessary translation 2023-01-18 20:36:53 +00:00
5d2ac214a8 5.0.0-ALPHA8 is next 2023-01-18 19:50:18 +00:00
36 changed files with 420 additions and 200 deletions

View File

@ -13,14 +13,15 @@ jobs:
strategy:
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: [8.0.27, 8.1.14, 8.2.1]
steps:
- name: Build and prepare PHP cache
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
phpstan:
name: PHPStan analysis
@ -31,16 +32,17 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: [8.0.27, 8.1.14, 8.2.1]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -69,16 +71,17 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: [8.0.27, 8.1.14, 8.2.1]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -107,7 +110,7 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: [8.0.27, 8.1.14, 8.2.1]
steps:
- uses: actions/checkout@v3
@ -115,10 +118,11 @@ jobs:
submodules: true
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php
@ -147,16 +151,17 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.23, 8.1.10]
php: [8.0.27, 8.1.14, 8.2.1]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
uses: pmmp/setup-php-action@6dd74c145250109942b08fc63cd5118edd2fd256
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
pm-version-major: "5"
- name: Install Composer
run: curl -sS https://getcomposer.org/installer | php

View File

@ -23,7 +23,8 @@ declare(strict_types=1);
const VERSIONS = [
"8.0",
"8.1"
"8.1",
"8.2"
];
$workflowFile = file_get_contents(__DIR__ . '/main.yml');

View File

@ -869,3 +869,43 @@ Released 18th January 2023.
- Built-in commands now declare their names inside the class constructor, rather than accepting them as parameters. This improves code consistency.
- `NetworkSession` disconnect APIs now accept `Translatable|string` instead of `string` to allow localized disconnect messages.
- All external usages of `KnownTranslationKeys` are now removed. All localized messages are now sent using `Translatable` objects (usually from `KnownTranslationFactory`).
# 5.0.0-ALPHA8
Released 23rd January 2023.
## Core
- Updated `ext-pthreads` requirement to `^5.1.0`. This version improves performance, memory usage, includes BC-breaking API changes, and removes a lot of confusing behaviour.
- See [`ext-pthreads` 5.0.0 release](https://github.com/pmmp/pthreads/releases/tag/5.0.0) for more information.
- For the most part, plugins will be unaffected, unless using `Threaded` objects directly, or directly interacting with other pthreads APIs.
## API
### Overview
- It's now possible to specify a different disconnect reason and disconnection screen message. This is useful if you want to display a fancy disconnect screen, but don't want to spam the server log with useless information.
### `pocketmine\event\player`
- The following API methods have been removed:
- `PlayerKickEvent->getReason()` - replaced by `getDisconnectReason()` and `getDisconnectScreenMessage()`
- `PlayerKickEvent->setReason()` - replaced by `setDisconnectReason()` and `setDisconnectScreenMessage()`
- `PlayerDuplicateLoginEvent->getDisconnectMessage()` - replaced by `getDisconnectReason()` and `getDisconnectScreenMessage()`
- `PlayerDuplicateLoginEvent->setDisconnectMessage()` - replaced by `setDisconnectReason()` and `setDisconnectScreenMessage()`
- The following new API methods have been added:
- `public PlayerKickEvent->getDisconnectReason() : Translatable|string` - returns the reason for the disconnection displayed in the console and server log
- `public PlayerKickEvent->setDisconnectReason(Translatable|string $disconnectReason) : void` - sets the reason for the disconnection displayed in the console and server log
- `public PlayerKickEvent->getDisconnectScreenMessage() : Translatable|string|null` - returns the message to be displayed on the disconnect screen (the message in `getDisconnectReason()` is used if null is returned)
- `public PlayerKickEvent->setDisconnectScreenMessage(Translatable|string|null $disconnectScreenMessage) : void` - sets the message to be displayed on the disconnect screen (the message in `setDisconnectReason()` is used if null is passed)
- `public PlayerDuplicateLoginEvent->getDisconnectReason() : Translatable|string` - returns the reason for the disconnection displayed in the console and server log
- `public PlayerDuplicateLoginEvent->setDisconnectReason(Translatable|string $disconnectReason) : void` - sets the reason for the disconnection displayed in the console and server log
- `public PlayerDuplicateLoginEvent->getDisconnectScreenMessage() : Translatable|string|null` - returns the message to be displayed on the disconnect screen (the message in `getDisconnectReason()` is used if null is returned)
- `public PlayerDuplicateLoginEvent->setDisconnectScreenMessage(Translatable|string|null $disconnectScreenMessage) : void` - sets the message to be displayed on the disconnect screen (the message in `setDisconnectReason()` is used if null is passed)
### `pocketmine\network`
- The following API methods have changed signatures:
- `NetworkSessionManager->close()` now accepts an additional `Translatable|string|null $disconnectScreenMessage` parameter.
### `pocketmine\player`
- The following API methods have changed signatures:
- `Player->kick()` now accepts an additional `Translatable|string|null $disconnectScreenMessage` parameter, which is the message to be displayed on the disconnect screen (the message in `$reason` is used if null is passed)
- `Player->disconnect()` now accepts an additional `Translatable|string|null $disconnectScreenMessage` parameter, which is the message to be displayed on the disconnect screen (the message in `$reason` is used if null is passed)
## Internals
- `NetworkSession` disconnect methods have been altered to allow specifying a different disconnect reason and disconnection screen message.

View File

@ -22,7 +22,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pthreads": "^4.0",
"ext-pthreads": "^5.1",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
@ -40,22 +40,22 @@
"pocketmine/bedrock-protocol": "~18.0.0+bedrock-1.19.50",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
"pocketmine/classloader": "^0.3.0",
"pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.18.0",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/log-pthreads": "^0.5.0",
"pocketmine/math": "^0.4.0",
"pocketmine/nbt": "^0.3.2",
"pocketmine/raklib": "^0.14.2",
"pocketmine/raklib-ipc": "^0.1.0",
"pocketmine/snooze": "^0.3.0",
"pocketmine/snooze": "^0.4.0",
"ramsey/uuid": "^4.1",
"symfony/filesystem": "^5.4"
},
"require-dev": {
"phpstan/phpstan": "1.9.12",
"phpstan/phpstan": "1.9.13",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"

98
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d6cafa9f9e236ad8d55c2eebdb51416a",
"content-hash": "17ca826af1a32c9768cd162e75d10ad5",
"packages": [
{
"name": "adhocore/json-comment",
@ -469,20 +469,20 @@
},
{
"name": "pocketmine/classloader",
"version": "0.2.0",
"version": "0.3.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/ClassLoader.git",
"reference": "49ea303993efdfb39cd302e2156d50aa78209e78"
"reference": "407caf521186ec1f03024f39031cc681ad491026"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/49ea303993efdfb39cd302e2156d50aa78209e78",
"reference": "49ea303993efdfb39cd302e2156d50aa78209e78",
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/407caf521186ec1f03024f39031cc681ad491026",
"reference": "407caf521186ec1f03024f39031cc681ad491026",
"shasum": ""
},
"require": {
"ext-pthreads": "~3.2.0 || ^4.0",
"ext-pthreads": "^5.0",
"ext-reflection": "*",
"php": "^8.0"
},
@ -491,8 +491,8 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-strict-rules": "^0.12.4",
"phpstan/phpstan": "1.9.4",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5"
},
"type": "library",
@ -508,9 +508,9 @@
"description": "Ad-hoc autoloading components used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/ClassLoader/issues",
"source": "https://github.com/pmmp/ClassLoader/tree/0.2.0"
"source": "https://github.com/pmmp/ClassLoader/tree/0.3.0"
},
"time": "2021-11-01T20:17:27+00:00"
"time": "2023-01-23T19:46:53+00:00"
},
{
"name": "pocketmine/color",
@ -591,16 +591,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.18.0",
"version": "2.18.3",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "0f50afc3d0fec29f769a62e93c71f8a0fb968f76"
"reference": "da25bfe9ee4822a84feb9b7e620c56ad4000aed0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/0f50afc3d0fec29f769a62e93c71f8a0fb968f76",
"reference": "0f50afc3d0fec29f769a62e93c71f8a0fb968f76",
"url": "https://api.github.com/repos/pmmp/Language/zipball/da25bfe9ee4822a84feb9b7e620c56ad4000aed0",
"reference": "da25bfe9ee4822a84feb9b7e620c56ad4000aed0",
"shasum": ""
},
"type": "library",
@ -608,9 +608,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.18.0"
"source": "https://github.com/pmmp/Language/tree/2.18.3"
},
"time": "2023-01-14T17:52:46+00:00"
"time": "2023-01-17T21:43:36+00:00"
},
{
"name": "pocketmine/log",
@ -655,21 +655,21 @@
},
{
"name": "pocketmine/log-pthreads",
"version": "0.4.0",
"version": "0.5.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/LogPthreads.git",
"reference": "61f709e8cf36bcc24e4efe02acded680a1ce23cd"
"reference": "0ecfea6dcfc9a9f5c86e126ac1661732de5c5666"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/61f709e8cf36bcc24e4efe02acded680a1ce23cd",
"reference": "61f709e8cf36bcc24e4efe02acded680a1ce23cd",
"url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/0ecfea6dcfc9a9f5c86e126ac1661732de5c5666",
"reference": "0ecfea6dcfc9a9f5c86e126ac1661732de5c5666",
"shasum": ""
},
"require": {
"ext-pthreads": "~3.2.0 || ^4.0",
"php": "^7.4 || ^8.0",
"ext-pthreads": "^5.0",
"php": "^8.0",
"pocketmine/log": "^0.4.0"
},
"conflict": {
@ -677,8 +677,8 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.88",
"phpstan/phpstan-strict-rules": "^0.12.4"
"phpstan/phpstan": "1.8.11",
"phpstan/phpstan-strict-rules": "^1.0"
},
"type": "library",
"autoload": {
@ -693,9 +693,9 @@
"description": "Logging components specialized for pthreads used by PocketMine-MP and related projects",
"support": {
"issues": "https://github.com/pmmp/LogPthreads/issues",
"source": "https://github.com/pmmp/LogPthreads/tree/0.4.0"
"source": "https://github.com/pmmp/LogPthreads/tree/0.5.0"
},
"time": "2021-11-01T21:42:09+00:00"
"time": "2023-01-23T19:52:12+00:00"
},
{
"name": "pocketmine/math",
@ -866,26 +866,26 @@
},
{
"name": "pocketmine/snooze",
"version": "0.3.1",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Snooze.git",
"reference": "0ac8fc2a781c419a1f64ebca4d5835028f59e29b"
"reference": "6b1d6cc645d674590ff9be2438ac00032f9ee292"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/0ac8fc2a781c419a1f64ebca4d5835028f59e29b",
"reference": "0ac8fc2a781c419a1f64ebca4d5835028f59e29b",
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/6b1d6cc645d674590ff9be2438ac00032f9ee292",
"reference": "6b1d6cc645d674590ff9be2438ac00032f9ee292",
"shasum": ""
},
"require": {
"ext-pthreads": "~3.2.0 || ^4.0",
"php-64bit": "^7.3 || ^8.0"
"ext-pthreads": "^5.0",
"php-64bit": "^8.0"
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan-strict-rules": "^0.12.4"
"phpstan/phpstan": "1.9.14",
"phpstan/phpstan-strict-rules": "^1.0"
},
"type": "library",
"autoload": {
@ -900,9 +900,9 @@
"description": "Thread notification management library for code using the pthreads extension",
"support": {
"issues": "https://github.com/pmmp/Snooze/issues",
"source": "https://github.com/pmmp/Snooze/tree/0.3.1"
"source": "https://github.com/pmmp/Snooze/tree/0.4.0"
},
"time": "2021-11-01T20:50:08+00:00"
"time": "2023-01-23T19:43:19+00:00"
},
{
"name": "ramsey/collection",
@ -1610,16 +1610,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.15.2",
"version": "v4.15.3",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"shasum": ""
},
"require": {
@ -1660,9 +1660,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3"
},
"time": "2022-11-12T15:38:23+00:00"
"time": "2023-01-16T22:05:37+00:00"
},
{
"name": "phar-io/manifest",
@ -1777,16 +1777,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.9.12",
"version": "1.9.13",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8"
"reference": "a0922426da3a7d0d9334e99a363f7f9f6e23e84f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/44a338ff0d5572c13fd77dfd91addb96e48c29f8",
"reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a0922426da3a7d0d9334e99a363f7f9f6e23e84f",
"reference": "a0922426da3a7d0d9334e99a363f7f9f6e23e84f",
"shasum": ""
},
"require": {
@ -1816,7 +1816,7 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.9.12"
"source": "https://github.com/phpstan/phpstan/tree/1.9.13"
},
"funding": [
{
@ -1832,7 +1832,7 @@
"type": "tidelift"
}
],
"time": "2023-01-17T10:44:04+00:00"
"time": "2023-01-18T15:26:53+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
@ -3396,7 +3396,7 @@
"ext-openssl": "*",
"ext-pcre": "*",
"ext-phar": "*",
"ext-pthreads": "^4.0",
"ext-pthreads": "^5.1",
"ext-reflection": "*",
"ext-simplexml": "*",
"ext-sockets": "*",

View File

@ -122,8 +122,8 @@ namespace pocketmine {
if(substr_count($pthreads_version, ".") < 2){
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "4.0.0") < 0 || version_compare($pthreads_version, "5.0.0") > 0){
$messages[] = "pthreads ^4.0.0 is required, while you have $pthreads_version.";
if(version_compare($pthreads_version, "5.1.0") < 0 || version_compare($pthreads_version, "6.0.0") >= 0){
$messages[] = "pthreads ^5.0.0 is required, while you have $pthreads_version.";
}
}

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.0.0-ALPHA7";
public const BASE_VERSION = "5.0.0-ALPHA8";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "alpha";

View File

@ -46,13 +46,17 @@ if($socket === false){
throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage");
}
$channel = new \Threaded();
/** @phpstan-var \ThreadedArray<int, string> $channel */
$channel = new \ThreadedArray();
$thread = new class($channel) extends \Thread{
/**
* @phpstan-param \ThreadedArray<int, string> $channel
*/
public function __construct(
private \Threaded $channel,
private \ThreadedArray $channel,
){}
public function run(){
public function run() : void{
require dirname(__DIR__, 2) . '/vendor/autoload.php';
$channel = $this->channel;
@ -75,7 +79,6 @@ while(!feof($socket)){
if(count($channel) === 0){
$channel->wait(1_000_000);
}
/** @var string|null $line */
$line = $channel->shift();
return $line;
});

View File

@ -0,0 +1,62 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\player;
use pocketmine\lang\Translatable;
trait PlayerDisconnectEventTrait{
/**
* Sets the kick reason shown in the server log and on the console.
*
* This should be a **short, simple, single-line** message.
* Do not use long or multi-line messages here - they will spam the log and server console with useless information.
*/
public function setDisconnectReason(Translatable|string $disconnectReason) : void{
$this->disconnectReason = $disconnectReason;
}
/**
* Returns the kick reason shown in the server log and on the console.
* When kicked by the /kick command, the default is something like "Kicked by admin.".
*/
public function getDisconnectReason() : Translatable|string{
return $this->disconnectReason;
}
/**
* Sets the message shown on the player's disconnection screen.
* This can be as long as you like, and may contain formatting and newlines.
* If this is set to null, the kick reason will be used as the disconnect screen message directly.
*/
public function setDisconnectScreenMessage(Translatable|string|null $disconnectScreenMessage) : void{
$this->disconnectScreenMessage = $disconnectScreenMessage;
}
/**
* Returns the message shown on the player's disconnection screen.
* When kicked by the /kick command, the default is something like "Kicked by admin.".
* If this is null, the kick reason will be used as the disconnect screen message directly.
*/
public function getDisconnectScreenMessage() : Translatable|string|null{ return $this->disconnectScreenMessage ?? $this->disconnectReason; }
}

View File

@ -26,7 +26,6 @@ namespace pocketmine\event\player;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
use pocketmine\event\Event;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Translatable;
use pocketmine\network\mcpe\NetworkSession;
@ -36,15 +35,14 @@ use pocketmine\network\mcpe\NetworkSession;
*/
class PlayerDuplicateLoginEvent extends Event implements Cancellable{
use CancellableTrait;
private Translatable|string $disconnectMessage;
use PlayerDisconnectEventTrait;
public function __construct(
private NetworkSession $connectingSession,
private NetworkSession $existingSession
){
$this->disconnectMessage = KnownTranslationFactory::disconnectionScreen_loggedinOtherLocation();
}
private NetworkSession $existingSession,
private Translatable|string $disconnectReason,
private Translatable|string|null $disconnectScreenMessage
){}
public function getConnectingSession() : NetworkSession{
return $this->connectingSession;
@ -53,15 +51,4 @@ class PlayerDuplicateLoginEvent extends Event implements Cancellable{
public function getExistingSession() : NetworkSession{
return $this->existingSession;
}
/**
* Returns the message shown to the session which gets disconnected.
*/
public function getDisconnectMessage() : Translatable|string{
return $this->disconnectMessage;
}
public function setDisconnectMessage(Translatable|string $message) : void{
$this->disconnectMessage = $message;
}
}

View File

@ -33,32 +33,17 @@ use pocketmine\player\Player;
*/
class PlayerKickEvent extends PlayerEvent implements Cancellable{
use CancellableTrait;
use PlayerDisconnectEventTrait;
public function __construct(
Player $player,
protected Translatable|string $reason,
protected Translatable|string $quitMessage
protected Translatable|string $disconnectReason,
protected Translatable|string $quitMessage,
protected Translatable|string|null $disconnectScreenMessage
){
$this->player = $player;
}
/**
* Sets the message shown on the kicked player's disconnection screen.
* This message is also displayed in the console and server log.
*/
public function setReason(Translatable|string $reason) : void{
$this->reason = $reason;
}
/**
* Returns the message shown on the kicked player's disconnection screen.
* This message is also displayed in the console and server log.
* When kicked by the /kick command, the default is something like "Kicked by admin.".
*/
public function getReason() : Translatable|string{
return $this->reason;
}
/**
* Sets the quit message broadcasted to other players.
*/

View File

@ -65,8 +65,8 @@ class PlayerPreLoginEvent extends Event implements Cancellable{
/**
* Returns an object containing self-proclaimed information about the connecting player.
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, it's unknown if the player is real or a
* hacker.
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, this could be a hacker posing as another
* player.
*/
public function getPlayerInfo() : PlayerInfo{
return $this->playerInfo;
@ -105,7 +105,7 @@ class PlayerPreLoginEvent extends Event implements Cancellable{
}
/**
* Sets a reason to disallow the player to continue continue authenticating, with a message.
* Sets a reason to disallow the player to continue authenticating, with a message.
* This can also be used to change kick messages for already-set flags.
*/
public function setKickReason(int $flag, Translatable|string $message) : void{

View File

@ -96,10 +96,13 @@ class NetworkSessionManager{
/**
* Terminates all connected sessions with the given reason.
*
* @param Translatable|string $reason Shown in the server log - this should be a short one-line message
* @param Translatable|string|null $disconnectScreenMessage Shown on the player's disconnection screen (null will use the reason)
*/
public function close(Translatable|string $reason = "") : void{
public function close(Translatable|string $reason = "", Translatable|string|null $disconnectScreenMessage = null) : void{
foreach($this->sessions as $session){
$session->disconnect($reason);
$session->disconnect($reason, $disconnectScreenMessage);
}
$this->sessions = [];
}

View File

@ -33,6 +33,7 @@ use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\ChunkPosition;
use pocketmine\network\mcpe\serializer\ChunkSerializer;
use pocketmine\scheduler\AsyncTask;
use pocketmine\thread\NonThreadSafeValue;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\io\FastChunkSerializer;
@ -43,14 +44,15 @@ class ChunkRequestTask extends AsyncTask{
protected string $chunk;
protected int $chunkX;
protected int $chunkZ;
protected Compressor $compressor;
/** @phpstan-var NonThreadSafeValue<Compressor> */
protected NonThreadSafeValue $compressor;
private string $tiles;
/**
* @phpstan-param (\Closure() : void)|null $onError
*/
public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor, ?\Closure $onError = null){
$this->compressor = $compressor;
$this->compressor = new NonThreadSafeValue($compressor);
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
$this->chunkX = $chunkX;
@ -66,7 +68,7 @@ class ChunkRequestTask extends AsyncTask{
$subCount = ChunkSerializer::getSubChunkCount($chunk);
$encoderContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
$payload = ChunkSerializer::serializeFullChunk($chunk, RuntimeBlockMapping::getInstance(), $encoderContext, $this->tiles);
$this->setResult($this->compressor->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $subCount, false, null, $payload))->getBuffer()));
$this->setResult($this->compressor->deserialize()->compress(PacketBatch::fromPackets($encoderContext, LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $subCount, false, null, $payload))->getBuffer()));
}
public function onError() : void{

View File

@ -602,22 +602,25 @@ class NetworkSession{
$this->invManager = null;
}
private function sendDisconnectPacket(Translatable|string $reason) : void{
if($reason instanceof Translatable){
$translated = $this->server->getLanguage()->translate($reason);
private function sendDisconnectPacket(Translatable|string $message) : void{
if($message instanceof Translatable){
$translated = $this->server->getLanguage()->translate($message);
}else{
$translated = $reason;
$translated = $message;
}
$this->sendDataPacket(DisconnectPacket::create($translated));
}
/**
* Disconnects the session, destroying the associated player (if it exists).
*
* @param Translatable|string $reason Shown in the server log - this should be a short one-line message
* @param Translatable|string|null $disconnectScreenMessage Shown on the player's disconnection screen (null will use the reason)
*/
public function disconnect(Translatable|string $reason, bool $notify = true) : void{
$this->tryDisconnect(function() use ($reason, $notify) : void{
public function disconnect(Translatable|string $reason, Translatable|string|null $disconnectScreenMessage = null, bool $notify = true) : void{
$this->tryDisconnect(function() use ($reason, $disconnectScreenMessage, $notify) : void{
if($notify){
$this->sendDisconnectPacket($reason);
$this->sendDisconnectPacket($disconnectScreenMessage ?? $reason);
}
if($this->player !== null){
$this->player->onPostDisconnect($reason, null);
@ -634,7 +637,7 @@ class NetworkSession{
function() use ($protocolVersion) : void{
$this->sendDataPacket(PlayStatusPacket::create($protocolVersion < ProtocolInfo::CURRENT_PROTOCOL ? PlayStatusPacket::LOGIN_FAILED_CLIENT : PlayStatusPacket::LOGIN_FAILED_SERVER), true);
},
$this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_disconnect_incompatibleProtocol((string) $protocolVersion))
KnownTranslationFactory::pocketmine_disconnect_incompatibleProtocol((string) $protocolVersion)
);
}
@ -654,9 +657,9 @@ class NetworkSession{
/**
* Called by the Player when it is closed (for example due to getting kicked).
*/
public function onPlayerDestroyed(Translatable|string $reason) : void{
$this->tryDisconnect(function() use ($reason) : void{
$this->sendDisconnectPacket($reason);
public function onPlayerDestroyed(Translatable|string $reason, Translatable|string $disconnectScreenMessage) : void{
$this->tryDisconnect(function() use ($disconnectScreenMessage) : void{
$this->sendDisconnectPacket($disconnectScreenMessage);
}, $reason);
}
@ -694,7 +697,7 @@ class NetworkSession{
if(!$this->authenticated){
if($authRequired){
$this->disconnect(KnownTranslationFactory::disconnectionScreen_notAuthenticated());
$this->disconnect("Not authenticated", KnownTranslationFactory::disconnectionScreen_notAuthenticated());
return;
}
if($this->info instanceof XboxLivePlayerInfo){
@ -728,14 +731,14 @@ class NetworkSession{
if($kickForXUIDMismatch($info instanceof XboxLivePlayerInfo ? $info->getXuid() : "")){
return;
}
$ev = new PlayerDuplicateLoginEvent($this, $existingSession);
$ev = new PlayerDuplicateLoginEvent($this, $existingSession, KnownTranslationFactory::disconnectionScreen_loggedinOtherLocation(), null);
$ev->call();
if($ev->isCancelled()){
$this->disconnect($ev->getDisconnectMessage());
$this->disconnect($ev->getDisconnectReason(), $ev->getDisconnectScreenMessage());
return;
}
$existingSession->disconnect($ev->getDisconnectMessage());
$existingSession->disconnect($ev->getDisconnectReason(), $ev->getDisconnectScreenMessage());
}
}

View File

@ -30,6 +30,7 @@ use pocketmine\network\mcpe\JwtUtils;
use pocketmine\network\mcpe\protocol\types\login\JwtChainLinkBody;
use pocketmine\network\mcpe\protocol\types\login\JwtHeader;
use pocketmine\scheduler\AsyncTask;
use pocketmine\thread\NonThreadSafeValue;
use function base64_decode;
use function igbinary_serialize;
use function igbinary_unserialize;
@ -49,8 +50,10 @@ class ProcessLoginTask extends AsyncTask{
* Whether the keychain signatures were validated correctly. This will be set to an error message if any link in the
* keychain is invalid for whatever reason (bad signature, not in nbf-exp window, etc). If this is non-null, the
* keychain might have been tampered with. The player will always be disconnected if this is non-null.
*
* @phpstan-var NonThreadSafeValue<Translatable>|string|null
*/
private Translatable|string|null $error = "Unknown";
private NonThreadSafeValue|string|null $error = "Unknown";
/**
* Whether the player is logged into Xbox Live. This is true if any link in the keychain is signed with the Mojang
* root public key.
@ -77,7 +80,8 @@ class ProcessLoginTask extends AsyncTask{
$this->clientPublicKey = $this->validateChain();
$this->error = null;
}catch(VerifyLoginException $e){
$this->error = $e->getDisconnectMessage();
$disconnectMessage = $e->getDisconnectMessage();
$this->error = $disconnectMessage instanceof Translatable ? new NonThreadSafeValue($disconnectMessage) : $disconnectMessage;
}
}
@ -195,6 +199,6 @@ class ProcessLoginTask extends AsyncTask{
* @phpstan-var \Closure(bool, bool, Translatable|string|null, ?string) : void $callback
*/
$callback = $this->fetchLocal(self::TLS_KEY_ON_COMPLETION);
$callback($this->authenticated, $this->authRequired, $this->error, $this->clientPublicKey);
$callback($this->authenticated, $this->authRequired, $this->error instanceof NonThreadSafeValue ? $this->error->deserialize() : $this->error, $this->clientPublicKey);
}
}

View File

@ -24,21 +24,26 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\compression;
use pocketmine\scheduler\AsyncTask;
use pocketmine\thread\NonThreadSafeValue;
class CompressBatchTask extends AsyncTask{
private const TLS_KEY_PROMISE = "promise";
/** @phpstan-var NonThreadSafeValue<Compressor> */
private NonThreadSafeValue $compressor;
public function __construct(
private string $data,
CompressBatchPromise $promise,
private Compressor $compressor
Compressor $compressor
){
$this->compressor = new NonThreadSafeValue($compressor);
$this->storeLocal(self::TLS_KEY_PROMISE, $promise);
}
public function onRun() : void{
$this->setResult($this->compressor->compress($this->data));
$this->setResult($this->compressor->deserialize()->compress($this->data));
}
public function onCompletion() : void{

View File

@ -93,7 +93,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
switch($packet->status){
case ResourcePackClientResponsePacket::STATUS_REFUSED:
//TODO: add lang strings for this
$this->session->disconnect("You must accept resource packs to join this server.", true);
$this->session->disconnect("Refused resource packs", "You must accept resource packs to join this server.", true);
break;
case ResourcePackClientResponsePacket::STATUS_SEND_PACKS:
foreach($packet->packIds as $uuid){

View File

@ -26,7 +26,10 @@ namespace pocketmine\network\mcpe\raklib;
use raklib\server\ipc\InterThreadChannelReader;
final class PthreadsChannelReader implements InterThreadChannelReader{
public function __construct(private \Threaded $buffer){}
/**
* @phpstan-param \ThreadedArray<int, string> $buffer
*/
public function __construct(private \ThreadedArray $buffer){}
public function read() : ?string{
return $this->buffer->shift();

View File

@ -26,7 +26,10 @@ namespace pocketmine\network\mcpe\raklib;
use raklib\server\ipc\InterThreadChannelWriter;
final class PthreadsChannelWriter implements InterThreadChannelWriter{
public function __construct(private \Threaded $buffer){}
/**
* @phpstan-param \ThreadedArray<int, string> $buffer
*/
public function __construct(private \ThreadedArray $buffer){}
public function write(string $str) : void{
$this->buffer[] = $str;

View File

@ -85,8 +85,10 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->sleeper = new SleeperNotifier();
$mainToThreadBuffer = new \Threaded();
$threadToMainBuffer = new \Threaded();
/** @phpstan-var \ThreadedArray<int, string> $mainToThreadBuffer */
$mainToThreadBuffer = new \ThreadedArray();
/** @phpstan-var \ThreadedArray<int, string> $threadToMainBuffer */
$threadToMainBuffer = new \ThreadedArray();
$this->rakLib = new RakLibServer(
$this->server->getLogger(),

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\raklib;
use pocketmine\snooze\SleeperNotifier;
use pocketmine\thread\NonThreadSafeValue;
use pocketmine\thread\Thread;
use raklib\generic\Socket;
use raklib\generic\SocketException;
@ -43,19 +44,27 @@ class RakLibServer extends Thread{
protected bool $cleanShutdown = false;
protected bool $ready = false;
protected string $mainPath;
public ?RakLibThreadCrashInfo $crashInfo = null;
/** @phpstan-var NonThreadSafeValue<RakLibThreadCrashInfo>|null */
public ?NonThreadSafeValue $crashInfo = null;
/** @phpstan-var NonThreadSafeValue<InternetAddress> */
protected NonThreadSafeValue $address;
/**
* @phpstan-param \ThreadedArray<int, string> $mainToThreadBuffer
* @phpstan-param \ThreadedArray<int, string> $threadToMainBuffer
*/
public function __construct(
protected \ThreadedLogger $logger,
protected \Threaded $mainToThreadBuffer,
protected \Threaded $threadToMainBuffer,
protected InternetAddress $address,
protected \ThreadedArray $mainToThreadBuffer,
protected \ThreadedArray $threadToMainBuffer,
InternetAddress $address,
protected int $serverId,
protected int $maxMtuSize,
protected int $protocolVersion,
protected SleeperNotifier $mainThreadNotifier
){
$this->mainPath = \pocketmine\PATH;
$this->address = new NonThreadSafeValue($address);
}
/**
@ -75,12 +84,12 @@ class RakLibServer extends Thread{
}
public function getCrashInfo() : ?RakLibThreadCrashInfo{
return $this->crashInfo;
return $this->crashInfo?->deserialize();
}
private function setCrashInfo(RakLibThreadCrashInfo $info) : void{
$this->synchronized(function(RakLibThreadCrashInfo $info) : void{
$this->crashInfo = $info;
$this->crashInfo = new NonThreadSafeValue($info);
$this->notify();
}, $info);
}
@ -91,7 +100,7 @@ class RakLibServer extends Thread{
while(!$this->ready && $this->crashInfo === null){
$this->wait();
}
$crashInfo = $this->crashInfo;
$crashInfo = $this->crashInfo?->deserialize();
if($crashInfo !== null){
if($crashInfo->getClass() === SocketException::class){
throw new SocketException($crashInfo->getMessage());
@ -110,7 +119,7 @@ class RakLibServer extends Thread{
register_shutdown_function([$this, "shutdownHandler"]);
try{
$socket = new Socket($this->address);
$socket = new Socket($this->address->deserialize());
}catch(SocketException $e){
$this->setCrashInfo(RakLibThreadCrashInfo::fromThrowable($e));
return;

View File

@ -27,8 +27,11 @@ use pocketmine\snooze\SleeperNotifier;
use raklib\server\ipc\InterThreadChannelWriter;
final class SnoozeAwarePthreadsChannelWriter implements InterThreadChannelWriter{
/**
* @phpstan-param \ThreadedArray<int, string> $buffer
*/
public function __construct(
private \Threaded $buffer,
private \ThreadedArray $buffer,
private SleeperNotifier $notifier
){}

View File

@ -2090,16 +2090,24 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
/**
* Kicks a player from the server
*
* @param Translatable|string $reason Shown in the server log - this should be a short one-line message
* @param Translatable|string|null $quitMessage Message to broadcast to online players (null will use default)
* @param Translatable|string|null $disconnectScreenMessage Shown on the player's disconnection screen (null will use the reason)
*/
public function kick(Translatable|string $reason = "", Translatable|string|null $quitMessage = null) : bool{
$ev = new PlayerKickEvent($this, $reason, $quitMessage ?? $this->getLeaveMessage());
public function kick(Translatable|string $reason = "", Translatable|string|null $quitMessage = null, Translatable|string|null $disconnectScreenMessage = null) : bool{
$ev = new PlayerKickEvent($this, $reason, $quitMessage ?? $this->getLeaveMessage(), $disconnectScreenMessage);
$ev->call();
if(!$ev->isCancelled()){
$reason = $ev->getReason();
$reason = $ev->getDisconnectReason();
if($reason === ""){
$reason = KnownTranslationFactory::disconnectionScreen_noReason();
}
$this->disconnect($reason, $ev->getQuitMessage());
$disconnectScreenMessage = $ev->getDisconnectScreenMessage() ?? $reason;
if($disconnectScreenMessage === ""){
$disconnectScreenMessage = KnownTranslationFactory::disconnectionScreen_noReason();
}
$this->disconnect($reason, $ev->getQuitMessage(), $disconnectScreenMessage);
return true;
}
@ -2116,15 +2124,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
*
* Note for internals developers: Do not call this from network sessions. It will cause a feedback loop.
*
* @param Translatable|string $reason Shown on the disconnect screen, and in the server log
* @param Translatable|string|null $quitMessage Message to broadcast to online players (null will use default)
* @param Translatable|string $reason Shown in the server log - this should be a short one-line message
* @param Translatable|string|null $quitMessage Message to broadcast to online players (null will use default)
* @param Translatable|string|null $disconnectScreenMessage Shown on the player's disconnection screen (null will use the reason)
*/
public function disconnect(Translatable|string $reason, Translatable|string|null $quitMessage = null) : void{
public function disconnect(Translatable|string $reason, Translatable|string|null $quitMessage = null, Translatable|string|null $disconnectScreenMessage = null) : void{
if(!$this->isConnected()){
return;
}
$this->getNetworkSession()->onPlayerDestroyed($reason);
$this->getNetworkSession()->onPlayerDestroyed($reason, $disconnectScreenMessage ?? $reason);
$this->onPostDisconnect($reason, $quitMessage);
}
@ -2132,6 +2141,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
* @internal
* This method executes post-disconnect actions and cleanups.
*
* @param Translatable|string $reason Shown in the server log - this should be a short one-line message
* @param Translatable|string|null $quitMessage Message to broadcast to online players (null will use default)
*/
public function onPostDisconnect(Translatable|string $reason, Translatable|string|null $quitMessage) : void{

View File

@ -158,7 +158,7 @@ class AsyncPool{
throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once");
}
$task->progressUpdates = new \Threaded();
$task->progressUpdates = new \ThreadedArray();
$task->setSubmitted();
$this->getWorker($worker)->stack($task);

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\scheduler;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\thread\NonThreadSafeValue;
use function igbinary_serialize;
use function igbinary_unserialize;
use function is_null;
use function is_scalar;
use function is_string;
use function spl_object_id;
/**
@ -51,15 +51,10 @@ use function spl_object_id;
* thread, e.g. during {@link AsyncTask::onCompletion()} or {@link AsyncTask::onProgressUpdate()}. This means that
* whatever you do in onRun() must be able to work without the Server instance.
*
* WARNING: Any non-Threaded objects WILL BE SERIALIZED when assigned to members of AsyncTasks or other Threaded object.
* If later accessed from said Threaded object, you will be operating on a COPY OF THE OBJECT, NOT THE ORIGINAL OBJECT.
* If you want to store non-serializable objects to access when the task completes, store them using
* If you want to store non-thread-safe objects to access when the task completes, store them using
* {@link AsyncTask::storeLocal}.
*
* WARNING: Arrays are converted to Volatile objects when assigned as members of Threaded objects.
* Keep this in mind when using arrays stored as members of your AsyncTask.
*/
abstract class AsyncTask extends \Threaded{
abstract class AsyncTask extends \ThreadedRunnable{
/**
* @var \ArrayObject|mixed[]|null object hash => mixed data
* @phpstan-var \ArrayObject<int, array<string, mixed>>|null
@ -71,10 +66,11 @@ abstract class AsyncTask extends \Threaded{
/** @var AsyncWorker|null $worker */
public $worker = null;
public \Threaded $progressUpdates;
/** @phpstan-var \ThreadedArray<int, string> */
public \ThreadedArray $progressUpdates;
private string|int|bool|null|float $result = null;
private bool $serialized = false;
/** @phpstan-var NonThreadSafeValue<mixed>|string|int|bool|float|null */
private NonThreadSafeValue|string|int|bool|null|float $result = null;
private bool $cancelRun = false;
private bool $submitted = false;
@ -117,15 +113,14 @@ abstract class AsyncTask extends \Threaded{
* @return mixed
*/
public function getResult(){
if($this->serialized){
if(!is_string($this->result)) throw new AssumptionFailedError("Result expected to be a serialized string");
return igbinary_unserialize($this->result);
if($this->result instanceof NonThreadSafeValue){
return $this->result->deserialize();
}
return $this->result;
}
public function setResult(mixed $result) : void{
$this->result = ($this->serialized = !is_scalar($result)) ? igbinary_serialize($result) : $result;
$this->result = is_scalar($result) || is_null($result) ? $result : new NonThreadSafeValue($result);
}
public function cancelRun() : void{
@ -164,15 +159,14 @@ abstract class AsyncTask extends \Threaded{
* @param mixed $progress A value that can be safely serialize()'ed.
*/
public function publishProgress(mixed $progress) : void{
$this->progressUpdates[] = igbinary_serialize($progress);
$this->progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable");
}
/**
* @internal Only call from AsyncPool.php on the main thread
*/
public function checkProgressUpdates() : void{
while($this->progressUpdates->count() !== 0){
$progress = $this->progressUpdates->shift();
while(($progress = $this->progressUpdates->shift()) !== null){
$this->onProgressUpdate(igbinary_unserialize($progress));
}
}

View File

@ -28,8 +28,11 @@ use pocketmine\Server;
use function error_reporting;
trait CommonThreadPartsTrait{
/** @var \Threaded|\ClassLoader[]|null */
private ?\Threaded $classLoaders = null;
/**
* @var \ThreadedArray|\ClassLoader[]|null
* @phpstan-var \ThreadedArray<int, \ClassLoader>|null
*/
private ?\ThreadedArray $classLoaders = null;
protected ?string $composerAutoloaderPath = null;
protected bool $isKilled = false;
@ -52,7 +55,7 @@ trait CommonThreadPartsTrait{
}
if($this->classLoaders === null){
$this->classLoaders = new \Threaded();
$this->classLoaders = new \ThreadedArray();
}else{
foreach($this->classLoaders as $k => $autoloader){
unset($this->classLoaders[$k]);

View File

@ -0,0 +1,57 @@
<?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\thread;
use function get_debug_type;
use function igbinary_serialize;
use function igbinary_unserialize;
/**
* This class automatically serializes values which can't be shared between threads.
* This class does NOT enable sharing the variable between threads. Each call to deserialize() will return a new copy
* of the variable.
*
* @phpstan-template TValue
*/
final class NonThreadSafeValue extends \ThreadedBase{
private string $variable;
/**
* @phpstan-param TValue $variable
*/
public function __construct(
mixed $variable
){
$this->variable = igbinary_serialize($variable) ?? throw new \InvalidArgumentException("Cannot serialize variable of type " . get_debug_type($variable));
}
/**
* Returns a deserialized copy of the original variable.
*
* @phpstan-return TValue
*/
public function deserialize() : mixed{
return igbinary_unserialize($this->variable);
}
}

View File

@ -25,7 +25,7 @@ namespace pocketmine\thread;
use function spl_object_id;
class ThreadManager extends \Volatile{
class ThreadManager extends \ThreadedBase{
private static ?self $instance = null;
@ -40,12 +40,19 @@ class ThreadManager extends \Volatile{
return self::$instance;
}
/** @phpstan-var \ThreadedArray<int, Thread|Worker> */
private \ThreadedArray $threads;
private function __construct(){
$this->threads = new \ThreadedArray();
}
public function add(Worker|Thread $thread) : void{
$this[spl_object_id($thread)] = $thread;
$this->threads[spl_object_id($thread)] = $thread;
}
public function remove(Worker|Thread $thread) : void{
unset($this[spl_object_id($thread)]);
unset($this->threads[spl_object_id($thread)]);
}
/**
@ -56,7 +63,7 @@ class ThreadManager extends \Volatile{
/**
* @var Worker|Thread $thread
*/
foreach($this as $key => $thread){
foreach($this->threads as $key => $thread){
$array[$key] = $thread;
}

View File

@ -194,6 +194,9 @@ class MainLogger extends \AttachableThreadedLogger implements \BufferedLogger{
Terminal::writeLine($message);
$this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL);
/**
* @var \ThreadedLoggerAttachment $attachment
*/
foreach($this->attachments as $attachment){
$attachment->log($level, $message);
}

View File

@ -30,14 +30,15 @@ use function is_resource;
use function touch;
final class MainLoggerThread extends \Thread{
private \Threaded $buffer;
/** @phpstan-var \ThreadedArray<int, string> */
private \ThreadedArray $buffer;
private bool $syncFlush = false;
private bool $shutdown = false;
public function __construct(
private string $logFile
){
$this->buffer = new \Threaded();
$this->buffer = new \ThreadedArray();
touch($this->logFile);
}
@ -72,9 +73,7 @@ final class MainLoggerThread extends \Thread{
* @param resource $logResource
*/
private function writeLogStream($logResource) : void{
while($this->buffer->count() > 0){
/** @var string $chunk */
$chunk = $this->buffer->shift();
while(($chunk = $this->buffer->shift()) !== null){
fwrite($logResource, $chunk);
}

View File

@ -92,7 +92,7 @@ abstract class Terminal{
self::$FORMAT_RESET = "\x1b[m";
$color = fn(int $code) => "\x1b[38;5;${code}m";
$color = fn(int $code) => "\x1b[38;5;{$code}m";
self::$COLOR_BLACK = $color(16);
self::$COLOR_DARK_BLUE = $color(19);

View File

@ -46,9 +46,9 @@ parameters:
path: ../../../src/plugin/PluginManager.php
-
message: "#^Offset \\(int\\|string\\) on array\\<pocketmine\\\\plugin\\\\Plugin\\> in isset\\(\\) always exists and is not nullable\\.$#"
message: "#^Parameter \\#1 \\$work of method Worker\\:\\:stack\\(\\) expects Threaded, pocketmine\\\\scheduler\\\\AsyncTask given\\.$#"
count: 1
path: ../../../src/plugin/PluginManager.php
path: ../../../src/scheduler/AsyncPool.php
-
message: "#^Static property pocketmine\\\\scheduler\\\\AsyncTask\\:\\:\\$threadLocalStorage \\(ArrayObject\\<int, array\\<string, mixed\\>\\>\\|null\\) does not accept non\\-empty\\-array\\<int, non\\-empty\\-array\\<string, mixed\\>\\>\\|ArrayObject\\<int, array\\<string, mixed\\>\\>\\.$#"
@ -56,12 +56,12 @@ parameters:
path: ../../../src/scheduler/AsyncTask.php
-
message: "#^Property pocketmine\\\\thread\\\\Thread\\:\\:\\$classLoaders \\(\\(iterable\\<ClassLoader\\>&Threaded\\)\\|null\\) does not accept array\\<int, ClassLoader\\>\\|\\(iterable\\<ClassLoader\\>&Threaded\\)\\.$#"
message: "#^Property pocketmine\\\\thread\\\\Thread\\:\\:\\$classLoaders \\(ThreadedArray\\<int, ClassLoader\\>\\|null\\) does not accept array\\{ClassLoader\\}\\|ThreadedArray\\<int, ClassLoader\\>\\.$#"
count: 1
path: ../../../src/thread/Thread.php
-
message: "#^Property pocketmine\\\\thread\\\\Worker\\:\\:\\$classLoaders \\(\\(iterable\\<ClassLoader\\>&Threaded\\)\\|null\\) does not accept array\\<int, ClassLoader\\>\\|\\(iterable\\<ClassLoader\\>&Threaded\\)\\.$#"
message: "#^Property pocketmine\\\\thread\\\\Worker\\:\\:\\$classLoaders \\(ThreadedArray\\<int, ClassLoader\\>\\|null\\) does not accept array\\{ClassLoader\\}\\|ThreadedArray\\<int, ClassLoader\\>\\.$#"
count: 1
path: ../../../src/thread/Worker.php

View File

@ -1,6 +1,33 @@
<?php
/**
* @implements \Traversable<array-key, mixed>
* @implements \IteratorAggregate<array-key, mixed>
*/
class Threaded implements \Traversable{}
abstract class ThreadedBase implements \IteratorAggregate{
/**
* @template TReturn
* @param \Closure() : TReturn $function
* @param mixed ...$args
* @return TReturn
*/
public function synchronized(\Closure $function, mixed ...$args) : mixed{}
}
/**
* @template TKey of array-key
* @template TValue
* @implements ArrayAccess<TKey, TValue>
*/
final class ThreadedArray extends ThreadedBase implements Countable, ArrayAccess{
/**
* @return TValue|null
*/
public function pop() : mixed{}
/**
* @return TValue|null
*/
public function shift() : mixed{}
}