Compare commits

..

18 Commits

Author SHA1 Message Date
a6042ec7e8 Merge branch 'minor-next' into gameplay-permissions 2024-12-01 23:55:09 +00:00
e494460cfd Introduce invulnerable permission
this eliminates all remaining usages of isCreative(), except for reach distance checks. Reach distance can't be migrated to a permission in an obvious way.
2024-12-01 17:15:26 +00:00
2c299e2b49 Fix CS 2024-12-01 16:51:16 +00:00
ac1e70cd96 Added permission for emoting 2024-12-01 16:50:26 +00:00
0bbd4af496 Remove TODO comment
the remaining bool flags are user input toggles, not abilities, so it doesn't make sense to move them to permissions.

permissions are intended for what the player is *allowed* to do, not what they *want* to do.
2024-12-01 16:48:09 +00:00
b0bfc30b07 Added noclip permission 2024-12-01 16:44:32 +00:00
a91cef37f6 Move some gamemode checks to instabreak checks 2024-12-01 16:42:30 +00:00
26afa97cdc Convert adventure mode checks to permissions 2024-12-01 16:12:22 +00:00
57082c8148 Added instabreak permission 2024-12-01 15:49:19 +00:00
74ee38ab99 Use permissions for more stuff 2024-12-01 15:34:00 +00:00
df069b0418 Fixed behavioural BC break in setAllowFlight() 2024-12-01 15:25:05 +00:00
ea43fd1917 Merge branch 'minor-next' into gameplay-permissions 2024-12-01 15:22:44 +00:00
ca5d9c3731 github web editor try not to suck challenge IMPOSSIBLE 2024-11-29 15:26:40 +00:00
cbcc4d24e0 Update DefaultPermissions.php 2024-11-29 15:25:33 +00:00
223fd74255 piece of shit web editor 2024-11-29 15:24:57 +00:00
0fef4c6683 Merge branch 'minor-next' into gameplay-permissions 2024-11-29 15:23:03 +00:00
e1ae9a7d69 fix CS 2023-01-30 22:21:14 +00:00
10a962daa2 First look at #5512: gameplay permissions 2023-01-16 22:12:07 +00:00
232 changed files with 3627 additions and 1977 deletions

View File

@ -3,6 +3,9 @@ contact_links:
- name: Help & support on Discord
url: https://discord.gg/bmSAZBG
about: We don't accept support requests on the issue tracker. Please try asking on Discord instead.
- name: Help & support on forums
url: https://forums.pmmp.io
about: We don't accept support requests on the issue tracker. Please try asking on forums instead.
- name: Documentation
url: https://pmmp.rtfd.io
about: PocketMine-MP documentation

View File

@ -37,7 +37,4 @@ updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: monthly
groups:
github-actions:
patterns: ["*"]
interval: weekly

View File

@ -53,7 +53,7 @@ jobs:
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.9.0
with:
push: true
context: ./pocketmine-mp
@ -66,7 +66,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.9.0
with:
push: true
context: ./pocketmine-mp
@ -79,7 +79,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.9.0
with:
push: true
context: ./pocketmine-mp
@ -92,7 +92,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.9.0
with:
push: true
context: ./pocketmine-mp

View File

@ -1,29 +0,0 @@
name: 'Clean up stale PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: -1
days-before-issue-close: -1
stale-pr-message: |
This PR has been marked as "Waiting on Author", but we haven't seen any activity in 7 days.
If there is no further activity, it will be closed in 28 days.
Note for maintainers: Adding an assignee to the PR will prevent it from being marked as stale.
close-pr-message: |
As this PR hasn't been updated for a while, unfortunately we'll have to close it.
days-before-pr-stale: 7
days-before-pr-close: 28
only-labels: "Status: Waiting on Author"
close-pr-label: "Resolution: Abandoned"
exempt-all-assignees: true

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "tests/plugins/DevTools"]
path = tests/plugins/DevTools
url = https://github.com/pmmp/DevTools.git
[submodule "build/php"]
path = build/php
url = https://github.com/pmmp/php-build-scripts.git

View File

@ -5,6 +5,7 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/build')
->in(__DIR__ . '/tests')
->in(__DIR__ . '/tools')
->notPath('plugins/DevTools')
->notName('PocketMine.php');
return (new PhpCsFixer\Config)

View File

@ -123,7 +123,7 @@ The following are required as a minimum for pull requests. PRs that don't meet t
- Remember, PRs with small diffs are much easier to review. Small PRs are generally reviewed and merged much faster than large ones.
- **Start small.** Try fixing minor bugs or doing something isolated (e.g. adding a new block or item) before attempting larger changes.
- This helps you get familiar with the codebase, the contribution process, and the expectations of maintainers.
- Check out ["Easy task" issues](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue+is%3Aopen+label%3A%22Easy+task%22) on the issues page for something that you could tackle without too much effort.
- Check out the [issues page]() for something that you could tackle without too much effort.
- **Do not copy-paste other people's code**. Many PRs involve discussion about the changes, and changes are often requested by reviewers. If you don't understand the code you're copy-pasting, your PR is likely to fail.
- **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE.
- **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode.

View File

@ -1,16 +0,0 @@
# 5.22.0
Released 4th December 2024.
**For Minecraft: Bedrock Edition 1.21.50**
This is a support release for Minecraft: Bedrock Edition 1.21.50.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.21.50.
- Removed support for earlier versions.

View File

@ -1,116 +0,0 @@
# 5.23.0
Released 5th December 2024.
This is a minor feature release, including new gameplay features, internals improvements, API additions and
deprecations, and improvements to timings.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- `/timings` now supports collecting timings from async task workers. These new timings will be shown alongside `Full Server Tick` timings, but will not be counted in total load.
- Added `/xp` command.
- `start.sh` will now emit warnings when the server process exits with an unusual exit code. This helps to detect unexpected segmentation faults and other kinds of native errors.
## Gameplay
- Added the following new items:
- End Crystal
- Goat Horn (all variants)
- Ice Bomb (from Education Edition)
- Recovery Compass
- Added the following enchantments:
- Frost Walker
- Sugarcane now self-destructs when there is no water adjacent to the base block.
- Added basic support for middle-clicking on entities to get their spawn eggs.
- Added sounds when drinking potions.
- Eating food is now allowed in creative mode and in peaceful difficulty.
## API
### `pocketmine\block`
- Extracted `MultiAnyFacingTrait` and `MultiAnySupportTrait` from `GlowLichen` to enable reuse in other blocks.
- The following API methods have been deprecated:
- `Campfire->getInventory()` - this was added by mistake and can't be well-supported given the way that blocks work
### `pocketmine\command`
- The following classes have been added:
- `ClosureCommand` - allows registering a closure to execute a command
### `pocketmine\event`
- Added APIs to `PlayerInteractEvent` to allow toggling item and block interactions.
- This allows various customisations, such as allowing interactions when sneaking, selectively disabling item or block reactions, etc.
- If both item and block interactions are disabled, the event is **not** cancelled (blocks can still be placed).
- The following API methods have been added:
- `public PlayerInteractEvent->setUseBlock(bool $useBlock) : void`
- `public PlayerInteractEvent->setUseItem(bool $useItem) : void`
- `public PlayerInteractEvent->useBlock() : bool` - returns whether the block can respond to the interaction (toggling levers, opening/closing doors, etc).
- `public PlayerInteractEvent->useItem() : bool` - returns whether the item can respond to the interaction (spawn eggs, flint & steel, etc).
- The following new classes have been added:
- `player\PlayerEntityPickEvent` - called when a player middle-clicks on an entity
### `pocketmine\inventory\transaction`
- The following API methods have been deprecated:
- `InventoryAction->onAddToTransaction()`
### `pocketmine\permission`
- The following API methods have been deprecated:
- `PermissionManager->getPermissionSubscriptions()`
- `PermissionManager->subscribeToPermission()`
- `PermissionManager->unsubscribeFromAllPermissions()`
- `PermissionManager->unsubscribeFromPermission()`
### `pocketmine\plugin`
- The following classes have been deprecated:
- `DiskResourceProvider`
- `ResourceProvider`
### `pocketmine\promise`
- `Promise::all()` now accepts zero promises. This will return an already-resolved promise with an empty array.
### `pocketmine\scheduler`
- Added PHPStan generic types to `TaskHandler` and related APIs in `TaskScheduler` and `Task`.
- The following API methods have been deprecated
- `AsyncTask->publishProgress()`
- `AsyncTask->onProgressUpdate()`
### `pocketmine\timings`
- Timings can now notify other code when timings are enabled/disabled, reloaded, or collected.
- The intent of this is to facilitate timings usage on other threads, and have the results collected into a single timings report.
- Timings cannot directly control timings on other threads, so these callbacks allow plugins to use custom mechanisms to toggle, reset and collect timings.
- PocketMine-MP currently uses this to collect timings from async task workers. More internal threads may be supported in the future.
- The following API methods have been added:
- `public static TimingsHandler::getCollectCallbacks() : ObjectSet<\Closure() : list<Promise<list<string>>>` - callbacks for (asynchronously) collecting timings (typically from other threads). The returned promises should be resolved with the result of `TimingsHandler::printCurrentThreadRecords()`.
- `public static TimingsHandler::getReloadCallbacks() : ObjectSet<\Closure() : void>` - callbacks called when timings are reset
- `public static TimingsHandler::getToggleCallbacks() : ObjectSet<\Closure(bool $enable) : void>` - callbacks called when timings are enabled/disabled
- `public static TimingsHandler::requestPrintTimings() : Promise<list<string>>` - asynchronously collects timing results from all threads and assembles them into a single report
- The following API methods have been deprecated:
- `TimingsHandler::printTimings()` - this function cannot support async timings collection. Use `TimingsHandler::requestPrintTimings()` instead.
- `Timings::getAsyncTaskErrorTimings()` - internal method that is no longer needed
- The following constants have been deprecated:
- `Timings::GROUP_BREAKDOWN` - no longer used
### `pocketmine\utils`
- The following API methods have been added:
- `public static Utils::getRandomFloat() : float` - returns a random float between 0 and 1. Drop-in replacement for `lcg_value()` in PHP 8.4.
## Internals
- Blocks are now always synced with the client during a right-click-block interaction. This clears mispredictions on the client in case the new `PlayerInteractEvent` flags were customized by plugins.
- `VanillaBlocks` and `VanillaItems` now use reflection to lookup TypeId constants by registration name, instead of requiring TypeIds to be manually specified.
- While this is obviously a hack, it prevents incorrect constants from being used when adding new blocks, and guarantees that the names of constants in `BlockTypeIds` and `ItemTypeIds` will match their corresponding entries in `VanillaBlocks` and `VanillaItems` respectively.
- It also significantly improves readability of `VanillaBlocks` and `VanillaItems`, as well as eliminating ugly code like `WoodLikeBlockIdHelper`.
- In PM6, the team is exploring options to redesign `VanillaBlocks` and `VanillaItems` to eliminate the need for defining separate TypeIds entirely.
- `ConsoleReader` now uses socket support in `proc_open()` to transmit IPC messages to the server process. Previously, a temporary socket server was used, which was unreliable in some conditions.
- Event handler tests have been converted to PHPUnit tests by mocking `Server` and `Plugin` instances. Previously, these required integration tests for these dependencies.
- Fixed various deprecation warnings in PHP 8.4.
- `netresearch/jsonmapper` is now used at `5.0.0`. The PMMP fork of this library has been removed, as it is no longer needed.
# 5.23.1
Released 5th December 2024.
## Fixes
- Fixed signs not creating a tile when placed.
## Internals
- Improved blockstate consistency check to detect tiles disappearing during refactors.

