Compare commits

..

95 Commits

Author SHA1 Message Date
2f5e08067d Release 4.12.10 2023-01-18 15:16:30 +00:00
a8556dff02 RakLibInterface: include Snooze events in Connection Handler timings 2023-01-18 15:02:33 +00:00
664089861a Bump phpstan/phpstan from 1.9.11 to 1.9.12 (#5517)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.11 to 1.9.12.
- [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.11...1.9.12)

---
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-18 14:27:51 +00:00
6c0254c1eb Block: document parameters of place() 2023-01-16 19:49:24 +00:00
0bb9fb09cc CS again 2023-01-16 19:39:24 +00:00
ab21fcdd67 Server: fixed load statistics not including Snooze processing time
this has been a bug ever since Snooze was first introduced. The load statistic, similarly to timings, did not account for time spent processing notifications between ticks. The problem is that this is often where a significant amoutn of the load actually comes from, because Snooze is most often activated due to incoming packets.

This change fixes the problem by including the time spent processing notifications since the previous tick in the current tick's usage metric.
2023-01-16 19:26:32 +00:00
b03df4f1e6 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2023-01-16 18:25:34 +00:00
0a2a6e2b3a 4.12.10 is next 2023-01-16 18:25:20 +00:00
0eb751c1c9 Release 4.12.9 2023-01-16 18:25:17 +00:00
b59b1e491e Bump phpunit/phpunit from 9.5.27 to 9.5.28 (#5514)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.27 to 9.5.28.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/9.5.28/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.27...9.5.28)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  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-16 18:06:42 +00:00
95e8c68fde Bump docker/build-push-action from 3.2.0 to 3.3.0 (#5513)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3.2.0...v3.3.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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-16 18:06:31 +00:00
7e16f9be8f InGamePacketHandler: handle block actions before use item transactions
the START_BREAK and transaction to break the block may arrive in the same packet, causing events to be fired in the wrong order.

fixes #5490
2023-01-16 17:50:11 +00:00
768650cee0 CS 2023-01-16 17:46:50 +00:00
c2c529e2da Include Snooze interrupts in timings results
fixes #5511

This requires any Timings instances to be updated to
pmmp/timings@5410f62436, otherwise the TPS
reported will be incorrect.
2023-01-16 17:40:39 +00:00
ba18a81e88 NetworkSession: fixed rate limit getting exhausted after 2.5 seconds during PvP 2023-01-13 15:28:03 +00:00
39218017ca Fixed walls and thin blocks not connecting to each other
closes #5498
2023-01-12 22:16:41 +00:00
f4a1d69075 Bell: fixed support requirements
this somehow got overlooked in the support types refactor.
2023-01-12 21:45:25 +00:00
cbeae906e1 Torch: remove unused variable 2023-01-12 21:34:44 +00:00
b25e8e26f0 BaseBanner: fixed incorrect support requirements 2023-01-12 21:31:50 +00:00
a79be994de World: fixed block placement when clicking on replaceable blocks
in vanilla, it appears to behave as if the player always clicked on the up face if a block was replaced.

In PM, we were still using the original face, which caused bugs when, for example, placing a button next to a wall by clicking on the side of tallgrass. The button would replace the tallgrass, but stick to the wall, instead of placing itself on the ground like vanilla expects.

This may appear unusual to anyone who also happens to implement canBePlacedAt(), since the facing behaviour will be different. However, this behaviour appears to match vanilla, and even slabs (which I feared might break because of this change) work perfectly.

In the future, it may be desirable to pass some other value here, such as null, to indicate that the clicked block is being replaced. However, that's a BC break and therefore outside of the scope of a stable bug fix.
2023-01-12 21:11:48 +00:00
e26c8b9e9f block: eliminate suspicious usages of $blockClicked in place() 2023-01-12 20:35:26 +00:00
4e9c3e101d Bell: fixed blocks not being able to be placed when not ringing the bell 2023-01-12 19:42:33 +00:00
d295e1be54 PressurePlate: destroy self when no support is present 2023-01-12 19:36:47 +00:00
2f3fcef97c Fixed blocks incorrectly using blockClicked for support checks
this caused some interesting bugs, such as being able to place floating pressure plates by clicking on the side of a solid block halfway up a wall.
2023-01-12 19:36:23 +00:00
c5056e0a43 phpstan 1.9.11 2023-01-12 19:01:57 +00:00
a47aa50477 Update composer dependencies 2023-01-12 18:59:18 +00:00
f7930a3a0b 4.12.9 is next 2023-01-09 00:04:40 +00:00
bb7df60a4d Release 4.12.8 2023-01-09 00:04:40 +00:00
992cb06da6 NetworkSession: fixed rate limit not being increased correctly on Windows
due to the 15ms scheduler interval, the server will often sleep 45ms instead of 50ms, which causes the budget not to get updated.
2023-01-09 00:01:56 +00:00
bb3f87f862 NetworkSession: allow 2 batches per tick
apparently InventoryTransactionPacket may arrive outside of the normal update cycle, since it's prioritized to reduce latency.
2023-01-09 00:00:39 +00:00
fc77b14760 4.12.8 is next 2023-01-08 20:23:18 +00:00
52b6f1a492 Release 4.12.7 2023-01-08 20:23:15 +00:00
0233e74f4f NetworkSession: micro optimisation - do not check if a debugger is active unless the packet limit is exceeded 2023-01-08 19:45:14 +00:00
dd355c58d8 NetworkSession: fix CS 2023-01-08 19:35:43 +00:00
267032cff9 NetworkSession: do not rate limit packets if a debugging session is active 2023-01-08 19:31:39 +00:00
6cecd690b2 CS 2023-01-07 21:33:53 +00:00
91e38d1f97 NetworkSession: compensate for server lag in batch budgeting 2023-01-07 21:33:34 +00:00
653178c1fd 4.12.7 is next 2023-01-07 16:22:01 +00:00
eb06535ed1 Release 4.12.6 2023-01-07 16:22:01 +00:00
f9bcc8e862 NetworkSession: added a rate-limit for incoming batches 2023-01-07 16:19:02 +00:00
f43ca405d4 4.12.6 is next 2023-01-06 00:44:58 +00:00
d146175d27 Release 4.12.5 2023-01-06 00:44:58 +00:00
3baa5ab712 InGamePacketHandler: removed obsolete workaround 2023-01-06 00:41:57 +00:00
c02bead12b Bump phpstan/phpstan from 1.9.6 to 1.9.7 (#5495)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.6 to 1.9.7.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.9.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.6...1.9.7)

---
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-05 16:46:45 +00:00
e9ca25c1cb typo 2023-01-05 16:45:56 +00:00
a513cca582 Stop Dependabot from creating PRs for locale-data minor and major versions 2023-01-05 16:45:22 +00:00
9a47c1d401 Bump shivammathur/setup-php from 2.22.0 to 2.23.0 (#5474)
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.22.0 to 2.23.0.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.22.0...2.23.0)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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-04 20:15:11 +00:00
433f5451d7 Improve World::notifyNeighbourBlockUpdate() doc (#5491) 2023-01-04 20:11:55 +00:00
64e505defb Bump phpstan/phpstan from 1.9.4 to 1.9.6 (#5492)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.4 to 1.9.6.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.9.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.4...1.9.6)

---
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-04 20:05:34 +00:00
33d1755eae Player: fixed not receiving slot updates for small crafting grid
I'm not really sure why this tracking logic is here and not in InventoryManager, but for now it is what it is.
2023-01-04 01:28:25 +00:00
fc63c54116 CraftingManager: more detailed type information for shapelessRecipes and shapedRecipes 2023-01-04 00:32:26 +00:00
db07976aab TypeConverter: do not send useless meta to the client 2023-01-03 23:50:31 +00:00
0e4b79ea77 InGamePacketHandler: fixed client-side predictions not getting rolled back for block placement 2023-01-03 19:43:21 +00:00
588c9b114b 4.12.5 is next 2023-01-03 19:30:02 +00:00
b095f606c1 Release 4.12.4 2023-01-03 19:29:47 +00:00
b312e93176 Limit list max size in transactions
this duct tape is to limit the impact of a security vulnerability being actively exploited.
2023-01-02 22:59:48 +00:00
61933624d2 NetworkSession: Lift batch limit to 1300 to allow shift-click crafting to work correctly
the theoretical limit for transactions in this case is 64x9 (inputs) + 64x9 (output on crafting grid) + 64 (outputs to main slot) + 64 CraftingEventPackets = 1280.
This is an extreme case which assumes that a recipe could generate up to 64x10 (640) output items per iteration, filling every slot of the output grid, which should never occur in any reasonable circumstances.
2022-12-30 22:11:35 +00:00
90beaeaeb4 4.12.4 is next 2022-12-28 21:01:50 +00:00
466f7e98ed Release 4.12.3 2022-12-28 21:01:47 +00:00
59be901efe Fixed unauthenticated sessions taking up player slots 2022-12-28 20:42:33 +00:00
5d2b0acfc8 GamemodeCommand: report failure if the target's game mode is already the desired game mode
this has irritated me for years.
2022-12-22 18:38:10 +00:00
7d1d62042c attempted fix for GitHub rate limiting php-cs-fixer installation
from https://github.com/shivammathur/setup-php/issues/678#issuecomment-1363128626
2022-12-22 18:13:03 +00:00
17dde140a5 Bump phpstan/phpstan-phpunit from 1.3.2 to 1.3.3 (#5465)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.3.2 to 1.3.3.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.3.2...1.3.3)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-phpunit
  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>
2022-12-22 15:23:10 +00:00
1e5597f0d5 World: account for null chunk edge case in tickChunk()
the target chunk may no longer be loaded if it was unloaded during a previous chunk's tick (e.g. during BlockGrowEvent).
Since the parent function iterates over a pre-selected array of chunks, the chunk will still be present in the list even if it's no longer loaded by the time it's reached.
2022-12-19 20:20:52 +00:00
97ef209c5f HandlerList: added missing class-string type for constructor 2022-12-19 16:26:36 +00:00
1305fd5fb2 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2022-12-19 15:52:56 +00:00
7a137f932f Updated build/php submodule to pmmp/php-build-scripts@6b605ed7c4 2022-12-19 15:52:44 +00:00
63e8b1cf3a Bump phpstan/phpstan from 1.9.3 to 1.9.4 (#5458)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.9.3 to 1.9.4.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.9.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.9.3...1.9.4)

---
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>
2022-12-19 15:39:59 +00:00
65bce762ff Updated transient Composer dependencies 2022-12-19 15:34:17 +00:00
529700bb8b PotionTypeIdMap: fixed uninitialized fields
I have no idea why this didn't ever cause a problem before... probably an edge-case in the typed properties implementation
2022-12-19 15:33:15 +00:00
437fa615b8 4.12.3 is next 2022-12-15 21:08:28 +00:00
0ee6cdb058 Release 4.12.2 2022-12-15 21:08:23 +00:00
97d6a79b25 Updated BedrockProtocol 2022-12-15 20:37:36 +00:00
8b5e4c1c16 Updated PHPStan 2022-12-15 20:16:56 +00:00
214a5ddc15 Bump netresearch/jsonmapper from 4.0.0 to 4.1.0 (#5446)
Bumps [netresearch/jsonmapper](https://github.com/cweiske/jsonmapper) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/cweiske/jsonmapper/releases)
- [Changelog](https://github.com/cweiske/jsonmapper/blob/master/ChangeLog)
- [Commits](https://github.com/cweiske/jsonmapper/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: netresearch/jsonmapper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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>
2022-12-15 19:28:11 +00:00
9f7dfe3355 Bump phpunit/phpunit from 9.5.26 to 9.5.27 (#5447)
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.26 to 9.5.27.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-9.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.26...9.5.27)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  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>
2022-12-15 19:21:39 +00:00
9b55d18393 Bump ncipollo/release-action from 1.11.2 to 1.12.0 (#5451)
Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/ncipollo/release-action/releases)
- [Commits](https://github.com/ncipollo/release-action/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: ncipollo/release-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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>
2022-12-15 19:20:28 +00:00
31465525e3 Fixed PHP-CS-Fixer not import global constants. (#5449) 2022-12-12 17:12:33 +00:00
74613b9b09 Bump phpstan/phpstan-phpunit from 1.2.2 to 1.3.0 (#5445)
Bumps [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) from 1.2.2 to 1.3.0.
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/1.2.2...1.3.0)

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

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>
2022-12-10 21:17:03 +00:00
1cefe24414 InGamePacketHandler: fixed transaction predictions not getting rolled back for failed use/release/interact transactions
this was caused by b5cfab497d.
2022-12-10 21:16:08 +00:00
8bf85d4a18 bootstrap: avoid using is_writable() to check for data directory access permissions
closes #5440
2022-12-06 14:13:37 +00:00
b5e6dec0c6 Filesystem: capture error information from attempted lock file creation 2022-12-06 14:06:08 +00:00
a3306914cc bootstrap: do not require the plugins dir to be writable
on a modern installation, the plugins folder does not need to be writable by the server. Perhaps DevTools should be checking this for /genplugin, but it's not required otherwise.
2022-12-06 13:58:50 +00:00
3b32ea1b0b Update PhpStorm code style settings 2022-12-06 13:22:54 +00:00
7ec32f981e Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2022-12-06 13:22:15 +00:00
b0c87e9d06 Added phpdoc_align rule for php-cs-fixer 2022-12-06 13:21:57 +00:00
99996b62d6 Align PhpDoc @param tags according to PHP-CS-Fixer 2022-12-06 13:21:20 +00:00
1cb6d9f5af Bump dessant/support-requests from 2 to 3 (#5442)
Bumps [dessant/support-requests](https://github.com/dessant/support-requests) from 2 to 3.
- [Release notes](https://github.com/dessant/support-requests/releases)
- [Changelog](https://github.com/dessant/support-requests/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dessant/support-requests/compare/v2...v3)

---
updated-dependencies:
- dependency-name: dessant/support-requests
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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>
2022-12-05 21:10:09 +00:00
0a9b52618d 4.12.2 is next 2022-12-04 23:11:48 +00:00
7ae6425d05 Release 4.12.1 2022-12-04 23:11:48 +00:00
b5cfab497d Clean up inventory content syncing, fixes #5441
these remnants should have been cleaned up in 4.11, but I somehow managed to skip over them.
2022-12-04 23:05:30 +00:00
774e23137e changelog: added detail about InventoryManager internals changes
this isn't considered part of the API, but certain specialized popular plugins (e.g. InvMenu) require it.
2022-12-01 20:48:38 +00:00
43bc3c7b25 Added tool to dump JSON contents of encoded crashdump data 2022-12-01 19:36:31 +00:00
eb62dc3294 Added documentation for RegistryTrait, EnumTrait and CloningRegistryTrait 2022-11-30 21:04:17 +00:00
279056fe2f 4.12.1 is next 2022-11-30 16:35:58 +00:00
97 changed files with 763 additions and 432 deletions

View File

@ -6,6 +6,12 @@ updates:
interval: daily interval: daily
time: "10:00" time: "10:00"
open-pull-requests-limit: 10 open-pull-requests-limit: 10
ignore:
#only allow patch updates for locale-data - this has to be updated manually due to codegen
- dependency-name: pocketmine/locale-data
update-types:
- "version-update:semver-major"
- "version-update:semver-minor"
- package-ecosystem: gitsubmodule - package-ecosystem: gitsubmodule
directory: "/" directory: "/"

View File

@ -46,7 +46,7 @@ jobs:
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}") run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
- name: Build image for tag - name: Build image for tag
uses: docker/build-push-action@v3.2.0 uses: docker/build-push-action@v3.3.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp
@ -59,7 +59,7 @@ jobs:
- name: Build image for major tag - name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable' if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.2.0 uses: docker/build-push-action@v3.3.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp
@ -72,7 +72,7 @@ jobs:
- name: Build image for minor tag - name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable' if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.2.0 uses: docker/build-push-action@v3.3.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp
@ -85,7 +85,7 @@ jobs:
- name: Build image for latest tag - name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable' if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.2.0 uses: docker/build-push-action@v3.3.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup PHP and tools - name: Setup PHP and tools
uses: shivammathur/setup-php@2.22.0 uses: shivammathur/setup-php@2.23.0
with: with:
php-version: 8.0 php-version: 8.0

View File

@ -18,7 +18,7 @@ jobs:
submodules: true submodules: true
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@2.22.0 uses: shivammathur/setup-php@2.23.0
with: with:
php-version: 8.0 php-version: 8.0
@ -69,7 +69,7 @@ jobs:
${{ github.workspace }}/build_info.json ${{ github.workspace }}/build_info.json
- name: Create draft release - name: Create draft release
uses: ncipollo/release-action@v1.11.2 uses: ncipollo/release-action@v1.12.0
with: with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }} commit: ${{ github.sha }}

View File

@ -195,10 +195,12 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup PHP and tools - name: Setup PHP and tools
uses: shivammathur/setup-php@2.22.0 uses: shivammathur/setup-php@2.23.0
with: with:
php-version: 8.0 php-version: 8.0
tools: php-cs-fixer:3.11 tools: php-cs-fixer:3.11
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run PHP-CS-Fixer - name: Run PHP-CS-Fixer
run: php-cs-fixer fix --dry-run --diff --ansi run: php-cs-fixer fix --dry-run --diff --ansi

View File

@ -8,7 +8,7 @@ jobs:
support: support:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/support-requests@v2 - uses: dessant/support-requests@v3
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
support-label: "Support request" support-label: "Support request"

View File

@ -62,6 +62,11 @@
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />
</indentOptions> </indentOptions>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="Shell Script">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="neon"> <codeStyleSettings language="neon">
<indentOptions> <indentOptions>
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />

View File

@ -66,6 +66,9 @@ BODY,
], ],
'indentation_type' => true, 'indentation_type' => true,
'logical_operators' => true, 'logical_operators' => true,
'native_constant_invocation' => [
'scope' => 'namespaced'
],
'native_function_invocation' => [ 'native_function_invocation' => [
'scope' => 'namespaced', 'scope' => 'namespaced',
'include' => ['@all'], 'include' => ['@all'],
@ -92,6 +95,12 @@ BODY,
], ],
'sort_algorithm' => 'alpha' 'sort_algorithm' => 'alpha'
], ],
'phpdoc_align' => [
'align' => 'vertical',
'tags' => [
'param',
]
],
'phpdoc_line_span' => [ 'phpdoc_line_span' => [
'property' => 'single', 'property' => 'single',
'method' => null, 'method' => null,

View File

@ -39,6 +39,7 @@ use function sprintf;
use function str_replace; use function str_replace;
use function substr; use function substr;
use const SORT_STRING; use const SORT_STRING;
use const STDERR;
if(count($argv) !== 2){ if(count($argv) !== 2){
fwrite(STDERR, "Provide a path to process\n"); fwrite(STDERR, "Provide a path to process\n");

View File

@ -40,6 +40,7 @@ use function rtrim;
use function sprintf; use function sprintf;
use function str_replace; use function str_replace;
use function unlink; use function unlink;
use const DIRECTORY_SEPARATOR;
use const PHP_EOL; use const PHP_EOL;
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';

View File

@ -95,6 +95,10 @@ Released 25th November 2022.
- Improved performance of `ContainerTrait` dropping items on block destroy. ([link](https://github.com/pmmp/PocketMine-MP/commits/24e72ec109c1442b09558df89b6833cf2f2e0ec7)) - Improved performance of `ContainerTrait` dropping items on block destroy. ([link](https://github.com/pmmp/PocketMine-MP/commits/24e72ec109c1442b09558df89b6833cf2f2e0ec7))
- Avoid repeated calls to `Position->getWorld()` (use local variables). ([link](https://github.com/pmmp/PocketMine-MP/commit/2940547026db40ce76deb46e992870de3ead79ad)) - Avoid repeated calls to `Position->getWorld()` (use local variables). ([link](https://github.com/pmmp/PocketMine-MP/commit/2940547026db40ce76deb46e992870de3ead79ad))
- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. ([link](https://github.com/pmmp/PocketMine-MP/commit/e90abecf38d9c57635fa0497514bba7e546a2469)) - Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. ([link](https://github.com/pmmp/PocketMine-MP/commit/e90abecf38d9c57635fa0497514bba7e546a2469))
- Inventories are now mapped on a per-slot basis. This means that more than one inventory can be mapped to the same window ID, which is necessary for correctly handling "UI" inventories like crafting tables.
- `InventoryManager->getWindow(int $windowId) : ?Inventory` is replaced by `locateWindowAndSlot` (see below).
- Added `InventoryManager->locateWindowAndSlot(int $windowId, int $netSlotId) : array{Inventory, int}` - accepts a window ID and absolute slot ID, and returns the associated inventory and the slot relative to the inventory's own start (for use with `getItem()` etc.).
- Slot offset mapping for "UI" inventories is now handled in `InventoryManager->createComplexSlotMapping()` instead of in `TypeConverter`.
- Console polling is now done on the main thread (no longer a performance concern). ([link](https://github.com/pmmp/PocketMine-MP/commit/b3f03d7ae645de67a54b7300c09b94eeca16298e)) - Console polling is now done on the main thread (no longer a performance concern). ([link](https://github.com/pmmp/PocketMine-MP/commit/b3f03d7ae645de67a54b7300c09b94eeca16298e))
- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. ([link](https://github.com/pmmp/PocketMine-MP/commit/2585160ca2c4df5758b8b980331307402ff9f0fb)) - Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. ([link](https://github.com/pmmp/PocketMine-MP/commit/2585160ca2c4df5758b8b980331307402ff9f0fb))
- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. ([link](https://github.com/pmmp/PocketMine-MP/commit/83e5b0adb6fa0dddec377182bb1c7945ac8f7820)) - `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. ([link](https://github.com/pmmp/PocketMine-MP/commit/83e5b0adb6fa0dddec377182bb1c7945ac8f7820))

View File

@ -12,3 +12,95 @@ Released 30th November 2022.
## General ## General
- Added support for Minecraft: Bedrock Edition 1.19.50. - Added support for Minecraft: Bedrock Edition 1.19.50.
- Removed support for older versions. - Removed support for older versions.
# 4.12.1
Released 4th December 2022.
## Fixes
- Fixed items glitching when dragging a stack of items across the crafting grid (desync issues).
# 4.12.2
Released 15th December 2022.
## Fixes
- Folder used for plugins (optionally specified by `--plugins`) is no longer required to be writable.
- Fixed broken writable check for server data folder (`is_writable()` broken on NFS and similar filesystems).
- `Filesystem::createLockFile()` exceptions now include more information about why the lock file could not be created.
- Fixed client-side item predictions not being rolled back when cancelling events such as `PlayerItemUseEvent`.
## Dependencies
- Updated BedrockProtocol to [17.1.0](https://github.com/pmmp/BedrockProtocol/releases/tag/17.1.0+bedrock-1.19.50). This adds some missing `LevelSoundEvent` constants and fixes the values for `ContainerUIIds`.
# 4.12.3
Released 28th December 2022.
## Fixes
- Fixed unauthenticated connections taking up player count slots, preventing players from joining.
- Fixed a possible crash in `World->tickChunk()` when plugins unload chunks during some events.
- `/gamemode` will now report a failure to change game mode if the player is already in the requested game mode.
# 4.12.4
Released 3rd January 2023.
## Fixes
- Added workarounds for an active exploit being used to deny service to servers.
# 4.12.5
Released 6th January 2023.
## Fixes
- Removed a workaround for an old client bug in custom form responses. The code contained a denial-of-service vulnerability.
# 4.12.6
Released 7th January 2023.
## Changes
- Added a new security measure to `NetworkSession` to detect and ban players who flood the server with packets.
# 4.12.7
Released 8th January 2023.
## Fixes
- Fixed players getting kicked when the server lags for too long.
- Fixed players getting kicked when a debugging session is active and a breakpoint is hit.
# 4.12.8
Released 9th January 2023.
## Fixes
- Fixed players getting kicked during PvP.
- Fixed players randomly getting kicked on Windows (improper rate limit handling wrt. 15ms timer resolution).
# 4.12.9
Released 16th January 2023.
## Improvements
### Timings
- Added new timers:
- `Server Mid-Tick Processing` - time spent processing Snooze interrupts between ticks (e.g. incoming network packets)
- `Server Tick Update Cycle` - time spent processing regular per-tick updates (e.g. entity movement, world updates, etc.) (`Server->tick()`)
- `Full Server Tick` timer now counts the total of `Server Mid-Tick Processing` and `Server Tick Update Cycle`, which generates more accurate performance metrics.
- Previously, this timer only counted the time spent during regular per-tick updates, and the time recorded by `Server Mid-Tick Processing` was not included in the report at all.
## Fixes
- Fixed blocks such as pressure plates being able to be placed without the correct supporting blocks if the clicked block was solid.
- Pressure plates now self-destruct when the block below them is removed.
- Fixed being unable to place blocks by clicking on the side of a bell (when the click doesn't result in ringing the bell).
- Fixed various rotation-aware blocks (e.g. stairs) behaving incorrectly when placed by clicking on the side of a replaceable block (e.g. tall grass).
- Fixed banners being able to be placed on top of blocks such as skulls.
- Fixed server-side collision boxes of walls and glass (which should connect, but didn't). Note that wall connections still don't show client side - this just fixes the collision boxes.
- Fixed `PlayerInteractEvent` with `LEFT_CLICK` sometimes firing before `BlockBreakEvent` when breaking blocks.
## Other changes
- Increased packet batch budget for player sessions.
# 4.12.10
Released 18th January 2023.
## Fixes
- Fixed reported server load not including the time spent processing Snooze interrupts between ticks (e.g. incoming network packets).
- Fixed `Connection Handler` entry in timings report not including time spent receiving packets.
## Note about server load & performance
This version will report higher apparent server load than previous versions. The actual performance of the server is unchanged; the previous reported load was inaccurate.
These bugs have been present for nearly 5 years (ever since the first introduction of Snooze in 3.0.0).

View File

@ -35,7 +35,7 @@
"fgrosse/phpasn1": "^2.3", "fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0", "netresearch/jsonmapper": "^4.0",
"pocketmine/bedrock-data": "~1.13.0+bedrock-1.19.50", "pocketmine/bedrock-data": "~1.13.0+bedrock-1.19.50",
"pocketmine/bedrock-protocol": "~17.0.0+bedrock-1.19.50", "pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50",
"pocketmine/binaryutils": "^0.2.1", "pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2", "pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0", "pocketmine/classloader": "^0.2.0",
@ -54,7 +54,7 @@
"webmozart/path-util": "^2.3" "webmozart/path-util": "^2.3"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.9.2", "phpstan/phpstan": "1.9.12",
"phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0", "phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2" "phpunit/phpunit": "^9.2"

198
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "6818c4f3d55e45758df83a56d60653cf", "content-hash": "1c5a3de092db3cda702d3f39d3da246c",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -123,24 +123,24 @@
}, },
{ {
"name": "fgrosse/phpasn1", "name": "fgrosse/phpasn1",
"version": "v2.4.0", "version": "v2.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/fgrosse/PHPASN1.git", "url": "https://github.com/fgrosse/PHPASN1.git",
"reference": "eef488991d53e58e60c9554b09b1201ca5ba9296" "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296", "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b",
"reference": "eef488991d53e58e60c9554b09b1201ca5ba9296", "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0" "php": "^7.1 || ^8.0"
}, },
"require-dev": { "require-dev": {
"php-coveralls/php-coveralls": "~2.0", "php-coveralls/php-coveralls": "~2.0",
"phpunit/phpunit": "^6.3 || ^7.0 || ^8.0" "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
}, },
"suggest": { "suggest": {
"ext-bcmath": "BCmath is the fallback extension for big integer calculations", "ext-bcmath": "BCmath is the fallback extension for big integer calculations",
@ -192,22 +192,23 @@
], ],
"support": { "support": {
"issues": "https://github.com/fgrosse/PHPASN1/issues", "issues": "https://github.com/fgrosse/PHPASN1/issues",
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0" "source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0"
}, },
"time": "2021-12-11T12:41:06+00:00" "abandoned": true,
"time": "2022-12-19T11:08:26+00:00"
}, },
{ {
"name": "netresearch/jsonmapper", "name": "netresearch/jsonmapper",
"version": "v4.0.0", "version": "v4.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/cweiske/jsonmapper.git", "url": "https://github.com/cweiske/jsonmapper.git",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -243,9 +244,9 @@
"support": { "support": {
"email": "cweiske@cweiske.de", "email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues", "issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0"
}, },
"time": "2020-12-01T19:48:11+00:00" "time": "2022-12-08T20:46:14+00:00"
}, },
{ {
"name": "pocketmine/bedrock-data", "name": "pocketmine/bedrock-data",
@ -275,16 +276,16 @@
}, },
{ {
"name": "pocketmine/bedrock-protocol", "name": "pocketmine/bedrock-protocol",
"version": "17.0.0+bedrock-1.19.50", "version": "17.1.0+bedrock-1.19.50",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git", "url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "272e10197bb1603c0a81075bf5b9dae0d081a6e2" "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/272e10197bb1603c0a81075bf5b9dae0d081a6e2", "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de",
"reference": "272e10197bb1603c0a81075bf5b9dae0d081a6e2", "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -298,7 +299,7 @@
"ramsey/uuid": "^4.1" "ramsey/uuid": "^4.1"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.9.0", "phpstan/phpstan": "1.9.3",
"phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.5"
@ -316,9 +317,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": { "support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues", "issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/17.0.0+bedrock-1.19.50" "source": "https://github.com/pmmp/BedrockProtocol/tree/17.1.0+bedrock-1.19.50"
}, },
"time": "2022-11-30T16:16:27+00:00" "time": "2022-12-15T20:34:49+00:00"
}, },
{ {
"name": "pocketmine/binaryutils", "name": "pocketmine/binaryutils",
@ -851,42 +852,53 @@
}, },
{ {
"name": "ramsey/collection", "name": "ramsey/collection",
"version": "1.2.2", "version": "1.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/collection.git", "url": "https://github.com/ramsey/collection.git",
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", "url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4",
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.3 || ^8", "php": "^7.4 || ^8.0",
"symfony/polyfill-php81": "^1.23" "symfony/polyfill-php81": "^1.23"
}, },
"require-dev": { "require-dev": {
"captainhook/captainhook": "^5.3", "captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "ergebnis/composer-normalize": "^2.28.3",
"ergebnis/composer-normalize": "^2.6", "fakerphp/faker": "^1.21",
"fakerphp/faker": "^1.5", "hamcrest/hamcrest-php": "^2.0",
"hamcrest/hamcrest-php": "^2", "jangregor/phpstan-prophecy": "^1.0",
"jangregor/phpstan-prophecy": "^0.8", "mockery/mockery": "^1.5",
"mockery/mockery": "^1.3", "php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0", "phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1", "phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^0.12.32", "phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^0.12.5", "phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^0.12.11", "phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^8.5 || ^9", "phpunit/phpunit": "^9.5",
"psy/psysh": "^0.10.4", "psalm/plugin-mockery": "^1.1",
"slevomat/coding-standard": "^6.3", "psalm/plugin-phpunit": "^0.18.4",
"squizlabs/php_codesniffer": "^3.5", "ramsey/coding-standard": "^2.0.3",
"vimeo/psalm": "^4.4" "ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
}, },
"type": "library", "type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Ramsey\\Collection\\": "src/" "Ramsey\\Collection\\": "src/"
@ -914,7 +926,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/collection/issues", "issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/1.2.2" "source": "https://github.com/ramsey/collection/tree/1.3.0"
}, },
"funding": [ "funding": [
{ {
@ -926,27 +938,27 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-10-10T03:01:02+00:00" "time": "2022-12-27T19:12:24+00:00"
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "4.6.0", "version": "4.7.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/uuid.git", "url": "https://github.com/ramsey/uuid.git",
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f" "reference": "433b2014e3979047db08a17a205f410ba3869cf2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/ad63bc700e7d021039e30ce464eba384c4a1d40f", "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f", "reference": "433b2014e3979047db08a17a205f410ba3869cf2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10", "brick/math": "^0.8.8 || ^0.9 || ^0.10",
"ext-json": "*", "ext-json": "*",
"php": "^8.0", "php": "^8.0",
"ramsey/collection": "^1.0" "ramsey/collection": "^1.2 || ^2.0"
}, },
"replace": { "replace": {
"rhumsaa/uuid": "self.version" "rhumsaa/uuid": "self.version"
@ -1006,7 +1018,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/uuid/issues", "issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.6.0" "source": "https://github.com/ramsey/uuid/tree/4.7.3"
}, },
"funding": [ "funding": [
{ {
@ -1018,7 +1030,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-05T23:03:38+00:00" "time": "2023-01-12T18:13:24+00:00"
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
@ -1524,30 +1536,30 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",
"version": "1.4.1", "version": "1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/instantiator.git", "url": "https://github.com/doctrine/instantiator.git",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1 || ^8.0" "php": "^7.1 || ^8.0"
}, },
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^9", "doctrine/coding-standard": "^9 || ^11",
"ext-pdo": "*", "ext-pdo": "*",
"ext-phar": "*", "ext-phar": "*",
"phpbench/phpbench": "^0.16 || ^1", "phpbench/phpbench": "^0.16 || ^1",
"phpstan/phpstan": "^1.4", "phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22" "vimeo/psalm": "^4.30 || ^5.4"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -1574,7 +1586,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/instantiator/issues", "issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.1" "source": "https://github.com/doctrine/instantiator/tree/1.5.0"
}, },
"funding": [ "funding": [
{ {
@ -1590,7 +1602,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-03T08:28:38+00:00" "time": "2022-12-30T00:15:36+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@ -1820,16 +1832,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.9.2", "version": "1.9.12",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa" "reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/44a338ff0d5572c13fd77dfd91addb96e48c29f8",
"reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa", "reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1859,7 +1871,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.9.2" "source": "https://github.com/phpstan/phpstan/tree/1.9.12"
}, },
"funding": [ "funding": [
{ {
@ -1875,25 +1887,25 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-10T09:56:11+00:00" "time": "2023-01-17T10:44:04+00:00"
}, },
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
"version": "1.2.2", "version": "1.3.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git", "url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "dea1f87344c6964c607d9076dee42d891f3923f0" "reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/dea1f87344c6964c607d9076dee42d891f3923f0", "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/54a24bd23e9e80ee918cdc24f909d376c2e273f7",
"reference": "dea1f87344c6964c607d9076dee42d891f3923f0", "reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.11" "phpstan/phpstan": "^1.9.3"
}, },
"conflict": { "conflict": {
"phpunit/phpunit": "<7.0" "phpunit/phpunit": "<7.0"
@ -1925,27 +1937,27 @@
"description": "PHPUnit extensions and rules for PHPStan", "description": "PHPUnit extensions and rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues", "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.2.2" "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.3"
}, },
"time": "2022-10-28T10:23:07+00:00" "time": "2022-12-21T15:25:00+00:00"
}, },
{ {
"name": "phpstan/phpstan-strict-rules", "name": "phpstan/phpstan-strict-rules",
"version": "1.4.4", "version": "1.4.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git", "url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6" "reference": "361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d",
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", "reference": "361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.6" "phpstan/phpstan": "^1.9.7"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0", "nikic/php-parser": "^4.13.0",
@ -1973,22 +1985,22 @@
"description": "Extra strict and opinionated rules for PHPStan", "description": "Extra strict and opinionated rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4" "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.5"
}, },
"time": "2022-09-21T11:38:17+00:00" "time": "2023-01-11T14:16:29+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.19", "version": "9.2.23",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559" "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c",
"reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559", "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2044,7 +2056,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23"
}, },
"funding": [ "funding": [
{ {
@ -2052,7 +2064,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-11-18T07:47:47+00:00" "time": "2022-12-28T12:41:10+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -2297,20 +2309,20 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "9.5.26", "version": "9.5.28",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e",
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/instantiator": "^1.3.1", "doctrine/instantiator": "^1.3.1 || ^2",
"ext-dom": "*", "ext-dom": "*",
"ext-json": "*", "ext-json": "*",
"ext-libxml": "*", "ext-libxml": "*",
@ -2379,7 +2391,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28"
}, },
"funding": [ "funding": [
{ {
@ -2395,7 +2407,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-10-28T06:00:21+00:00" "time": "2023-01-14T12:32:24+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",

View File

@ -39,11 +39,14 @@ namespace pocketmine {
use function extension_loaded; use function extension_loaded;
use function function_exists; use function function_exists;
use function getcwd; use function getcwd;
use function is_dir;
use function mkdir;
use function phpversion; use function phpversion;
use function preg_match; use function preg_match;
use function preg_quote; use function preg_quote;
use function realpath; use function realpath;
use function version_compare; use function version_compare;
use const DIRECTORY_SEPARATOR;
require_once __DIR__ . '/VersionInfo.php'; require_once __DIR__ . '/VersionInfo.php';
@ -273,25 +276,33 @@ JIT_WARNING
$pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins"; $pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX); Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
if(!@mkdir($dataPath, 0777, true) && (!is_dir($dataPath) || !is_writable($dataPath))){ if(!@mkdir($dataPath, 0777, true) && !is_dir($dataPath)){
critical_error("Unable to create/access data directory at $dataPath. Check that the target location is accessible by the current user."); critical_error("Unable to create/access data directory at $dataPath. Check that the target location is accessible by the current user.");
exit(1); exit(1);
} }
//this has to be done after we're sure the data path exists //this has to be done after we're sure the data path exists
$dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR; $dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
if(!@mkdir($pluginPath, 0777, true) && (!is_dir($pluginPath) || !is_writable($pluginPath))){
critical_error("Unable to create plugin directory at $pluginPath. Check that the target location is accessible by the current user.");
exit(1);
}
$pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
$lockFilePath = Path::join($dataPath, 'server.lock'); $lockFilePath = Path::join($dataPath, 'server.lock');
if(($pid = Filesystem::createLockFile($lockFilePath)) !== null){ try{
$pid = Filesystem::createLockFile($lockFilePath);
}catch(\InvalidArgumentException $e){
critical_error($e->getMessage());
critical_error("Please ensure that there is enough space on the disk and that the current user has read/write permissions to the selected data directory $dataPath.");
exit(1);
}
if($pid !== null){
critical_error("Another " . VersionInfo::NAME . " instance (PID $pid) is already using this folder (" . realpath($dataPath) . ")."); critical_error("Another " . VersionInfo::NAME . " instance (PID $pid) is already using this folder (" . realpath($dataPath) . ").");
critical_error("Please stop the other server first before running a new one."); critical_error("Please stop the other server first before running a new one.");
exit(1); exit(1);
} }
if(!@mkdir($pluginPath, 0777, true) && !is_dir($pluginPath)){
critical_error("Unable to create plugin directory at $pluginPath. Check that the target location is accessible by the current user.");
exit(1);
}
$pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
//Logger has a dependency on timezone //Logger has a dependency on timezone
Timezone::init(); Timezone::init();

View File

@ -186,7 +186,7 @@ class Server{
private static ?Server $instance = null; private static ?Server $instance = null;
private SleeperHandler $tickSleeper; private TimeTrackingSleeperHandler $tickSleeper;
private BanList $banByName; private BanList $banByName;
@ -781,7 +781,8 @@ class Server{
self::$instance = $this; self::$instance = $this;
$this->startTime = microtime(true); $this->startTime = microtime(true);
$this->tickSleeper = new SleeperHandler(); Timings::init();
$this->tickSleeper = new TimeTrackingSleeperHandler(Timings::$serverInterrupts);
$this->signalHandler = new SignalHandler(function() : void{ $this->signalHandler = new SignalHandler(function() : void{
$this->logger->info("Received signal interrupt, stopping the server"); $this->logger->info("Received signal interrupt, stopping the server");
@ -961,7 +962,6 @@ class Server{
))); )));
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName()))); $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
Timings::init();
TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false)); TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false));
$this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", 20); $this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", 20);
@ -1865,14 +1865,16 @@ class Server{
Timings::$serverTick->stopTiming(); Timings::$serverTick->stopTiming();
$now = microtime(true); $now = microtime(true);
$this->currentTPS = min(20, 1 / max(0.001, $now - $tickTime)); $totalTickTimeSeconds = $now - $tickTime + ($this->tickSleeper->getNotificationProcessingTime() / 1_000_000_000);
$this->currentUse = min(1, ($now - $tickTime) / 0.05); $this->currentTPS = min(20, 1 / max(0.001, $totalTickTimeSeconds));
$this->currentUse = min(1, $totalTickTimeSeconds / 0.05);
TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate); TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate);
$idx = $this->tickCounter % 20; $idx = $this->tickCounter % 20;
$this->tickAverage[$idx] = $this->currentTPS; $this->tickAverage[$idx] = $this->currentTPS;
$this->useAverage[$idx] = $this->currentUse; $this->useAverage[$idx] = $this->currentUse;
$this->tickSleeper->resetNotificationProcessingTime();
if(($this->nextTick - $tickTime) < -1){ if(($this->nextTick - $tickTime) < -1){
$this->nextTick = $tickTime; $this->nextTick = $tickTime;

View File

@ -0,0 +1,64 @@
<?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;
use pocketmine\snooze\SleeperHandler;
use pocketmine\timings\TimingsHandler;
use function hrtime;
/**
* Custom Snooze sleeper handler which captures notification processing time.
* @internal
*/
final class TimeTrackingSleeperHandler extends SleeperHandler{
private int $notificationProcessingTimeNs = 0;
public function __construct(
private TimingsHandler $timings
){
parent::__construct();
}
/**
* Returns the time in nanoseconds spent processing notifications since the last reset.
*/
public function getNotificationProcessingTime() : int{ return $this->notificationProcessingTimeNs; }
/**
* Resets the notification processing time tracker to zero.
*/
public function resetNotificationProcessingTime() : void{ $this->notificationProcessingTimeNs = 0; }
public function processNotifications() : void{
$startTime = hrtime(true);
$this->timings->startTiming();
try{
parent::processNotifications();
}finally{
$this->notificationProcessingTimeNs += hrtime(true) - $startTime;
$this->timings->stopTiming();
}
}
}

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{ final class VersionInfo{
public const NAME = "PocketMine-MP"; public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.12.0"; public const BASE_VERSION = "4.12.10";
public const IS_DEVELOPMENT_BUILD = false; public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable"; public const BUILD_CHANNEL = "stable";

View File

@ -112,7 +112,14 @@ abstract class BaseBanner extends Transparent{
return SupportType::NONE(); return SupportType::NONE();
} }
private function canBeSupportedBy(Block $block) : bool{
return $block->isSolid();
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
return false;
}
if($item instanceof ItemBanner){ if($item instanceof ItemBanner){
$this->color = $item->getColor(); $this->color = $item->getColor();
$this->setPatterns($item->getPatterns()); $this->setPatterns($item->getPatterns());
@ -124,7 +131,7 @@ abstract class BaseBanner extends Transparent{
abstract protected function getSupportingFace() : int; abstract protected function getSupportingFace() : int;
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide($this->getSupportingFace())->getId() === BlockLegacyIds::AIR){ if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
$this->position->getWorld()->useBreakOn($this->position); $this->position->getWorld()->useBreakOn($this->position);
} }
} }

View File

@ -114,14 +114,13 @@ final class Bell extends Transparent{
return $this; return $this;
} }
private function canBeSupportedBy(Block $block) : bool{ private function canBeSupportedBy(Block $block, int $face) : bool{
//TODO: this isn't the actual logic, but it's the closest approximation we can support for now return !$block->getSupportType($face)->equals(SupportType::NONE());
return $block->isSolid();
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face === Facing::UP){ if($face === Facing::UP){
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()))){ if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()), Facing::UP)){
return false; return false;
} }
if($player !== null){ if($player !== null){
@ -129,18 +128,18 @@ final class Bell extends Transparent{
} }
$this->setAttachmentType(BellAttachmentType::FLOOR()); $this->setAttachmentType(BellAttachmentType::FLOOR());
}elseif($face === Facing::DOWN){ }elseif($face === Facing::DOWN){
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()))){ if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()), Facing::DOWN)){
return false; return false;
} }
$this->setAttachmentType(BellAttachmentType::CEILING()); $this->setAttachmentType(BellAttachmentType::CEILING());
}else{ }else{
$this->setFacing($face); $this->setFacing($face);
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))))){ if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))), $face)){
$this->setAttachmentType(BellAttachmentType::ONE_WALL()); $this->setAttachmentType(BellAttachmentType::ONE_WALL());
}else{ }else{
return false; return false;
} }
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)))){ if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)), Facing::opposite($face))){
$this->setAttachmentType(BellAttachmentType::TWO_WALLS()); $this->setAttachmentType(BellAttachmentType::TWO_WALLS());
} }
} }
@ -149,10 +148,10 @@ final class Bell extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if( if(
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP))) || ($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP), Facing::DOWN)) ||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN))) || ($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN), Facing::UP)) ||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))) || ($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)) ||
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing))))) ($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing), Facing::opposite($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)))
){ ){
$this->position->getWorld()->useBreakOn($this->position); $this->position->getWorld()->useBreakOn($this->position);
} }
@ -161,21 +160,20 @@ final class Bell extends Transparent{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){ if($player !== null){
$faceHit = Facing::opposite($player->getHorizontalFacing()); $faceHit = Facing::opposite($player->getHorizontalFacing());
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
$this->ring($faceHit);
}
if($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)){
$this->ring($faceHit);
}
if( if(
$this->attachmentType->equals(BellAttachmentType::CEILING()) ||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)) ||
(
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) && ($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true)) ($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
)
){ ){
$this->ring($faceHit); $this->ring($faceHit);
return true;
} }
} }
return true; return false;
} }
public function ring(int $faceHit) : void{ public function ring(int $faceHit) : void{

View File

@ -253,6 +253,14 @@ class Block{
* Generates a block transaction to set all blocks affected by placing this block. Usually this is just the block * Generates a block transaction to set all blocks affected by placing this block. Usually this is just the block
* itself, but may be multiple blocks in some cases (such as doors). * itself, but may be multiple blocks in some cases (such as doors).
* *
* @param BlockTransaction $tx Blocks to be set should be added to this transaction (do not modify thr world directly)
* @param Item $item Item used to place the block
* @param Block $blockReplace Block expected to be replaced
* @param Block $blockClicked Block that was clicked using the item
* @param int $face Face of the clicked block which was clicked
* @param Vector3 $clickVector Exact position inside the clicked block where the click occurred, relative to the block's position
* @param Player|null $player Player who placed the block, or null if it was not a player
*
* @return bool whether the placement should go ahead * @return bool whether the placement should go ahead
*/ */
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{

View File

@ -61,7 +61,7 @@ abstract class Button extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($blockClicked, $face)){ if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
$this->facing = $face; $this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }

View File

@ -175,7 +175,7 @@ class ItemFrame extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face === Facing::DOWN || $face === Facing::UP || !$blockClicked->isSolid()){ if($face === Facing::DOWN || $face === Facing::UP || !$blockReplace->getSide(Facing::opposite($face))->isSolid()){
return false; return false;
} }

View File

@ -70,7 +70,7 @@ class Ladder extends Transparent{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($blockClicked, $face) && Facing::axis($face) !== Axis::Y){ if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face) && Facing::axis($face) !== Axis::Y){
$this->facing = $face; $this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }

View File

@ -95,7 +95,7 @@ class Lever extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockClicked, $face)){ if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
return false; return false;
} }