View File

@ -32,11 +32,11 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"netresearch/jsonmapper": "~v5.0.0",
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
"pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
"pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50",
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.50",
"pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40",
"pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40",
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
@ -78,6 +78,7 @@
"sort-packages": true
},
"scripts": {
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make ./ --relative tests/plugins/DevTools --out plugins/DevTools.phar",
"make-server": [
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
"@php -dphar.readonly=0 build/server-phar.php"

143
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "732102eca72dc1d29e7b67dfbce07653",
"content-hash": "c57e8f52250edfd03906219fe14fc240",
"packages": [
{
"name": "adhocore/json-comment",
@ -125,57 +125,6 @@
],
"time": "2023-11-29T23:19:16+00:00"
},
{
"name": "netresearch/jsonmapper",
"version": "v5.0.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
"reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
"autoload": {
"psr-0": {
"JsonMapper": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"OSL-3.0"
],
"authors": [
{
"name": "Christian Weiske",
"email": "cweiske@cweiske.de",
"homepage": "http://github.com/cweiske/jsonmapper/",
"role": "Developer"
}
],
"description": "Map nested JSON structures onto PHP classes",
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0"
},
"time": "2024-09-08T10:20:00+00:00"
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "5.0.0",
@ -204,16 +153,16 @@
},
{
"name": "pocketmine/bedrock-data",
"version": "2.15.0+bedrock-1.21.50",
"version": "2.14.1+bedrock-1.21.40",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad"
"reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/4a41864ed09613ecec6791e2ae076a8ec7089cc4",
"reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4",
"shasum": ""
},
"type": "library",
@ -224,22 +173,22 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.50"
"source": "https://github.com/pmmp/BedrockData/tree/2.14.1+bedrock-1.21.40"
},
"time": "2024-12-04T12:59:12+00:00"
"time": "2024-11-12T21:36:20+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
"version": "1.14.0",
"version": "1.13.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
"reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb"
"reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/9fc7c9bbb558a017395c1cb7dd819c033ee971bb",
"reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1cf81305f2ffcf7dde9577c4f16a55c765192b03",
"reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03",
"shasum": ""
},
"type": "library",
@ -250,22 +199,22 @@
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.14.0"
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.1"
},
"time": "2024-12-04T12:22:49+00:00"
"time": "2024-11-12T21:33:17+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "35.0.0+bedrock-1.21.50",
"version": "35.0.0+bedrock-1.21.40",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435"
"reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
"reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
"shasum": ""
},
"require": {
@ -296,9 +245,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.50"
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.40"
},
"time": "2024-12-04T13:02:00+00:00"
"time": "2024-10-24T15:45:43+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -616,6 +565,60 @@
},
"time": "2023-07-14T13:01:49+00:00"
},
{
"name": "pocketmine/netresearch-jsonmapper",
"version": "v4.4.999",
"source": {
"type": "git",
"url": "https://github.com/pmmp/netresearch-jsonmapper.git",
"reference": "9a6610033d56e358e86a3e4fd5f87063c7318833"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833",
"reference": "9a6610033d56e358e86a3e4fd5f87063c7318833",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=7.1"
},
"replace": {
"netresearch/jsonmapper": "~4.2.0"
},
"require-dev": {
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
"autoload": {
"psr-0": {
"JsonMapper": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"OSL-3.0"
],
"authors": [
{
"name": "Christian Weiske",
"email": "cweiske@cweiske.de",
"homepage": "http://github.com/cweiske/jsonmapper/",
"role": "Developer"
}
],
"description": "Fork of netresearch/jsonmapper with security fixes needed by pocketmine/pocketmine-mp",
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999"
},
"time": "2024-02-23T13:17:01+00:00"
},
{
"name": "pocketmine/raklib",
"version": "1.1.1",

View File

@ -10,6 +10,8 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
rules:
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule

View File

@ -79,7 +79,6 @@ use pocketmine\player\PlayerDataLoadException;
use pocketmine\player\PlayerDataProvider;
use pocketmine\player\PlayerDataSaveException;
use pocketmine\player\PlayerInfo;
use pocketmine\plugin\FolderPluginLoader;
use pocketmine\plugin\PharPluginLoader;
use pocketmine\plugin\PluginEnableOrder;
use pocketmine\plugin\PluginGraylist;
@ -345,10 +344,6 @@ class Server{
return $this->maxPlayers;
}
public function setMaxPlayers(int $maxPlayers) : void{
$this->maxPlayers = $maxPlayers;
}
/**
* Returns whether the server requires that players be authenticated to Xbox Live. If true, connecting players who
* are not logged into Xbox Live will be disconnected.
@ -1027,7 +1022,6 @@ class Server{
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->dataPath, "plugin_data"), $pluginGraylist);
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
$this->pluginManager->registerInterface(new ScriptPluginLoader());
$this->pluginManager->registerInterface(new FolderPluginLoader($this->autoloader));
$providerManager = new WorldProviderManager();
if(

View File

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

View File

@ -116,7 +116,7 @@ class Bamboo extends Transparent{
}
public function getModelPositionOffset() : ?Vector3{
$seed = self::getOffsetSeed($this->position->x, 0, $this->position->z);
$seed = self::getOffsetSeed($this->position->getFloorX(), 0, $this->position->getFloorZ());
$retX = (($seed % 12) + 1) / 16;
$retZ = ((($seed >> 8) % 12) + 1) / 16;
return new Vector3($retX, 0, $retZ);
@ -135,7 +135,7 @@ class Bamboo extends Transparent{
private function seekToTop() : Bamboo{
$world = $this->position->getWorld();
$top = $this;
while(($next = $world->getBlock($top->position->getSide(Facing::UP))) instanceof Bamboo && $next->hasSameTypeId($this)){
while(($next = $world->getBlock($top->position->up())) instanceof Bamboo && $next->hasSameTypeId($this)){
$top = $next;
}
return $top;
@ -144,7 +144,7 @@ class Bamboo extends Transparent{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer){
$top = $this->seekToTop();
if($top->grow(self::getMaxHeight($top->position->x, $top->position->z), mt_rand(1, 2), $player)){
if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){
$item->pop();
return true;
}
@ -159,12 +159,12 @@ class Bamboo extends Transparent{
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->getSide(Facing::UP))->canBeReplaced()){
if(!$world->getBlock($this->position->up())->canBeReplaced()){
return false;
}
$height = 1;
while($world->getBlock($this->position->getSide(Facing::DOWN, $height))->hasSameTypeId($this)){
while($world->getBlock($this->position->subtract(0, $height, 0))->hasSameTypeId($this)){
if(++$height >= $maxHeight){
return false;
}
@ -201,7 +201,7 @@ class Bamboo extends Transparent{
$tx = new BlockTransaction($world);
foreach($newBlocks as $idx => $newBlock){
$tx->addBlock($this->position->getSide(Facing::DOWN, $idx - $growAmount), $newBlock);
$tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock);
}
$ev = new StructureGrowEvent($this, $tx, $player);
@ -221,10 +221,10 @@ class Bamboo extends Transparent{
$world = $this->position->getWorld();
if($this->ready){
$this->ready = false;
if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->x, $this->position->z), 1, null)){
if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1, null)){
$world->setBlock($this->position, $this);
}
}elseif($world->getBlock($this->position->getSide(Facing::UP))->canBeReplaced()){
}elseif($world->getBlock($this->position->up())->canBeReplaced()){
$this->ready = true;
$world->setBlock($this->position, $this);
}

View File

@ -73,14 +73,14 @@ final class BambooSapling extends Flowable{
private function grow(?Player $player) : bool{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->getSide(Facing::UP))->canBeReplaced()){
if(!$world->getBlock($this->position->up())->canBeReplaced()){
return false;
}
$tx = new BlockTransaction($world);
$bamboo = VanillaBlocks::BAMBOO();
$tx->addBlock($this->position, $bamboo)
->addBlock($this->position->getSide(Facing::UP), (clone $bamboo)->setLeafSize(Bamboo::SMALL_LEAVES));
->addBlock($this->position->up(), (clone $bamboo)->setLeafSize(Bamboo::SMALL_LEAVES));
$ev = new StructureGrowEvent($this, $tx, $player);
$ev->call();
@ -102,7 +102,7 @@ final class BambooSapling extends Flowable{
if($world->getFullLight($this->position) < 9 || !$this->grow(null)){
$world->setBlock($this->position, $this);
}
}elseif($world->getBlock($this->position->getSide(Facing::UP))->canBeReplaced()){
}elseif($world->getBlock($this->position->up())->canBeReplaced()){
$this->ready = true;
$world->setBlock($this->position, $this);
}

View File

@ -99,10 +99,10 @@ abstract class BaseBigDripleaf extends Transparent{
return false;
}
$pos = $head->position;
$up = $pos->getSide(Facing::UP);
$up = $pos->up();
$world = $pos->getWorld();
if(
!$world->isInWorld($up->x, $up->y, $up->z) ||
!$world->isInWorld($up->getFloorX(), $up->getFloorY(), $up->getFloorZ()) ||
$world->getBlock($up)->getTypeId() !== BlockTypeIds::AIR
){
return false;

View File

@ -27,7 +27,6 @@ use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use function mt_rand;
abstract class BaseCoral extends Transparent{
@ -56,9 +55,11 @@ abstract class BaseCoral extends Transparent{
public function isSolid() : bool{ return false; }
protected function isCoveredWithWater() : bool{
$world = $this->position->getWorld();
$hasWater = false;
foreach(Facing::ALL as $side){
if($this->getSide($side) instanceof Water){
foreach($this->position->sides() as $vector3){
if($world->getBlock($vector3) instanceof Water){
$hasWater = true;
break;
}

View File

@ -145,7 +145,7 @@ abstract class BaseSign extends Transparent{
private function changeSignGlowingState(bool $glowing, Player $player, Item $item) : bool{
if($this->text->isGlowing() !== $glowing && $this->doSignChange(new SignText($this->text->getLines(), $this->text->getBaseColor(), $glowing), $player, $item)){
$this->position->getWorld()->addSound($this->position->center(), new InkSacUseSound());
$this->position->getWorld()->addSound($this->position, new InkSacUseSound());
return true;
}
return false;
@ -183,7 +183,7 @@ abstract class BaseSign extends Transparent{
$color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
$this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
){
$this->position->getWorld()->addSound($this->position->center(), new DyeUseSound());
$this->position->getWorld()->addSound($this->position, new DyeUseSound());
return true;
}
}elseif(match($item->getTypeId()){

View File

@ -128,7 +128,7 @@ class Bed extends Transparent{
$player->sendMessage(TextFormat::GRAY . "This bed is incomplete");
return true;
}elseif($playerPos->distanceSquared($this->position->center()) > 4 && $playerPos->distanceSquared($other->position->center()) > 4){
}elseif($playerPos->distanceSquared($this->position) > 4 && $playerPos->distanceSquared($other->position) > 4){
$player->sendMessage(KnownTranslationFactory::tile_bed_tooFar()->prefix(TextFormat::GRAY));
return true;
}

View File

@ -143,10 +143,10 @@ final class Bell extends Transparent{
public function ring(int $faceHit) : void{
$world = $this->position->getWorld();
$world->addSound($this->position->center(), new BellRingSound());
$world->addSound($this->position, new BellRingSound());
$tile = $world->getTile($this->position);
if($tile instanceof TileBell){
$world->broadcastPacketOnBlock($this->position, $tile->createFakeUpdatePacket($faceHit));
$world->broadcastPacketToViewers($this->position, $tile->createFakeUpdatePacket($faceHit));
}
}

View File

@ -93,7 +93,7 @@ class BigDripleafHead extends BaseBigDripleaf{
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
if($this->leafState !== DripleafState::FULL_TILT){
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT);
$this->position->getWorld()->addSound($this->position->center(), new DripleafTiltDownSound());
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
}
}
@ -101,13 +101,13 @@ class BigDripleafHead extends BaseBigDripleaf{
if($this->leafState !== DripleafState::STABLE){
if($this->leafState === DripleafState::FULL_TILT){
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE));
$this->position->getWorld()->addSound($this->position->center(), new DripleafTiltUpSound());
$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
}else{
$this->setTiltAndScheduleTick(match($this->leafState){
DripleafState::UNSTABLE => DripleafState::PARTIAL_TILT,
DripleafState::PARTIAL_TILT => DripleafState::FULL_TILT,
});
$this->position->getWorld()->addSound($this->position->center(), new DripleafTiltDownSound());
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
}
}
}

View File

@ -73,7 +73,7 @@ class Block{
protected BlockIdentifier $idInfo;
protected string $fallbackName;
protected BlockTypeInfo $typeInfo;
protected BlockPosition $position;
protected Position $position;
/** @var AxisAlignedBB[]|null */
protected ?array $collisionBoxes = null;
@ -107,7 +107,7 @@ class Block{
$this->idInfo = $idInfo;
$this->fallbackName = $name;
$this->typeInfo = $typeInfo;
$this->position = new BlockPosition(0, 0, 0, null);
$this->position = new Position(0, 0, 0, null);
$calculator = new RuntimeDataSizeCalculator();
$this->describeBlockItemState($calculator);
@ -377,7 +377,7 @@ class Block{
*/
public function writeStateToWorld() : void{
$world = $this->position->getWorld();
$chunk = $world->loadChunk($this->position->x >> Chunk::COORD_BIT_SIZE, $this->position->z >> Chunk::COORD_BIT_SIZE);
$chunk = $world->getOrLoadChunkAtPosition($this->position);
if($chunk === null){
throw new AssumptionFailedError("World::setBlock() should have loaded the chunk before calling this method");
}
@ -398,7 +398,7 @@ class Block{
* @var Tile $tile
* @see Tile::__construct()
*/
$tile = new $tileType($this->position);
$tile = new $tileType($world, $this->position->asVector3());
$world->addTile($tile);
}
}
@ -608,7 +608,7 @@ class Block{
return false;
}
final public function getPosition() : BlockPosition{
final public function getPosition() : Position{
return $this->position;
}
@ -616,7 +616,7 @@ class Block{
* @internal
*/
final public function position(World $world, int $x, int $y, int $z) : void{
$this->position = new BlockPosition($x, $y, $z, $world);
$this->position = new Position($x, $y, $z, $world);
$this->collisionBoxes = null;
}
@ -912,9 +912,9 @@ class Block{
if($this->collisionBoxes === null){
$this->collisionBoxes = $this->recalculateCollisionBoxes();
$extraOffset = $this->getModelPositionOffset();
[$dx, $dy, $dz] = $extraOffset !== null ? [$extraOffset->x, $extraOffset->y, $extraOffset->z] : [0, 0, 0];
$offset = $extraOffset !== null ? $this->position->addVector($extraOffset) : $this->position;
foreach($this->collisionBoxes as $bb){
$bb->offset($this->position->x + $dx, $this->position->y + $dy, $this->position->z + $dz);
$bb->offset($offset->x, $offset->y, $offset->z);
}
}

View File

@ -1,110 +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\block;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\world\World;
use function iterator_to_array;
final class BlockPosition implements \Stringable{
public function __construct(
public readonly int $x,
public readonly int $y,
public readonly int $z,
private ?World $world //TODO: make this non-nullable (requires Blocks not to reference positions)
){}
/**
* Returns the position's world if valid. Throws an error if the world is unexpectedly invalid.
*
* @throws AssumptionFailedError
*/
public function getWorld() : World{
if($this->world === null || !$this->world->isLoaded()){
throw new AssumptionFailedError("Position world is null or has been unloaded");
}
return $this->world;
}
/**
* Checks if this object has a valid reference to a loaded world
*/
public function isValid() : bool{
if($this->world !== null && !$this->world->isLoaded()){
$this->world = null;
return false;
}
return $this->world !== null;
}
public function asVector3() : Vector3{
return new Vector3($this->x, $this->y, $this->z);
}
public function center() : Vector3{
return new Vector3($this->x + 0.5, $this->y + 0.5, $this->z + 0.5);
}
public static function fromVector3(Vector3 $vector3, World $world) : self{
return new self($vector3->getFloorX(), $vector3->getFloorY(), $vector3->getFloorZ(), $world);
}
public function getSide(int $side, int $step = 1) : BlockPosition{
$offset = Facing::OFFSET[$side] ?? throw new \InvalidArgumentException("Invalid side $side");
[$dx, $dy, $dz] = $offset;
return new BlockPosition($this->x + ($dx * $step), $this->y + ($dy * $step), $this->z + ($dz * $step), $this->world);
}
public function add(int $x, int $y, int $z) : BlockPosition{
return new BlockPosition($this->x + $x, $this->y + $y, $this->z + $z, $this->world);
}
/**
* @phpstan-return \Generator<int, self, void, void>
*/
public function getAllSides() : \Generator{
foreach(Facing::ALL as $facing){
yield $this->getSide($facing);
}
}
/**
* @phpstan-return list<self>
*/
public function getAllSidesArray() : array{
return iterator_to_array($this->getAllSides(), preserve_keys: false);
}
public function __toString() : string{
$worldName = $this->world?->getFolderName() ?? "???";
return "BlockPosition(x=$this->x,y=$this->y,z=$this->z,world={$worldName}";
}
}

View File

@ -67,7 +67,7 @@ abstract class Button extends Flowable{
$world = $this->position->getWorld();
$world->setBlock($this->position, $this);
$world->scheduleDelayedBlockUpdate($this->position, $this->getActivationTime());
$world->addSound($this->position->center(), new RedstonePowerOnSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOnSound());
}
return true;
@ -78,7 +78,7 @@ abstract class Button extends Flowable{
$this->pressed = false;
$world = $this->position->getWorld();
$world->setBlock($this->position, $this);
$world->addSound($this->position->center(), new RedstonePowerOffSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound());
}
}

View File

@ -76,6 +76,6 @@ class CakeWithCandle extends BaseCake{
public function onConsume(Living $consumer) : void{
parent::onConsume($consumer);
$this->position->getWorld()->dropItem($this->position->center(), $this->getCandle()->asItem());
$this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), $this->getCandle()->asItem());
}
}

View File

@ -185,7 +185,7 @@ class Campfire extends Transparent{
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){
$item->pop();
$this->ignite();
$this->position->getWorld()->addSound($this->position->center(), new BlazeShootSound());
$this->position->getWorld()->addSound($this->position, new BlazeShootSound());
return true;
}elseif($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($item instanceof Durable){
@ -205,7 +205,7 @@ class Campfire extends Transparent{
$ingredient->setCount(1);
if(count($this->inventory->addItem($ingredient)) === 0){
$item->pop();
$this->position->getWorld()->addSound($this->position->center(), new ItemFrameAddItemSound());
$this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
return true;
}
}
@ -259,26 +259,26 @@ class Campfire extends Transparent{
$this->inventory->setItem($slot, VanillaItems::AIR());
$this->setCookingTime($slot, 0);
$this->position->getWorld()->dropItem($this->position->asVector3()->add(0.5, 1, 0.5), $ev->getResult());
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult());
}
}
if(count($items) > 0){
$this->position->getWorld()->setBlock($this->position, $this);
}
if(mt_rand(1, 6) === 1){
$this->position->getWorld()->addSound($this->position->center(), $furnaceType->getCookSound());
$this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
}
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
}
}
private function extinguish() : void{
$this->position->getWorld()->addSound($this->position->center(), new FireExtinguishSound());
$this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(false));
}
private function ignite() : void{
$this->position->getWorld()->addSound($this->position->center(), new FlintSteelSound());
$this->position->getWorld()->addSound($this->position, new FlintSteelSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(true));
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
}

View File

@ -69,7 +69,7 @@ final class Cauldron extends Transparent{
*/
private function fill(int $amount, FillableCauldron $result, Item $usedItem, Item $returnedItem, array &$returnedItems) : void{
$this->position->getWorld()->setBlock($this->position, $result->setFillLevel($amount));
$this->position->getWorld()->addSound($this->position->center(), $result->getFillSound());
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), $result->getFillSound());
$usedItem->pop();
$returnedItems[] = $returnedItem;
@ -95,10 +95,10 @@ final class Cauldron extends Transparent{
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if($this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
if($world->getBlock($this->position->up())->getTypeId() === BlockTypeIds::WATER){
$cauldron = VanillaBlocks::WATER_CAULDRON()->setFillLevel(FillableCauldron::MAX_FILL_LEVEL);
$world->setBlock($this->position, $cauldron);
$world->addSound($this->position->center(), $cauldron->getFillSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), $cauldron->getFillSound());
}
}
}

View File

@ -90,8 +90,8 @@ class CaveVines extends Flowable{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($this->berries){
$this->position->getWorld()->dropItem($this->position->center(), $this->asItem());
$this->position->getWorld()->addSound($this->position->center(), new GlowBerriesPickSound());
$this->position->getWorld()->dropItem($this->position, $this->asItem());
$this->position->getWorld()->addSound($this->position, new GlowBerriesPickSound());
$this->position->getWorld()->setBlock($this->position, $this->setBerries(false));
return true;
@ -117,7 +117,7 @@ class CaveVines extends Flowable{
if($this->age < self::MAX_AGE && mt_rand(1, 10) === 1){
$growthPos = $this->position->getSide(Facing::DOWN);
$world = $growthPos->getWorld();
if($world->isInWorld($growthPos->x, $growthPos->y, $growthPos->z)){
if($world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ())){
$block = $world->getBlock($growthPos);
if($block->getTypeId() === BlockTypeIds::AIR){
$newState = VanillaBlocks::CAVE_VINES()

View File

@ -27,12 +27,15 @@ use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\entity\projectile\Projectile;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\ChorusFlowerDieSound;
use pocketmine\world\sound\ChorusFlowerGrowSound;
use pocketmine\world\World;
use function array_rand;
use function min;
use function mt_rand;
@ -51,22 +54,24 @@ final class ChorusFlower extends Flowable{
}
private function canBeSupportedAt(Block $block) : bool{
$down = $block->getSide(Facing::DOWN);
$position = $block->position;
$world = $position->getWorld();
$down = $world->getBlock($position->down());
if($down->getTypeId() === BlockTypeIds::END_STONE || $down->getTypeId() === BlockTypeIds::CHORUS_PLANT){
return true;
}
$plantAdjacent = false;
foreach(Facing::HORIZONTAL as $side){
$sideBlock = $block->getSide($side);
foreach($position->sidesAroundAxis(Axis::Y) as $sidePosition){
$block = $world->getBlock($sidePosition);
if($sideBlock->getTypeId() === BlockTypeIds::CHORUS_PLANT){
if($block->getTypeId() === BlockTypeIds::CHORUS_PLANT){
if($plantAdjacent){ //at most one plant may be horizontally adjacent
return false;
}
$plantAdjacent = true;
}elseif($sideBlock->getTypeId() !== BlockTypeIds::AIR){
}elseif($block->getTypeId() !== BlockTypeIds::AIR){
return false;
}
}
@ -87,7 +92,7 @@ final class ChorusFlower extends Flowable{
$stemHeight = 0;
$endStoneBelow = false;
for($yOffset = 0; $yOffset < self::MAX_STEM_HEIGHT; $yOffset++, $stemHeight++){
$down = $this->getSide(Facing::DOWN, $yOffset + 1);
$down = $world->getBlock($this->position->down($yOffset + 1));
if($down->getTypeId() !== BlockTypeIds::CHORUS_PLANT){
if($down->getTypeId() === BlockTypeIds::END_STONE){
@ -100,13 +105,12 @@ final class ChorusFlower extends Flowable{
return [$stemHeight, $endStoneBelow];
}
private function allHorizontalBlocksEmpty(BlockPosition $position, ?int $except) : bool{
$world = $position->getWorld();
foreach(Facing::HORIZONTAL as $facing){
private function allHorizontalBlocksEmpty(World $world, Vector3 $position, ?int $except) : bool{
foreach($position->sidesAroundAxis(Axis::Y) as $facing => $sidePosition){
if($facing === $except){
continue;
}
if($world->getBlock($position->getSide($facing))->getTypeId() !== BlockTypeIds::AIR){
if($world->getBlock($sidePosition)->getTypeId() !== BlockTypeIds::AIR){
return false;
}
}
@ -117,7 +121,7 @@ final class ChorusFlower extends Flowable{
private function canGrowUpwards(int $stemHeight, bool $endStoneBelow) : bool{
$world = $this->position->getWorld();
$up = $this->position->getSide(Facing::UP);
$up = $this->position->up();
if(
//the space above must be empty and writable
!$world->isInWorld($up->x, $up->y, $up->z) ||
@ -125,7 +129,7 @@ final class ChorusFlower extends Flowable{
(
//the space above that must be empty, but doesn't need to be writable
$world->isInWorld($up->x, $up->y + 1, $up->z) &&
$world->getBlock($up->getSide(Facing::UP))->getTypeId() !== BlockTypeIds::AIR
$world->getBlock($up->up())->getTypeId() !== BlockTypeIds::AIR
)
){
return false;
@ -141,7 +145,7 @@ final class ChorusFlower extends Flowable{
}
}
return $this->allHorizontalBlocksEmpty($up, null);
return $this->allHorizontalBlocksEmpty($world, $up, null);
}
private function grow(int $facing, int $ageChange, ?BlockTransaction $tx) : BlockTransaction{
@ -179,8 +183,8 @@ final class ChorusFlower extends Flowable{
$sidePosition = $this->position->getSide($facing);
if(
$world->getBlock($sidePosition)->getTypeId() === BlockTypeIds::AIR &&
$world->getBlock($sidePosition->getSide(Facing::DOWN))->getTypeId() === BlockTypeIds::AIR &&
$this->allHorizontalBlocksEmpty($sidePosition, Facing::opposite($facing))
$world->getBlock($sidePosition->down())->getTypeId() === BlockTypeIds::AIR &&
$this->allHorizontalBlocksEmpty($world, $sidePosition, Facing::opposite($facing))
){
$tx = $this->grow($facing, 1, $tx);
}
@ -192,10 +196,10 @@ final class ChorusFlower extends Flowable{
$ev = new StructureGrowEvent($this, $tx, null);
$ev->call();
if(!$ev->isCancelled() && $tx->apply()){
$world->addSound($this->position->center(), new ChorusFlowerGrowSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ChorusFlowerGrowSound());
}
}else{
$world->addSound($this->position->center(), new ChorusFlowerDieSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ChorusFlowerDieSound());
$this->position->getWorld()->setBlock($this->position, $this->setAge(self::MAX_AGE));
}
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use function mt_rand;
@ -50,18 +51,21 @@ final class ChorusPlant extends Flowable{
}
private function canBeSupportedAt(Block $block) : bool{
$down = $block->getSide(Facing::DOWN);
$verticalAir = $down->getTypeId() === BlockTypeIds::AIR || $block->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR;
$position = $block->position;
$world = $position->getWorld();
foreach(Facing::HORIZONTAL as $facing){
$sideBlock = $block->getSide($facing);
$down = $world->getBlock($position->down());
$verticalAir = $down->getTypeId() === BlockTypeIds::AIR || $world->getBlock($position->up())->getTypeId() === BlockTypeIds::AIR;
if($sideBlock->getTypeId() === BlockTypeIds::CHORUS_PLANT){
foreach($position->sidesAroundAxis(Axis::Y) as $sidePosition){
$block = $world->getBlock($sidePosition);
if($block->getTypeId() === BlockTypeIds::CHORUS_PLANT){
if(!$verticalAir){
return false;
}
if($this->canBeSupportedBy($sideBlock->getSide(Facing::DOWN))){
if($this->canBeSupportedBy($block->getSide(Facing::DOWN))){
return true;
}
}

View File

@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use function mt_rand;
final class CoralBlock extends Opaque{
@ -40,9 +39,11 @@ final class CoralBlock extends Opaque{
public function onScheduledUpdate() : void{
if(!$this->dead){
$world = $this->position->getWorld();
$hasWater = false;
foreach(Facing::ALL as $facing){
if($this->getSide($facing) instanceof Water){
foreach($this->position->sides() as $vector3){
if($world->getBlock($vector3) instanceof Water){
$hasWater = true;
break;
}

View File

@ -63,7 +63,7 @@ class Dirt extends Opaque{
$item->applyDamage(1);
$newBlock = $this->dirtType === DirtType::NORMAL ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
$center = $this->position->center();
$center = $this->position->add(0.5, 0.5, 0.5);
$world->addSound($center, new ItemUseOnBlockSound($newBlock));
$world->setBlock($this->position, $newBlock);
if($this->dirtType === DirtType::ROOTED){
@ -83,7 +83,7 @@ class Dirt extends Opaque{
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType() === PotionType::WATER){
$item->pop();
$world->setBlock($this->position, VanillaBlocks::MUD());
$world->addSound($this->position->center(), new WaterSplashSound(0.5));
$world->addSound($this->position, new WaterSplashSound(0.5));
return true;
}

View File

@ -152,7 +152,7 @@ class Door extends Transparent{
}
$world->setBlock($this->position, $this);
$world->addSound($this->position->center(), new DoorSound());
$world->addSound($this->position, new DoorSound());
return true;
}

View File

@ -29,7 +29,7 @@ use pocketmine\block\utils\SupportType;
use pocketmine\event\block\BlockTeleportEvent;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\GameMode;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\world\particle\DragonEggTeleportParticle;
use pocketmine\world\World;
@ -50,7 +50,7 @@ class DragonEgg extends Transparent implements Fallable{
}
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
if($player !== null && !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE)){
$this->teleport();
return true;
}
@ -66,17 +66,16 @@ class DragonEgg extends Transparent implements Fallable{
$this->position->z + mt_rand(-16, 16)
);
if($block instanceof Air){
//TODO: this needs migrating to BlockPosition, but having World in there presents some issues
$ev = new BlockTeleportEvent($this, $block->position->asVector3());
$ev = new BlockTeleportEvent($this, $block->position);
$ev->call();
if($ev->isCancelled()){
break;
}
$blockPos = $ev->getTo();
$world->addParticle($this->position->center(), new DragonEggTeleportParticle($this->position->x - $blockPos->getFloorX(), $this->position->y - $blockPos->getFloorY(), $this->position->z - $blockPos->getFloorZ()));
$world->addParticle($this->position, new DragonEggTeleportParticle($this->position->x - $blockPos->x, $this->position->y - $blockPos->y, $this->position->z - $blockPos->z));
$world->setBlock($this->position, VanillaBlocks::AIR());
$world->setBlockAt($blockPos->getFloorX(), $blockPos->getFloorY(), $blockPos->getFloorZ(), $this);
$world->setBlock($blockPos, $this);
break;
}
}

View File

@ -161,9 +161,9 @@ class Farmland extends Transparent{
protected function canHydrate() : bool{
$world = $this->position->getWorld();
$startX = $this->position->x - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
$startY = $this->position->y;
$startZ = $this->position->z - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
$startX = $this->position->getFloorX() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
$startY = $this->position->getFloorY();
$startZ = $this->position->getFloorZ() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
if($this->waterPositionIndex !== self::WATER_POSITION_INDEX_UNKNOWN){
$raw = $this->waterPositionIndex;

View File

@ -111,7 +111,7 @@ class FenceGate extends Transparent{
$world = $this->position->getWorld();
$world->setBlock($this->position, $this);
$world->addSound($this->position->center(), new DoorSound());
$world->addSound($this->position, new DoorSound());
return true;
}

View File

@ -79,7 +79,7 @@ abstract class FillableCauldron extends Transparent{
return;
}
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->fillLevel + $amount));
$this->position->getWorld()->addSound($this->position->center(), $this->getFillSound());
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), $this->getFillSound());
$usedItem->pop();
$returnedItems[] = $returnedItem;
@ -94,7 +94,7 @@ abstract class FillableCauldron extends Transparent{
}
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->fillLevel - $amount));
$this->position->getWorld()->addSound($this->position->center(), $this->getEmptySound());
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), $this->getEmptySound());
$usedItem->pop();
$returnedItems[] = $returnedItem;

View File

@ -161,7 +161,7 @@ class Fire extends BaseFire{
$ageDivisor = $this->age + 30;
for($y = -1; $y <= 4; ++$y){
$targetY = $y + $this->position->y;
$targetY = $y + (int) $this->position->y;
if($targetY < World::Y_MIN || $targetY >= World::Y_MAX){
continue;
}
@ -169,12 +169,12 @@ class Fire extends BaseFire{
$randomBound = 100 + ($y > 1 ? ($y - 1) * 100 : 0);
for($z = -1; $z <= 1; ++$z){
$targetZ = $z + $this->position->z;
$targetZ = $z + (int) $this->position->z;
for($x = -1; $x <= 1; ++$x){
if($x === 0 && $y === 0 && $z === 0){
continue;
}
$targetX = $x + $this->position->x;
$targetX = $x + (int) $this->position->x;
if(!$world->isInWorld($targetX, $targetY, $targetZ)){
continue;
}
@ -190,10 +190,9 @@ class Fire extends BaseFire{
//TODO: fire can't spread if it's raining in any horizontally adjacent block, or the current one
$encouragement = 0;
foreach(Facing::ALL as $facing){
$sidePos = $block->position->getSide($facing);
if($world->isInWorld($sidePos->x, $sidePos->y, $sidePos->z)){
$encouragement = max($encouragement, $world->getBlockAt($sidePos->x, $sidePos->y, $sidePos->z)->getFlameEncouragement());
foreach($block->position->sides() as $vector3){
if($world->isInWorld($vector3->x, $vector3->y, $vector3->z)){
$encouragement = max($encouragement, $world->getBlockAt($vector3->x, $vector3->y, $vector3->z)->getFlameEncouragement());
}
}

View File

@ -58,7 +58,7 @@ final class FloorCoralFan extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player !== null){
$playerBlockPos = $player->getPosition()->floor();
$directionVector = $blockReplace->position->asVector3()->subtractVector($playerBlockPos)->normalize();
$directionVector = $blockReplace->position->subtractVector($playerBlockPos)->normalize();
$angle = rad2deg(atan2($directionVector->getZ(), $directionVector->getX()));
if($angle <= 45 || 315 <= $angle || (135 <= $angle && $angle <= 225)){

View File

@ -111,7 +111,7 @@ class FlowerPot extends Flowable{
$removedItems = $player->getInventory()->addItem(...$removedItems);
}
foreach($removedItems as $drops){
$world->dropItem($this->position->center(), $drops);
$world->dropItem($this->position->add(0.5, 0.5, 0.5), $drops);
}
$this->setPlant(null);

View File

@ -73,7 +73,7 @@ class Furnace extends Opaque{
$furnace = $world->getTile($this->position);
if($furnace instanceof TileFurnace && $furnace->onUpdate()){
if(mt_rand(1, 60) === 1){ //in vanilla this is between 1 and 5 seconds; try to average about 3
$world->addSound($this->position->center(), $furnace->getFurnaceType()->getCookSound());
$world->addSound($this->position, $furnace->getFurnaceType()->getCookSound());
}
$world->scheduleDelayedBlockUpdate($this->position, 1); //TODO: check this
}

View File

@ -32,6 +32,7 @@ use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\World;
use function count;
use function shuffle;
@ -83,8 +84,7 @@ class GlowLichen extends Transparent{
return $result->setFace($spreadFace, true);
}
private function spread(BlockPosition $replacePos, int $spreadFace) : bool{
$world = $replacePos->getWorld();
private function spread(World $world, Vector3 $replacePos, int $spreadFace) : bool{
$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
$supportFace = Facing::opposite($spreadFace);
@ -117,10 +117,12 @@ class GlowLichen extends Transparent{
}
private function spreadAroundSupport(int $sourceFace) : bool{
$world = $this->position->getWorld();
$supportPos = $this->position->getSide($sourceFace);
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
$replacePos = $supportPos->getSide($spreadFace);
if($this->spread($replacePos, Facing::opposite($spreadFace))){
if($this->spread($world, $replacePos, Facing::opposite($spreadFace))){
return true;
}
}
@ -129,9 +131,11 @@ class GlowLichen extends Transparent{
}
private function spreadAdjacentToSupport(int $sourceFace) : bool{
$world = $this->position->getWorld();
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
$replacePos = $this->position->getSide($spreadFace);
if($this->spread($replacePos, $sourceFace)){
if($this->spread($world, $replacePos, $sourceFace)){
return true;
}
}
@ -140,7 +144,7 @@ class GlowLichen extends Transparent{
private function spreadWithinSelf(int $sourceFace) : bool{
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
if(!$this->hasFace($spreadFace) && $this->spread($this->position, $spreadFace)){
if(!$this->hasFace($spreadFace) && $this->spread($this->position->getWorld(), $this->position, $spreadFace)){
return true;
}
}

View File

@ -88,7 +88,7 @@ class Grass extends Opaque{
$world = $this->position->getWorld();
if($item instanceof Fertilizer){
$item->pop();
TallGrassObject::growGrass($world, $this->position->center(), new Random(mt_rand()), 8, 2);
TallGrassObject::growGrass($world, $this->position, new Random(mt_rand()), 8, 2);
return true;
}
@ -96,14 +96,14 @@ class Grass extends Opaque{
if($item instanceof Hoe){
$item->applyDamage(1);
$newBlock = VanillaBlocks::FARMLAND();
$world->addSound($this->position->center(), new ItemUseOnBlockSound($newBlock));
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
$world->setBlock($this->position, $newBlock);
return true;
}elseif($item instanceof Shovel){
$item->applyDamage(1);
$newBlock = VanillaBlocks::GRASS_PATH();
$world->addSound($this->position->center(), new ItemUseOnBlockSound($newBlock));
$world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock));
$world->setBlock($this->position, $newBlock);
return true;

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
class Ice extends Transparent{
@ -39,7 +40,8 @@ class Ice extends Transparent{
}
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{
if(($player === null || $player->isSurvival()) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
//TODO: we should probably pass instaBreak in here, since events can override it
if(($player === null || !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE)) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::WATER());
return true;
}

View File

@ -135,11 +135,11 @@ class ItemFrame extends Flowable{
if($this->framedItem !== null){
$this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS;
$this->position->getWorld()->addSound($this->position->center(), new ItemFrameRotateItemSound());
$this->position->getWorld()->addSound($this->position, new ItemFrameRotateItemSound());
}elseif(!$item->isNull()){
$this->framedItem = $item->pop();
$this->position->getWorld()->addSound($this->position->center(), new ItemFrameAddItemSound());
$this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
}else{
return true;
}
@ -155,8 +155,8 @@ class ItemFrame extends Flowable{
}
$world = $this->position->getWorld();
if(Utils::getRandomFloat() <= $this->itemDropChance){
$world->dropItem($this->position->center(), clone $this->framedItem);
$world->addSound($this->position->center(), new ItemFrameRemoveItemSound());
$world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem);
$world->addSound($this->position, new ItemFrameRemoveItemSound());
}
$this->setFramedItem(null);
$world->setBlock($this->position, $this);

View File

@ -61,7 +61,7 @@ class Jukebox extends Opaque{
public function ejectRecord() : void{
if($this->record !== null){
$this->position->getWorld()->dropItem($this->position->asVector3()->add(0.5, 1, 0.5), $this->record);
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $this->record);
$this->record = null;
$this->stopSound();
}
@ -76,12 +76,12 @@ class Jukebox extends Opaque{
public function startSound() : void{
if($this->record !== null){
$this->position->getWorld()->addSound($this->position->center(), new RecordSound($this->record->getRecordType()));
$this->position->getWorld()->addSound($this->position, new RecordSound($this->record->getRecordType()));
}
}
public function stopSound() : void{
$this->position->getWorld()->addSound($this->position->center(), new RecordStopSound());
$this->position->getWorld()->addSound($this->position, new RecordStopSound());
}
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -51,7 +51,7 @@ class Ladder extends Transparent{
}
public function onEntityInside(Entity $entity) : bool{
if($entity instanceof Living && $entity->getPosition()->floor()->distanceSquared($this->position->asVector3()) < 1){ //entity coordinates must be inside block
if($entity instanceof Living && $entity->getPosition()->floor()->distanceSquared($this->position) < 1){ //entity coordinates must be inside block
$entity->resetFallDistance();
$entity->onGround = true;
}

View File

@ -81,7 +81,7 @@ class Leaves extends Transparent{
* @phpstan-param array<int, true> $visited
* @phpstan-param-out array<int, true> $visited
*/
protected function findLog(BlockPosition $pos, array &$visited = [], int $distance = 0) : bool{
protected function findLog(Vector3 $pos, array &$visited = [], int $distance = 0) : bool{
$index = World::blockHash($pos->x, $pos->y, $pos->z);
if(isset($visited[$index])){
return false;

View File

@ -123,7 +123,7 @@ class Lectern extends Transparent{
if($this->book === null && $item instanceof WritableBookBase){
$world = $this->position->getWorld();
$world->setBlock($this->position, $this->setBook($item));
$world->addSound($this->position->center(), new LecternPlaceBookSound());
$world->addSound($this->position, new LecternPlaceBookSound());
$item->pop();
}
return true;
@ -132,7 +132,7 @@ class Lectern extends Transparent{
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
if($this->book !== null){
$world = $this->position->getWorld();
$world->dropItem($this->position->getSide(Facing::UP)->center(), $this->book);
$world->dropItem($this->position->up(), $this->book);
$world->setBlock($this->position, $this->setBook(null));
}
return false;

View File

@ -95,7 +95,7 @@ class Lever extends Flowable{
$world = $this->position->getWorld();
$world->setBlock($this->position, $this);
$world->addSound(
$this->position->center(),
$this->position->add(0.5, 0.5, 0.5),
$this->activated ? new RedstonePowerOnSound() : new RedstonePowerOffSound()
);
return true;

View File

@ -165,9 +165,9 @@ abstract class Liquid extends Transparent{
$vX = $vY = $vZ = 0;
$x = $this->position->x;
$y = $this->position->y;
$z = $this->position->z;
$x = $this->position->getFloorX();
$y = $this->position->getFloorY();
$z = $this->position->getFloorZ();
$decay = $this->getEffectiveFlowDecay($this);
@ -259,9 +259,9 @@ abstract class Liquid extends Transparent{
$world = $this->position->getWorld();
$x = $this->position->x;
$y = $this->position->y;
$z = $this->position->z;
$x = $this->position->getFloorX();
$y = $this->position->getFloorY();
$z = $this->position->getFloorZ();
if(!$this->isSource()){
$smallestFlowDecay = -100;
@ -368,7 +368,7 @@ abstract class Liquid extends Transparent{
protected function liquidCollide(Block $cause, Block $result) : bool{
if(BlockEventHelper::form($this, $result, $cause)){
$this->position->getWorld()->addSound($this->position->center(), new FizzSound(2.6 + (Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.8));
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.8));
}
return true;
}

View File

@ -117,7 +117,7 @@ class NetherVines extends Flowable{
for($i = 1; $i <= $growthAmount; $i++){
$growthPos = $pos->getSide($this->growthFace, $i);
if(!$world->isInWorld($growthPos->x, $growthPos->y, $growthPos->z) || !$world->getBlock($growthPos)->canBeReplaced()){
if(!$world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ()) || !$world->getBlock($growthPos)->canBeReplaced()){
break;
}
$tx->addBlock($growthPos, (clone $top)->setAge(min(++$age, self::MAX_AGE)));

View File

@ -89,7 +89,7 @@ class PinkPetals extends Flowable{
if($this->count < self::MAX_COUNT){
$grew = BlockEventHelper::grow($this, (clone $this)->setCount($this->count + 1), $player);
}else{
$this->position->getWorld()->dropItem($this->position->center(), $this->asItem());
$this->position->getWorld()->dropItem($this->position->add(0, 0.5, 0), $this->asItem());
$grew = true;
}
if($grew){

View File

@ -73,7 +73,7 @@ final class PitcherCrop extends Flowable{
$tx = new BlockTransaction($this->position->getWorld());
$tx->addBlock($this->position, VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(false));
$tx->addBlock($this->position->getSide(Facing::UP), VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(true));
$tx->addBlock($this->position->up(), VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(true));
$ev = new StructureGrowEvent($this, $tx, $player);
$ev->call();

View File

@ -27,7 +27,6 @@ use pocketmine\block\tile\Cauldron as TileCauldron;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\CauldronEmptyPotionSound;
@ -108,10 +107,10 @@ final class PotionCauldron extends FillableCauldron{
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if($this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
if($world->getBlock($this->position->up())->getTypeId() === BlockTypeIds::WATER){
$cauldron = VanillaBlocks::WATER_CAULDRON()->setFillLevel(FillableCauldron::MAX_FILL_LEVEL);
$world->setBlock($this->position, $cauldron);
$world->addSound($this->position->center(), $cauldron->getFillSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), $cauldron->getFillSound());
}
}
}

View File

@ -139,7 +139,7 @@ abstract class PressurePlate extends Transparent{
if($newState !== null){
$world->setBlock($this->position, $newState);
if($pressedChange !== null){
$world->addSound($this->position->center(), $pressedChange ?
$world->addSound($this->position, $pressedChange ?
new PressurePlateActivateSound($this) :
new PressurePlateDeactivateSound($this)
);

View File

@ -38,7 +38,7 @@ class Pumpkin extends Opaque{
$item->applyDamage(1);
$world = $this->position->getWorld();
$world->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face));
$world->dropItem($this->position->center(), VanillaItems::PUMPKIN_SEEDS()->setCount(1));
$world->dropItem($this->position->add(0.5, 0.5, 0.5), VanillaItems::PUMPKIN_SEEDS()->setCount(1));
return true;
}
return false;

View File

@ -81,7 +81,7 @@ class Sapling extends Flowable{
public function onRandomTick() : void{
$world = $this->position->getWorld();
if($world->getFullLightAt($this->position->x, $this->position->y, $this->position->z) >= 8 && mt_rand(1, 7) === 1){
if($world->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 && mt_rand(1, 7) === 1){
if($this->ready){
$this->grow(null);
}else{
@ -94,7 +94,7 @@ class Sapling extends Flowable{
private function grow(?Player $player) : bool{
$random = new Random(mt_rand());
$tree = TreeFactory::get($random, $this->saplingType->getTreeType());
$transaction = $tree?->getBlockTransaction($this->position->getWorld(), $this->position->x, $this->position->y, $this->position->z, $random);
$transaction = $tree?->getBlockTransaction($this->position->getWorld(), $this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ(), $random);
if($transaction === null){
return false;
}

View File

@ -33,6 +33,7 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
use function mt_rand;
class SmallDripleaf extends Transparent{
@ -97,9 +98,9 @@ class SmallDripleaf extends Transparent{
return false;
}
private function canGrowTo(BlockPosition $pos) : bool{
private function canGrowTo(Position $pos) : bool{
$world = $pos->getWorld();
if(!$world->isInWorld($pos->x, $pos->y, $pos->z)){
if(!$world->isInWorld($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ())){
return false;
}
$block = $world->getBlock($pos);

View File

@ -32,6 +32,7 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
class Sugarcane extends Flowable{
use AgeableTrait;
@ -41,16 +42,16 @@ class Sugarcane extends Flowable{
public const MAX_AGE = 15;
private function seekToBottom() : BlockPosition{
private function seekToBottom() : Position{
$world = $this->position->getWorld();
$bottom = $this->position;
while(($next = $world->getBlock($bottom->getSide(Facing::DOWN)))->hasSameTypeId($this)){
while(($next = $world->getBlock($bottom->down()))->hasSameTypeId($this)){
$bottom = $next->position;
}
return $bottom;
}
private function grow(BlockPosition $pos, ?Player $player = null) : bool{
private function grow(Position $pos, ?Player $player = null) : bool{
$grew = false;
$world = $pos->getWorld();
for($y = 1; $y < 3; ++$y){

View File

@ -81,9 +81,8 @@ class SweetBerryBush extends Flowable{
}
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
$world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
$posV3 = $this->position->center();
$world->dropItem($posV3, $this->asItem()->setCount($dropAmount));
$world->addSound($posV3, new SweetBerriesPickSound());
$world->dropItem($this->position, $this->asItem()->setCount($dropAmount));
$world->addSound($this->position, new SweetBerriesPickSound());
}
return true;

View File

@ -100,7 +100,7 @@ class TNT extends Opaque{
$mot = (new Random())->nextSignedFloat() * M_PI * 2;
$tnt = new PrimedTNT(Location::fromObject($this->position->asVector3()->add(0.5, 0, 0.5), $world));
$tnt = new PrimedTNT(Location::fromObject($this->position->add(0.5, 0, 0.5), $world));
$tnt->setFuse($fuse);
$tnt->setWorksUnderwater($this->worksUnderwater);
$tnt->setMotion(new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02));

View File

@ -88,7 +88,7 @@ class Trapdoor extends Transparent{
$this->open = !$this->open;
$world = $this->position->getWorld();
$world->setBlock($this->position, $this);
$world->addSound($this->position->center(), new DoorSound());
$world->addSound($this->position, new DoorSound());
return true;
}
}

View File

@ -54,7 +54,6 @@ use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
use pocketmine\block\tile\NormalFurnace as TileNormalFurnace;
use pocketmine\block\tile\Note as TileNote;
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
use pocketmine\block\tile\Sign as TileSign;
use pocketmine\block\tile\Smoker as TileSmoker;
use pocketmine\block\tile\Tile;
use pocketmine\block\utils\AmethystTrait;
@ -783,8 +782,8 @@ use function strtolower;
* @method static Water WATER()
* @method static WaterCauldron WATER_CAULDRON()
* @method static NetherVines WEEPING_VINES()
* @method static WeightedPressurePlate WEIGHTED_PRESSURE_PLATE_HEAVY()
* @method static WeightedPressurePlate WEIGHTED_PRESSURE_PLATE_LIGHT()
* @method static WeightedPressurePlateHeavy WEIGHTED_PRESSURE_PLATE_HEAVY()
* @method static WeightedPressurePlateLight WEIGHTED_PRESSURE_PLATE_LIGHT()
* @method static Wheat WHEAT()
* @method static Flower WHITE_TULIP()
* @method static WitherRose WITHER_ROSE()
@ -1180,14 +1179,14 @@ final class VanillaBlocks{
self::register("lily_pad", fn(BID $id) => new WaterLily($id, "Lily Pad", new Info(BreakInfo::instant())));
$weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD));
self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlate(
self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlateHeavy(
$id,
"Weighted Pressure Plate Heavy",
$weightedPressurePlateBreakInfo,
deactivationDelayTicks: 10,
signalStrengthFactor: 0.1
));
self::register("weighted_pressure_plate_light", fn(BID $id) => new WeightedPressurePlate(
self::register("weighted_pressure_plate_light", fn(BID $id) => new WeightedPressurePlateLight(
$id,
"Weighted Pressure Plate Light",
$weightedPressurePlateBreakInfo,
@ -1360,8 +1359,8 @@ final class VanillaBlocks{
WoodType::WARPED => VanillaItems::WARPED_SIGN(...),
WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...),
};
self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem));
self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem));
}
}

View File

@ -36,7 +36,6 @@ use pocketmine\item\Potion;
use pocketmine\item\PotionType;
use pocketmine\item\SplashPotion;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\CauldronAddDyeSound;
@ -120,7 +119,7 @@ final class WaterCauldron extends FillableCauldron{
}) !== null && ($newColor = $dyeColor->getRgbValue())->toRGBA() !== $this->customWaterColor?->toRGBA()
){
$world->setBlock($this->position, $this->setCustomWaterColor($this->customWaterColor === null ? $newColor : Color::mix($this->customWaterColor, $newColor)));
$world->addSound($this->position->center(), new CauldronAddDyeSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronAddDyeSound());
$item->pop();
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
@ -140,12 +139,12 @@ final class WaterCauldron extends FillableCauldron{
} && $item->getCustomColor()?->toRGBA() !== $this->customWaterColor->toRGBA()){
$item->setCustomColor($this->customWaterColor);
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::DYE_ARMOR_USE_AMOUNT));
$world->addSound($this->position->center(), new CauldronDyeItemSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronDyeItemSound());
}
}elseif($item->getCustomColor() !== null){
$item->clearCustomColor();
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_ARMOR_USE_AMOUNT));
$world->addSound($this->position->center(), new CauldronCleanItemSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
}
}elseif($item instanceof Banner){
$patterns = $item->getPatterns();
@ -154,7 +153,7 @@ final class WaterCauldron extends FillableCauldron{
$item->setPatterns($patterns);
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_BANNER_USE_AMOUNT));
$world->addSound($this->position->center(), new CauldronCleanItemSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
}
}elseif(ItemTypeIds::toBlockTypeId($item->getTypeId()) === BlockTypeIds::DYED_SHULKER_BOX){
if($this->customWaterColor === null){
@ -165,7 +164,7 @@ final class WaterCauldron extends FillableCauldron{
$returnedItems[] = $newItem;
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_SHULKER_BOX_USE_AMOUNT));
$world->addSound($this->position->center(), new CauldronCleanItemSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
}
}else{
match($item->getTypeId()){
@ -196,13 +195,13 @@ final class WaterCauldron extends FillableCauldron{
public function onNearbyBlockChange() : void{
$hasCustomWaterColor = $this->customWaterColor !== null;
if($this->getFillLevel() < self::MAX_FILL_LEVEL || $hasCustomWaterColor){
if($this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
$world = $this->position->getWorld();
if($world->getBlock($this->position->up())->getTypeId() === BlockTypeIds::WATER){
if($hasCustomWaterColor){
//TODO: particles
}
$world = $this->position->getWorld();
$world->setBlock($this->position, $this->setCustomWaterColor(null)->setFillLevel(FillableCauldron::MAX_FILL_LEVEL));
$world->addSound($this->position->center(), $this->getFillSound());
$world->addSound($this->position->add(0.5, 0.5, 0.5), $this->getFillSound());
}
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
/**
* @deprecated
*/
class WeightedPressurePlateHeavy extends WeightedPressurePlate{
}

View File

@ -0,0 +1,31 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
/**
* @deprecated
*/
class WeightedPressurePlateLight extends WeightedPressurePlate{
}

View File

@ -67,7 +67,7 @@ class Wood extends Opaque{
$item->applyDamage(1);
$this->stripped = true;
$this->position->getWorld()->setBlock($this->position, $this);
$this->position->getWorld()->addSound($this->position->center(), new ItemUseOnBlockSound($this));
$this->position->getWorld()->addSound($this->position, new ItemUseOnBlockSound($this));
return true;
}
return false;

View File

@ -50,7 +50,7 @@ trait AnimatedBlockInventoryTrait{
if($this->holder->isValid() && $this->getViewerCount() === 1){
//TODO: this crap really shouldn't be managed by the inventory
$this->animateBlock(true);
$this->holder->getWorld()->addSound($this->holder->center(), $this->getOpenSound());
$this->holder->getWorld()->addSound($this->holder->add(0.5, 0.5, 0.5), $this->getOpenSound());
}
}
@ -60,7 +60,7 @@ trait AnimatedBlockInventoryTrait{
if($this->holder->isValid() && $this->getViewerCount() === 1){
//TODO: this crap really shouldn't be managed by the inventory
$this->animateBlock(false);
$this->holder->getWorld()->addSound($this->holder->center(), $this->getCloseSound());
$this->holder->getWorld()->addSound($this->holder->add(0.5, 0.5, 0.5), $this->getCloseSound());
}
parent::onClose($who);
}

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
class AnvilInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
@ -33,7 +33,7 @@ class AnvilInventory extends SimpleInventory implements BlockInventory, Temporar
public const SLOT_INPUT = 0;
public const SLOT_MATERIAL = 1;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(2);
}

View File

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\Barrel;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
use pocketmine\world\sound\BarrelCloseSound;
use pocketmine\world\sound\BarrelOpenSound;
use pocketmine\world\sound\Sound;
@ -33,7 +33,7 @@ use pocketmine\world\sound\Sound;
class BarrelInventory extends SimpleInventory implements BlockInventory{
use AnimatedBlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(27);
}

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\world\Position;
interface BlockInventory{
public function getHolder() : BlockPosition;
public function getHolder() : Position;
}

View File

@ -23,12 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\world\Position;
trait BlockInventoryTrait{
protected BlockPosition $holder;
protected Position $holder;
public function getHolder() : BlockPosition{
public function getHolder() : Position{
return $this->holder;
}
}

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
class BrewingStandInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
@ -35,7 +35,7 @@ class BrewingStandInventory extends SimpleInventory implements BlockInventory{
public const SLOT_BOTTLE_RIGHT = 3;
public const SLOT_FUEL = 4;
public function __construct(BlockPosition $holder, int $size = 5){
public function __construct(Position $holder, int $size = 5){
$this->holder = $holder;
parent::__construct($size);
}

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
class CampfireInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(4);
}

View File

@ -23,14 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
final class CartographyTableInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(2);
}

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition as ProtocolBlockPosition;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\world\Position;
use pocketmine\world\sound\ChestCloseSound;
use pocketmine\world\sound\ChestOpenSound;
use pocketmine\world\sound\Sound;
@ -34,7 +34,7 @@ use pocketmine\world\sound\Sound;
class ChestInventory extends SimpleInventory implements BlockInventory{
use AnimatedBlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(27);
}
@ -51,6 +51,6 @@ class ChestInventory extends SimpleInventory implements BlockInventory{
$holder = $this->getHolder();
//event ID is always 1 for a chest
$holder->getWorld()->broadcastPacketOnBlock($holder, BlockEventPacket::create(new ProtocolBlockPosition($holder->x, $holder->y, $holder->z), 1, $isOpen ? 1 : 0));
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
}
}

View File

@ -23,14 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\crafting\CraftingGrid;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
final class CraftingTableInventory extends CraftingGrid implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(CraftingGrid::SIZE_BIG);
}

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\event\player\PlayerEnchantingOptionsRequestEvent;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\item\enchantment\EnchantingHelper as Helper;
use pocketmine\item\enchantment\EnchantingOption;
use pocketmine\item\Item;
use pocketmine\world\Position;
use function array_values;
use function count;
@ -45,7 +45,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
*/
private array $options = [];
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(2);
}

View File

@ -23,14 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\block\tile\EnderChest;
use pocketmine\inventory\DelegateInventory;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\PlayerEnderInventory;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition as ProtocolBlockPosition;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\player\Player;
use pocketmine\world\Position;
use pocketmine\world\sound\EnderChestCloseSound;
use pocketmine\world\sound\EnderChestOpenSound;
use pocketmine\world\sound\Sound;
@ -44,7 +44,7 @@ class EnderChestInventory extends DelegateInventory implements BlockInventory{
}
public function __construct(
BlockPosition $holder,
Position $holder,
private PlayerEnderInventory $inventory
){
parent::__construct($inventory);
@ -75,7 +75,7 @@ class EnderChestInventory extends DelegateInventory implements BlockInventory{
$holder = $this->getHolder();
//event ID is always 1 for a chest
$holder->getWorld()->broadcastPacketOnBlock($holder, BlockEventPacket::create(new ProtocolBlockPosition($holder->x, $holder->y, $holder->z), 1, $isOpen ? 1 : 0));
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
}
public function onClose(Player $who) : void{

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\crafting\FurnaceType;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\world\Position;
class FurnaceInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
@ -36,7 +36,7 @@ class FurnaceInventory extends SimpleInventory implements BlockInventory{
public const SLOT_RESULT = 2;
public function __construct(
BlockPosition $holder,
Position $holder,
private FurnaceType $furnaceType
){
$this->holder = $holder;

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
class HopperInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(BlockPosition $holder, int $size = 5){
public function __construct(Position $holder, int $size = 5){
$this->holder = $holder;
parent::__construct($size);
}

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
final class LoomInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
@ -34,7 +34,7 @@ final class LoomInventory extends SimpleInventory implements BlockInventory, Tem
public const SLOT_DYE = 1;
public const SLOT_PATTERN = 2;
public function __construct(BlockPosition $holder, int $size = 3){
public function __construct(Position $holder, int $size = 3){
$this->holder = $holder;
parent::__construct($size);
}

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\block\BlockTypeIds;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition as ProtocolBlockPosition;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\world\Position;
use pocketmine\world\sound\ShulkerBoxCloseSound;
use pocketmine\world\sound\ShulkerBoxOpenSound;
use pocketmine\world\sound\Sound;
@ -37,7 +37,7 @@ use pocketmine\world\sound\Sound;
class ShulkerBoxInventory extends SimpleInventory implements BlockInventory{
use AnimatedBlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(27);
}
@ -62,6 +62,6 @@ class ShulkerBoxInventory extends SimpleInventory implements BlockInventory{
$holder = $this->getHolder();
//event ID is always 1 for a chest
$holder->getWorld()->broadcastPacketOnBlock($holder, BlockEventPacket::create(new ProtocolBlockPosition($holder->x, $holder->y, $holder->z), 1, $isOpen ? 1 : 0));
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(BlockPosition::fromVector3($holder), 1, $isOpen ? 1 : 0));
}
}

View File

@ -23,14 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
final class SmithingTableInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(3);
}

View File

@ -23,16 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\BlockPosition;
use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\world\Position;
class StonecutterInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;
public const SLOT_INPUT = 0;
public function __construct(BlockPosition $holder){
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(1);
}

View File

@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\inventory\BarrelInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
class Barrel extends Spawnable implements Container, Nameable{
use NameableTrait;
@ -33,8 +34,8 @@ class Barrel extends Spawnable implements Container, Nameable{
protected BarrelInventory $inventory;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new BarrelInventory($this->position);
}

View File

@ -88,6 +88,6 @@ final class Bell extends Spawnable{
default => throw new AssumptionFailedError("Unreachable")
});
$nbt->setInt(self::TAG_TICKS, 0);
return BlockActorDataPacket::create(new BlockPosition($this->position->x, $this->position->y, $this->position->z), new CacheableNbt($nbt));
return BlockActorDataPacket::create(BlockPosition::fromVector3($this->position), new CacheableNbt($nbt));
}
}

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\inventory\BrewingStandInventory;
use pocketmine\crafting\BrewingRecipe;
use pocketmine\event\block\BrewingFuelUseEvent;
@ -32,10 +31,12 @@ use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\player\Player;
use pocketmine\world\sound\PotionFinishBrewingSound;
use pocketmine\world\World;
use function array_map;
use function count;
@ -59,11 +60,11 @@ class BrewingStand extends Spawnable implements Container, Nameable{
private int $maxFuelTime = 0;
private int $remainingFuelTime = 0;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new BrewingStandInventory($this->position);
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(static function(Inventory $unused) use ($position) : void{
$position->getWorld()->scheduleDelayedBlockUpdate($position, 1);
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(static function(Inventory $unused) use ($world, $pos) : void{
$world->scheduleDelayedBlockUpdate($pos, 1);
}));
}
@ -214,7 +215,7 @@ class BrewingStand extends Spawnable implements Container, Nameable{
}
if($anythingBrewed){
$this->position->getWorld()->addSound($this->position->center(), new PotionFinishBrewingSound());
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new PotionFinishBrewingSound());
}
$ingredient->pop();

View File

@ -23,15 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\Campfire as BlockCampfire;
use pocketmine\block\inventory\CampfireInventory;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\world\World;
class Campfire extends Spawnable implements Container{
use ContainerTrait;
@ -50,15 +51,14 @@ class Campfire extends Spawnable implements Container{
/** @var array<int, int> */
private array $cookingTimes = [];
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new CampfireInventory($this->position);
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
static function(Inventory $unused) use ($position) : void{
$world = $position->getWorld();
$block = $world->getBlock($position);
static function(Inventory $unused) use ($world, $pos) : void{
$block = $world->getBlock($pos);
if($block instanceof BlockCampfire){
$world->setBlock($position, $block);
$world->setBlock($pos, $block);
}
})
);

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\inventory\ChestInventory;
use pocketmine\block\inventory\DoubleChestInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\world\format\Chunk;
use pocketmine\world\World;
use function abs;
class Chest extends Spawnable implements Container, Nameable{
@ -50,8 +50,8 @@ class Chest extends Spawnable implements Container, Nameable{
private ?int $pairX = null;
private ?int $pairZ = null;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new ChestInventory($this->position);
}

View File

@ -23,17 +23,18 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\data\bedrock\item\SavedItemData;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\world\World;
use function count;
class ChiseledBookshelf extends Tile implements Container{
@ -45,8 +46,8 @@ class ChiseledBookshelf extends Tile implements Container{
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
}

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\inventory\Inventory;
@ -32,6 +31,7 @@ use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\world\Position;
/**
* This trait implements most methods in the {@link Container} interface. It should only be used by Tiles.
@ -90,9 +90,9 @@ trait ContainerTrait{
}
/**
* @see Tile::getPosition()
* @see Position::asPosition()
*/
abstract protected function getPosition() : BlockPosition;
abstract protected function getPosition() : Position;
/**
* @see Tile::onBlockDestroyedHook()
@ -102,7 +102,7 @@ trait ContainerTrait{
$pos = $this->getPosition();
$world = $pos->getWorld();
$dropPos = $pos->center();
$dropPos = $pos->add(0.5, 0.5, 0.5);
foreach($inv->getContents() as $k => $item){
$world->dropItem($dropPos, $item);
}

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\Furnace as BlockFurnace;
use pocketmine\block\inventory\FurnaceInventory;
use pocketmine\crafting\FurnaceRecipe;
@ -33,9 +32,11 @@ use pocketmine\event\inventory\FurnaceSmeltEvent;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\player\Player;
use pocketmine\world\World;
use function array_map;
use function max;
@ -52,12 +53,12 @@ abstract class Furnace extends Spawnable implements Container, Nameable{
private int $cookTime = 0;
private int $maxFuelTime = 0;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new FurnaceInventory($this->position, $this->getFurnaceType());
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
static function(Inventory $unused) use ($position) : void{
$position->getWorld()->scheduleDelayedBlockUpdate($position, 1);
static function(Inventory $unused) use ($world, $pos) : void{
$world->scheduleDelayedBlockUpdate($pos, 1);
})
);
}

View File

@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\inventory\HopperInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
class Hopper extends Spawnable implements Container, Nameable{
@ -37,8 +38,8 @@ class Hopper extends Spawnable implements Container, Nameable{
private HopperInventory $inventory;
private int $transferCooldown = 0;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new HopperInventory($this->position);
}

View File

@ -23,12 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\world\World;
/**
* @deprecated
@ -43,9 +44,9 @@ class ItemFrame extends Spawnable{
private int $itemRotation = 0;
private float $itemDropChance = 1.0;
public function __construct(BlockPosition $position){
public function __construct(World $world, Vector3 $pos){
$this->item = VanillaItems::AIR();
parent::__construct($position);
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{

View File

@ -65,6 +65,6 @@ class Jukebox extends Spawnable{
}
protected function onBlockDestroyedHook() : void{
$this->position->getWorld()->addSound($this->position->center(), new RecordStopSound());
$this->position->getWorld()->addSound($this->position, new RecordStopSound());
}
}

View File

@ -23,11 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\inventory\ShulkerBoxInventory;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
class ShulkerBox extends Spawnable implements Container, Nameable{
use NameableTrait {
@ -41,8 +42,8 @@ class ShulkerBox extends Spawnable implements Container, Nameable{
protected ShulkerBoxInventory $inventory;
public function __construct(BlockPosition $position){
parent::__construct($position);
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new ShulkerBoxInventory($this->position);
}

View File

@ -23,14 +23,15 @@ declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\BlockPosition;
use pocketmine\block\utils\SignText;
use pocketmine\color\Color;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\utils\Binary;
use pocketmine\world\World;
use function array_pad;
use function array_slice;
use function explode;
@ -71,9 +72,9 @@ class Sign extends Spawnable{
protected ?int $editorEntityRuntimeId = null;
public function __construct(BlockPosition $position){
public function __construct(World $world, Vector3 $pos){
$this->text = new SignText();
parent::__construct($position);
parent::__construct($world, $pos);
}
private function readTextTag(CompoundTag $nbt, bool $lightingBugResolved) : void{

Some files were not shown because too many files have changed in this diff Show More