View File

@ -45,7 +45,7 @@ abstract class PressurePlate extends Transparent{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($blockClicked)){ if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }
return false; return false;
@ -55,5 +55,11 @@ abstract class PressurePlate extends Transparent{
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE()); return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
} }
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
//TODO //TODO
} }

View File

@ -41,7 +41,7 @@ class Thin extends Transparent{
foreach(Facing::HORIZONTAL as $facing){ foreach(Facing::HORIZONTAL as $facing){
$side = $this->getSide($facing); $side = $this->getSide($facing);
if($side instanceof Thin || $side->isFullCube()){ if($side instanceof Thin || $side instanceof Wall || $side->isFullCube()){
$this->connections[$facing] = true; $this->connections[$facing] = true;
}else{ }else{
unset($this->connections[$facing]); unset($this->connections[$facing]);

View File

@ -65,7 +65,6 @@ class Torch extends Flowable{
} }
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
$below = $this->getSide(Facing::DOWN);
$face = Facing::opposite($this->facing); $face = Facing::opposite($this->facing);
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){ if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
@ -74,10 +73,7 @@ class Torch extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){ if($face !== Facing::DOWN && $this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
$this->facing = Facing::UP;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
$this->facing = $face; $this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}else{ }else{

View File

@ -116,7 +116,7 @@ class Vine extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockClicked->isFullCube() || Facing::axis($face) === Axis::Y){ if(!$blockReplace->getSide(Facing::opposite($face))->isFullCube() || Facing::axis($face) === Axis::Y){
return false; return false;
} }

View File

@ -39,7 +39,7 @@ class Wall extends Transparent{
foreach(Facing::HORIZONTAL as $facing){ foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing); $block = $this->getSide($facing);
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){ if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || ($block->isSolid() && !$block->isTransparent())){
$this->connections[$facing] = $facing; $this->connections[$facing] = $facing;
}else{ }else{
unset($this->connections[$facing]); unset($this->connections[$facing]);

View File

@ -113,7 +113,7 @@ final class WallCoralFan extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$axis = Facing::axis($face); $axis = Facing::axis($face);
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockClicked, $face)){ if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
return false; return false;
} }
$this->facing = $face; $this->facing = $face;

View File

@ -39,19 +39,23 @@ class WaterLily extends Flowable{
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)]; return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
if($blockClicked instanceof Water){ return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
$up = $blockClicked->getSide(Facing::UP);
if($up->canBeReplaced()){
return parent::place($tx, $item, $up, $blockClicked, $face, $clickVector, $player);
}
} }
private function canBeSupportedBy(Block $block) : bool{
return $block instanceof Water;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
return false; return false;
} }
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(!($this->getSide(Facing::DOWN) instanceof Water)){ if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position); $this->position->getWorld()->useBreakOn($this->position);
} }
} }

View File

@ -72,6 +72,11 @@ class GamemodeCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException(); throw new InvalidCommandSyntaxException();
} }
if($target->getGamemode()->equals($gameMode)){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
return true;
}
$target->setGamemode($gameMode); $target->setGamemode($gameMode);
if(!$gameMode->equals($target->getGamemode())){ if(!$gameMode->equals($target->getGamemode())){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName())); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));

View File

@ -34,9 +34,15 @@ use function usort;
class CraftingManager{ class CraftingManager{
use DestructorCallbackTrait; use DestructorCallbackTrait;
/** @var ShapedRecipe[][] */ /**
* @var ShapedRecipe[][]
* @phpstan-var array<string, list<ShapedRecipe>>
*/
protected $shapedRecipes = []; protected $shapedRecipes = [];
/** @var ShapelessRecipe[][] */ /**
* @var ShapelessRecipe[][]
* @phpstan-var array<string, list<ShapelessRecipe>>
*/
protected $shapelessRecipes = []; protected $shapelessRecipes = [];
/** /**
@ -133,6 +139,7 @@ class CraftingManager{
/** /**
* @return ShapelessRecipe[][] * @return ShapelessRecipe[][]
* @phpstan-return array<string, list<ShapelessRecipe>>
*/ */
public function getShapelessRecipes() : array{ public function getShapelessRecipes() : array{
return $this->shapelessRecipes; return $this->shapelessRecipes;
@ -140,6 +147,7 @@ class CraftingManager{
/** /**
* @return ShapedRecipe[][] * @return ShapedRecipe[][]
* @phpstan-return array<string, list<ShapedRecipe>>
*/ */
public function getShapedRecipes() : array{ public function getShapedRecipes() : array{
return $this->shapedRecipes; return $this->shapedRecipes;

View File

@ -37,6 +37,7 @@ class ShapelessRecipe implements CraftingRecipe{
/** /**
* @param Item[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array. * @param Item[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
* @param Item[] $results List of result items created by this recipe. * @param Item[] $results List of result items created by this recipe.
*
* TODO: we'll want to make the type parameter mandatory in PM5 * TODO: we'll want to make the type parameter mandatory in PM5
*/ */
public function __construct(array $ingredients, array $results, ?ShapelessRecipeType $type = null){ public function __construct(array $ingredients, array $results, ?ShapelessRecipeType $type = null){

View File

@ -33,13 +33,13 @@ final class PotionTypeIdMap{
* @var PotionType[] * @var PotionType[]
* @phpstan-var array<int, PotionType> * @phpstan-var array<int, PotionType>
*/ */
private array $idToEnum; private array $idToEnum = [];
/** /**
* @var int[] * @var int[]
* @phpstan-var array<int, int> * @phpstan-var array<int, int>
*/ */
private array $enumToId; private array $enumToId = [];
private function __construct(){ private function __construct(){
$this->register(PotionTypeIds::WATER, PotionType::WATER()); $this->register(PotionTypeIds::WATER, PotionType::WATER());

View File

@ -31,6 +31,10 @@ class HandlerList{
/** @var RegisteredListener[][] */ /** @var RegisteredListener[][] */
private array $handlerSlots = []; private array $handlerSlots = [];
/**
* @phpstan-template TEvent of Event
* @phpstan-param class-string<TEvent> $class
*/
public function __construct( public function __construct(
private string $class, private string $class,
private ?HandlerList $parentList private ?HandlerList $parentList

View File

@ -80,6 +80,10 @@ class Network{
return $this->sessionManager->getSessionCount(); return $this->sessionManager->getSessionCount();
} }
public function getValidConnectionCount() : int{
return $this->sessionManager->getValidSessionCount();
}
public function tick() : void{ public function tick() : void{
foreach($this->interfaces as $interface){ foreach($this->interfaces as $interface){
$interface->tick(); $interface->tick();

View File

@ -32,12 +32,25 @@ class NetworkSessionManager{
/** @var NetworkSession[] */ /** @var NetworkSession[] */
private array $sessions = []; private array $sessions = [];
/** @var NetworkSession[] */
private array $pendingLoginSessions = [];
/** /**
* Adds a network session to the manager. This should only be called on session creation. * Adds a network session to the manager. This should only be called on session creation.
*/ */
public function add(NetworkSession $session) : void{ public function add(NetworkSession $session) : void{
$idx = spl_object_id($session); $idx = spl_object_id($session);
$this->sessions[$idx] = $session; $this->sessions[$idx] = $session;
$this->pendingLoginSessions[$idx] = $session;
}
/**
* Marks the session as having sent a login request. After this point, they are counted towards the total player
* count.
*/
public function markLoginReceived(NetworkSession $session) : void{
$idx = spl_object_id($session);
unset($this->pendingLoginSessions[$idx]);
} }
/** /**
@ -47,15 +60,24 @@ class NetworkSessionManager{
public function remove(NetworkSession $session) : void{ public function remove(NetworkSession $session) : void{
$idx = spl_object_id($session); $idx = spl_object_id($session);
unset($this->sessions[$idx]); unset($this->sessions[$idx]);
unset($this->pendingLoginSessions[$idx]);
} }
/** /**
* Returns the number of known connected sessions. * Returns the number of known connected sessions, including sessions which have not yet sent a login request.
*/ */
public function getSessionCount() : int{ public function getSessionCount() : int{
return count($this->sessions); return count($this->sessions);
} }
/**
* Returns the number of connected sessions which have either sent a login request, or have already completed the
* login sequence.
*/
public function getValidSessionCount() : int{
return count($this->sessions) - count($this->pendingLoginSessions);
}
/** @return NetworkSession[] */ /** @return NetworkSession[] */
public function getSessions() : array{ return $this->sessions; } public function getSessions() : array{ return $this->sessions; }

View File

@ -191,8 +191,10 @@ class InventoryManager{
*/ */
public function addPredictedSlotChanges(array $networkInventoryActions) : void{ public function addPredictedSlotChanges(array $networkInventoryActions) : void{
foreach($networkInventoryActions as $action){ foreach($networkInventoryActions as $action){
if($action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && isset($this->windowMap[$action->windowId])){ if($action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && (
//this won't cover stuff like crafting grid due to too much magic isset($this->windowMap[$action->windowId]) ||
($action->windowId === ContainerIds::UI && isset($this->complexSlotToWindowMap[$action->inventorySlot]))
)){
try{ try{
$item = TypeConverter::getInstance()->netItemStackToCore($action->newItem->getItemStack()); $item = TypeConverter::getInstance()->netItemStackToCore($action->newItem->getItemStack());
}catch(TypeConversionException $e){ }catch(TypeConversionException $e){

View File

@ -128,20 +128,39 @@ use function array_values;
use function base64_encode; use function base64_encode;
use function bin2hex; use function bin2hex;
use function count; use function count;
use function function_exists;
use function get_class; use function get_class;
use function hrtime;
use function in_array; use function in_array;
use function intdiv;
use function json_encode; use function json_encode;
use function ksort; use function ksort;
use function min;
use function strcasecmp; use function strcasecmp;
use function strlen; use function strlen;
use function strtolower; use function strtolower;
use function substr; use function substr;
use function time; use function time;
use function ucfirst; use function ucfirst;
use function xdebug_is_debugger_active;
use const JSON_THROW_ON_ERROR; use const JSON_THROW_ON_ERROR;
use const SORT_NUMERIC; use const SORT_NUMERIC;
class NetworkSession{ class NetworkSession{
private const INCOMING_PACKET_BATCH_PER_TICK = 2; //usually max 1 per tick, but transactions may arrive separately
private const INCOMING_PACKET_BATCH_MAX_BUDGET = 100 * self::INCOMING_PACKET_BATCH_PER_TICK; //enough to account for a 5-second lag spike
/**
* At most this many more packets can be received. If this reaches zero, any additional packets received will cause
* the player to be kicked from the server.
* This number is increased every tick up to a maximum limit.
*
* @see self::INCOMING_PACKET_BATCH_PER_TICK
* @see self::INCOMING_PACKET_BATCH_MAX_BUDGET
*/
private int $incomingPacketBatchBudget = self::INCOMING_PACKET_BATCH_MAX_BUDGET;
private int $lastPacketBudgetUpdateTimeNs;
private \PrefixedLogger $logger; private \PrefixedLogger $logger;
private ?Player $player = null; private ?Player $player = null;
private ?PlayerInfo $info = null; private ?PlayerInfo $info = null;
@ -199,6 +218,7 @@ class NetworkSession{
$this->disposeHooks = new ObjectSet(); $this->disposeHooks = new ObjectSet();
$this->connectTime = time(); $this->connectTime = time();
$this->lastPacketBudgetUpdateTimeNs = hrtime(true);
$this->setHandler(new SessionStartPacketHandler( $this->setHandler(new SessionStartPacketHandler(
$this->server, $this->server,
@ -229,6 +249,7 @@ class NetworkSession{
$this->info = $info; $this->info = $info;
$this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET); $this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET);
$this->logger->setPrefix($this->getLogPrefix()); $this->logger->setPrefix($this->getLogPrefix());
$this->manager->markLoginReceived($this);
}, },
function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{ function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{
$this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey); $this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey);
@ -338,6 +359,17 @@ class NetworkSession{
return; return;
} }
if($this->incomingPacketBatchBudget <= 0){
if(!function_exists('xdebug_is_debugger_active') || !xdebug_is_debugger_active()){
throw new PacketHandlingException("Receiving packets too fast");
}else{
//when a debugging session is active, the server may halt at any point for an indefinite length of time,
//in which time the client will continue to send packets
$this->incomingPacketBatchBudget = self::INCOMING_PACKET_BATCH_MAX_BUDGET;
}
}
$this->incomingPacketBatchBudget--;
if($this->cipher !== null){ if($this->cipher !== null){
Timings::$playerNetworkReceiveDecrypt->startTiming(); Timings::$playerNetworkReceiveDecrypt->startTiming();
try{ try{
@ -365,7 +397,7 @@ class NetworkSession{
} }
try{ try{
foreach((new PacketBatch($decompressed))->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){ foreach((new PacketBatch($decompressed))->getPackets($this->packetPool, $this->packetSerializerContext, 1300) as [$packet, $buffer]){
if($packet === null){ if($packet === null){
$this->logger->debug("Unknown packet: " . base64_encode($buffer)); $this->logger->debug("Unknown packet: " . base64_encode($buffer));
throw new PacketHandlingException("Unknown packet received"); throw new PacketHandlingException("Unknown packet received");
@ -1128,5 +1160,16 @@ class NetworkSession{
} }
$this->flushSendBuffer(); $this->flushSendBuffer();
$nowNs = hrtime(true);
$timeSinceLastUpdateNs = $nowNs - $this->lastPacketBudgetUpdateTimeNs;
if($timeSinceLastUpdateNs > 50_000_000){
$ticksSinceLastUpdate = intdiv($timeSinceLastUpdateNs, 50_000_000);
$this->incomingPacketBatchBudget = min(
$this->incomingPacketBatchBudget + (self::INCOMING_PACKET_BATCH_PER_TICK * 2 * $ticksSinceLastUpdate),
self::INCOMING_PACKET_BATCH_MAX_BUDGET
);
$this->lastPacketBudgetUpdateTimeNs = $nowNs;
}
} }
} }

View File

@ -175,6 +175,7 @@ class TypeConverter{
$nbt = new CompoundTag(); $nbt = new CompoundTag();
} }
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage()); $nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
$meta = 0;
}elseif($isBlockItem && $itemStack->getMeta() !== 0){ }elseif($isBlockItem && $itemStack->getMeta() !== 0){
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the //TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata //client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
@ -183,6 +184,7 @@ class TypeConverter{
$nbt = new CompoundTag(); $nbt = new CompoundTag();
} }
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta()); $nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
$meta = 0;
} }
} }

View File

@ -34,6 +34,7 @@ use function openssl_digest;
use function openssl_error_string; use function openssl_error_string;
use function openssl_pkey_derive; use function openssl_pkey_derive;
use function str_pad; use function str_pad;
use const STR_PAD_LEFT;
final class EncryptionUtils{ final class EncryptionUtils{

View File

@ -112,7 +112,6 @@ use function array_push;
use function base64_encode; use function base64_encode;
use function count; use function count;
use function fmod; use function fmod;
use function implode;
use function in_array; use function in_array;
use function is_bool; use function is_bool;
use function is_infinite; use function is_infinite;
@ -122,12 +121,9 @@ use function json_encode;
use function max; use function max;
use function mb_strlen; use function mb_strlen;
use function microtime; use function microtime;
use function preg_match;
use function sprintf; use function sprintf;
use function strlen; use function strlen;
use function strpos; use function strpos;
use function substr;
use function trim;
use const JSON_THROW_ON_ERROR; use const JSON_THROW_ON_ERROR;
/** /**
@ -252,18 +248,11 @@ class InGamePacketHandler extends PacketHandler{
$packetHandled = true; $packetHandled = true;
$useItemTransaction = $packet->getItemInteractionData();
if($useItemTransaction !== null){
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
$packetHandled = false;
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
}else{
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
}
}
$blockActions = $packet->getBlockActions(); $blockActions = $packet->getBlockActions();
if($blockActions !== null){ if($blockActions !== null){
if(count($blockActions) > 100){
throw new PacketHandlingException("Too many block actions in PlayerAuthInputPacket");
}
foreach($blockActions as $k => $blockAction){ foreach($blockActions as $k => $blockAction){
$actionHandled = false; $actionHandled = false;
if($blockAction instanceof PlayerBlockActionStopBreak){ if($blockAction instanceof PlayerBlockActionStopBreak){
@ -279,6 +268,20 @@ class InGamePacketHandler extends PacketHandler{
} }
} }
$useItemTransaction = $packet->getItemInteractionData();
if($useItemTransaction !== null){
if(count($useItemTransaction->getTransactionData()->getActions()) > 100){
throw new PacketHandlingException("Too many actions in item use transaction");
}
$this->inventoryManager->addPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions());
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
$packetHandled = false;
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
}else{
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
}
}
return $packetHandled; return $packetHandled;
} }
@ -310,6 +313,12 @@ class InGamePacketHandler extends PacketHandler{
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{ public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
$result = true; $result = true;
if(count($packet->trData->getActions()) > 100){
throw new PacketHandlingException("Too many actions in inventory transaction");
}
$this->inventoryManager->addPredictedSlotChanges($packet->trData->getActions());
if($packet->trData instanceof NormalTransactionData){ if($packet->trData instanceof NormalTransactionData){
$result = $this->handleNormalTransaction($packet->trData); $result = $this->handleNormalTransaction($packet->trData);
}elseif($packet->trData instanceof MismatchTransactionData){ }elseif($packet->trData instanceof MismatchTransactionData){
@ -324,9 +333,7 @@ class InGamePacketHandler extends PacketHandler{
$result = $this->handleReleaseItemTransaction($packet->trData); $result = $this->handleReleaseItemTransaction($packet->trData);
} }
if(!$result){ if($this->craftingTransaction === null){ //don't sync if we're waiting to complete a crafting transaction
$this->inventoryManager->syncAll();
}else{
$this->inventoryManager->syncMismatchedPredictedSlotChanges(); $this->inventoryManager->syncMismatchedPredictedSlotChanges();
} }
return $result; return $result;
@ -381,15 +388,9 @@ class InGamePacketHandler extends PacketHandler{
} }
$this->player->setUsingItem(false); $this->player->setUsingItem(false);
try{ try{
$this->inventoryManager->onTransactionStart($this->craftingTransaction);
$this->craftingTransaction->execute(); $this->craftingTransaction->execute();
}catch(TransactionException $e){ }catch(TransactionException $e){
$this->session->getLogger()->debug("Failed to execute crafting transaction: " . $e->getMessage()); $this->session->getLogger()->debug("Failed to execute crafting transaction: " . $e->getMessage());
//TODO: only sync slots that the client tried to change
foreach($this->craftingTransaction->getInventories() as $inventory){
$this->inventoryManager->syncContents($inventory);
}
return false; return false;
}finally{ }finally{
$this->craftingTransaction = null; $this->craftingTransaction = null;
@ -409,18 +410,12 @@ class InGamePacketHandler extends PacketHandler{
$this->player->setUsingItem(false); $this->player->setUsingItem(false);
$transaction = new InventoryTransaction($this->player, $actions); $transaction = new InventoryTransaction($this->player, $actions);
$this->inventoryManager->onTransactionStart($transaction);
try{ try{
$transaction->execute(); $transaction->execute();
}catch(TransactionException $e){ }catch(TransactionException $e){
$logger = $this->session->getLogger(); $logger = $this->session->getLogger();
$logger->debug("Failed to execute inventory transaction: " . $e->getMessage()); $logger->debug("Failed to execute inventory transaction: " . $e->getMessage());
$logger->debug("Actions: " . json_encode($data->getActions())); $logger->debug("Actions: " . json_encode($data->getActions()));
foreach($transaction->getInventories() as $inventory){
$this->inventoryManager->syncContents($inventory);
}
return false; return false;
} }
} }
@ -430,7 +425,6 @@ class InGamePacketHandler extends PacketHandler{
private function handleUseItemTransaction(UseItemTransactionData $data) : bool{ private function handleUseItemTransaction(UseItemTransactionData $data) : bool{
$this->player->selectHotbarSlot($data->getHotbarSlot()); $this->player->selectHotbarSlot($data->getHotbarSlot());
$this->inventoryManager->addPredictedSlotChanges($data->getActions());
switch($data->getActionType()){ switch($data->getActionType()){
case UseItemTransactionData::ACTION_CLICK_BLOCK: case UseItemTransactionData::ACTION_CLICK_BLOCK:
@ -516,9 +510,7 @@ class InGamePacketHandler extends PacketHandler{
} }
$this->player->selectHotbarSlot($data->getHotbarSlot()); $this->player->selectHotbarSlot($data->getHotbarSlot());
$this->inventoryManager->addPredictedSlotChanges($data->getActions());
//TODO: use transactiondata for rollbacks here
switch($data->getActionType()){ switch($data->getActionType()){
case UseItemOnEntityTransactionData::ACTION_INTERACT: case UseItemOnEntityTransactionData::ACTION_INTERACT:
$this->player->interactEntity($target, $data->getClickPosition()); $this->player->interactEntity($target, $data->getClickPosition());
@ -533,14 +525,9 @@ class InGamePacketHandler extends PacketHandler{
private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{ private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{
$this->player->selectHotbarSlot($data->getHotbarSlot()); $this->player->selectHotbarSlot($data->getHotbarSlot());
$this->inventoryManager->addPredictedSlotChanges($data->getActions());
//TODO: use transactiondata for rollbacks here (resending entire inventory is very wasteful) if($data->getActionType() == ReleaseItemTransactionData::ACTION_RELEASE){
switch($data->getActionType()){ $this->player->releaseHeldItem();
case ReleaseItemTransactionData::ACTION_RELEASE:
if(!$this->player->releaseHeldItem()){
$this->inventoryManager->syncContents($this->player->getInventory());
}
return true; return true;
} }
@ -884,60 +871,17 @@ class InGamePacketHandler extends PacketHandler{
//TODO: make APIs for this to allow plugins to use this information //TODO: make APIs for this to allow plugins to use this information
return $this->player->onFormSubmit($packet->formId, null); return $this->player->onFormSubmit($packet->formId, null);
}elseif($packet->formData !== null){ }elseif($packet->formData !== null){
return $this->player->onFormSubmit($packet->formId, self::stupid_json_decode($packet->formData, true)); try{
$responseData = json_decode($packet->formData, true, self::MAX_FORM_RESPONSE_DEPTH, JSON_THROW_ON_ERROR);
}catch(\JsonException $e){
throw PacketHandlingException::wrap($e, "Failed to decode form response data");
}
return $this->player->onFormSubmit($packet->formId, $responseData);
}else{ }else{
throw new PacketHandlingException("Expected either formData or cancelReason to be set in ModalFormResponsePacket"); throw new PacketHandlingException("Expected either formData or cancelReason to be set in ModalFormResponsePacket");
} }
} }
/**
* Hack to work around a stupid bug in Minecraft W10 which causes empty strings to be sent unquoted in form responses.
*
* @return mixed
* @throws PacketHandlingException
*/
private static function stupid_json_decode(string $json, bool $assoc = false){
if(preg_match('/^\[(.+)\]$/s', $json, $matches) > 0){
$raw = $matches[1];
$lastComma = -1;
$newParts = [];
$inQuotes = false;
for($i = 0, $len = strlen($raw); $i <= $len; ++$i){
if($i === $len || ($raw[$i] === "," && !$inQuotes)){
$part = substr($raw, $lastComma + 1, $i - ($lastComma + 1));
if(trim($part) === ""){ //regular parts will have quotes or something else that makes them non-empty
$part = '""';
}
$newParts[] = $part;
$lastComma = $i;
}elseif($raw[$i] === '"'){
if(!$inQuotes){
$inQuotes = true;
}else{
$backslashes = 0;
for(; $backslashes < $i && $raw[$i - $backslashes - 1] === "\\"; ++$backslashes){}
if(($backslashes % 2) === 0){ //unescaped quote
$inQuotes = false;
}
}
}
}
$fixed = "[" . implode(",", $newParts) . "]";
try{
return json_decode($fixed, $assoc, self::MAX_FORM_RESPONSE_DEPTH, JSON_THROW_ON_ERROR);
}catch(\JsonException $e){
throw PacketHandlingException::wrap($e, "Failed to fix JSON (original: $json, modified: $fixed)");
}
}
try{
return json_decode($json, $assoc, self::MAX_FORM_RESPONSE_DEPTH, JSON_THROW_ON_ERROR);
}catch(\JsonException $e){
throw PacketHandlingException::wrap($e);
}
}
public function handleServerSettingsRequest(ServerSettingsRequestPacket $packet) : bool{ public function handleServerSettingsRequest(ServerSettingsRequestPacket $packet) : bool{
return false; //TODO: GUI stuff return false; //TODO: GUI stuff
} }

View File

@ -123,7 +123,7 @@ class LoginPacketHandler extends PacketHandler{
$this->session->getPort(), $this->session->getPort(),
$this->server->requiresAuthentication() $this->server->requiresAuthentication()
); );
if($this->server->getNetwork()->getConnectionCount() > $this->server->getMaxPlayers()){ if($this->server->getNetwork()->getValidConnectionCount() > $this->server->getMaxPlayers()){
$ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_FULL, KnownTranslationKeys::DISCONNECTIONSCREEN_SERVERFULL); $ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_FULL, KnownTranslationKeys::DISCONNECTIONSCREEN_SERVERFULL);
} }
if(!$this->server->isWhitelisted($playerInfo->getUsername())){ if(!$this->server->isWhitelisted($playerInfo->getUsername())){

View File

@ -36,6 +36,8 @@ use pocketmine\network\NetworkInterfaceStartException;
use pocketmine\network\PacketHandlingException; use pocketmine\network\PacketHandlingException;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\snooze\SleeperNotifier; use pocketmine\snooze\SleeperNotifier;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use raklib\generic\SocketException; use raklib\generic\SocketException;
use raklib\protocol\EncapsulatedPacket; use raklib\protocol\EncapsulatedPacket;
@ -52,6 +54,7 @@ use function mt_rand;
use function random_bytes; use function random_bytes;
use function rtrim; use function rtrim;
use function substr; use function substr;
use const PHP_INT_MAX;
class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
/** /**
@ -109,7 +112,12 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
public function start() : void{ public function start() : void{
$this->server->getTickSleeper()->addNotifier($this->sleeper, function() : void{ $this->server->getTickSleeper()->addNotifier($this->sleeper, function() : void{
Timings::$connection->startTiming();
try{
while($this->eventReceiver->handle($this)); while($this->eventReceiver->handle($this));
}finally{
Timings::$connection->stopTiming();
}
}); });
$this->server->getLogger()->debug("Waiting for RakLib to start..."); $this->server->getLogger()->debug("Waiting for RakLib to start...");
try{ try{

View File

@ -40,6 +40,7 @@ use function strlen;
use function time; use function time;
use function trim; use function trim;
use const AF_INET; use const AF_INET;
use const AF_INET6;
use const IPPROTO_IPV6; use const IPPROTO_IPV6;
use const IPV6_V6ONLY; use const IPV6_V6ONLY;
use const PHP_INT_MAX; use const PHP_INT_MAX;

View File

@ -73,6 +73,12 @@ use function sprintf;
use function strlen; use function strlen;
use function trim; use function trim;
use const AF_INET; use const AF_INET;
use const PREG_BACKTRACK_LIMIT_ERROR;
use const PREG_BAD_UTF8_ERROR;
use const PREG_BAD_UTF8_OFFSET_ERROR;
use const PREG_INTERNAL_ERROR;
use const PREG_JIT_STACKLIMIT_ERROR;
use const PREG_RECURSION_LIMIT_ERROR;
use const SO_RCVTIMEO; use const SO_RCVTIMEO;
use const SOCK_DGRAM; use const SOCK_DGRAM;
use const SOCKET_ETIMEDOUT; use const SOCKET_ETIMEDOUT;

View File

@ -2448,7 +2448,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->cursorInventory = new PlayerCursorInventory($this); $this->cursorInventory = new PlayerCursorInventory($this);
$this->craftingGrid = new PlayerCraftingInventory($this); $this->craftingGrid = new PlayerCraftingInventory($this);
$this->addPermanentInventories($this->inventory, $this->armorInventory, $this->cursorInventory, $this->offHandInventory); $this->addPermanentInventories($this->inventory, $this->armorInventory, $this->cursorInventory, $this->offHandInventory, $this->craftingGrid);
//TODO: more windows //TODO: more windows
} }

View File

@ -45,6 +45,7 @@ use function stream_copy_to_stream;
use function strpos; use function strpos;
use function strtolower; use function strtolower;
use function trim; use function trim;
use const DIRECTORY_SEPARATOR;
abstract class PluginBase implements Plugin, CommandExecutor{ abstract class PluginBase implements Plugin, CommandExecutor{
private bool $isEnabled = false; private bool $isEnabled = false;

View File

@ -41,6 +41,8 @@ abstract class Timings{
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $serverTick; public static $serverTick;
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $serverInterrupts;
/** @var TimingsHandler */
public static $memoryManager; public static $memoryManager;
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $garbageCollector; public static $garbageCollector;
@ -140,7 +142,8 @@ abstract class Timings{
self::$initialized = true; self::$initialized = true;
self::$fullTick = new TimingsHandler("Full Server Tick"); self::$fullTick = new TimingsHandler("Full Server Tick");
self::$serverTick = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Full Server Tick", self::$fullTick); self::$serverTick = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Server Tick Update Cycle", self::$fullTick);
self::$serverInterrupts = new TimingsHandler(self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Server Mid-Tick Processing", self::$fullTick);
self::$memoryManager = new TimingsHandler("Memory Manager"); self::$memoryManager = new TimingsHandler("Memory Manager");
self::$garbageCollector = new TimingsHandler("Garbage Collector", self::$memoryManager); self::$garbageCollector = new TimingsHandler("Garbage Collector", self::$memoryManager);
self::$titleTick = new TimingsHandler("Console Title Tick"); self::$titleTick = new TimingsHandler("Console Title Tick");

View File

@ -29,6 +29,7 @@ use pocketmine\lang\Translatable;
use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissibleBase;
use pocketmine\permission\PermissibleDelegateTrait; use pocketmine\permission\PermissibleDelegateTrait;
use pocketmine\Server; use pocketmine\Server;
use const PHP_INT_MAX;
/** /**
* Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and * Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and

View File

@ -23,6 +23,10 @@ declare(strict_types=1);
namespace pocketmine\utils; namespace pocketmine\utils;
/**
* This trait offers the same functionality as RegistryTrait, but also clones any returned objects to prevent outside
* modification.
*/
trait CloningRegistryTrait{ trait CloningRegistryTrait{
use RegistryTrait; use RegistryTrait;

View File

@ -55,6 +55,7 @@ use const CASE_LOWER;
use const JSON_BIGINT_AS_STRING; use const JSON_BIGINT_AS_STRING;
use const JSON_PRETTY_PRINT; use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR; use const JSON_THROW_ON_ERROR;
use const YAML_UTF8_ENCODING;
/** /**
* Config Class for simple config manipulation of multiple formats. * Config Class for simple config manipulation of multiple formats.

View File

@ -23,6 +23,13 @@ declare(strict_types=1);
namespace pocketmine\utils; namespace pocketmine\utils;
/**
* This trait allows a class to simulate a Java-style enum. Members are exposed as static methods and handled via
* __callStatic().
*
* Classes using this trait need to include \@method tags in their class docblock for every enum member.
* Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php
*/
trait EnumTrait{ trait EnumTrait{
use RegistryTrait; use RegistryTrait;
use NotCloneable; use NotCloneable;

View File

@ -187,9 +187,10 @@ final class Filesystem{
* @throws \InvalidArgumentException if the lock file path is invalid (e.g. parent directory doesn't exist, permission denied) * @throws \InvalidArgumentException if the lock file path is invalid (e.g. parent directory doesn't exist, permission denied)
*/ */
public static function createLockFile(string $lockFilePath) : ?int{ public static function createLockFile(string $lockFilePath) : ?int{
$resource = fopen($lockFilePath, "a+b"); try{
if($resource === false){ $resource = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($lockFilePath, "a+b"));
throw new \InvalidArgumentException("Invalid lock file path or read/write permissions denied"); }catch(\ErrorException $e){
throw new \InvalidArgumentException("Failed to open lock file: " . $e->getMessage(), 0, $e);
} }
if(!flock($resource, LOCK_EX | LOCK_NB)){ if(!flock($resource, LOCK_EX | LOCK_NB)){
//wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the //wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the

View File

@ -28,6 +28,13 @@ use function count;
use function mb_strtoupper; use function mb_strtoupper;
use function preg_match; use function preg_match;
/**
* This trait allows a class to simulate object class constants, since PHP doesn't currently support this.
* These faux constants are exposed in static class methods, which are handled using __callStatic().
*
* Classes using this trait need to include \@method tags in their class docblock for every faux constant.
* Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php
*/
trait RegistryTrait{ trait RegistryTrait{
/** /**
* @var object[] * @var object[]

View File

@ -58,6 +58,7 @@ use pocketmine\item\StringToItemParser;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
@ -1222,7 +1223,8 @@ class World implements ChunkManager{
private function tickChunk(int $chunkX, int $chunkZ) : void{ private function tickChunk(int $chunkX, int $chunkZ) : void{
$chunk = $this->getChunk($chunkX, $chunkZ); $chunk = $this->getChunk($chunkX, $chunkZ);
if($chunk === null){ if($chunk === null){
throw new \InvalidArgumentException("Chunk is not loaded"); //the chunk may have been unloaded during a previous chunk's update (e.g. during BlockGrowEvent)
return;
} }
foreach($this->getChunkEntities($chunkX, $chunkZ) as $entity){ foreach($this->getChunkEntities($chunkX, $chunkZ) as $entity){
$entity->onRandomUpdate(); $entity->onRandomUpdate();
@ -1320,7 +1322,9 @@ class World implements ChunkManager{
/** /**
* Notify the blocks at and around the position that the block at the position may have changed. * Notify the blocks at and around the position that the block at the position may have changed.
* This will cause onNeighbourBlockUpdate() to be called for these blocks. * This will cause onNearbyBlockChange() to be called for these blocks.
*
* @see Block::onNearbyBlockChange()
*/ */
public function notifyNeighbourBlockUpdate(Vector3 $pos) : void{ public function notifyNeighbourBlockUpdate(Vector3 $pos) : void{
$this->tryAddToNeighbourUpdateQueue($pos); $this->tryAddToNeighbourUpdateQueue($pos);
@ -1952,6 +1956,10 @@ class World implements ChunkManager{
if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){
$blockReplace = $blockClicked; $blockReplace = $blockClicked;
//TODO: while this mimics the vanilla behaviour with replaceable blocks, we should really pass some other
//value like NULL and let place() deal with it. This will look like a bug to anyone who doesn't know about
//the vanilla behaviour.
$face = Facing::UP;
$hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z);
}elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){ }elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){
return false; return false;

View File

@ -15,11 +15,6 @@ parameters:
count: 1 count: 1
path: ../../../src/entity/projectile/Projectile.php path: ../../../src/entity/projectile/Projectile.php
-
message: "#^Empty array passed to foreach\\.$#"
count: 1
path: ../../../src/network/mcpe/cache/ChunkCache.php
- -
message: "#^Match arm comparison between 4 and 4 is always true\\.$#" message: "#^Match arm comparison between 4 and 4 is always true\\.$#"
count: 1 count: 1
@ -51,7 +46,7 @@ parameters:
path: ../../../src/plugin/PluginManager.php path: ../../../src/plugin/PluginManager.php
- -
message: "#^Offset \\(int\\|string\\) on non\\-empty\\-array\\<pocketmine\\\\plugin\\\\Plugin\\> in isset\\(\\) always exists and is not nullable\\.$#" message: "#^Offset \\(int\\|string\\) on array\\<pocketmine\\\\plugin\\\\Plugin\\> in isset\\(\\) always exists and is not nullable\\.$#"
count: 1 count: 1
path: ../../../src/plugin/PluginManager.php path: ../../../src/plugin/PluginManager.php
@ -70,11 +65,6 @@ parameters:
count: 1 count: 1
path: ../../../src/thread/Worker.php path: ../../../src/thread/Worker.php
-
message: "#^Offset \\(int\\|string\\) on non\\-empty\\-array\\<pocketmine\\\\world\\\\World\\> in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: ../../../src/world/WorldManager.php
- -
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#" message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
count: 2 count: 2

View File

@ -1,68 +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\network\mcpe\handler;
use PHPUnit\Framework\TestCase;
class StupidJsonDecodeTest extends TestCase{
/**
* @var \Closure
* @phpstan-var \Closure(string $json, bool $assoc=) : mixed
*/
private $stupidJsonDecodeFunc;
public function setUp() : void{
$this->stupidJsonDecodeFunc = (new \ReflectionMethod(InGamePacketHandler::class, 'stupid_json_decode'))->getClosure();
}
/**
* @return mixed[][]
* @phpstan-return list<array{string,mixed}>
*/
public function stupidJsonDecodeProvider() : array{
return [
["[\n \"a\",\"b,c,d,e\\\" \",,0,1,2, false, 0.001]", ['a', 'b,c,d,e" ', '', 0, 1, 2, false, 0.001]],
["0", 0],
["false", false],
["null", null],
['["\",,\"word","a\",,\"word2",]', ['",,"word', 'a",,"word2', '']],
['["\",,\"word","a\",,\"word2",""]', ['",,"word', 'a",,"word2', '']],
['["Hello,, PocketMine"]', ['Hello,, PocketMine']],
['[,]', ['', '']],
['[]', []]
];
}
/**
* @dataProvider stupidJsonDecodeProvider
*
* @param mixed $expect
*
* @throws \ReflectionException
*/
public function testStupidJsonDecode(string $brokenJson, $expect) : void{
$decoded = ($this->stupidJsonDecodeFunc)($brokenJson, true);
self::assertEquals($expect, $decoded);
}
}

View File

@ -43,6 +43,10 @@ use function rename;
use function round; use function round;
use function scandir; use function scandir;
use function unlink; use function unlink;
use const PATHINFO_EXTENSION;
use const PHP_BINARY;
use const SCANDIR_SORT_NONE;
use const SORT_NUMERIC;
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';

View File

@ -0,0 +1,89 @@
<?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\tools\decode_crashdump;
use function array_pop;
use function array_slice;
use function base64_decode;
use function count;
use function file;
use function file_put_contents;
use function fwrite;
use function implode;
use function json_decode;
use function json_encode;
use function realpath;
use function trim;
use function zlib_decode;
use const FILE_IGNORE_NEW_LINES;
use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_SLASHES;
use const PHP_EOL;
use const STDERR;
if(count($argv) === 2){
$input = $argv[1];
$output = "decoded.json";
}elseif(count($argv) === 3){
[, $input, $output] = $argv;
}else{
fwrite(STDERR, "Required arguments: input file, output file" . PHP_EOL);
exit(1);
}
$lines = file($input, FILE_IGNORE_NEW_LINES);
if($lines === false){
fwrite(STDERR, "Unable to read file $input" . PHP_EOL);
exit(1);
}
$start = -1;
foreach($lines as $num => $line){
if(trim($line) === "===BEGIN CRASH DUMP==="){
$start = $num + 1;
break;
}
}
if($start === -1){
fwrite(STDERR, "Crashdump encoded data not found in target file" . PHP_EOL);
exit(1);
}
$data = array_slice($lines, $start);
array_pop($data);
$zlibData = base64_decode(implode("", $data), true);
if($zlibData === false){
fwrite(STDERR, "Invalid encoded data in crashdump" . PHP_EOL);
exit(1);
}
$decoded = zlib_decode($zlibData);
if($decoded === false){
fwrite(STDERR, "Invalid compressed data in crashdump" . PHP_EOL);
exit(1);
}
file_put_contents($output, json_encode(json_decode($decoded), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
echo "Wrote decoded crashdump to " . realpath($output) . PHP_EOL;

View File

@ -49,9 +49,13 @@ use function socket_strerror;
use function strlen; use function strlen;
use function time; use function time;
use function trim; use function trim;
use const AF_INET;
use const MSG_DONTROUTE; use const MSG_DONTROUTE;
use const PHP_BINARY; use const PHP_BINARY;
use const PHP_INT_MAX; use const PHP_INT_MAX;
use const SOCK_DGRAM;
use const SOL_UDP;
use const STDIN;
require_once 'vendor/autoload.php'; require_once 'vendor/autoload.php';