mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-25 04:44:03 +00:00
Release 5.23.0 (#6561)
This commit is contained in:
commit
ea8f971287
107
changelogs/5.23.md
Normal file
107
changelogs/5.23.md
Normal file
@ -0,0 +1,107 @@
|
||||
# 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.
|
@ -32,7 +32,7 @@
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"netresearch/jsonmapper": "~v5.0.0",
|
||||
"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",
|
||||
@ -41,7 +41,7 @@
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.7.0",
|
||||
"pocketmine/locale-data": "~2.19.0",
|
||||
"pocketmine/locale-data": "~2.22.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/math": "~1.0.0",
|
||||
"pocketmine/nbt": "~1.0.0",
|
||||
|
119
composer.lock
generated
119
composer.lock
generated
@ -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": "26d10b9381ab4e19684ca0b7a5a11a42",
|
||||
"content-hash": "732102eca72dc1d29e7b67dfbce07653",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -125,6 +125,57 @@
|
||||
],
|
||||
"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",
|
||||
@ -420,16 +471,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "2.19.6",
|
||||
"version": "2.22.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e"
|
||||
"reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e",
|
||||
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
|
||||
"reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -437,9 +488,9 @@
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/2.19.6"
|
||||
"source": "https://github.com/pmmp/Language/tree/2.22.0"
|
||||
},
|
||||
"time": "2023-08-08T16:53:23+00:00"
|
||||
"time": "2024-11-16T13:28:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
@ -565,60 +616,6 @@
|
||||
},
|
||||
"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",
|
||||
|
@ -89,6 +89,8 @@ use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\scheduler\TimingsCollectionTask;
|
||||
use pocketmine\scheduler\TimingsControlTask;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\stats\SendUsageTask;
|
||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||
@ -894,7 +896,36 @@ class Server{
|
||||
$poolSize = max(1, (int) $poolSize);
|
||||
}
|
||||
|
||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
||||
|
||||
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper);
|
||||
$this->asyncPool->addWorkerStartHook(function(int $i) : void{
|
||||
if(TimingsHandler::isEnabled()){
|
||||
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled(true), $i);
|
||||
}
|
||||
});
|
||||
TimingsHandler::getToggleCallbacks()->add(function(bool $enable) : void{
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled($enable), $workerId);
|
||||
}
|
||||
});
|
||||
TimingsHandler::getReloadCallbacks()->add(function() : void{
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
$this->asyncPool->submitTaskToWorker(TimingsControlTask::reload(), $workerId);
|
||||
}
|
||||
});
|
||||
TimingsHandler::getCollectCallbacks()->add(function() : array{
|
||||
$promises = [];
|
||||
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||
$resolver = new PromiseResolver();
|
||||
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
|
||||
|
||||
$promises[] = $resolver->getPromise();
|
||||
}
|
||||
|
||||
return $promises;
|
||||
});
|
||||
|
||||
$netCompressionThreshold = -1;
|
||||
if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){
|
||||
@ -968,9 +999,6 @@ class Server{
|
||||
)));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||
|
||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
||||
|
||||
DefaultPermissions::registerCorePermissions();
|
||||
|
||||
$this->commandMap = new SimpleCommandMap($this);
|
||||
|
@ -31,8 +31,8 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.22.1";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BASE_VERSION = "5.23.0";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
/**
|
||||
|
@ -35,10 +35,10 @@ use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\AnvilFallSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function lcg_value;
|
||||
use function round;
|
||||
|
||||
class Anvil extends Transparent implements Fallable{
|
||||
@ -97,7 +97,7 @@ class Anvil extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function onHitGround(FallingBlock $blockEntity) : bool{
|
||||
if(lcg_value() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){
|
||||
if(Utils::getRandomFloat() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){
|
||||
if($this->damage !== self::VERY_DAMAGED){
|
||||
$this->damage = $this->damage + 1;
|
||||
}else{
|
||||
|
@ -69,6 +69,10 @@ class Campfire extends Transparent{
|
||||
|
||||
private const UPDATE_INTERVAL_TICKS = 10;
|
||||
|
||||
/**
|
||||
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
|
||||
* has never been set in the world.
|
||||
*/
|
||||
protected CampfireInventory $inventory;
|
||||
|
||||
/**
|
||||
@ -129,6 +133,10 @@ class Campfire extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
|
||||
* has never been set in the world.
|
||||
*/
|
||||
public function getInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ use pocketmine\event\entity\EntityTrampleFarmlandEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\utils\Utils;
|
||||
use function intdiv;
|
||||
use function lcg_value;
|
||||
|
||||
class Farmland extends Transparent{
|
||||
public const MAX_WETNESS = 7;
|
||||
@ -148,7 +148,7 @@ class Farmland extends Transparent{
|
||||
}
|
||||
|
||||
public function onEntityLand(Entity $entity) : ?float{
|
||||
if($entity instanceof Living && lcg_value() < $entity->getFallDistance() - 0.5){
|
||||
if($entity instanceof Living && Utils::getRandomFloat() < $entity->getFallDistance() - 0.5){
|
||||
$ev = new EntityTrampleFarmlandEvent($entity, $this);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
|
@ -24,60 +24,20 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\MultiAnySupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\World;
|
||||
use function array_key_first;
|
||||
use function count;
|
||||
use function shuffle;
|
||||
|
||||
class GlowLichen extends Transparent{
|
||||
|
||||
/** @var int[] */
|
||||
protected array $faces = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->facingFlags($this->faces);
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getFaces() : array{ return $this->faces; }
|
||||
|
||||
public function hasFace(int $face) : bool{
|
||||
return isset($this->faces[$face]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
* @return $this
|
||||
*/
|
||||
public function setFaces(array $faces) : self{
|
||||
$uniqueFaces = [];
|
||||
foreach($faces as $face){
|
||||
Facing::validate($face);
|
||||
$uniqueFaces[$face] = $face;
|
||||
}
|
||||
$this->faces = $uniqueFaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setFace(int $face, bool $value) : self{
|
||||
Facing::validate($face);
|
||||
if($value){
|
||||
$this->faces[$face] = $face;
|
||||
}else{
|
||||
unset($this->faces[$face]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
use MultiAnySupportTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return 7;
|
||||
@ -102,39 +62,11 @@ class GlowLichen extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->faces = $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
|
||||
$availableFaces = $this->getAvailableFaces();
|
||||
|
||||
if(count($availableFaces) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$opposite = Facing::opposite($face);
|
||||
$placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces);
|
||||
$this->faces[$placedFace] = $placedFace;
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if($this->getAdjacentSupportType($face) !== SupportType::FULL){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$world = $this->position->getWorld();
|
||||
if(count($this->faces) === 0){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
protected function getInitialPlaceFaces(Block $blockReplace) : array{
|
||||
return $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
|
||||
}
|
||||
|
||||
private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{
|
||||
@ -261,17 +193,4 @@ class GlowLichen extends Transparent{
|
||||
public function getFlammability() : int{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int> $faces
|
||||
*/
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
return $faces;
|
||||
}
|
||||
}
|
||||
|
@ -31,13 +31,13 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\ItemFrameAddItemSound;
|
||||
use pocketmine\world\sound\ItemFrameRemoveItemSound;
|
||||
use pocketmine\world\sound\ItemFrameRotateItemSound;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
use function lcg_value;
|
||||
|
||||
class ItemFrame extends Flowable{
|
||||
use AnyFacingTrait;
|
||||
@ -154,7 +154,7 @@ class ItemFrame extends Flowable{
|
||||
return false;
|
||||
}
|
||||
$world = $this->position->getWorld();
|
||||
if(lcg_value() <= $this->itemDropChance){
|
||||
if(Utils::getRandomFloat() <= $this->itemDropChance){
|
||||
$world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem);
|
||||
$world->addSound($this->position, new ItemFrameRemoveItemSound());
|
||||
}
|
||||
@ -185,7 +185,7 @@ class ItemFrame extends Flowable{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
$drops = parent::getDropsForCompatibleTool($item);
|
||||
if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){
|
||||
if($this->framedItem !== null && Utils::getRandomFloat() <= $this->itemDropChance){
|
||||
$drops[] = clone $this->framedItem;
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,9 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\sound\FizzSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function lcg_value;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
public const MAX_DECAY = 7;
|
||||
@ -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->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 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;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class Magma extends Opaque{
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if($entity instanceof Living && !$entity->isSneaking()){
|
||||
if($entity instanceof Living && !$entity->isSneaking() && $entity->getFrostWalkerLevel() === 0){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev);
|
||||
}
|
||||
|
@ -36,7 +36,9 @@ use pocketmine\world\Position;
|
||||
|
||||
class Sugarcane extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
use StaticSupportTrait {
|
||||
onNearbyBlockChange as onSupportBlockChange;
|
||||
}
|
||||
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
@ -97,7 +99,13 @@ class Sugarcane extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)){
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasSameTypeId($this)){
|
||||
if(!$this->hasNearbyWater($down)){
|
||||
$this->position->getWorld()->useBreakOn($this->position, createParticles: true);
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->age === self::MAX_AGE){
|
||||
$this->grow($this->position);
|
||||
}else{
|
||||
@ -123,4 +131,23 @@ class Sugarcane extends Flowable{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasNearbyWater(Block $down) : bool{
|
||||
foreach($down->getHorizontalSides() as $sideBlock){
|
||||
$blockId = $sideBlock->getTypeId();
|
||||
if($blockId === BlockTypeIds::WATER || $blockId === BlockTypeIds::FROSTED_ICE){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasSameTypeId($this) && !$this->hasNearbyWater($down)){
|
||||
$this->position->getWorld()->useBreakOn($this->position, createParticles: true);
|
||||
}else{
|
||||
$this->onSupportBlockChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,263 +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\block\BlockIdentifier as BID;
|
||||
use pocketmine\block\BlockTypeIds as Ids;
|
||||
use pocketmine\block\tile\Sign as TileSign;
|
||||
use pocketmine\block\utils\LeavesType;
|
||||
use pocketmine\block\utils\SaplingType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\item\VanillaItems;
|
||||
|
||||
/**
|
||||
* All wood-like blocks have different IDs for different wood types.
|
||||
*
|
||||
* We can't make these dynamic, because some types of wood have different type properties (e.g. crimson and warped planks
|
||||
* are not flammable, but all other planks are).
|
||||
*
|
||||
* In the future, it's entirely within the realm of reason that the other types of wood may differ in other ways, such
|
||||
* as flammability, hardness, required tool tier, etc.
|
||||
* Therefore, to stay on the safe side of Mojang, wood-like blocks have static types. This does unfortunately generate
|
||||
* a lot of ugly code.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class WoodLikeBlockIdHelper{
|
||||
|
||||
public static function getPlanksIdentifier(WoodType $type) : BID{
|
||||
return new BID(match($type){
|
||||
WoodType::OAK => Ids::OAK_PLANKS,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_PLANKS,
|
||||
WoodType::BIRCH => Ids::BIRCH_PLANKS,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_PLANKS,
|
||||
WoodType::ACACIA => Ids::ACACIA_PLANKS,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_PLANKS,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_PLANKS,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_PLANKS,
|
||||
WoodType::WARPED => Ids::WARPED_PLANKS,
|
||||
WoodType::CHERRY => Ids::CHERRY_PLANKS,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getFenceIdentifier(WoodType $type) : BID{
|
||||
return new BID(match($type){
|
||||
WoodType::OAK => Ids::OAK_FENCE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_FENCE,
|
||||
WoodType::BIRCH => Ids::BIRCH_FENCE,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_FENCE,
|
||||
WoodType::ACACIA => Ids::ACACIA_FENCE,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_FENCE,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_FENCE,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_FENCE,
|
||||
WoodType::WARPED => Ids::WARPED_FENCE,
|
||||
WoodType::CHERRY => Ids::CHERRY_FENCE,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getSlabIdentifier(WoodType $type) : BID{
|
||||
return new BID(match($type){
|
||||
WoodType::OAK => Ids::OAK_SLAB,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_SLAB,
|
||||
WoodType::BIRCH => Ids::BIRCH_SLAB,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_SLAB,
|
||||
WoodType::ACACIA => Ids::ACACIA_SLAB,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_SLAB,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_SLAB,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_SLAB,
|
||||
WoodType::WARPED => Ids::WARPED_SLAB,
|
||||
WoodType::CHERRY => Ids::CHERRY_SLAB,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getLogIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_LOG,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_LOG,
|
||||
WoodType::BIRCH => Ids::BIRCH_LOG,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_LOG,
|
||||
WoodType::ACACIA => Ids::ACACIA_LOG,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_LOG,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_LOG,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_STEM,
|
||||
WoodType::WARPED => Ids::WARPED_STEM,
|
||||
WoodType::CHERRY => Ids::CHERRY_LOG,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getAllSidedLogIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_WOOD,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_WOOD,
|
||||
WoodType::BIRCH => Ids::BIRCH_WOOD,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_WOOD,
|
||||
WoodType::ACACIA => Ids::ACACIA_WOOD,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_WOOD,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_WOOD,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_HYPHAE,
|
||||
WoodType::WARPED => Ids::WARPED_HYPHAE,
|
||||
WoodType::CHERRY => Ids::CHERRY_WOOD,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getLeavesIdentifier(LeavesType $leavesType) : BID{
|
||||
return new BID(match($leavesType){
|
||||
LeavesType::OAK => Ids::OAK_LEAVES,
|
||||
LeavesType::SPRUCE => Ids::SPRUCE_LEAVES,
|
||||
LeavesType::BIRCH => Ids::BIRCH_LEAVES,
|
||||
LeavesType::JUNGLE => Ids::JUNGLE_LEAVES,
|
||||
LeavesType::ACACIA => Ids::ACACIA_LEAVES,
|
||||
LeavesType::DARK_OAK => Ids::DARK_OAK_LEAVES,
|
||||
LeavesType::MANGROVE => Ids::MANGROVE_LEAVES,
|
||||
LeavesType::AZALEA => Ids::AZALEA_LEAVES,
|
||||
LeavesType::FLOWERING_AZALEA => Ids::FLOWERING_AZALEA_LEAVES,
|
||||
LeavesType::CHERRY => Ids::CHERRY_LEAVES,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getSaplingIdentifier(SaplingType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
SaplingType::OAK => Ids::OAK_SAPLING,
|
||||
SaplingType::SPRUCE => Ids::SPRUCE_SAPLING,
|
||||
SaplingType::BIRCH => Ids::BIRCH_SAPLING,
|
||||
SaplingType::JUNGLE => Ids::JUNGLE_SAPLING,
|
||||
SaplingType::ACACIA => Ids::ACACIA_SAPLING,
|
||||
SaplingType::DARK_OAK => Ids::DARK_OAK_SAPLING,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BID[]|\Closure[]
|
||||
* @phpstan-return array{BID, BID, \Closure() : \pocketmine\item\Item}
|
||||
*/
|
||||
public static function getSignInfo(WoodType $treeType) : array{
|
||||
$make = fn(int $floorId, int $wallId, \Closure $getItem) => [
|
||||
new BID($floorId, TileSign::class),
|
||||
new BID($wallId, TileSign::class),
|
||||
$getItem
|
||||
];
|
||||
return match($treeType){
|
||||
WoodType::OAK => $make(Ids::OAK_SIGN, Ids::OAK_WALL_SIGN, fn() => VanillaItems::OAK_SIGN()),
|
||||
WoodType::SPRUCE => $make(Ids::SPRUCE_SIGN, Ids::SPRUCE_WALL_SIGN, fn() => VanillaItems::SPRUCE_SIGN()),
|
||||
WoodType::BIRCH => $make(Ids::BIRCH_SIGN, Ids::BIRCH_WALL_SIGN, fn() => VanillaItems::BIRCH_SIGN()),
|
||||
WoodType::JUNGLE => $make(Ids::JUNGLE_SIGN, Ids::JUNGLE_WALL_SIGN, fn() => VanillaItems::JUNGLE_SIGN()),
|
||||
WoodType::ACACIA => $make(Ids::ACACIA_SIGN, Ids::ACACIA_WALL_SIGN, fn() => VanillaItems::ACACIA_SIGN()),
|
||||
WoodType::DARK_OAK => $make(Ids::DARK_OAK_SIGN, Ids::DARK_OAK_WALL_SIGN, fn() => VanillaItems::DARK_OAK_SIGN()),
|
||||
WoodType::MANGROVE => $make(Ids::MANGROVE_SIGN, Ids::MANGROVE_WALL_SIGN, fn() => VanillaItems::MANGROVE_SIGN()),
|
||||
WoodType::CRIMSON => $make(Ids::CRIMSON_SIGN, Ids::CRIMSON_WALL_SIGN, fn() => VanillaItems::CRIMSON_SIGN()),
|
||||
WoodType::WARPED => $make(Ids::WARPED_SIGN, Ids::WARPED_WALL_SIGN, fn() => VanillaItems::WARPED_SIGN()),
|
||||
WoodType::CHERRY => $make(Ids::CHERRY_SIGN, Ids::CHERRY_WALL_SIGN, fn() => VanillaItems::CHERRY_SIGN()),
|
||||
};
|
||||
}
|
||||
|
||||
public static function getTrapdoorIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_TRAPDOOR,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR,
|
||||
WoodType::BIRCH => Ids::BIRCH_TRAPDOOR,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_TRAPDOOR,
|
||||
WoodType::ACACIA => Ids::ACACIA_TRAPDOOR,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_TRAPDOOR,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_TRAPDOOR,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_TRAPDOOR,
|
||||
WoodType::WARPED => Ids::WARPED_TRAPDOOR,
|
||||
WoodType::CHERRY => Ids::CHERRY_TRAPDOOR,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getButtonIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_BUTTON,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_BUTTON,
|
||||
WoodType::BIRCH => Ids::BIRCH_BUTTON,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_BUTTON,
|
||||
WoodType::ACACIA => Ids::ACACIA_BUTTON,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_BUTTON,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_BUTTON,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_BUTTON,
|
||||
WoodType::WARPED => Ids::WARPED_BUTTON,
|
||||
WoodType::CHERRY => Ids::CHERRY_BUTTON,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getPressurePlateIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_PRESSURE_PLATE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE,
|
||||
WoodType::BIRCH => Ids::BIRCH_PRESSURE_PLATE,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_PRESSURE_PLATE,
|
||||
WoodType::ACACIA => Ids::ACACIA_PRESSURE_PLATE,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_PRESSURE_PLATE,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_PRESSURE_PLATE,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_PRESSURE_PLATE,
|
||||
WoodType::WARPED => Ids::WARPED_PRESSURE_PLATE,
|
||||
WoodType::CHERRY => Ids::CHERRY_PRESSURE_PLATE,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getDoorIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_DOOR,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_DOOR,
|
||||
WoodType::BIRCH => Ids::BIRCH_DOOR,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_DOOR,
|
||||
WoodType::ACACIA => Ids::ACACIA_DOOR,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_DOOR,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_DOOR,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_DOOR,
|
||||
WoodType::WARPED => Ids::WARPED_DOOR,
|
||||
WoodType::CHERRY => Ids::CHERRY_DOOR,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getFenceGateIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_FENCE_GATE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE,
|
||||
WoodType::BIRCH => Ids::BIRCH_FENCE_GATE,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_FENCE_GATE,
|
||||
WoodType::ACACIA => Ids::ACACIA_FENCE_GATE,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_FENCE_GATE,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_FENCE_GATE,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_FENCE_GATE,
|
||||
WoodType::WARPED => Ids::WARPED_FENCE_GATE,
|
||||
WoodType::CHERRY => Ids::CHERRY_FENCE_GATE,
|
||||
});
|
||||
}
|
||||
|
||||
public static function getStairsIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_STAIRS,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_STAIRS,
|
||||
WoodType::BIRCH => Ids::BIRCH_STAIRS,
|
||||
WoodType::JUNGLE => Ids::JUNGLE_STAIRS,
|
||||
WoodType::ACACIA => Ids::ACACIA_STAIRS,
|
||||
WoodType::DARK_OAK => Ids::DARK_OAK_STAIRS,
|
||||
WoodType::MANGROVE => Ids::MANGROVE_STAIRS,
|
||||
WoodType::CRIMSON => Ids::CRIMSON_STAIRS,
|
||||
WoodType::WARPED => Ids::WARPED_STAIRS,
|
||||
WoodType::CHERRY => Ids::CHERRY_STAIRS,
|
||||
});
|
||||
}
|
||||
}
|
72
src/block/utils/MultiAnyFacingTrait.php
Normal file
72
src/block/utils/MultiAnyFacingTrait.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?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\utils;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
/**
|
||||
* Used by blocks that can have multiple target faces in the area of one solid block, such as covering three sides of a corner.
|
||||
*/
|
||||
trait MultiAnyFacingTrait{
|
||||
|
||||
/** @var int[] */
|
||||
protected array $faces = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->facingFlags($this->faces);
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getFaces() : array{ return $this->faces; }
|
||||
|
||||
public function hasFace(int $face) : bool{
|
||||
return isset($this->faces[$face]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
* @return $this
|
||||
*/
|
||||
public function setFaces(array $faces) : self{
|
||||
$uniqueFaces = [];
|
||||
foreach($faces as $face){
|
||||
Facing::validate($face);
|
||||
$uniqueFaces[$face] = $face;
|
||||
}
|
||||
$this->faces = $uniqueFaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setFace(int $face, bool $value) : self{
|
||||
Facing::validate($face);
|
||||
if($value){
|
||||
$this->faces[$face] = $face;
|
||||
}else{
|
||||
unset($this->faces[$face]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
96
src/block/utils/MultiAnySupportTrait.php
Normal file
96
src/block/utils/MultiAnySupportTrait.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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\utils;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function array_key_first;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Used by blocks that have multiple support requirements in the area of one solid block, such as covering three sides of a corner.
|
||||
* Prevents placement if support isn't available and automatically destroys a block side if it's support is removed.
|
||||
*/
|
||||
trait MultiAnySupportTrait{
|
||||
use MultiAnyFacingTrait;
|
||||
|
||||
/**
|
||||
* Returns a list of faces that block should already have when placed.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
abstract protected function getInitialPlaceFaces(Block $blockReplace) : array;
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->faces = $this->getInitialPlaceFaces($blockReplace);
|
||||
$availableFaces = $this->getAvailableFaces();
|
||||
|
||||
if(count($availableFaces) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$opposite = Facing::opposite($face);
|
||||
$placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces);
|
||||
$this->faces[$placedFace] = $placedFace;
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if($this->getAdjacentSupportType($face) !== SupportType::FULL){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$world = $this->position->getWorld();
|
||||
if(count($this->faces) === 0){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int> $faces
|
||||
*/
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
return $faces;
|
||||
}
|
||||
}
|
60
src/command/ClosureCommand.php
Normal file
60
src/command/ClosureCommand.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
/**
|
||||
* @phpstan-type Execute \Closure(CommandSender $sender, Command $command, string $commandLabel, list<string> $args) : mixed
|
||||
*/
|
||||
final class ClosureCommand extends Command{
|
||||
/** @phpstan-var Execute */
|
||||
private \Closure $execute;
|
||||
|
||||
/**
|
||||
* @param string[] $permissions
|
||||
* @phpstan-param Execute $execute
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
\Closure $execute,
|
||||
array $permissions,
|
||||
Translatable|string $description = "",
|
||||
Translatable|string|null $usageMessage = null,
|
||||
array $aliases = []
|
||||
){
|
||||
Utils::validateCallableSignature(
|
||||
fn(CommandSender $sender, Command $command, string $commandLabel, array $args) : mixed => 1,
|
||||
$execute,
|
||||
);
|
||||
$this->execute = $execute;
|
||||
parent::__construct($name, $description, $usageMessage, $aliases);
|
||||
$this->setPermissions($permissions);
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
return ($this->execute)($sender, $this, $commandLabel, $args);
|
||||
}
|
||||
}
|
@ -64,6 +64,7 @@ use pocketmine\command\defaults\TransferServerCommand;
|
||||
use pocketmine\command\defaults\VanillaCommand;
|
||||
use pocketmine\command\defaults\VersionCommand;
|
||||
use pocketmine\command\defaults\WhitelistCommand;
|
||||
use pocketmine\command\defaults\XpCommand;
|
||||
use pocketmine\command\utils\CommandStringHelper;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
@ -132,7 +133,8 @@ class SimpleCommandMap implements CommandMap{
|
||||
new TitleCommand(),
|
||||
new TransferServerCommand(),
|
||||
new VersionCommand(),
|
||||
new WhitelistCommand()
|
||||
new WhitelistCommand(),
|
||||
new XpCommand(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -26,28 +26,28 @@ namespace pocketmine\command\defaults;
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\scheduler\BulkCurlTask;
|
||||
use pocketmine\scheduler\BulkCurlTaskOperation;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\InternetException;
|
||||
use pocketmine\utils\InternetRequestResult;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\YmlServerProperties;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function count;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
use function fseek;
|
||||
use function fwrite;
|
||||
use function http_build_query;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
use function mkdir;
|
||||
use function stream_get_contents;
|
||||
use function strtolower;
|
||||
use const CURLOPT_AUTOREFERER;
|
||||
use const CURLOPT_FOLLOWLOCATION;
|
||||
@ -101,82 +101,91 @@ class TimingsCommand extends VanillaCommand{
|
||||
TimingsHandler::reload();
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset());
|
||||
}elseif($mode === "merged" || $mode === "report" || $paste){
|
||||
$timings = "";
|
||||
if($paste){
|
||||
$fileTimings = Utils::assumeNotFalse(fopen("php://temp", "r+b"), "Opening php://temp should never fail");
|
||||
}else{
|
||||
$index = 0;
|
||||
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
|
||||
|
||||
if(!file_exists($timingFolder)){
|
||||
mkdir($timingFolder, 0777);
|
||||
}
|
||||
$timings = Path::join($timingFolder, "timings.txt");
|
||||
while(file_exists($timings)){
|
||||
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
|
||||
}
|
||||
|
||||
$fileTimings = fopen($timings, "a+b");
|
||||
}
|
||||
$lines = TimingsHandler::printTimings();
|
||||
foreach($lines as $line){
|
||||
fwrite($fileTimings, $line . PHP_EOL);
|
||||
}
|
||||
|
||||
if($paste){
|
||||
fseek($fileTimings, 0);
|
||||
$data = [
|
||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||
"data" => $content = stream_get_contents($fileTimings)
|
||||
];
|
||||
fclose($fileTimings);
|
||||
|
||||
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask(
|
||||
[new BulkCurlTaskOperation(
|
||||
"https://$host?upload=true",
|
||||
10,
|
||||
[],
|
||||
[
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"User-Agent: $agent",
|
||||
"Content-Type: application/x-www-form-urlencoded"
|
||||
],
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]
|
||||
)],
|
||||
function(array $results) use ($sender, $host) : void{
|
||||
/** @phpstan-var array<InternetRequestResult|InternetException> $results */
|
||||
if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
|
||||
return;
|
||||
}
|
||||
$result = $results[0];
|
||||
if($result instanceof InternetException){
|
||||
$sender->getServer()->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
$response = json_decode($result->getBody(), true);
|
||||
if(is_array($response) && isset($response["id"])){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
|
||||
"https://" . $host . "/?id=" . $response["id"]));
|
||||
}else{
|
||||
$sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
|
||||
}
|
||||
}
|
||||
));
|
||||
}else{
|
||||
fclose($fileTimings);
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
|
||||
}
|
||||
$timingsPromise = TimingsHandler::requestPrintTimings();
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
|
||||
$timingsPromise->onCompletion(
|
||||
fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender),
|
||||
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
|
||||
);
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $lines
|
||||
* @phpstan-param list<string> $lines
|
||||
*/
|
||||
private function createReportFile(array $lines, CommandSender $sender) : void{
|
||||
$index = 0;
|
||||
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
|
||||
|
||||
if(!file_exists($timingFolder)){
|
||||
mkdir($timingFolder, 0777);
|
||||
}
|
||||
$timings = Path::join($timingFolder, "timings.txt");
|
||||
while(file_exists($timings)){
|
||||
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
|
||||
}
|
||||
|
||||
$fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b"));
|
||||
foreach($lines as $line){
|
||||
fwrite($fileTimings, $line . PHP_EOL);
|
||||
}
|
||||
fclose($fileTimings);
|
||||
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $lines
|
||||
* @phpstan-param list<string> $lines
|
||||
*/
|
||||
private function uploadReport(array $lines, CommandSender $sender) : void{
|
||||
$data = [
|
||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||
"data" => implode("\n", $lines)
|
||||
];
|
||||
|
||||
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask(
|
||||
[new BulkCurlTaskOperation(
|
||||
"https://$host?upload=true",
|
||||
10,
|
||||
[],
|
||||
[
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"User-Agent: $agent",
|
||||
"Content-Type: application/x-www-form-urlencoded"
|
||||
],
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]
|
||||
)],
|
||||
function(array $results) use ($sender, $host) : void{
|
||||
/** @phpstan-var array<InternetRequestResult|InternetException> $results */
|
||||
if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
|
||||
return;
|
||||
}
|
||||
$result = $results[0];
|
||||
if($result instanceof InternetException){
|
||||
$sender->getServer()->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
$response = json_decode($result->getBody(), true);
|
||||
if(is_array($response) && isset($response["id"])){
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
|
||||
"https://" . $host . "/?id=" . $response["id"]));
|
||||
}else{
|
||||
$sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
89
src/command/defaults/XpCommand.php
Normal file
89
src/command/defaults/XpCommand.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function abs;
|
||||
use function count;
|
||||
use function str_ends_with;
|
||||
use function substr;
|
||||
|
||||
class XpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"xp",
|
||||
KnownTranslationFactory::pocketmine_command_xp_description(),
|
||||
KnownTranslationFactory::pocketmine_command_xp_usage()
|
||||
);
|
||||
$this->setPermissions([
|
||||
DefaultPermissionNames::COMMAND_XP_SELF,
|
||||
DefaultPermissionNames::COMMAND_XP_OTHER
|
||||
]);
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
if(count($args) < 1){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$xpManager = $player->getXpManager();
|
||||
if(str_ends_with($args[0], "L")){
|
||||
$xpLevelAttr = $player->getAttributeMap()->get(Attribute::EXPERIENCE_LEVEL) ?? throw new AssumptionFailedError();
|
||||
$maxXpLevel = (int) $xpLevelAttr->getMaxValue();
|
||||
$currentXpLevel = $xpManager->getXpLevel();
|
||||
$xpLevels = $this->getInteger($sender, substr($args[0], 0, -1), -$currentXpLevel, $maxXpLevel - $currentXpLevel);
|
||||
if($xpLevels >= 0){
|
||||
$xpManager->addXpLevels($xpLevels, false);
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_success_levels((string) $xpLevels, $player->getName()));
|
||||
}else{
|
||||
$xpLevels = abs($xpLevels);
|
||||
$xpManager->subtractXpLevels($xpLevels);
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_success_negative_levels((string) $xpLevels, $player->getName()));
|
||||
}
|
||||
}else{
|
||||
$xp = $this->getInteger($sender, $args[0], max: Limits::INT32_MAX);
|
||||
if($xp < 0){
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_failure_widthdrawXp()->prefix(TextFormat::RED));
|
||||
}else{
|
||||
$xpManager->addXp($xp, false);
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_xp_success((string) $xp, $player->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -29,23 +29,21 @@ use pocketmine\utils\Process;
|
||||
use function cli_set_process_title;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function feof;
|
||||
use function fwrite;
|
||||
use function stream_socket_client;
|
||||
use function is_numeric;
|
||||
use const PHP_EOL;
|
||||
use const STDOUT;
|
||||
|
||||
if(count($argv) !== 2 || !is_numeric($argv[1])){
|
||||
echo "Usage: " . $argv[0] . " <command token seed>" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$commandTokenSeed = (int) $argv[1];
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 2){
|
||||
die("Please provide a server to connect to");
|
||||
}
|
||||
|
||||
@cli_set_process_title('PocketMine-MP Console Reader');
|
||||
$errCode = null;
|
||||
$errMessage = null;
|
||||
$socket = stream_socket_client($argv[1], $errCode, $errMessage, 15.0);
|
||||
if($socket === false){
|
||||
throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage");
|
||||
}
|
||||
|
||||
/** @phpstan-var ThreadSafeArray<int, string> $channel */
|
||||
$channel = new ThreadSafeArray();
|
||||
@ -75,15 +73,15 @@ $thread = new class($channel) extends NativeThread{
|
||||
};
|
||||
|
||||
$thread->start(NativeThread::INHERIT_NONE);
|
||||
while(!feof($socket)){
|
||||
while(true){
|
||||
$line = $channel->synchronized(function() use ($channel) : ?string{
|
||||
if(count($channel) === 0){
|
||||
$channel->wait(1_000_000);
|
||||
}
|
||||
$line = $channel->shift();
|
||||
return $line;
|
||||
return $channel->shift();
|
||||
});
|
||||
if(@fwrite($socket, ($line ?? "") . "\n") === false){
|
||||
$message = $line !== null ? ConsoleReaderChildProcessUtils::createMessage($line, $commandTokenSeed) : "";
|
||||
if(@fwrite(STDOUT, $message . "\n") === false){
|
||||
//Always send even if there's no line, to check if the parent is alive
|
||||
//If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return
|
||||
//false even though the connection is actually broken. However, fwrite() will fail.
|
||||
|
@ -29,19 +29,16 @@ use Symfony\Component\Filesystem\Path;
|
||||
use function base64_encode;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function mt_rand;
|
||||
use function preg_replace;
|
||||
use function proc_close;
|
||||
use function proc_open;
|
||||
use function proc_terminate;
|
||||
use function rtrim;
|
||||
use function sprintf;
|
||||
use function stream_select;
|
||||
use function stream_socket_accept;
|
||||
use function stream_socket_get_name;
|
||||
use function stream_socket_server;
|
||||
use function stream_socket_shutdown;
|
||||
use function trim;
|
||||
use const PHP_BINARY;
|
||||
use const STREAM_SHUT_RDWR;
|
||||
|
||||
/**
|
||||
* This pile of shit exists because PHP on Windows is broken, and can't handle stream_select() on stdin or pipes
|
||||
@ -58,44 +55,44 @@ use const STREAM_SHUT_RDWR;
|
||||
* communication.
|
||||
*/
|
||||
final class ConsoleReaderChildProcessDaemon{
|
||||
public const TOKEN_DELIMITER = ":";
|
||||
public const TOKEN_HASH_ALGO = "xxh3";
|
||||
|
||||
private \PrefixedLogger $logger;
|
||||
/** @var resource */
|
||||
private $subprocess;
|
||||
/** @var resource */
|
||||
private $socket;
|
||||
private int $commandTokenSeed;
|
||||
|
||||
public function __construct(
|
||||
\Logger $logger
|
||||
){
|
||||
$this->logger = new \PrefixedLogger($logger, "Console Reader Daemon");
|
||||
$this->commandTokenSeed = mt_rand();
|
||||
$this->prepareSubprocess();
|
||||
}
|
||||
|
||||
private function prepareSubprocess() : void{
|
||||
$server = stream_socket_server("tcp://127.0.0.1:0");
|
||||
if($server === false){
|
||||
throw new \RuntimeException("Failed to open console reader socket server");
|
||||
}
|
||||
$address = Utils::assumeNotFalse(stream_socket_get_name($server, false), "stream_socket_get_name() shouldn't return false here");
|
||||
|
||||
//Windows sucks, and likes to corrupt UTF-8 file paths when they travel to the subprocess, so we base64 encode
|
||||
//the path to avoid the problem. This is an abysmally shitty hack, but here we are :(
|
||||
$sub = Utils::assumeNotFalse(proc_open(
|
||||
[PHP_BINARY, '-dopcache.enable_cli=0', '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address],
|
||||
[
|
||||
PHP_BINARY,
|
||||
'-dopcache.enable_cli=0',
|
||||
'-r',
|
||||
sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))),
|
||||
(string) $this->commandTokenSeed
|
||||
],
|
||||
[
|
||||
1 => ['socket'],
|
||||
2 => fopen("php://stderr", "w"),
|
||||
],
|
||||
$pipes
|
||||
), "Something has gone horribly wrong");
|
||||
|
||||
$client = stream_socket_accept($server, 15);
|
||||
if($client === false){
|
||||
throw new AssumptionFailedError("stream_socket_accept() returned false");
|
||||
}
|
||||
stream_socket_shutdown($server, STREAM_SHUT_RDWR);
|
||||
|
||||
$this->subprocess = $sub;
|
||||
$this->socket = $client;
|
||||
$this->socket = $pipes[1];
|
||||
}
|
||||
|
||||
private function shutdownSubprocess() : void{
|
||||
@ -104,7 +101,6 @@ final class ConsoleReaderChildProcessDaemon{
|
||||
//the first place).
|
||||
proc_terminate($this->subprocess);
|
||||
proc_close($this->subprocess);
|
||||
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
|
||||
}
|
||||
|
||||
public function readLine() : ?string{
|
||||
@ -112,13 +108,27 @@ final class ConsoleReaderChildProcessDaemon{
|
||||
$w = null;
|
||||
$e = null;
|
||||
if(stream_select($r, $w, $e, 0, 0) === 1){
|
||||
$command = fgets($this->socket);
|
||||
if($command === false){
|
||||
$line = fgets($this->socket);
|
||||
if($line === false){
|
||||
$this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)");
|
||||
$this->shutdownSubprocess();
|
||||
$this->prepareSubprocess();
|
||||
return null;
|
||||
}
|
||||
$line = rtrim($line, "\n");
|
||||
|
||||
if($line === ""){
|
||||
//keepalive
|
||||
return null;
|
||||
}
|
||||
|
||||
$command = ConsoleReaderChildProcessUtils::parseMessage($line, $this->commandTokenSeed);
|
||||
if($command === null){
|
||||
//this is not a command - it may be some kind of error output from the subprocess
|
||||
//write it directly to the console
|
||||
$this->logger->warning("Unexpected output from child process: $line");
|
||||
return null;
|
||||
}
|
||||
|
||||
$command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
|
||||
$command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid");
|
||||
|
71
src/console/ConsoleReaderChildProcessUtils.php
Normal file
71
src/console/ConsoleReaderChildProcessUtils.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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\console;
|
||||
|
||||
use function hash;
|
||||
use function strlen;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
|
||||
final class ConsoleReaderChildProcessUtils{
|
||||
public const TOKEN_DELIMITER = ":";
|
||||
public const TOKEN_HASH_ALGO = "xxh3";
|
||||
|
||||
private function __construct(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IPC message to transmit a user's input command to the parent process.
|
||||
*
|
||||
* Unfortunately we can't currently provide IPC pipes other than stdout/stderr to subprocesses on Windows, so this
|
||||
* adds a hash of the user input (with a counter as salt) to prevent unintended process output (like error messages)
|
||||
* from being treated as user input.
|
||||
*/
|
||||
public static function createMessage(string $line, int &$counter) : string{
|
||||
$token = hash(self::TOKEN_HASH_ALGO, $line, options: ['seed' => $counter]);
|
||||
$counter++;
|
||||
return $line . self::TOKEN_DELIMITER . $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a command from an IPC message from the console reader subprocess.
|
||||
* Returns the user's input command, or null if this isn't a user input.
|
||||
*/
|
||||
public static function parseMessage(string $message, int &$counter) : ?string{
|
||||
$delimiterPos = strrpos($message, self::TOKEN_DELIMITER);
|
||||
if($delimiterPos !== false){
|
||||
$left = substr($message, 0, $delimiterPos);
|
||||
$right = substr($message, $delimiterPos + strlen(self::TOKEN_DELIMITER));
|
||||
$expectedToken = hash(self::TOKEN_HASH_ALGO, $left, options: ['seed' => $counter]);
|
||||
|
||||
if($expectedToken === $right){
|
||||
$counter++;
|
||||
return $left;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -66,5 +66,7 @@ final class EnchantmentIdMap{
|
||||
$this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING());
|
||||
|
||||
$this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK());
|
||||
|
||||
$this->register(EnchantmentIds::FROST_WALKER, VanillaEnchantments::FROST_WALKER());
|
||||
}
|
||||
}
|
||||
|
48
src/data/bedrock/GoatHornTypeIdMap.php
Normal file
48
src/data/bedrock/GoatHornTypeIdMap.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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\data\bedrock;
|
||||
|
||||
use pocketmine\item\GoatHornType;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class GoatHornTypeIdMap{
|
||||
use SingletonTrait;
|
||||
/** @phpstan-use IntSaveIdMapTrait<GoatHornType> */
|
||||
use IntSaveIdMapTrait;
|
||||
|
||||
private function __construct(){
|
||||
foreach(GoatHornType::cases() as $case){
|
||||
$this->register(match($case){
|
||||
GoatHornType::PONDER => GoatHornTypeIds::PONDER,
|
||||
GoatHornType::SING => GoatHornTypeIds::SING,
|
||||
GoatHornType::SEEK => GoatHornTypeIds::SEEK,
|
||||
GoatHornType::FEEL => GoatHornTypeIds::FEEL,
|
||||
GoatHornType::ADMIRE => GoatHornTypeIds::ADMIRE,
|
||||
GoatHornType::CALL => GoatHornTypeIds::CALL,
|
||||
GoatHornType::YEARN => GoatHornTypeIds::YEARN,
|
||||
GoatHornType::DREAM => GoatHornTypeIds::DREAM
|
||||
}, $case);
|
||||
}
|
||||
}
|
||||
}
|
35
src/data/bedrock/GoatHornTypeIds.php
Normal file
35
src/data/bedrock/GoatHornTypeIds.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?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\data\bedrock;
|
||||
|
||||
final class GoatHornTypeIds{
|
||||
public const PONDER = 0;
|
||||
public const SING = 1;
|
||||
public const SEEK = 2;
|
||||
public const FEEL = 3;
|
||||
public const ADMIRE = 4;
|
||||
public const CALL = 5;
|
||||
public const YEARN = 6;
|
||||
public const DREAM = 7;
|
||||
}
|
@ -31,6 +31,7 @@ use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\VanillaBlocks as Blocks;
|
||||
use pocketmine\data\bedrock\CompoundTypeIds;
|
||||
use pocketmine\data\bedrock\DyeColorIdMap;
|
||||
use pocketmine\data\bedrock\GoatHornTypeIdMap;
|
||||
use pocketmine\data\bedrock\item\ItemTypeNames as Ids;
|
||||
use pocketmine\data\bedrock\item\SavedItemData as Data;
|
||||
use pocketmine\data\bedrock\MedicineTypeIdMap;
|
||||
@ -38,6 +39,7 @@ use pocketmine\data\bedrock\PotionTypeIdMap;
|
||||
use pocketmine\data\bedrock\SuspiciousStewTypeIdMap;
|
||||
use pocketmine\item\Banner;
|
||||
use pocketmine\item\Dye;
|
||||
use pocketmine\item\GoatHorn;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\Medicine;
|
||||
use pocketmine\item\Potion;
|
||||
@ -230,6 +232,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::EMERALD, Items::EMERALD());
|
||||
$this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK());
|
||||
$this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE());
|
||||
$this->map1to1Item(Ids::END_CRYSTAL, Items::END_CRYSTAL());
|
||||
$this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL());
|
||||
$this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE());
|
||||
$this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
@ -263,6 +266,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::HONEY_BOTTLE, Items::HONEY_BOTTLE());
|
||||
$this->map1to1Item(Ids::HONEYCOMB, Items::HONEYCOMB());
|
||||
$this->map1to1Item(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::ICE_BOMB, Items::ICE_BOMB());
|
||||
$this->map1to1Item(Ids::INK_SAC, Items::INK_SAC());
|
||||
$this->map1to1Item(Ids::IRON_AXE, Items::IRON_AXE());
|
||||
$this->map1to1Item(Ids::IRON_BOOTS, Items::IRON_BOOTS());
|
||||
@ -348,6 +352,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::RAW_COPPER, Items::RAW_COPPER());
|
||||
$this->map1to1Item(Ids::RAW_GOLD, Items::RAW_GOLD());
|
||||
$this->map1to1Item(Ids::RAW_IRON, Items::RAW_IRON());
|
||||
$this->map1to1Item(Ids::RECOVERY_COMPASS, Items::RECOVERY_COMPASS());
|
||||
$this->map1to1Item(Ids::REDSTONE, Items::REDSTONE_DUST());
|
||||
$this->map1to1Item(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::ROTTEN_FLESH, Items::ROTTEN_FLESH());
|
||||
@ -483,6 +488,14 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
},
|
||||
fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor())
|
||||
);
|
||||
$this->map1to1ItemWithMeta(
|
||||
Ids::GOAT_HORN,
|
||||
Items::GOAT_HORN(),
|
||||
function(GoatHorn $item, int $meta) : void{
|
||||
$item->setHornType(GoatHornTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown goat horn type ID $meta"));
|
||||
},
|
||||
fn(GoatHorn $item) => GoatHornTypeIdMap::getInstance()->toId($item->getHornType())
|
||||
);
|
||||
$this->map1to1ItemWithMeta(
|
||||
Ids::MEDICINE,
|
||||
Items::MEDICINE(),
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityMotionEvent;
|
||||
use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\event\entity\EntitySpawnEvent;
|
||||
use pocketmine\event\entity\EntityTeleportEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector2;
|
||||
@ -74,7 +75,6 @@ use function deg2rad;
|
||||
use function floor;
|
||||
use function fmod;
|
||||
use function get_class;
|
||||
use function lcg_value;
|
||||
use function sin;
|
||||
use function spl_object_id;
|
||||
use const M_PI_2;
|
||||
@ -909,7 +909,7 @@ abstract class Entity{
|
||||
return false;
|
||||
}
|
||||
|
||||
$force = lcg_value() * 0.2 + 0.1;
|
||||
$force = Utils::getRandomFloat() * 0.2 + 0.1;
|
||||
|
||||
$this->motion = match($direction){
|
||||
Facing::WEST => $this->motion->withComponents(-$force, null, null),
|
||||
@ -1563,6 +1563,13 @@ abstract class Entity{
|
||||
$this->hasSpawned = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item that players will equip when middle-clicking on this entity.
|
||||
*/
|
||||
public function getPickedItem() : ?Item{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags the entity to be removed from the world on the next tick.
|
||||
*/
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\data\bedrock\PotionTypeIdMap;
|
||||
use pocketmine\data\bedrock\PotionTypeIds;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\EntityDataHelper as Helper;
|
||||
use pocketmine\entity\object\EndCrystal;
|
||||
use pocketmine\entity\object\ExperienceOrb;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\entity\object\ItemEntity;
|
||||
@ -42,6 +43,7 @@ use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\entity\projectile\Egg;
|
||||
use pocketmine\entity\projectile\EnderPearl;
|
||||
use pocketmine\entity\projectile\ExperienceBottle;
|
||||
use pocketmine\entity\projectile\IceBomb;
|
||||
use pocketmine\entity\projectile\Snowball;
|
||||
use pocketmine\entity\projectile\SplashPotion;
|
||||
use pocketmine\item\Item;
|
||||
@ -92,6 +94,10 @@ final class EntityFactory{
|
||||
return new Egg(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['Egg', 'minecraft:egg']);
|
||||
|
||||
$this->register(EndCrystal::class, function(World $world, CompoundTag $nbt) : EndCrystal{
|
||||
return new EndCrystal(Helper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['EnderCrystal', 'minecraft:ender_crystal']);
|
||||
|
||||
$this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{
|
||||
return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['ThrownEnderpearl', 'minecraft:ender_pearl']);
|
||||
@ -115,6 +121,10 @@ final class EntityFactory{
|
||||
return new FallingBlock(Helper::parseLocation($nbt, $world), FallingBlock::parseBlockNBT(RuntimeBlockStateRegistry::getInstance(), $nbt), $nbt);
|
||||
}, ['FallingSand', 'minecraft:falling_block']);
|
||||
|
||||
$this->register(IceBomb::class, function(World $world, CompoundTag $nbt) : IceBomb{
|
||||
return new IceBomb(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['minecraft:ice_bomb']);
|
||||
|
||||
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
|
||||
$itemTag = $nbt->getCompoundTag(ItemEntity::TAG_ITEM);
|
||||
if($itemTag === null){
|
||||
|
@ -34,6 +34,7 @@ interface FoodSource extends Consumable{
|
||||
|
||||
/**
|
||||
* Returns whether a Human eating this FoodSource must have a non-full hunger bar.
|
||||
* This is ignored in creative mode and in peaceful difficulty.
|
||||
*/
|
||||
public function requiresHunger() : bool;
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\TotemUseSound;
|
||||
use pocketmine\world\World;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use function array_fill;
|
||||
@ -189,8 +190,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
return $this->hungerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the Human can eat food. This may return a different result than {@link HungerManager::isHungry()},
|
||||
* as HungerManager only handles the hunger bar.
|
||||
*/
|
||||
public function canEat() : bool{
|
||||
return $this->hungerManager->isHungry() || $this->getWorld()->getDifficulty() === World::DIFFICULTY_PEACEFUL;
|
||||
}
|
||||
|
||||
public function consumeObject(Consumable $consumable) : bool{
|
||||
if($consumable instanceof FoodSource && $consumable->requiresHunger() && !$this->hungerManager->isHungry()){
|
||||
if($consumable instanceof FoodSource && $consumable->requiresHunger() && !$this->canEat()){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,8 @@ class HungerManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Human may consume objects requiring hunger.
|
||||
* Returns whether the food level is below the maximum.
|
||||
* This doesn't decide if the entity can eat food. Use {@link Human::canEat()} for that.
|
||||
*/
|
||||
public function isHungry() : bool{
|
||||
return $this->getFood() < $this->getMaxFood();
|
||||
|
@ -25,6 +25,8 @@ namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\block\Water;
|
||||
use pocketmine\data\bedrock\EffectIdMap;
|
||||
use pocketmine\entity\animation\DeathAnimation;
|
||||
use pocketmine\entity\animation\HurtAnimation;
|
||||
@ -44,6 +46,7 @@ use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\math\VoxelRayTrace;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -58,18 +61,19 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\sound\BurpSound;
|
||||
use pocketmine\world\sound\EntityLandSound;
|
||||
use pocketmine\world\sound\EntityLongFallSound;
|
||||
use pocketmine\world\sound\EntityShortFallSound;
|
||||
use pocketmine\world\sound\ItemBreakSound;
|
||||
use function abs;
|
||||
use function array_shift;
|
||||
use function atan2;
|
||||
use function ceil;
|
||||
use function count;
|
||||
use function floor;
|
||||
use function ksort;
|
||||
use function lcg_value;
|
||||
use function max;
|
||||
use function min;
|
||||
use function mt_getrandmax;
|
||||
@ -128,6 +132,8 @@ abstract class Living extends Entity{
|
||||
protected bool $gliding = false;
|
||||
protected bool $swimming = false;
|
||||
|
||||
private ?int $frostWalkerLevel = null;
|
||||
|
||||
protected function getInitialDragMultiplier() : float{ return 0.02; }
|
||||
|
||||
protected function getInitialGravity() : float{ return 0.08; }
|
||||
@ -151,6 +157,14 @@ abstract class Living extends Entity{
|
||||
$this->getViewers(),
|
||||
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this)
|
||||
)));
|
||||
$this->armorInventory->getListeners()->add(new CallbackInventoryListener(
|
||||
onSlotChange: function(Inventory $inventory, int $slot) : void{
|
||||
if($slot === ArmorInventory::SLOT_FEET){
|
||||
$this->frostWalkerLevel = null;
|
||||
}
|
||||
},
|
||||
onContentChange: function() : void{ $this->frostWalkerLevel = null; }
|
||||
));
|
||||
|
||||
$health = $this->getMaxHealth();
|
||||
|
||||
@ -490,7 +504,7 @@ abstract class Living extends Entity{
|
||||
$helmet = $this->armorInventory->getHelmet();
|
||||
if($helmet instanceof Armor){
|
||||
$finalDamage = $source->getFinalDamage();
|
||||
$this->damageItem($helmet, (int) round($finalDamage * 4 + lcg_value() * $finalDamage * 2));
|
||||
$this->damageItem($helmet, (int) round($finalDamage * 4 + Utils::getRandomFloat() * $finalDamage * 2));
|
||||
$this->armorInventory->setHelmet($helmet);
|
||||
}
|
||||
}
|
||||
@ -687,6 +701,47 @@ abstract class Living extends Entity{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function move(float $dx, float $dy, float $dz) : void{
|
||||
$oldX = $this->location->x;
|
||||
$oldZ = $this->location->z;
|
||||
|
||||
parent::move($dx, $dy, $dz);
|
||||
|
||||
$frostWalkerLevel = $this->getFrostWalkerLevel();
|
||||
if($frostWalkerLevel > 0 && (abs($this->location->x - $oldX) > self::MOTION_THRESHOLD || abs($this->location->z - $oldZ) > self::MOTION_THRESHOLD)){
|
||||
$this->applyFrostWalker($frostWalkerLevel);
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyFrostWalker(int $level) : void{
|
||||
$radius = $level + 2;
|
||||
$world = $this->getWorld();
|
||||
|
||||
$baseX = $this->location->getFloorX();
|
||||
$y = $this->location->getFloorY() - 1;
|
||||
$baseZ = $this->location->getFloorZ();
|
||||
|
||||
$frostedIce = VanillaBlocks::FROSTED_ICE();
|
||||
for($x = $baseX - $radius; $x <= $baseX + $radius; $x++){
|
||||
for($z = $baseZ - $radius; $z <= $baseZ + $radius; $z++){
|
||||
$block = $world->getBlockAt($x, $y, $z);
|
||||
if(
|
||||
!$block instanceof Water ||
|
||||
!$block->isSource() ||
|
||||
$world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR ||
|
||||
count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
$world->setBlockAt($x, $y, $z, $frostedIce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getFrostWalkerLevel() : int{
|
||||
return $this->frostWalkerLevel ??= $this->armorInventory->getBoots()->getEnchantmentLevel(VanillaEnchantments::FROST_WALKER());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water.
|
||||
*/
|
||||
@ -697,7 +752,7 @@ abstract class Living extends Entity{
|
||||
$this->setBreathing(false);
|
||||
|
||||
if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(VanillaEnchantments::RESPIRATION())) <= 0 ||
|
||||
lcg_value() <= (1 / ($respirationLevel + 1))
|
||||
Utils::getRandomFloat() <= (1 / ($respirationLevel + 1))
|
||||
){
|
||||
$ticks -= $tickDiff;
|
||||
if($ticks <= -20){
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\entity;
|
||||
use pocketmine\entity\animation\SquidInkCloudAnimation;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -124,4 +125,8 @@ class Squid extends WaterAnimal{
|
||||
VanillaItems::INK_SAC()->setCount(mt_rand(1, 3))
|
||||
];
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::SQUID_SPAWN_EGG();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||
@ -87,6 +89,10 @@ class Villager extends Living implements Ageable{
|
||||
return $this->baby;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::VILLAGER_SPAWN_EGG();
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby);
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use function mt_rand;
|
||||
@ -65,4 +66,8 @@ class Zombie extends Living{
|
||||
//TODO: check for equipment and whether it's a baby
|
||||
return 5;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::ZOMBIE_SPAWN_EGG();
|
||||
}
|
||||
}
|
||||
|
146
src/entity/object/EndCrystal.php
Normal file
146
src/entity/object/EndCrystal.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?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\entity\object;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntitySizeInfo;
|
||||
use pocketmine\entity\Explosive;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityPreExplodeEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||
use pocketmine\world\Explosion;
|
||||
|
||||
class EndCrystal extends Entity implements Explosive{
|
||||
private const TAG_SHOWBASE = "ShowBottom"; //TAG_Byte
|
||||
|
||||
private const TAG_BLOCKTARGET_X = "BlockTargetX"; //TAG_Int
|
||||
private const TAG_BLOCKTARGET_Y = "BlockTargetY"; //TAG_Int
|
||||
private const TAG_BLOCKTARGET_Z = "BlockTargetZ"; //TAG_Int
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::ENDER_CRYSTAL; }
|
||||
|
||||
protected bool $showBase = false;
|
||||
protected ?Vector3 $beamTarget = null;
|
||||
|
||||
protected function getInitialSizeInfo() : EntitySizeInfo{ return new EntitySizeInfo(2.0, 2.0); }
|
||||
|
||||
protected function getInitialDragMultiplier() : float{ return 1.0; }
|
||||
|
||||
protected function getInitialGravity() : float{ return 0.0; }
|
||||
|
||||
public function isFireProof() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::END_CRYSTAL();
|
||||
}
|
||||
|
||||
public function showBase() : bool{
|
||||
return $this->showBase;
|
||||
}
|
||||
|
||||
public function setShowBase(bool $showBase) : void{
|
||||
$this->showBase = $showBase;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function getBeamTarget() : ?Vector3{
|
||||
return $this->beamTarget;
|
||||
}
|
||||
|
||||
public function setBeamTarget(?Vector3 $beamTarget) : void{
|
||||
$this->beamTarget = $beamTarget;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function attack(EntityDamageEvent $source) : void{
|
||||
parent::attack($source);
|
||||
if(
|
||||
$source->getCause() !== EntityDamageEvent::CAUSE_VOID &&
|
||||
!$this->isFlaggedForDespawn() &&
|
||||
!$source->isCancelled()
|
||||
){
|
||||
$this->flagForDespawn();
|
||||
$this->explode();
|
||||
}
|
||||
}
|
||||
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
$this->setMaxHealth(1);
|
||||
$this->setHealth(1);
|
||||
|
||||
$this->setShowBase($nbt->getByte(self::TAG_SHOWBASE, 0) === 1);
|
||||
|
||||
if(
|
||||
($beamXTag = $nbt->getTag(self::TAG_BLOCKTARGET_X)) instanceof IntTag &&
|
||||
($beamYTag = $nbt->getTag(self::TAG_BLOCKTARGET_Y)) instanceof IntTag &&
|
||||
($beamZTag = $nbt->getTag(self::TAG_BLOCKTARGET_Z)) instanceof IntTag
|
||||
){
|
||||
$this->setBeamTarget(new Vector3($beamXTag->getValue(), $beamYTag->getValue(), $beamZTag->getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
|
||||
$nbt->setByte(self::TAG_SHOWBASE, $this->showBase ? 1 : 0);
|
||||
if($this->beamTarget !== null){
|
||||
$nbt->setInt(self::TAG_BLOCKTARGET_X, $this->beamTarget->getFloorX());
|
||||
$nbt->setInt(self::TAG_BLOCKTARGET_Y, $this->beamTarget->getFloorY());
|
||||
$nbt->setInt(self::TAG_BLOCKTARGET_Z, $this->beamTarget->getFloorZ());
|
||||
}
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
public function explode() : void{
|
||||
$ev = new EntityPreExplodeEvent($this, 6);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this);
|
||||
if($ev->isBlockBreaking()){
|
||||
$explosion->explodeA();
|
||||
}
|
||||
$explosion->explodeB();
|
||||
}
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
|
||||
$properties->setGenericFlag(EntityMetadataFlags::SHOWBASE, $this->showBase);
|
||||
$properties->setBlockPos(EntityMetadataProperties::BLOCK_TARGET, BlockPosition::fromVector3($this->beamTarget ?? Vector3::zero()));
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ use pocketmine\entity\Location;
|
||||
use pocketmine\event\entity\EntityBlockChangeEvent;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -194,6 +195,10 @@ class FallingBlock extends Entity{
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return $this->block->asItem();
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntitySizeInfo;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -165,6 +166,10 @@ class Painting extends Entity{
|
||||
));
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::PAINTING();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the painting motive (which image is displayed on the painting)
|
||||
*/
|
||||
|
@ -23,11 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity\object;
|
||||
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntitySizeInfo;
|
||||
use pocketmine\entity\Explosive;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityPreExplodeEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
@ -127,6 +129,10 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
}
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaBlocks::TNT()->setWorksUnderwater($this->worksUnderwater)->asItem();
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
|
||||
|
86
src/entity/projectile/IceBomb.php
Normal file
86
src/entity/projectile/IceBomb.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?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\entity\projectile;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\event\entity\ProjectileHitEvent;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\world\particle\ItemBreakParticle;
|
||||
use pocketmine\world\sound\IceBombHitSound;
|
||||
|
||||
class IceBomb extends Throwable{
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::ICE_BOMB; }
|
||||
|
||||
public function getResultDamage() : int{
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected function calculateInterceptWithBlock(Block $block, Vector3 $start, Vector3 $end) : ?RayTraceResult{
|
||||
if($block->getTypeId() === BlockTypeIds::WATER){
|
||||
$pos = $block->getPosition();
|
||||
|
||||
return AxisAlignedBB::one()->offset($pos->x, $pos->y, $pos->z)->calculateIntercept($start, $end);
|
||||
}
|
||||
|
||||
return parent::calculateInterceptWithBlock($block, $start, $end);
|
||||
}
|
||||
|
||||
protected function onHit(ProjectileHitEvent $event) : void{
|
||||
$world = $this->getWorld();
|
||||
$pos = $this->location;
|
||||
|
||||
$world->addSound($pos, new IceBombHitSound());
|
||||
$itemBreakParticle = new ItemBreakParticle(VanillaItems::ICE_BOMB());
|
||||
for($i = 0; $i < 6; ++$i){
|
||||
$world->addParticle($pos, $itemBreakParticle);
|
||||
}
|
||||
}
|
||||
|
||||
protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{
|
||||
parent::onHitBlock($blockHit, $hitResult);
|
||||
|
||||
$pos = $blockHit->getPosition();
|
||||
$world = $pos->getWorld();
|
||||
$posX = $pos->getFloorX();
|
||||
$posY = $pos->getFloorY();
|
||||
$posZ = $pos->getFloorZ();
|
||||
|
||||
$ice = VanillaBlocks::ICE();
|
||||
for($x = $posX - 1; $x <= $posX + 1; $x++){
|
||||
for($y = $posY - 1; $y <= $posY + 1; $y++){
|
||||
for($z = $posZ - 1; $z <= $posZ + 1; $z++){
|
||||
if($world->getBlockAt($x, $y, $z)->getTypeId() === BlockTypeIds::WATER){
|
||||
$world->setBlockAt($x, $y, $z, $ice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\entity\object\EndCrystal;
|
||||
use pocketmine\event\entity\EntityCombustByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageByChildEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
@ -96,7 +97,7 @@ abstract class Projectile extends Entity{
|
||||
}
|
||||
|
||||
public function canCollideWith(Entity $entity) : bool{
|
||||
return $entity instanceof Living && !$this->onGround;
|
||||
return ($entity instanceof Living || $entity instanceof EndCrystal) && !$this->onGround;
|
||||
}
|
||||
|
||||
public function canBeCollidedWith() : bool{
|
||||
|
53
src/event/player/PlayerEntityPickEvent.php
Normal file
53
src/event/player/PlayerEntityPickEvent.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Called when a player middle-clicks on an entity to get an item in creative mode.
|
||||
*/
|
||||
class PlayerEntityPickEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
Player $player,
|
||||
private Entity $entityClicked,
|
||||
private Item $resultItem
|
||||
){
|
||||
$this->player = $player;
|
||||
}
|
||||
|
||||
public function getEntity() : Entity{
|
||||
return $this->entityClicked;
|
||||
}
|
||||
|
||||
public function getResultItem() : Item{
|
||||
return $this->resultItem;
|
||||
}
|
||||
}
|
@ -42,6 +42,9 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
|
||||
|
||||
protected Vector3 $touchVector;
|
||||
|
||||
protected bool $useItem = true;
|
||||
protected bool $useBlock = true;
|
||||
|
||||
public function __construct(
|
||||
Player $player,
|
||||
protected Item $item,
|
||||
@ -73,4 +76,28 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
|
||||
public function getFace() : int{
|
||||
return $this->blockFace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the item may react to the interaction. If disabled, items such as spawn eggs will not activate.
|
||||
* This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking.
|
||||
*/
|
||||
public function useItem() : bool{ return $this->useItem; }
|
||||
|
||||
/**
|
||||
* Sets whether the used item may react to the interaction. If false, items such as spawn eggs will not activate.
|
||||
* This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking.
|
||||
*/
|
||||
public function setUseItem(bool $useItem) : void{ $this->useItem = $useItem; }
|
||||
|
||||
/**
|
||||
* Returns whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not
|
||||
* respond, containers will not open, etc.
|
||||
*/
|
||||
public function useBlock() : bool{ return $this->useBlock; }
|
||||
|
||||
/**
|
||||
* Sets whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not
|
||||
* respond, containers will not open, etc.
|
||||
*/
|
||||
public function setUseBlock(bool $useBlock) : void{ $this->useBlock = $useBlock; }
|
||||
}
|
||||
|
@ -111,6 +111,9 @@ class InventoryTransaction{
|
||||
if(!isset($this->actions[$hash = spl_object_id($action)])){
|
||||
$this->actions[$hash] = $action;
|
||||
$action->onAddToTransaction($this);
|
||||
if($action instanceof SlotChangeAction && !isset($this->inventories[$inventoryId = spl_object_id($action->getInventory())])){
|
||||
$this->inventories[$inventoryId] = $action->getInventory();
|
||||
}
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Tried to add the same action to a transaction twice");
|
||||
}
|
||||
@ -129,16 +132,6 @@ class InventoryTransaction{
|
||||
$this->actions = $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal This method should not be used by plugins, it's used to add tracked inventories for InventoryActions
|
||||
* involving inventories.
|
||||
*/
|
||||
public function addInventory(Inventory $inventory) : void{
|
||||
if(!isset($this->inventories[$hash = spl_object_id($inventory)])){
|
||||
$this->inventories[$hash] = $inventory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] $needItems
|
||||
* @param Item[] $haveItems
|
||||
|
@ -60,6 +60,7 @@ abstract class InventoryAction{
|
||||
|
||||
/**
|
||||
* Called when the action is added to the specified InventoryTransaction.
|
||||
* @deprecated
|
||||
*/
|
||||
public function onAddToTransaction(InventoryTransaction $transaction) : void{
|
||||
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\inventory\transaction\action;
|
||||
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\SlotValidatedInventory;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
@ -85,13 +84,6 @@ class SlotChangeAction extends InventoryAction{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this action's target inventory to the transaction's inventory list.
|
||||
*/
|
||||
public function onAddToTransaction(InventoryTransaction $transaction) : void{
|
||||
$transaction->addInventory($this->inventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item into the target inventory.
|
||||
*/
|
||||
|
@ -33,7 +33,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
use function lcg_value;
|
||||
use pocketmine\utils\Utils;
|
||||
use function mt_rand;
|
||||
|
||||
class Armor extends Durable{
|
||||
@ -129,7 +129,7 @@ class Armor extends Durable{
|
||||
|
||||
$chance = 1 / ($unbreakingLevel + 1);
|
||||
for($i = 0; $i < $amount; ++$i){
|
||||
if(mt_rand(1, 100) > 60 && lcg_value() > $chance){ //unbreaking only applies to armor 40% of the time at best
|
||||
if(mt_rand(1, 100) > 60 && Utils::getRandomFloat() > $chance){ //unbreaking only applies to armor 40% of the time at best
|
||||
$negated++;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\item;
|
||||
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use function lcg_value;
|
||||
use pocketmine\utils\Utils;
|
||||
use function min;
|
||||
|
||||
abstract class Durable extends Item{
|
||||
@ -87,7 +87,7 @@ abstract class Durable extends Item{
|
||||
|
||||
$chance = 1 / ($unbreakingLevel + 1);
|
||||
for($i = 0; $i < $amount; ++$i){
|
||||
if(lcg_value() > $chance){
|
||||
if(Utils::getRandomFloat() > $chance){
|
||||
$negated++;
|
||||
}
|
||||
}
|
||||
|
59
src/item/EndCrystal.php
Normal file
59
src/item/EndCrystal.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?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\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\entity\object\EndCrystal as EntityEndCrystal;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function count;
|
||||
|
||||
class EndCrystal extends Item{
|
||||
|
||||
public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
|
||||
if($blockClicked->getTypeId() === BlockTypeIds::OBSIDIAN || $blockClicked->getTypeId() === BlockTypeIds::BEDROCK){
|
||||
$pos = $blockClicked->getPosition();
|
||||
$world = $pos->getWorld();
|
||||
$bb = AxisAlignedBB::one()
|
||||
->offset($pos->getX(), $pos->getY(), $pos->getZ())
|
||||
->extend(Facing::UP, 1);
|
||||
if(
|
||||
count($world->getNearbyEntities($bb)) === 0 &&
|
||||
$blockClicked->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR &&
|
||||
$blockClicked->getSide(Facing::UP, 2)->getTypeId() === BlockTypeIds::AIR
|
||||
){
|
||||
$crystal = new EntityEndCrystal(Location::fromObject($pos->add(0.5, 1, 0.5), $world));
|
||||
$crystal->spawnToAll();
|
||||
|
||||
$this->pop();
|
||||
return ItemUseResult::SUCCESS;
|
||||
}
|
||||
}
|
||||
return ItemUseResult::NONE;
|
||||
}
|
||||
}
|
@ -44,6 +44,6 @@ abstract class Food extends Item implements FoodSourceItem{
|
||||
}
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool{
|
||||
return !$this->requiresHunger() || $player->getHungerManager()->isHungry();
|
||||
return !$this->requiresHunger() || $player->canEat();
|
||||
}
|
||||
}
|
||||
|
71
src/item/GoatHorn.php
Normal file
71
src/item/GoatHorn.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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\item;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\GoatHornSound;
|
||||
|
||||
class GoatHorn extends Item implements Releasable{
|
||||
|
||||
private GoatHornType $goatHornType = GoatHornType::PONDER;
|
||||
|
||||
protected function describeState(RuntimeDataDescriber $w) : void{
|
||||
$w->enum($this->goatHornType);
|
||||
}
|
||||
|
||||
public function getHornType() : GoatHornType{ return $this->goatHornType; }
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setHornType(GoatHornType $type) : self{
|
||||
$this->goatHornType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getCooldownTicks() : int{
|
||||
return 140;
|
||||
}
|
||||
|
||||
public function getCooldownTag() : ?string{
|
||||
return ItemCooldownTags::GOAT_HORN;
|
||||
}
|
||||
|
||||
public function canStartUsingItem(Player $player) : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{
|
||||
$position = $player->getPosition();
|
||||
$position->getWorld()->addSound($position, new GoatHornSound($this->goatHornType));
|
||||
|
||||
return ItemUseResult::SUCCESS;
|
||||
}
|
||||
}
|
36
src/item/GoatHornType.php
Normal file
36
src/item/GoatHornType.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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\item;
|
||||
|
||||
enum GoatHornType{
|
||||
|
||||
case PONDER;
|
||||
case SING;
|
||||
case SEEK;
|
||||
case FEEL;
|
||||
case ADMIRE;
|
||||
case CALL;
|
||||
case YEARN;
|
||||
case DREAM;
|
||||
}
|
48
src/item/IceBomb.php
Normal file
48
src/item/IceBomb.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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\item;
|
||||
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\entity\projectile\IceBomb as IceBombEntity;
|
||||
use pocketmine\entity\projectile\Throwable;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class IceBomb extends ProjectileItem{
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 16;
|
||||
}
|
||||
|
||||
protected function createEntity(Location $location, Player $thrower) : Throwable{
|
||||
return new IceBombEntity($location, $thrower);
|
||||
}
|
||||
|
||||
public function getThrowForce() : float{
|
||||
return 1.5;
|
||||
}
|
||||
|
||||
public function getCooldownTicks() : int{
|
||||
return 10;
|
||||
}
|
||||
}
|
@ -324,8 +324,12 @@ final class ItemTypeIds{
|
||||
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285;
|
||||
public const PITCHER_POD = 20286;
|
||||
public const NAME_TAG = 20287;
|
||||
public const GOAT_HORN = 20288;
|
||||
public const END_CRYSTAL = 20289;
|
||||
public const ICE_BOMB = 20290;
|
||||
public const RECOVERY_COMPASS = 20291;
|
||||
|
||||
public const FIRST_UNUSED_ITEM_ID = 20288;
|
||||
public const FIRST_UNUSED_ITEM_ID = 20292;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\item;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\BottleEmptySound;
|
||||
|
||||
class Potion extends Item implements ConsumableItem{
|
||||
|
||||
@ -50,7 +51,7 @@ class Potion extends Item implements ConsumableItem{
|
||||
}
|
||||
|
||||
public function onConsume(Living $consumer) : void{
|
||||
|
||||
$consumer->broadcastSound(new BottleEmptySound());
|
||||
}
|
||||
|
||||
public function getAdditionalEffects() : array{
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\effect\VanillaEffects;
|
||||
use function lcg_value;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class RottenFlesh extends Food{
|
||||
|
||||
@ -38,7 +38,7 @@ class RottenFlesh extends Food{
|
||||
}
|
||||
|
||||
public function getAdditionalEffects() : array{
|
||||
if(lcg_value() <= 0.8){
|
||||
if(Utils::getRandomFloat() <= 0.8){
|
||||
return [
|
||||
new EffectInstance(VanillaEffects::HUNGER(), 600)
|
||||
];
|
||||
|
@ -27,15 +27,15 @@ use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\World;
|
||||
use function lcg_value;
|
||||
|
||||
abstract class SpawnEgg extends Item{
|
||||
|
||||
abstract protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity;
|
||||
|
||||
public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
|
||||
$entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), lcg_value() * 360, 0);
|
||||
$entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), Utils::getRandomFloat() * 360, 0);
|
||||
|
||||
if($this->hasCustomName()){
|
||||
$entity->setNameTag($this->getCustomName());
|
||||
|
@ -1184,6 +1184,13 @@ final class StringToItemParser extends StringToTParser{
|
||||
|
||||
$result->register($prefix("dye"), fn() => Items::DYE()->setColor($color));
|
||||
}
|
||||
|
||||
foreach(GoatHornType::cases() as $goatHornType){
|
||||
$prefix = fn(string $name) => strtolower($goatHornType->name) . "_" . $name;
|
||||
|
||||
$result->register($prefix("goat_horn"), fn() => Items::GOAT_HORN()->setHornType($goatHornType));
|
||||
}
|
||||
|
||||
foreach(SuspiciousStewType::cases() as $suspiciousStewType){
|
||||
$prefix = fn(string $name) => strtolower($suspiciousStewType->name) . "_" . $name;
|
||||
|
||||
@ -1323,6 +1330,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("enchanted_book", fn() => Items::ENCHANTED_BOOK());
|
||||
$result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE());
|
||||
$result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE());
|
||||
$result->register("end_crystal", fn() => Items::END_CRYSTAL());
|
||||
$result->register("ender_pearl", fn() => Items::ENDER_PEARL());
|
||||
$result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE());
|
||||
$result->register("eye_armor_trim_smithing_template", fn() => Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
@ -1341,6 +1349,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("glow_berries", fn() => Items::GLOW_BERRIES());
|
||||
$result->register("glow_ink_sac", fn() => Items::GLOW_INK_SAC());
|
||||
$result->register("glowstone_dust", fn() => Items::GLOWSTONE_DUST());
|
||||
$result->register("goat_horn", fn() => Items::GOAT_HORN());
|
||||
$result->register("gold_axe", fn() => Items::GOLDEN_AXE());
|
||||
$result->register("gold_boots", fn() => Items::GOLDEN_BOOTS());
|
||||
$result->register("gold_chestplate", fn() => Items::GOLDEN_CHESTPLATE());
|
||||
@ -1369,6 +1378,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("honey_bottle", fn() => Items::HONEY_BOTTLE());
|
||||
$result->register("host_armor_trim_smithing_template", fn() => Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("honeycomb", fn() => Items::HONEYCOMB());
|
||||
$result->register("ice_bomb", fn() => Items::ICE_BOMB());
|
||||
$result->register("ink_sac", fn() => Items::INK_SAC());
|
||||
$result->register("iron_axe", fn() => Items::IRON_AXE());
|
||||
$result->register("iron_boots", fn() => Items::IRON_BOOTS());
|
||||
@ -1471,6 +1481,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("record_strad", fn() => Items::RECORD_STRAD());
|
||||
$result->register("record_wait", fn() => Items::RECORD_WAIT());
|
||||
$result->register("record_ward", fn() => Items::RECORD_WARD());
|
||||
$result->register("recovery_compass", fn() => Items::RECOVERY_COMPASS());
|
||||
$result->register("redstone", fn() => Items::REDSTONE_DUST());
|
||||
$result->register("redstone_dust", fn() => Items::REDSTONE_DUST());
|
||||
$result->register("rib_armor_trim_smithing_template", fn() => Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
|
@ -33,11 +33,12 @@ use pocketmine\entity\Zombie;
|
||||
use pocketmine\inventory\ArmorInventory;
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags;
|
||||
use pocketmine\item\ItemIdentifier as IID;
|
||||
use pocketmine\item\ItemTypeIds as Ids;
|
||||
use pocketmine\item\VanillaArmorMaterials as ArmorMaterials;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\utils\CloningRegistryTrait;
|
||||
use pocketmine\world\World;
|
||||
use function is_int;
|
||||
use function mb_strtoupper;
|
||||
use function strtolower;
|
||||
|
||||
/**
|
||||
@ -157,6 +158,7 @@ use function strtolower;
|
||||
* @method static EnchantedBook ENCHANTED_BOOK()
|
||||
* @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE()
|
||||
* @method static EnderPearl ENDER_PEARL()
|
||||
* @method static EndCrystal END_CRYSTAL()
|
||||
* @method static ExperienceBottle EXPERIENCE_BOTTLE()
|
||||
* @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Item FEATHER()
|
||||
@ -171,6 +173,7 @@ use function strtolower;
|
||||
* @method static Item GLOWSTONE_DUST()
|
||||
* @method static GlowBerries GLOW_BERRIES()
|
||||
* @method static Item GLOW_INK_SAC()
|
||||
* @method static GoatHorn GOAT_HORN()
|
||||
* @method static GoldenApple GOLDEN_APPLE()
|
||||
* @method static Axe GOLDEN_AXE()
|
||||
* @method static Armor GOLDEN_BOOTS()
|
||||
@ -189,6 +192,7 @@ use function strtolower;
|
||||
* @method static Item HONEYCOMB()
|
||||
* @method static HoneyBottle HONEY_BOTTLE()
|
||||
* @method static Item HOST_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static IceBomb ICE_BOMB()
|
||||
* @method static Item INK_SAC()
|
||||
* @method static Axe IRON_AXE()
|
||||
* @method static Armor IRON_BOOTS()
|
||||
@ -280,6 +284,7 @@ use function strtolower;
|
||||
* @method static Record RECORD_STRAD()
|
||||
* @method static Record RECORD_WAIT()
|
||||
* @method static Record RECORD_WARD()
|
||||
* @method static Item RECOVERY_COMPASS()
|
||||
* @method static Redstone REDSTONE_DUST()
|
||||
* @method static Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static RottenFlesh ROTTEN_FLESH()
|
||||
@ -339,8 +344,29 @@ final class VanillaItems{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
protected static function register(string $name, Item $item) : void{
|
||||
/**
|
||||
* @phpstan-template TItem of Item
|
||||
* @phpstan-param \Closure(IID) : TItem $createItem
|
||||
* @phpstan-return TItem
|
||||
*/
|
||||
protected static function register(string $name, \Closure $createItem) : Item{
|
||||
//this sketchy hack allows us to avoid manually writing the constants inline
|
||||
//since type IDs are generated from this class anyway, I'm OK with this hack
|
||||
//nonetheless, we should try to get rid of it in a future major version (e.g by using string type IDs)
|
||||
$reflect = new \ReflectionClass(ItemTypeIds::class);
|
||||
$typeId = $reflect->getConstant(mb_strtoupper($name));
|
||||
if(!is_int($typeId)){
|
||||
//this allows registering new stuff without adding new type ID constants
|
||||
//this reduces the number of mandatory steps to test new features in local development
|
||||
\GlobalLogger::get()->error(self::class . ": No constant type ID found for $name, generating a new one");
|
||||
$typeId = ItemTypeIds::newId();
|
||||
}
|
||||
|
||||
$item = $createItem(new IID($typeId));
|
||||
|
||||
self::_registryRegister($name, $item);
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -360,242 +386,240 @@ final class VanillaItems{
|
||||
self::registerTierToolItems();
|
||||
self::registerSmithingTemplates();
|
||||
|
||||
self::register("air", Blocks::AIR()->asItem()->setCount(0));
|
||||
//this doesn't use the regular register() because it doesn't have an item typeID
|
||||
//in the future we'll probably want to dissociate this from the air block and make a proper null item
|
||||
self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0));
|
||||
|
||||
self::register("acacia_sign", new ItemBlockWallOrFloor(new IID(Ids::ACACIA_SIGN), Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
|
||||
self::register("amethyst_shard", new Item(new IID(Ids::AMETHYST_SHARD), "Amethyst Shard"));
|
||||
self::register("apple", new Apple(new IID(Ids::APPLE), "Apple"));
|
||||
self::register("arrow", new Arrow(new IID(Ids::ARROW), "Arrow"));
|
||||
self::register("baked_potato", new BakedPotato(new IID(Ids::BAKED_POTATO), "Baked Potato"));
|
||||
self::register("bamboo", new Bamboo(new IID(Ids::BAMBOO), "Bamboo"));
|
||||
self::register("banner", new Banner(new IID(Ids::BANNER), Blocks::BANNER(), Blocks::WALL_BANNER()));
|
||||
self::register("beetroot", new Beetroot(new IID(Ids::BEETROOT), "Beetroot"));
|
||||
self::register("beetroot_seeds", new BeetrootSeeds(new IID(Ids::BEETROOT_SEEDS), "Beetroot Seeds"));
|
||||
self::register("beetroot_soup", new BeetrootSoup(new IID(Ids::BEETROOT_SOUP), "Beetroot Soup"));
|
||||
self::register("birch_sign", new ItemBlockWallOrFloor(new IID(Ids::BIRCH_SIGN), Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN()));
|
||||
self::register("blaze_powder", new Item(new IID(Ids::BLAZE_POWDER), "Blaze Powder"));
|
||||
self::register("blaze_rod", new BlazeRod(new IID(Ids::BLAZE_ROD), "Blaze Rod"));
|
||||
self::register("bleach", new Item(new IID(Ids::BLEACH), "Bleach"));
|
||||
self::register("bone", new Item(new IID(Ids::BONE), "Bone"));
|
||||
self::register("bone_meal", new Fertilizer(new IID(Ids::BONE_MEAL), "Bone Meal"));
|
||||
self::register("book", new Book(new IID(Ids::BOOK), "Book", [EnchantmentTags::ALL]));
|
||||
self::register("bow", new Bow(new IID(Ids::BOW), "Bow", [EnchantmentTags::BOW]));
|
||||
self::register("bowl", new Bowl(new IID(Ids::BOWL), "Bowl"));
|
||||
self::register("bread", new Bread(new IID(Ids::BREAD), "Bread"));
|
||||
self::register("brick", new Item(new IID(Ids::BRICK), "Brick"));
|
||||
self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket"));
|
||||
self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot"));
|
||||
self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal"));
|
||||
self::register("cherry_sign", new ItemBlockWallOrFloor(new IID(Ids::CHERRY_SIGN), Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN()));
|
||||
self::register("chemical_aluminium_oxide", new Item(new IID(Ids::CHEMICAL_ALUMINIUM_OXIDE), "Aluminium Oxide"));
|
||||
self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia"));
|
||||
self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate"));
|
||||
self::register("chemical_benzene", new Item(new IID(Ids::CHEMICAL_BENZENE), "Benzene"));
|
||||
self::register("chemical_boron_trioxide", new Item(new IID(Ids::CHEMICAL_BORON_TRIOXIDE), "Boron Trioxide"));
|
||||
self::register("chemical_calcium_bromide", new Item(new IID(Ids::CHEMICAL_CALCIUM_BROMIDE), "Calcium Bromide"));
|
||||
self::register("chemical_calcium_chloride", new Item(new IID(Ids::CHEMICAL_CALCIUM_CHLORIDE), "Calcium Chloride"));
|
||||
self::register("chemical_cerium_chloride", new Item(new IID(Ids::CHEMICAL_CERIUM_CHLORIDE), "Cerium Chloride"));
|
||||
self::register("chemical_charcoal", new Item(new IID(Ids::CHEMICAL_CHARCOAL), "Charcoal"));
|
||||
self::register("chemical_crude_oil", new Item(new IID(Ids::CHEMICAL_CRUDE_OIL), "Crude Oil"));
|
||||
self::register("chemical_glue", new Item(new IID(Ids::CHEMICAL_GLUE), "Glue"));
|
||||
self::register("chemical_hydrogen_peroxide", new Item(new IID(Ids::CHEMICAL_HYDROGEN_PEROXIDE), "Hydrogen Peroxide"));
|
||||
self::register("chemical_hypochlorite", new Item(new IID(Ids::CHEMICAL_HYPOCHLORITE), "Hypochlorite"));
|
||||
self::register("chemical_ink", new Item(new IID(Ids::CHEMICAL_INK), "Ink"));
|
||||
self::register("chemical_iron_sulphide", new Item(new IID(Ids::CHEMICAL_IRON_SULPHIDE), "Iron Sulphide"));
|
||||
self::register("chemical_latex", new Item(new IID(Ids::CHEMICAL_LATEX), "Latex"));
|
||||
self::register("chemical_lithium_hydride", new Item(new IID(Ids::CHEMICAL_LITHIUM_HYDRIDE), "Lithium Hydride"));
|
||||
self::register("chemical_luminol", new Item(new IID(Ids::CHEMICAL_LUMINOL), "Luminol"));
|
||||
self::register("chemical_magnesium_nitrate", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_NITRATE), "Magnesium Nitrate"));
|
||||
self::register("chemical_magnesium_oxide", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_OXIDE), "Magnesium Oxide"));
|
||||
self::register("chemical_magnesium_salts", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_SALTS), "Magnesium Salts"));
|
||||
self::register("chemical_mercuric_chloride", new Item(new IID(Ids::CHEMICAL_MERCURIC_CHLORIDE), "Mercuric Chloride"));
|
||||
self::register("chemical_polyethylene", new Item(new IID(Ids::CHEMICAL_POLYETHYLENE), "Polyethylene"));
|
||||
self::register("chemical_potassium_chloride", new Item(new IID(Ids::CHEMICAL_POTASSIUM_CHLORIDE), "Potassium Chloride"));
|
||||
self::register("chemical_potassium_iodide", new Item(new IID(Ids::CHEMICAL_POTASSIUM_IODIDE), "Potassium Iodide"));
|
||||
self::register("chemical_rubbish", new Item(new IID(Ids::CHEMICAL_RUBBISH), "Rubbish"));
|
||||
self::register("chemical_salt", new Item(new IID(Ids::CHEMICAL_SALT), "Salt"));
|
||||
self::register("chemical_soap", new Item(new IID(Ids::CHEMICAL_SOAP), "Soap"));
|
||||
self::register("chemical_sodium_acetate", new Item(new IID(Ids::CHEMICAL_SODIUM_ACETATE), "Sodium Acetate"));
|
||||
self::register("chemical_sodium_fluoride", new Item(new IID(Ids::CHEMICAL_SODIUM_FLUORIDE), "Sodium Fluoride"));
|
||||
self::register("chemical_sodium_hydride", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDRIDE), "Sodium Hydride"));
|
||||
self::register("chemical_sodium_hydroxide", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDROXIDE), "Sodium Hydroxide"));
|
||||
self::register("chemical_sodium_hypochlorite", new Item(new IID(Ids::CHEMICAL_SODIUM_HYPOCHLORITE), "Sodium Hypochlorite"));
|
||||
self::register("chemical_sodium_oxide", new Item(new IID(Ids::CHEMICAL_SODIUM_OXIDE), "Sodium Oxide"));
|
||||
self::register("chemical_sugar", new Item(new IID(Ids::CHEMICAL_SUGAR), "Sugar"));
|
||||
self::register("chemical_sulphate", new Item(new IID(Ids::CHEMICAL_SULPHATE), "Sulphate"));
|
||||
self::register("chemical_tungsten_chloride", new Item(new IID(Ids::CHEMICAL_TUNGSTEN_CHLORIDE), "Tungsten Chloride"));
|
||||
self::register("chemical_water", new Item(new IID(Ids::CHEMICAL_WATER), "Water"));
|
||||
self::register("chorus_fruit", new ChorusFruit(new IID(Ids::CHORUS_FRUIT), "Chorus Fruit"));
|
||||
self::register("clay", new Item(new IID(Ids::CLAY), "Clay"));
|
||||
self::register("clock", new Clock(new IID(Ids::CLOCK), "Clock"));
|
||||
self::register("clownfish", new Clownfish(new IID(Ids::CLOWNFISH), "Clownfish"));
|
||||
self::register("coal", new Coal(new IID(Ids::COAL), "Coal"));
|
||||
self::register("cocoa_beans", new CocoaBeans(new IID(Ids::COCOA_BEANS), "Cocoa Beans"));
|
||||
self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass", [EnchantmentTags::COMPASS]));
|
||||
self::register("cooked_chicken", new CookedChicken(new IID(Ids::COOKED_CHICKEN), "Cooked Chicken"));
|
||||
self::register("cooked_fish", new CookedFish(new IID(Ids::COOKED_FISH), "Cooked Fish"));
|
||||
self::register("cooked_mutton", new CookedMutton(new IID(Ids::COOKED_MUTTON), "Cooked Mutton"));
|
||||
self::register("cooked_porkchop", new CookedPorkchop(new IID(Ids::COOKED_PORKCHOP), "Cooked Porkchop"));
|
||||
self::register("cooked_rabbit", new CookedRabbit(new IID(Ids::COOKED_RABBIT), "Cooked Rabbit"));
|
||||
self::register("cooked_salmon", new CookedSalmon(new IID(Ids::COOKED_SALMON), "Cooked Salmon"));
|
||||
self::register("cookie", new Cookie(new IID(Ids::COOKIE), "Cookie"));
|
||||
self::register("copper_ingot", new Item(new IID(Ids::COPPER_INGOT), "Copper Ingot"));
|
||||
self::register("coral_fan", new CoralFan(new IID(Ids::CORAL_FAN)));
|
||||
self::register("crimson_sign", new ItemBlockWallOrFloor(new IID(Ids::CRIMSON_SIGN), Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN()));
|
||||
self::register("dark_oak_sign", new ItemBlockWallOrFloor(new IID(Ids::DARK_OAK_SIGN), Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN()));
|
||||
self::register("diamond", new Item(new IID(Ids::DIAMOND), "Diamond"));
|
||||
self::register("disc_fragment_5", new Item(new IID(Ids::DISC_FRAGMENT_5), "Disc Fragment (5)"));
|
||||
self::register("dragon_breath", new Item(new IID(Ids::DRAGON_BREATH), "Dragon's Breath"));
|
||||
self::register("dried_kelp", new DriedKelp(new IID(Ids::DRIED_KELP), "Dried Kelp"));
|
||||
self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
|
||||
self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard"));
|
||||
self::register("apple", fn(IID $id) => new Apple($id, "Apple"));
|
||||
self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow"));
|
||||
self::register("baked_potato", fn(IID $id) => new BakedPotato($id, "Baked Potato"));
|
||||
self::register("bamboo", fn(IID $id) => new Bamboo($id, "Bamboo"));
|
||||
self::register("banner", fn(IID $id) => new Banner($id, Blocks::BANNER(), Blocks::WALL_BANNER()));
|
||||
self::register("beetroot", fn(IID $id) => new Beetroot($id, "Beetroot"));
|
||||
self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds"));
|
||||
self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup"));
|
||||
self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN()));
|
||||
self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder"));
|
||||
self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod"));
|
||||
self::register("bleach", fn(IID $id) => new Item($id, "Bleach"));
|
||||
self::register("bone", fn(IID $id) => new Item($id, "Bone"));
|
||||
self::register("bone_meal", fn(IID $id) => new Fertilizer($id, "Bone Meal"));
|
||||
self::register("book", fn(IID $id) => new Book($id, "Book", [EnchantmentTags::ALL]));
|
||||
self::register("bow", fn(IID $id) => new Bow($id, "Bow", [EnchantmentTags::BOW]));
|
||||
self::register("bowl", fn(IID $id) => new Bowl($id, "Bowl"));
|
||||
self::register("bread", fn(IID $id) => new Bread($id, "Bread"));
|
||||
self::register("brick", fn(IID $id) => new Item($id, "Brick"));
|
||||
self::register("bucket", fn(IID $id) => new Bucket($id, "Bucket"));
|
||||
self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot"));
|
||||
self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal"));
|
||||
self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN()));
|
||||
self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide"));
|
||||
self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia"));
|
||||
self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate"));
|
||||
self::register("chemical_benzene", fn(IID $id) => new Item($id, "Benzene"));
|
||||
self::register("chemical_boron_trioxide", fn(IID $id) => new Item($id, "Boron Trioxide"));
|
||||
self::register("chemical_calcium_bromide", fn(IID $id) => new Item($id, "Calcium Bromide"));
|
||||
self::register("chemical_calcium_chloride", fn(IID $id) => new Item($id, "Calcium Chloride"));
|
||||
self::register("chemical_cerium_chloride", fn(IID $id) => new Item($id, "Cerium Chloride"));
|
||||
self::register("chemical_charcoal", fn(IID $id) => new Item($id, "Charcoal"));
|
||||
self::register("chemical_crude_oil", fn(IID $id) => new Item($id, "Crude Oil"));
|
||||
self::register("chemical_glue", fn(IID $id) => new Item($id, "Glue"));
|
||||
self::register("chemical_hydrogen_peroxide", fn(IID $id) => new Item($id, "Hydrogen Peroxide"));
|
||||
self::register("chemical_hypochlorite", fn(IID $id) => new Item($id, "Hypochlorite"));
|
||||
self::register("chemical_ink", fn(IID $id) => new Item($id, "Ink"));
|
||||
self::register("chemical_iron_sulphide", fn(IID $id) => new Item($id, "Iron Sulphide"));
|
||||
self::register("chemical_latex", fn(IID $id) => new Item($id, "Latex"));
|
||||
self::register("chemical_lithium_hydride", fn(IID $id) => new Item($id, "Lithium Hydride"));
|
||||
self::register("chemical_luminol", fn(IID $id) => new Item($id, "Luminol"));
|
||||
self::register("chemical_magnesium_nitrate", fn(IID $id) => new Item($id, "Magnesium Nitrate"));
|
||||
self::register("chemical_magnesium_oxide", fn(IID $id) => new Item($id, "Magnesium Oxide"));
|
||||
self::register("chemical_magnesium_salts", fn(IID $id) => new Item($id, "Magnesium Salts"));
|
||||
self::register("chemical_mercuric_chloride", fn(IID $id) => new Item($id, "Mercuric Chloride"));
|
||||
self::register("chemical_polyethylene", fn(IID $id) => new Item($id, "Polyethylene"));
|
||||
self::register("chemical_potassium_chloride", fn(IID $id) => new Item($id, "Potassium Chloride"));
|
||||
self::register("chemical_potassium_iodide", fn(IID $id) => new Item($id, "Potassium Iodide"));
|
||||
self::register("chemical_rubbish", fn(IID $id) => new Item($id, "Rubbish"));
|
||||
self::register("chemical_salt", fn(IID $id) => new Item($id, "Salt"));
|
||||
self::register("chemical_soap", fn(IID $id) => new Item($id, "Soap"));
|
||||
self::register("chemical_sodium_acetate", fn(IID $id) => new Item($id, "Sodium Acetate"));
|
||||
self::register("chemical_sodium_fluoride", fn(IID $id) => new Item($id, "Sodium Fluoride"));
|
||||
self::register("chemical_sodium_hydride", fn(IID $id) => new Item($id, "Sodium Hydride"));
|
||||
self::register("chemical_sodium_hydroxide", fn(IID $id) => new Item($id, "Sodium Hydroxide"));
|
||||
self::register("chemical_sodium_hypochlorite", fn(IID $id) => new Item($id, "Sodium Hypochlorite"));
|
||||
self::register("chemical_sodium_oxide", fn(IID $id) => new Item($id, "Sodium Oxide"));
|
||||
self::register("chemical_sugar", fn(IID $id) => new Item($id, "Sugar"));
|
||||
self::register("chemical_sulphate", fn(IID $id) => new Item($id, "Sulphate"));
|
||||
self::register("chemical_tungsten_chloride", fn(IID $id) => new Item($id, "Tungsten Chloride"));
|
||||
self::register("chemical_water", fn(IID $id) => new Item($id, "Water"));
|
||||
self::register("chorus_fruit", fn(IID $id) => new ChorusFruit($id, "Chorus Fruit"));
|
||||
self::register("clay", fn(IID $id) => new Item($id, "Clay"));
|
||||
self::register("clock", fn(IID $id) => new Clock($id, "Clock"));
|
||||
self::register("clownfish", fn(IID $id) => new Clownfish($id, "Clownfish"));
|
||||
self::register("coal", fn(IID $id) => new Coal($id, "Coal"));
|
||||
self::register("cocoa_beans", fn(IID $id) => new CocoaBeans($id, "Cocoa Beans"));
|
||||
self::register("compass", fn(IID $id) => new Compass($id, "Compass", [EnchantmentTags::COMPASS]));
|
||||
self::register("cooked_chicken", fn(IID $id) => new CookedChicken($id, "Cooked Chicken"));
|
||||
self::register("cooked_fish", fn(IID $id) => new CookedFish($id, "Cooked Fish"));
|
||||
self::register("cooked_mutton", fn(IID $id) => new CookedMutton($id, "Cooked Mutton"));
|
||||
self::register("cooked_porkchop", fn(IID $id) => new CookedPorkchop($id, "Cooked Porkchop"));
|
||||
self::register("cooked_rabbit", fn(IID $id) => new CookedRabbit($id, "Cooked Rabbit"));
|
||||
self::register("cooked_salmon", fn(IID $id) => new CookedSalmon($id, "Cooked Salmon"));
|
||||
self::register("cookie", fn(IID $id) => new Cookie($id, "Cookie"));
|
||||
self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot"));
|
||||
self::register("coral_fan", fn(IID $id) => new CoralFan($id));
|
||||
self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN()));
|
||||
self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN()));
|
||||
self::register("diamond", fn(IID $id) => new Item($id, "Diamond"));
|
||||
self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)"));
|
||||
self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath"));
|
||||
self::register("dried_kelp", fn(IID $id) => new DriedKelp($id, "Dried Kelp"));
|
||||
//TODO: add interface to dye-colour objects
|
||||
self::register("dye", new Dye(new IID(Ids::DYE), "Dye"));
|
||||
self::register("echo_shard", new Item(new IID(Ids::ECHO_SHARD), "Echo Shard"));
|
||||
self::register("egg", new Egg(new IID(Ids::EGG), "Egg"));
|
||||
self::register("emerald", new Item(new IID(Ids::EMERALD), "Emerald"));
|
||||
self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book", [EnchantmentTags::ALL]));
|
||||
self::register("enchanted_golden_apple", new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE), "Enchanted Golden Apple"));
|
||||
self::register("ender_pearl", new EnderPearl(new IID(Ids::ENDER_PEARL), "Ender Pearl"));
|
||||
self::register("experience_bottle", new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE), "Bottle o' Enchanting"));
|
||||
self::register("feather", new Item(new IID(Ids::FEATHER), "Feather"));
|
||||
self::register("fermented_spider_eye", new Item(new IID(Ids::FERMENTED_SPIDER_EYE), "Fermented Spider Eye"));
|
||||
self::register("fire_charge", new FireCharge(new IID(Ids::FIRE_CHARGE), "Fire Charge"));
|
||||
self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod", [EnchantmentTags::FISHING_ROD]));
|
||||
self::register("flint", new Item(new IID(Ids::FLINT), "Flint"));
|
||||
self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL]));
|
||||
self::register("ghast_tear", new Item(new IID(Ids::GHAST_TEAR), "Ghast Tear"));
|
||||
self::register("glass_bottle", new GlassBottle(new IID(Ids::GLASS_BOTTLE), "Glass Bottle"));
|
||||
self::register("glistering_melon", new Item(new IID(Ids::GLISTERING_MELON), "Glistering Melon"));
|
||||
self::register("glow_berries", new GlowBerries(new IID(Ids::GLOW_BERRIES), "Glow Berries"));
|
||||
self::register("glow_ink_sac", new Item(new IID(Ids::GLOW_INK_SAC), "Glow Ink Sac"));
|
||||
self::register("glowstone_dust", new Item(new IID(Ids::GLOWSTONE_DUST), "Glowstone Dust"));
|
||||
self::register("gold_ingot", new Item(new IID(Ids::GOLD_INGOT), "Gold Ingot"));
|
||||
self::register("gold_nugget", new Item(new IID(Ids::GOLD_NUGGET), "Gold Nugget"));
|
||||
self::register("golden_apple", new GoldenApple(new IID(Ids::GOLDEN_APPLE), "Golden Apple"));
|
||||
self::register("golden_carrot", new GoldenCarrot(new IID(Ids::GOLDEN_CARROT), "Golden Carrot"));
|
||||
self::register("gunpowder", new Item(new IID(Ids::GUNPOWDER), "Gunpowder"));
|
||||
self::register("heart_of_the_sea", new Item(new IID(Ids::HEART_OF_THE_SEA), "Heart of the Sea"));
|
||||
self::register("honey_bottle", new HoneyBottle(new IID(Ids::HONEY_BOTTLE), "Honey Bottle"));
|
||||
self::register("honeycomb", new Item(new IID(Ids::HONEYCOMB), "Honeycomb"));
|
||||
self::register("ink_sac", new Item(new IID(Ids::INK_SAC), "Ink Sac"));
|
||||
self::register("iron_ingot", new Item(new IID(Ids::IRON_INGOT), "Iron Ingot"));
|
||||
self::register("iron_nugget", new Item(new IID(Ids::IRON_NUGGET), "Iron Nugget"));
|
||||
self::register("jungle_sign", new ItemBlockWallOrFloor(new IID(Ids::JUNGLE_SIGN), Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN()));
|
||||
self::register("lapis_lazuli", new Item(new IID(Ids::LAPIS_LAZULI), "Lapis Lazuli"));
|
||||
self::register("lava_bucket", new LiquidBucket(new IID(Ids::LAVA_BUCKET), "Lava Bucket", Blocks::LAVA()));
|
||||
self::register("leather", new Item(new IID(Ids::LEATHER), "Leather"));
|
||||
self::register("magma_cream", new Item(new IID(Ids::MAGMA_CREAM), "Magma Cream"));
|
||||
self::register("mangrove_sign", new ItemBlockWallOrFloor(new IID(Ids::MANGROVE_SIGN), Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN()));
|
||||
self::register("medicine", new Medicine(new IID(Ids::MEDICINE), "Medicine"));
|
||||
self::register("melon", new Melon(new IID(Ids::MELON), "Melon"));
|
||||
self::register("melon_seeds", new MelonSeeds(new IID(Ids::MELON_SEEDS), "Melon Seeds"));
|
||||
self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket"));
|
||||
self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart"));
|
||||
self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew"));
|
||||
self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag"));
|
||||
self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell"));
|
||||
self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick"));
|
||||
self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz"));
|
||||
self::register("nether_star", new Item(new IID(Ids::NETHER_STAR), "Nether Star"));
|
||||
self::register("netherite_ingot", new class(new IID(Ids::NETHERITE_INGOT), "Netherite Ingot") extends Item{
|
||||
self::register("dye", fn(IID $id) => new Dye($id, "Dye"));
|
||||
self::register("echo_shard", fn(IID $id) => new Item($id, "Echo Shard"));
|
||||
self::register("egg", fn(IID $id) => new Egg($id, "Egg"));
|
||||
self::register("emerald", fn(IID $id) => new Item($id, "Emerald"));
|
||||
self::register("enchanted_book", fn(IID $id) => new EnchantedBook($id, "Enchanted Book", [EnchantmentTags::ALL]));
|
||||
self::register("enchanted_golden_apple", fn(IID $id) => new GoldenAppleEnchanted($id, "Enchanted Golden Apple"));
|
||||
self::register("end_crystal", fn(IID $id) => new EndCrystal($id, "End Crystal"));
|
||||
self::register("ender_pearl", fn(IID $id) => new EnderPearl($id, "Ender Pearl"));
|
||||
self::register("experience_bottle", fn(IID $id) => new ExperienceBottle($id, "Bottle o' Enchanting"));
|
||||
self::register("feather", fn(IID $id) => new Item($id, "Feather"));
|
||||
self::register("fermented_spider_eye", fn(IID $id) => new Item($id, "Fermented Spider Eye"));
|
||||
self::register("fire_charge", fn(IID $id) => new FireCharge($id, "Fire Charge"));
|
||||
self::register("fishing_rod", fn(IID $id) => new FishingRod($id, "Fishing Rod", [EnchantmentTags::FISHING_ROD]));
|
||||
self::register("flint", fn(IID $id) => new Item($id, "Flint"));
|
||||
self::register("flint_and_steel", fn(IID $id) => new FlintSteel($id, "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL]));
|
||||
self::register("ghast_tear", fn(IID $id) => new Item($id, "Ghast Tear"));
|
||||
self::register("glass_bottle", fn(IID $id) => new GlassBottle($id, "Glass Bottle"));
|
||||
self::register("glistering_melon", fn(IID $id) => new Item($id, "Glistering Melon"));
|
||||
self::register("glow_berries", fn(IID $id) => new GlowBerries($id, "Glow Berries"));
|
||||
self::register("glow_ink_sac", fn(IID $id) => new Item($id, "Glow Ink Sac"));
|
||||
self::register("glowstone_dust", fn(IID $id) => new Item($id, "Glowstone Dust"));
|
||||
self::register("goat_horn", fn(IID $id) => new GoatHorn($id, "Goat Horn"));
|
||||
self::register("gold_ingot", fn(IID $id) => new Item($id, "Gold Ingot"));
|
||||
self::register("gold_nugget", fn(IID $id) => new Item($id, "Gold Nugget"));
|
||||
self::register("golden_apple", fn(IID $id) => new GoldenApple($id, "Golden Apple"));
|
||||
self::register("golden_carrot", fn(IID $id) => new GoldenCarrot($id, "Golden Carrot"));
|
||||
self::register("gunpowder", fn(IID $id) => new Item($id, "Gunpowder"));
|
||||
self::register("heart_of_the_sea", fn(IID $id) => new Item($id, "Heart of the Sea"));
|
||||
self::register("honey_bottle", fn(IID $id) => new HoneyBottle($id, "Honey Bottle"));
|
||||
self::register("honeycomb", fn(IID $id) => new Item($id, "Honeycomb"));
|
||||
self::register("ice_bomb", fn(IID $id) => new IceBomb($id, "Ice Bomb"));
|
||||
self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac"));
|
||||
self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot"));
|
||||
self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget"));
|
||||
self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN()));
|
||||
self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli"));
|
||||
self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA()));
|
||||
self::register("leather", fn(IID $id) => new Item($id, "Leather"));
|
||||
self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream"));
|
||||
self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN()));
|
||||
self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine"));
|
||||
self::register("melon", fn(IID $id) => new Melon($id, "Melon"));
|
||||
self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds"));
|
||||
self::register("milk_bucket", fn(IID $id) => new MilkBucket($id, "Milk Bucket"));
|
||||
self::register("minecart", fn(IID $id) => new Minecart($id, "Minecart"));
|
||||
self::register("mushroom_stew", fn(IID $id) => new MushroomStew($id, "Mushroom Stew"));
|
||||
self::register("name_tag", fn(IID $id) => new NameTag($id, "Name Tag"));
|
||||
self::register("nautilus_shell", fn(IID $id) => new Item($id, "Nautilus Shell"));
|
||||
self::register("nether_brick", fn(IID $id) => new Item($id, "Nether Brick"));
|
||||
self::register("nether_quartz", fn(IID $id) => new Item($id, "Nether Quartz"));
|
||||
self::register("nether_star", fn(IID $id) => new Item($id, "Nether Star"));
|
||||
self::register("netherite_ingot", fn(IID $id) => new class($id, "Netherite Ingot") extends Item{
|
||||
public function isFireProof() : bool{ return true; }
|
||||
});
|
||||
self::register("netherite_scrap", new class(new IID(Ids::NETHERITE_SCRAP), "Netherite Scrap") extends Item{
|
||||
self::register("netherite_scrap", fn(IID $id) => new class($id, "Netherite Scrap") extends Item{
|
||||
public function isFireProof() : bool{ return true; }
|
||||
});
|
||||
self::register("oak_sign", new ItemBlockWallOrFloor(new IID(Ids::OAK_SIGN), Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
|
||||
self::register("painting", new PaintingItem(new IID(Ids::PAINTING), "Painting"));
|
||||
self::register("paper", new Item(new IID(Ids::PAPER), "Paper"));
|
||||
self::register("phantom_membrane", new Item(new IID(Ids::PHANTOM_MEMBRANE), "Phantom Membrane"));
|
||||
self::register("pitcher_pod", new PitcherPod(new IID(Ids::PITCHER_POD), "Pitcher Pod"));
|
||||
self::register("poisonous_potato", new PoisonousPotato(new IID(Ids::POISONOUS_POTATO), "Poisonous Potato"));
|
||||
self::register("popped_chorus_fruit", new Item(new IID(Ids::POPPED_CHORUS_FRUIT), "Popped Chorus Fruit"));
|
||||
self::register("potato", new Potato(new IID(Ids::POTATO), "Potato"));
|
||||
self::register("potion", new Potion(new IID(Ids::POTION), "Potion"));
|
||||
self::register("prismarine_crystals", new Item(new IID(Ids::PRISMARINE_CRYSTALS), "Prismarine Crystals"));
|
||||
self::register("prismarine_shard", new Item(new IID(Ids::PRISMARINE_SHARD), "Prismarine Shard"));
|
||||
self::register("pufferfish", new Pufferfish(new IID(Ids::PUFFERFISH), "Pufferfish"));
|
||||
self::register("pumpkin_pie", new PumpkinPie(new IID(Ids::PUMPKIN_PIE), "Pumpkin Pie"));
|
||||
self::register("pumpkin_seeds", new PumpkinSeeds(new IID(Ids::PUMPKIN_SEEDS), "Pumpkin Seeds"));
|
||||
self::register("rabbit_foot", new Item(new IID(Ids::RABBIT_FOOT), "Rabbit's Foot"));
|
||||
self::register("rabbit_hide", new Item(new IID(Ids::RABBIT_HIDE), "Rabbit Hide"));
|
||||
self::register("rabbit_stew", new RabbitStew(new IID(Ids::RABBIT_STEW), "Rabbit Stew"));
|
||||
self::register("raw_beef", new RawBeef(new IID(Ids::RAW_BEEF), "Raw Beef"));
|
||||
self::register("raw_chicken", new RawChicken(new IID(Ids::RAW_CHICKEN), "Raw Chicken"));
|
||||
self::register("raw_copper", new Item(new IID(Ids::RAW_COPPER), "Raw Copper"));
|
||||
self::register("raw_fish", new RawFish(new IID(Ids::RAW_FISH), "Raw Fish"));
|
||||
self::register("raw_gold", new Item(new IID(Ids::RAW_GOLD), "Raw Gold"));
|
||||
self::register("raw_iron", new Item(new IID(Ids::RAW_IRON), "Raw Iron"));
|
||||
self::register("raw_mutton", new RawMutton(new IID(Ids::RAW_MUTTON), "Raw Mutton"));
|
||||
self::register("raw_porkchop", new RawPorkchop(new IID(Ids::RAW_PORKCHOP), "Raw Porkchop"));
|
||||
self::register("raw_rabbit", new RawRabbit(new IID(Ids::RAW_RABBIT), "Raw Rabbit"));
|
||||
self::register("raw_salmon", new RawSalmon(new IID(Ids::RAW_SALMON), "Raw Salmon"));
|
||||
self::register("record_11", new Record(new IID(Ids::RECORD_11), RecordType::DISK_11, "Record 11"));
|
||||
self::register("record_13", new Record(new IID(Ids::RECORD_13), RecordType::DISK_13, "Record 13"));
|
||||
self::register("record_5", new Record(new IID(Ids::RECORD_5), RecordType::DISK_5, "Record 5"));
|
||||
self::register("record_blocks", new Record(new IID(Ids::RECORD_BLOCKS), RecordType::DISK_BLOCKS, "Record Blocks"));
|
||||
self::register("record_cat", new Record(new IID(Ids::RECORD_CAT), RecordType::DISK_CAT, "Record Cat"));
|
||||
self::register("record_chirp", new Record(new IID(Ids::RECORD_CHIRP), RecordType::DISK_CHIRP, "Record Chirp"));
|
||||
self::register("record_far", new Record(new IID(Ids::RECORD_FAR), RecordType::DISK_FAR, "Record Far"));
|
||||
self::register("record_mall", new Record(new IID(Ids::RECORD_MALL), RecordType::DISK_MALL, "Record Mall"));
|
||||
self::register("record_mellohi", new Record(new IID(Ids::RECORD_MELLOHI), RecordType::DISK_MELLOHI, "Record Mellohi"));
|
||||
self::register("record_otherside", new Record(new IID(Ids::RECORD_OTHERSIDE), RecordType::DISK_OTHERSIDE, "Record Otherside"));
|
||||
self::register("record_pigstep", new Record(new IID(Ids::RECORD_PIGSTEP), RecordType::DISK_PIGSTEP, "Record Pigstep"));
|
||||
self::register("record_stal", new Record(new IID(Ids::RECORD_STAL), RecordType::DISK_STAL, "Record Stal"));
|
||||
self::register("record_strad", new Record(new IID(Ids::RECORD_STRAD), RecordType::DISK_STRAD, "Record Strad"));
|
||||
self::register("record_wait", new Record(new IID(Ids::RECORD_WAIT), RecordType::DISK_WAIT, "Record Wait"));
|
||||
self::register("record_ward", new Record(new IID(Ids::RECORD_WARD), RecordType::DISK_WARD, "Record Ward"));
|
||||
self::register("redstone_dust", new Redstone(new IID(Ids::REDSTONE_DUST), "Redstone"));
|
||||
self::register("rotten_flesh", new RottenFlesh(new IID(Ids::ROTTEN_FLESH), "Rotten Flesh"));
|
||||
self::register("scute", new Item(new IID(Ids::SCUTE), "Scute"));
|
||||
self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears", [EnchantmentTags::SHEARS]));
|
||||
self::register("shulker_shell", new Item(new IID(Ids::SHULKER_SHELL), "Shulker Shell"));
|
||||
self::register("slimeball", new Item(new IID(Ids::SLIMEBALL), "Slimeball"));
|
||||
self::register("snowball", new Snowball(new IID(Ids::SNOWBALL), "Snowball"));
|
||||
self::register("spider_eye", new SpiderEye(new IID(Ids::SPIDER_EYE), "Spider Eye"));
|
||||
self::register("splash_potion", new SplashPotion(new IID(Ids::SPLASH_POTION), "Splash Potion"));
|
||||
self::register("spruce_sign", new ItemBlockWallOrFloor(new IID(Ids::SPRUCE_SIGN), Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN()));
|
||||
self::register("spyglass", new Spyglass(new IID(Ids::SPYGLASS), "Spyglass"));
|
||||
self::register("steak", new Steak(new IID(Ids::STEAK), "Steak"));
|
||||
self::register("stick", new Stick(new IID(Ids::STICK), "Stick"));
|
||||
self::register("string", new StringItem(new IID(Ids::STRING), "String"));
|
||||
self::register("sugar", new Item(new IID(Ids::SUGAR), "Sugar"));
|
||||
self::register("suspicious_stew", new SuspiciousStew(new IID(Ids::SUSPICIOUS_STEW), "Suspicious Stew"));
|
||||
self::register("sweet_berries", new SweetBerries(new IID(Ids::SWEET_BERRIES), "Sweet Berries"));
|
||||
self::register("torchflower_seeds", new TorchflowerSeeds(new IID(Ids::TORCHFLOWER_SEEDS), "Torchflower Seeds"));
|
||||
self::register("totem", new Totem(new IID(Ids::TOTEM), "Totem of Undying"));
|
||||
self::register("warped_sign", new ItemBlockWallOrFloor(new IID(Ids::WARPED_SIGN), Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
|
||||
self::register("water_bucket", new LiquidBucket(new IID(Ids::WATER_BUCKET), "Water Bucket", Blocks::WATER()));
|
||||
self::register("wheat", new Item(new IID(Ids::WHEAT), "Wheat"));
|
||||
self::register("wheat_seeds", new WheatSeeds(new IID(Ids::WHEAT_SEEDS), "Wheat Seeds"));
|
||||
self::register("writable_book", new WritableBook(new IID(Ids::WRITABLE_BOOK), "Book & Quill"));
|
||||
self::register("written_book", new WrittenBook(new IID(Ids::WRITTEN_BOOK), "Written Book"));
|
||||
self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
|
||||
self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting"));
|
||||
self::register("paper", fn(IID $id) => new Item($id, "Paper"));
|
||||
self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane"));
|
||||
self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod"));
|
||||
self::register("poisonous_potato", fn(IID $id) => new PoisonousPotato($id, "Poisonous Potato"));
|
||||
self::register("popped_chorus_fruit", fn(IID $id) => new Item($id, "Popped Chorus Fruit"));
|
||||
self::register("potato", fn(IID $id) => new Potato($id, "Potato"));
|
||||
self::register("potion", fn(IID $id) => new Potion($id, "Potion"));
|
||||
self::register("prismarine_crystals", fn(IID $id) => new Item($id, "Prismarine Crystals"));
|
||||
self::register("prismarine_shard", fn(IID $id) => new Item($id, "Prismarine Shard"));
|
||||
self::register("pufferfish", fn(IID $id) => new Pufferfish($id, "Pufferfish"));
|
||||
self::register("pumpkin_pie", fn(IID $id) => new PumpkinPie($id, "Pumpkin Pie"));
|
||||
self::register("pumpkin_seeds", fn(IID $id) => new PumpkinSeeds($id, "Pumpkin Seeds"));
|
||||
self::register("rabbit_foot", fn(IID $id) => new Item($id, "Rabbit's Foot"));
|
||||
self::register("rabbit_hide", fn(IID $id) => new Item($id, "Rabbit Hide"));
|
||||
self::register("rabbit_stew", fn(IID $id) => new RabbitStew($id, "Rabbit Stew"));
|
||||
self::register("raw_beef", fn(IID $id) => new RawBeef($id, "Raw Beef"));
|
||||
self::register("raw_chicken", fn(IID $id) => new RawChicken($id, "Raw Chicken"));
|
||||
self::register("raw_copper", fn(IID $id) => new Item($id, "Raw Copper"));
|
||||
self::register("raw_fish", fn(IID $id) => new RawFish($id, "Raw Fish"));
|
||||
self::register("raw_gold", fn(IID $id) => new Item($id, "Raw Gold"));
|
||||
self::register("raw_iron", fn(IID $id) => new Item($id, "Raw Iron"));
|
||||
self::register("raw_mutton", fn(IID $id) => new RawMutton($id, "Raw Mutton"));
|
||||
self::register("raw_porkchop", fn(IID $id) => new RawPorkchop($id, "Raw Porkchop"));
|
||||
self::register("raw_rabbit", fn(IID $id) => new RawRabbit($id, "Raw Rabbit"));
|
||||
self::register("raw_salmon", fn(IID $id) => new RawSalmon($id, "Raw Salmon"));
|
||||
self::register("record_11", fn(IID $id) => new Record($id, RecordType::DISK_11, "Record 11"));
|
||||
self::register("record_13", fn(IID $id) => new Record($id, RecordType::DISK_13, "Record 13"));
|
||||
self::register("record_5", fn(IID $id) => new Record($id, RecordType::DISK_5, "Record 5"));
|
||||
self::register("record_blocks", fn(IID $id) => new Record($id, RecordType::DISK_BLOCKS, "Record Blocks"));
|
||||
self::register("record_cat", fn(IID $id) => new Record($id, RecordType::DISK_CAT, "Record Cat"));
|
||||
self::register("record_chirp", fn(IID $id) => new Record($id, RecordType::DISK_CHIRP, "Record Chirp"));
|
||||
self::register("record_far", fn(IID $id) => new Record($id, RecordType::DISK_FAR, "Record Far"));
|
||||
self::register("record_mall", fn(IID $id) => new Record($id, RecordType::DISK_MALL, "Record Mall"));
|
||||
self::register("record_mellohi", fn(IID $id) => new Record($id, RecordType::DISK_MELLOHI, "Record Mellohi"));
|
||||
self::register("record_otherside", fn(IID $id) => new Record($id, RecordType::DISK_OTHERSIDE, "Record Otherside"));
|
||||
self::register("record_pigstep", fn(IID $id) => new Record($id, RecordType::DISK_PIGSTEP, "Record Pigstep"));
|
||||
self::register("record_stal", fn(IID $id) => new Record($id, RecordType::DISK_STAL, "Record Stal"));
|
||||
self::register("record_strad", fn(IID $id) => new Record($id, RecordType::DISK_STRAD, "Record Strad"));
|
||||
self::register("record_wait", fn(IID $id) => new Record($id, RecordType::DISK_WAIT, "Record Wait"));
|
||||
self::register("record_ward", fn(IID $id) => new Record($id, RecordType::DISK_WARD, "Record Ward"));
|
||||
self::register("recovery_compass", fn(IID $id) => new Item($id, "Recovery Compass"));
|
||||
self::register("redstone_dust", fn(IID $id) => new Redstone($id, "Redstone"));
|
||||
self::register("rotten_flesh", fn(IID $id) => new RottenFlesh($id, "Rotten Flesh"));
|
||||
self::register("scute", fn(IID $id) => new Item($id, "Scute"));
|
||||
self::register("shears", fn(IID $id) => new Shears($id, "Shears", [EnchantmentTags::SHEARS]));
|
||||
self::register("shulker_shell", fn(IID $id) => new Item($id, "Shulker Shell"));
|
||||
self::register("slimeball", fn(IID $id) => new Item($id, "Slimeball"));
|
||||
self::register("snowball", fn(IID $id) => new Snowball($id, "Snowball"));
|
||||
self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye"));
|
||||
self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion"));
|
||||
self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN()));
|
||||
self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass"));
|
||||
self::register("steak", fn(IID $id) => new Steak($id, "Steak"));
|
||||
self::register("stick", fn(IID $id) => new Stick($id, "Stick"));
|
||||
self::register("string", fn(IID $id) => new StringItem($id, "String"));
|
||||
self::register("sugar", fn(IID $id) => new Item($id, "Sugar"));
|
||||
self::register("suspicious_stew", fn(IID $id) => new SuspiciousStew($id, "Suspicious Stew"));
|
||||
self::register("sweet_berries", fn(IID $id) => new SweetBerries($id, "Sweet Berries"));
|
||||
self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds"));
|
||||
self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying"));
|
||||
self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
|
||||
self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER()));
|
||||
self::register("wheat", fn(IID $id) => new Item($id, "Wheat"));
|
||||
self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds"));
|
||||
self::register("writable_book", fn(IID $id) => new WritableBook($id, "Book & Quill"));
|
||||
self::register("written_book", fn(IID $id) => new WrittenBook($id, "Written Book"));
|
||||
|
||||
foreach(BoatType::cases() as $type){
|
||||
//boat type is static, because different types of wood may have different properties
|
||||
self::register(strtolower($type->name) . "_boat", new Boat(new IID(match($type){
|
||||
BoatType::OAK => Ids::OAK_BOAT,
|
||||
BoatType::SPRUCE => Ids::SPRUCE_BOAT,
|
||||
BoatType::BIRCH => Ids::BIRCH_BOAT,
|
||||
BoatType::JUNGLE => Ids::JUNGLE_BOAT,
|
||||
BoatType::ACACIA => Ids::ACACIA_BOAT,
|
||||
BoatType::DARK_OAK => Ids::DARK_OAK_BOAT,
|
||||
BoatType::MANGROVE => Ids::MANGROVE_BOAT,
|
||||
}), $type->getDisplayName() . " Boat", $type));
|
||||
self::register(strtolower($type->name) . "_boat", fn(IID $id) => new Boat($id, $type->getDisplayName() . " Boat", $type));
|
||||
}
|
||||
}
|
||||
|
||||
private static function registerSpawnEggs() : void{
|
||||
self::register("zombie_spawn_egg", new class(new IID(Ids::ZOMBIE_SPAWN_EGG), "Zombie Spawn Egg") extends SpawnEgg{
|
||||
self::register("zombie_spawn_egg", fn(IID $id) => new class($id, "Zombie Spawn Egg") extends SpawnEgg{
|
||||
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
return new Zombie(Location::fromObject($pos, $world, $yaw, $pitch));
|
||||
}
|
||||
});
|
||||
self::register("squid_spawn_egg", new class(new IID(Ids::SQUID_SPAWN_EGG), "Squid Spawn Egg") extends SpawnEgg{
|
||||
self::register("squid_spawn_egg", fn(IID $id) => new class($id, "Squid Spawn Egg") extends SpawnEgg{
|
||||
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
return new Squid(Location::fromObject($pos, $world, $yaw, $pitch));
|
||||
}
|
||||
});
|
||||
self::register("villager_spawn_egg", new class(new IID(Ids::VILLAGER_SPAWN_EGG), "Villager Spawn Egg") extends SpawnEgg{
|
||||
self::register("villager_spawn_egg", fn(IID $id) => new class($id, "Villager Spawn Egg") extends SpawnEgg{
|
||||
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
return new Villager(Location::fromObject($pos, $world, $yaw, $pitch));
|
||||
}
|
||||
@ -603,87 +627,87 @@ final class VanillaItems{
|
||||
}
|
||||
|
||||
private static function registerTierToolItems() : void{
|
||||
self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE]));
|
||||
self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD, [EnchantmentTags::AXE]));
|
||||
self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON, [EnchantmentTags::AXE]));
|
||||
self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE, [EnchantmentTags::AXE]));
|
||||
self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE, [EnchantmentTags::AXE]));
|
||||
self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD, [EnchantmentTags::AXE]));
|
||||
self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND, [EnchantmentTags::HOE]));
|
||||
self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD, [EnchantmentTags::HOE]));
|
||||
self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON, [EnchantmentTags::HOE]));
|
||||
self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE, [EnchantmentTags::HOE]));
|
||||
self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE, [EnchantmentTags::HOE]));
|
||||
self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD, [EnchantmentTags::HOE]));
|
||||
self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND, [EnchantmentTags::PICKAXE]));
|
||||
self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD, [EnchantmentTags::PICKAXE]));
|
||||
self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON, [EnchantmentTags::PICKAXE]));
|
||||
self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE, [EnchantmentTags::PICKAXE]));
|
||||
self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE, [EnchantmentTags::PICKAXE]));
|
||||
self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD, [EnchantmentTags::PICKAXE]));
|
||||
self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND, [EnchantmentTags::SHOVEL]));
|
||||
self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD, [EnchantmentTags::SHOVEL]));
|
||||
self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON, [EnchantmentTags::SHOVEL]));
|
||||
self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE, [EnchantmentTags::SHOVEL]));
|
||||
self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE, [EnchantmentTags::SHOVEL]));
|
||||
self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD, [EnchantmentTags::SHOVEL]));
|
||||
self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND, [EnchantmentTags::SWORD]));
|
||||
self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD, [EnchantmentTags::SWORD]));
|
||||
self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON, [EnchantmentTags::SWORD]));
|
||||
self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE, [EnchantmentTags::SWORD]));
|
||||
self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE, [EnchantmentTags::SWORD]));
|
||||
self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD]));
|
||||
self::register("diamond_axe", fn(IID $id) => new Axe($id, "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE]));
|
||||
self::register("golden_axe", fn(IID $id) => new Axe($id, "Golden Axe", ToolTier::GOLD, [EnchantmentTags::AXE]));
|
||||
self::register("iron_axe", fn(IID $id) => new Axe($id, "Iron Axe", ToolTier::IRON, [EnchantmentTags::AXE]));
|
||||
self::register("netherite_axe", fn(IID $id) => new Axe($id, "Netherite Axe", ToolTier::NETHERITE, [EnchantmentTags::AXE]));
|
||||
self::register("stone_axe", fn(IID $id) => new Axe($id, "Stone Axe", ToolTier::STONE, [EnchantmentTags::AXE]));
|
||||
self::register("wooden_axe", fn(IID $id) => new Axe($id, "Wooden Axe", ToolTier::WOOD, [EnchantmentTags::AXE]));
|
||||
self::register("diamond_hoe", fn(IID $id) => new Hoe($id, "Diamond Hoe", ToolTier::DIAMOND, [EnchantmentTags::HOE]));
|
||||
self::register("golden_hoe", fn(IID $id) => new Hoe($id, "Golden Hoe", ToolTier::GOLD, [EnchantmentTags::HOE]));
|
||||
self::register("iron_hoe", fn(IID $id) => new Hoe($id, "Iron Hoe", ToolTier::IRON, [EnchantmentTags::HOE]));
|
||||
self::register("netherite_hoe", fn(IID $id) => new Hoe($id, "Netherite Hoe", ToolTier::NETHERITE, [EnchantmentTags::HOE]));
|
||||
self::register("stone_hoe", fn(IID $id) => new Hoe($id, "Stone Hoe", ToolTier::STONE, [EnchantmentTags::HOE]));
|
||||
self::register("wooden_hoe", fn(IID $id) => new Hoe($id, "Wooden Hoe", ToolTier::WOOD, [EnchantmentTags::HOE]));
|
||||
self::register("diamond_pickaxe", fn(IID $id) => new Pickaxe($id, "Diamond Pickaxe", ToolTier::DIAMOND, [EnchantmentTags::PICKAXE]));
|
||||
self::register("golden_pickaxe", fn(IID $id) => new Pickaxe($id, "Golden Pickaxe", ToolTier::GOLD, [EnchantmentTags::PICKAXE]));
|
||||
self::register("iron_pickaxe", fn(IID $id) => new Pickaxe($id, "Iron Pickaxe", ToolTier::IRON, [EnchantmentTags::PICKAXE]));
|
||||
self::register("netherite_pickaxe", fn(IID $id) => new Pickaxe($id, "Netherite Pickaxe", ToolTier::NETHERITE, [EnchantmentTags::PICKAXE]));
|
||||
self::register("stone_pickaxe", fn(IID $id) => new Pickaxe($id, "Stone Pickaxe", ToolTier::STONE, [EnchantmentTags::PICKAXE]));
|
||||
self::register("wooden_pickaxe", fn(IID $id) => new Pickaxe($id, "Wooden Pickaxe", ToolTier::WOOD, [EnchantmentTags::PICKAXE]));
|
||||
self::register("diamond_shovel", fn(IID $id) => new Shovel($id, "Diamond Shovel", ToolTier::DIAMOND, [EnchantmentTags::SHOVEL]));
|
||||
self::register("golden_shovel", fn(IID $id) => new Shovel($id, "Golden Shovel", ToolTier::GOLD, [EnchantmentTags::SHOVEL]));
|
||||
self::register("iron_shovel", fn(IID $id) => new Shovel($id, "Iron Shovel", ToolTier::IRON, [EnchantmentTags::SHOVEL]));
|
||||
self::register("netherite_shovel", fn(IID $id) => new Shovel($id, "Netherite Shovel", ToolTier::NETHERITE, [EnchantmentTags::SHOVEL]));
|
||||
self::register("stone_shovel", fn(IID $id) => new Shovel($id, "Stone Shovel", ToolTier::STONE, [EnchantmentTags::SHOVEL]));
|
||||
self::register("wooden_shovel", fn(IID $id) => new Shovel($id, "Wooden Shovel", ToolTier::WOOD, [EnchantmentTags::SHOVEL]));
|
||||
self::register("diamond_sword", fn(IID $id) => new Sword($id, "Diamond Sword", ToolTier::DIAMOND, [EnchantmentTags::SWORD]));
|
||||
self::register("golden_sword", fn(IID $id) => new Sword($id, "Golden Sword", ToolTier::GOLD, [EnchantmentTags::SWORD]));
|
||||
self::register("iron_sword", fn(IID $id) => new Sword($id, "Iron Sword", ToolTier::IRON, [EnchantmentTags::SWORD]));
|
||||
self::register("netherite_sword", fn(IID $id) => new Sword($id, "Netherite Sword", ToolTier::NETHERITE, [EnchantmentTags::SWORD]));
|
||||
self::register("stone_sword", fn(IID $id) => new Sword($id, "Stone Sword", ToolTier::STONE, [EnchantmentTags::SWORD]));
|
||||
self::register("wooden_sword", fn(IID $id) => new Sword($id, "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD]));
|
||||
}
|
||||
|
||||
private static function registerArmorItems() : void{
|
||||
self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS]));
|
||||
self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS]));
|
||||
self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS]));
|
||||
self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS]));
|
||||
self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS]));
|
||||
self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS]));
|
||||
self::register("chainmail_boots", fn(IID $id) => new Armor($id, "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS]));
|
||||
self::register("diamond_boots", fn(IID $id) => new Armor($id, "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS]));
|
||||
self::register("golden_boots", fn(IID $id) => new Armor($id, "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS]));
|
||||
self::register("iron_boots", fn(IID $id) => new Armor($id, "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS]));
|
||||
self::register("leather_boots", fn(IID $id) => new Armor($id, "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS]));
|
||||
self::register("netherite_boots", fn(IID $id) => new Armor($id, "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS]));
|
||||
|
||||
self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("chainmail_chestplate", fn(IID $id) => new Armor($id, "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("diamond_chestplate", fn(IID $id) => new Armor($id, "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("golden_chestplate", fn(IID $id) => new Armor($id, "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("iron_chestplate", fn(IID $id) => new Armor($id, "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("leather_tunic", fn(IID $id) => new Armor($id, "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE]));
|
||||
self::register("netherite_chestplate", fn(IID $id) => new Armor($id, "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE]));
|
||||
|
||||
self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET]));
|
||||
self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET]));
|
||||
self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET]));
|
||||
self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET]));
|
||||
self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET]));
|
||||
self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET]));
|
||||
self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET]));
|
||||
self::register("chainmail_helmet", fn(IID $id) => new Armor($id, "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET]));
|
||||
self::register("diamond_helmet", fn(IID $id) => new Armor($id, "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET]));
|
||||
self::register("golden_helmet", fn(IID $id) => new Armor($id, "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET]));
|
||||
self::register("iron_helmet", fn(IID $id) => new Armor($id, "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET]));
|
||||
self::register("leather_cap", fn(IID $id) => new Armor($id, "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET]));
|
||||
self::register("netherite_helmet", fn(IID $id) => new Armor($id, "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET]));
|
||||
self::register("turtle_helmet", fn(IID $id) => new TurtleHelmet($id, "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET]));
|
||||
|
||||
self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("chainmail_leggings", fn(IID $id) => new Armor($id, "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("diamond_leggings", fn(IID $id) => new Armor($id, "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("golden_leggings", fn(IID $id) => new Armor($id, "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("iron_leggings", fn(IID $id) => new Armor($id, "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("leather_pants", fn(IID $id) => new Armor($id, "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS]));
|
||||
self::register("netherite_leggings", fn(IID $id) => new Armor($id, "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS]));
|
||||
}
|
||||
|
||||
private static function registerSmithingTemplates() : void{
|
||||
self::register("netherite_upgrade_smithing_template", new Item(new IID(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE), "Netherite Upgrade Smithing Template"));
|
||||
self::register("coast_armor_trim_smithing_template", new Item(new IID(Ids::COAST_ARMOR_TRIM_SMITHING_TEMPLATE), "Coast Armor Trim Smithing Template"));
|
||||
self::register("dune_armor_trim_smithing_template", new Item(new IID(Ids::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE), "Dune Armor Trim Smithing Template"));
|
||||
self::register("eye_armor_trim_smithing_template", new Item(new IID(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE), "Eye Armor Trim Smithing Template"));
|
||||
self::register("host_armor_trim_smithing_template", new Item(new IID(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE), "Host Armor Trim Smithing Template"));
|
||||
self::register("raiser_armor_trim_smithing_template", new Item(new IID(Ids::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE), "Raiser Armor Trim Smithing Template"));
|
||||
self::register("rib_armor_trim_smithing_template", new Item(new IID(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE), "Rib Armor Trim Smithing Template"));
|
||||
self::register("sentry_armor_trim_smithing_template", new Item(new IID(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE), "Sentry Armor Trim Smithing Template"));
|
||||
self::register("shaper_armor_trim_smithing_template", new Item(new IID(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE), "Shaper Armor Trim Smithing Template"));
|
||||
self::register("silence_armor_trim_smithing_template", new Item(new IID(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE), "Silence Armor Trim Smithing Template"));
|
||||
self::register("snout_armor_trim_smithing_template", new Item(new IID(Ids::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE), "Snout Armor Trim Smithing Template"));
|
||||
self::register("spire_armor_trim_smithing_template", new Item(new IID(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE), "Spire Armor Trim Smithing Template"));
|
||||
self::register("tide_armor_trim_smithing_template", new Item(new IID(Ids::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE), "Tide Armor Trim Smithing Template"));
|
||||
self::register("vex_armor_trim_smithing_template", new Item(new IID(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE), "Vex Armor Trim Smithing Template"));
|
||||
self::register("ward_armor_trim_smithing_template", new Item(new IID(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE), "Ward Armor Trim Smithing Template"));
|
||||
self::register("wayfinder_armor_trim_smithing_template", new Item(new IID(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE), "Wayfinder Armor Trim Smithing Template"));
|
||||
self::register("wild_armor_trim_smithing_template", new Item(new IID(Ids::WILD_ARMOR_TRIM_SMITHING_TEMPLATE), "Wild Armor Trim Smithing Template"));
|
||||
self::register("netherite_upgrade_smithing_template", fn(IID $id) => new Item($id, "Netherite Upgrade Smithing Template"));
|
||||
self::register("coast_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Coast Armor Trim Smithing Template"));
|
||||
self::register("dune_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Dune Armor Trim Smithing Template"));
|
||||
self::register("eye_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Eye Armor Trim Smithing Template"));
|
||||
self::register("host_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Host Armor Trim Smithing Template"));
|
||||
self::register("raiser_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Raiser Armor Trim Smithing Template"));
|
||||
self::register("rib_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Rib Armor Trim Smithing Template"));
|
||||
self::register("sentry_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Sentry Armor Trim Smithing Template"));
|
||||
self::register("shaper_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Shaper Armor Trim Smithing Template"));
|
||||
self::register("silence_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Silence Armor Trim Smithing Template"));
|
||||
self::register("snout_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Snout Armor Trim Smithing Template"));
|
||||
self::register("spire_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Spire Armor Trim Smithing Template"));
|
||||
self::register("tide_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Tide Armor Trim Smithing Template"));
|
||||
self::register("vex_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Vex Armor Trim Smithing Template"));
|
||||
self::register("ward_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Ward Armor Trim Smithing Template"));
|
||||
self::register("wayfinder_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Wayfinder Armor Trim Smithing Template"));
|
||||
self::register("wild_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Wild Armor Trim Smithing Template"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ final class AvailableEnchantmentRegistry{
|
||||
$this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]);
|
||||
$this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []);
|
||||
$this->register(Enchantments::AQUA_AFFINITY(), [Tags::HELMET], []);
|
||||
$this->register(Enchantments::FROST_WALKER(), [/* no primary items */], [Tags::BOOTS]);
|
||||
$this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []);
|
||||
$this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []);
|
||||
$this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []);
|
||||
|
@ -44,6 +44,7 @@ final class StringToEnchantmentParser extends StringToTParser{
|
||||
$result->register("fire_protection", fn() => VanillaEnchantments::FIRE_PROTECTION());
|
||||
$result->register("flame", fn() => VanillaEnchantments::FLAME());
|
||||
$result->register("fortune", fn() => VanillaEnchantments::FORTUNE());
|
||||
$result->register("frost_walker", fn() => VanillaEnchantments::FROST_WALKER());
|
||||
$result->register("infinity", fn() => VanillaEnchantments::INFINITY());
|
||||
$result->register("knockback", fn() => VanillaEnchantments::KNOCKBACK());
|
||||
$result->register("mending", fn() => VanillaEnchantments::MENDING());
|
||||
|
@ -41,6 +41,7 @@ use pocketmine\utils\RegistryTrait;
|
||||
* @method static ProtectionEnchantment FIRE_PROTECTION()
|
||||
* @method static Enchantment FLAME()
|
||||
* @method static Enchantment FORTUNE()
|
||||
* @method static Enchantment FROST_WALKER()
|
||||
* @method static Enchantment INFINITY()
|
||||
* @method static KnockbackEnchantment KNOCKBACK()
|
||||
* @method static Enchantment MENDING()
|
||||
@ -145,6 +146,16 @@ final class VanillaEnchantments{
|
||||
fn(int $level) : int => 10 * $level,
|
||||
30
|
||||
));
|
||||
|
||||
self::register("FROST_WALKER", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_frostwalker(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
fn(int $level) : int => 10 * $level,
|
||||
15
|
||||
));
|
||||
self::register("AQUA_AFFINITY", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_waterWorker(),
|
||||
Rarity::RARE,
|
||||
|
@ -603,6 +603,31 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_WHITELIST_USAGE, []);
|
||||
}
|
||||
|
||||
public static function commands_xp_failure_widthdrawXp() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_XP_FAILURE_WIDTHDRAWXP, []);
|
||||
}
|
||||
|
||||
public static function commands_xp_success(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS, [
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function commands_xp_success_levels(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS_LEVELS, [
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function commands_xp_success_negative_levels(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS_NEGATIVE_LEVELS, [
|
||||
0 => $param0,
|
||||
1 => $param1,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function death_attack_anvil(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ANVIL, [
|
||||
0 => $param0,
|
||||
@ -667,6 +692,12 @@ final class KnownTranslationFactory{
|
||||
]);
|
||||
}
|
||||
|
||||
public static function death_attack_flyIntoWall(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_FLYINTOWALL, [
|
||||
0 => $param0,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function death_attack_generic(Translatable|string $param0) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [
|
||||
0 => $param0,
|
||||
@ -1025,6 +1056,14 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_CHIRP_DESC, []);
|
||||
}
|
||||
|
||||
public static function item_record_creator_desc() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_CREATOR_DESC, []);
|
||||
}
|
||||
|
||||
public static function item_record_creator_music_box_desc() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_CREATOR_MUSIC_BOX_DESC, []);
|
||||
}
|
||||
|
||||
public static function item_record_far_desc() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_FAR_DESC, []);
|
||||
}
|
||||
@ -1045,6 +1084,14 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_PIGSTEP_DESC, []);
|
||||
}
|
||||
|
||||
public static function item_record_precipice_desc() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_PRECIPICE_DESC, []);
|
||||
}
|
||||
|
||||
public static function item_record_relic_desc() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_RELIC_DESC, []);
|
||||
}
|
||||
|
||||
public static function item_record_stal_desc() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::ITEM_RECORD_STAL_DESC, []);
|
||||
}
|
||||
@ -1394,6 +1441,10 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_timings_collect() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_COLLECT, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_timings_description() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_DESCRIPTION, []);
|
||||
}
|
||||
@ -1536,6 +1587,14 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_WHITELIST_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_xp_description() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_XP_DESCRIPTION, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_command_xp_usage() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_XP_USAGE, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_crash_archive(Translatable|string $param0, Translatable|string $param1) : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_CRASH_ARCHIVE, [
|
||||
0 => $param0,
|
||||
@ -2056,6 +2115,14 @@ final class KnownTranslationFactory{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_permission_command_xp_other() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_XP_OTHER, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_permission_command_xp_self() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_XP_SELF, []);
|
||||
}
|
||||
|
||||
public static function pocketmine_permission_group_console() : Translatable{
|
||||
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_CONSOLE, []);
|
||||
}
|
||||
|
@ -137,6 +137,10 @@ final class KnownTranslationKeys{
|
||||
public const COMMANDS_WHITELIST_REMOVE_SUCCESS = "commands.whitelist.remove.success";
|
||||
public const COMMANDS_WHITELIST_REMOVE_USAGE = "commands.whitelist.remove.usage";
|
||||
public const COMMANDS_WHITELIST_USAGE = "commands.whitelist.usage";
|
||||
public const COMMANDS_XP_FAILURE_WIDTHDRAWXP = "commands.xp.failure.widthdrawXp";
|
||||
public const COMMANDS_XP_SUCCESS = "commands.xp.success";
|
||||
public const COMMANDS_XP_SUCCESS_LEVELS = "commands.xp.success.levels";
|
||||
public const COMMANDS_XP_SUCCESS_NEGATIVE_LEVELS = "commands.xp.success.negative.levels";
|
||||
public const DEATH_ATTACK_ANVIL = "death.attack.anvil";
|
||||
public const DEATH_ATTACK_ARROW = "death.attack.arrow";
|
||||
public const DEATH_ATTACK_ARROW_ITEM = "death.attack.arrow.item";
|
||||
@ -147,6 +151,7 @@ final class KnownTranslationKeys{
|
||||
public const DEATH_ATTACK_FALL = "death.attack.fall";
|
||||
public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock";
|
||||
public const DEATH_ATTACK_FIREWORKS = "death.attack.fireworks";
|
||||
public const DEATH_ATTACK_FLYINTOWALL = "death.attack.flyIntoWall";
|
||||
public const DEATH_ATTACK_GENERIC = "death.attack.generic";
|
||||
public const DEATH_ATTACK_INFIRE = "death.attack.inFire";
|
||||
public const DEATH_ATTACK_INWALL = "death.attack.inWall";
|
||||
@ -227,11 +232,15 @@ final class KnownTranslationKeys{
|
||||
public const ITEM_RECORD_BLOCKS_DESC = "item.record_blocks.desc";
|
||||
public const ITEM_RECORD_CAT_DESC = "item.record_cat.desc";
|
||||
public const ITEM_RECORD_CHIRP_DESC = "item.record_chirp.desc";
|
||||
public const ITEM_RECORD_CREATOR_DESC = "item.record_creator.desc";
|
||||
public const ITEM_RECORD_CREATOR_MUSIC_BOX_DESC = "item.record_creator_music_box.desc";
|
||||
public const ITEM_RECORD_FAR_DESC = "item.record_far.desc";
|
||||
public const ITEM_RECORD_MALL_DESC = "item.record_mall.desc";
|
||||
public const ITEM_RECORD_MELLOHI_DESC = "item.record_mellohi.desc";
|
||||
public const ITEM_RECORD_OTHERSIDE_DESC = "item.record_otherside.desc";
|
||||
public const ITEM_RECORD_PIGSTEP_DESC = "item.record_pigstep.desc";
|
||||
public const ITEM_RECORD_PRECIPICE_DESC = "item.record_precipice.desc";
|
||||
public const ITEM_RECORD_RELIC_DESC = "item.record_relic.desc";
|
||||
public const ITEM_RECORD_STAL_DESC = "item.record_stal.desc";
|
||||
public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc";
|
||||
public const ITEM_RECORD_WAIT_DESC = "item.record_wait.desc";
|
||||
@ -306,6 +315,7 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_COMMAND_TIME_DESCRIPTION = "pocketmine.command.time.description";
|
||||
public const POCKETMINE_COMMAND_TIME_USAGE = "pocketmine.command.time.usage";
|
||||
public const POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED = "pocketmine.command.timings.alreadyEnabled";
|
||||
public const POCKETMINE_COMMAND_TIMINGS_COLLECT = "pocketmine.command.timings.collect";
|
||||
public const POCKETMINE_COMMAND_TIMINGS_DESCRIPTION = "pocketmine.command.timings.description";
|
||||
public const POCKETMINE_COMMAND_TIMINGS_DISABLE = "pocketmine.command.timings.disable";
|
||||
public const POCKETMINE_COMMAND_TIMINGS_ENABLE = "pocketmine.command.timings.enable";
|
||||
@ -336,6 +346,8 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_COMMAND_VERSION_SERVERSOFTWAREVERSION = "pocketmine.command.version.serverSoftwareVersion";
|
||||
public const POCKETMINE_COMMAND_VERSION_USAGE = "pocketmine.command.version.usage";
|
||||
public const POCKETMINE_COMMAND_WHITELIST_DESCRIPTION = "pocketmine.command.whitelist.description";
|
||||
public const POCKETMINE_COMMAND_XP_DESCRIPTION = "pocketmine.command.xp.description";
|
||||
public const POCKETMINE_COMMAND_XP_USAGE = "pocketmine.command.xp.usage";
|
||||
public const POCKETMINE_CRASH_ARCHIVE = "pocketmine.crash.archive";
|
||||
public const POCKETMINE_CRASH_CREATE = "pocketmine.crash.create";
|
||||
public const POCKETMINE_CRASH_ERROR = "pocketmine.crash.error";
|
||||
@ -449,6 +461,8 @@ final class KnownTranslationKeys{
|
||||
public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_LIST = "pocketmine.permission.command.whitelist.list";
|
||||
public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_RELOAD = "pocketmine.permission.command.whitelist.reload";
|
||||
public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE = "pocketmine.permission.command.whitelist.remove";
|
||||
public const POCKETMINE_PERMISSION_COMMAND_XP_OTHER = "pocketmine.permission.command.xp.other";
|
||||
public const POCKETMINE_PERMISSION_COMMAND_XP_SELF = "pocketmine.permission.command.xp.self";
|
||||
public const POCKETMINE_PERMISSION_GROUP_CONSOLE = "pocketmine.permission.group.console";
|
||||
public const POCKETMINE_PERMISSION_GROUP_OPERATOR = "pocketmine.permission.group.operator";
|
||||
public const POCKETMINE_PERMISSION_GROUP_USER = "pocketmine.permission.group.user";
|
||||
|
@ -497,15 +497,18 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){
|
||||
$this->onFailedBlockAction($vBlockPos, $data->getFace());
|
||||
}
|
||||
$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos);
|
||||
//always sync this in case plugins caused a different result than the client expected
|
||||
//we *could* try to enhance detection of plugin-altered behaviour, but this would require propagating
|
||||
//more information up the stack. For now I think this is good enough.
|
||||
//if only the client would tell us what blocks it thinks changed...
|
||||
$this->syncBlocksNearby($vBlockPos, $data->getFace());
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->breakBlock($vBlockPos)){
|
||||
$this->onFailedBlockAction($vBlockPos, null);
|
||||
$this->syncBlocksNearby($vBlockPos, null);
|
||||
}
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
@ -533,9 +536,9 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used to execute rollbacks when an action fails on a block.
|
||||
* Syncs blocks nearby to ensure that the client and server agree on the world's blocks after a block interaction.
|
||||
*/
|
||||
private function onFailedBlockAction(Vector3 $blockPos, ?int $face) : void{
|
||||
private function syncBlocksNearby(Vector3 $blockPos, ?int $face) : void{
|
||||
if($blockPos->distanceSquared($this->player->getLocation()) < 10000){
|
||||
$blocks = $blockPos->sidesArray();
|
||||
if($face !== null){
|
||||
@ -672,7 +675,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{
|
||||
return false; //TODO
|
||||
return $this->player->pickEntity($packet->actorUniqueId);
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
@ -686,7 +689,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
case PlayerAction::START_BREAK:
|
||||
self::validateFacing($face);
|
||||
if(!$this->player->attackBlock($pos, $face)){
|
||||
$this->onFailedBlockAction($pos, $face);
|
||||
$this->syncBlocksNearby($pos, $face);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1002,7 +1005,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
$lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ());
|
||||
if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){
|
||||
if(!$lectern->onPageTurn($packet->page)){
|
||||
$this->onFailedBlockAction($lectern->getPosition(), null);
|
||||
$this->syncBlocksNearby($lectern->getPosition(), null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ final class DefaultPermissionNames{
|
||||
public const COMMAND_WHITELIST_LIST = "pocketmine.command.whitelist.list";
|
||||
public const COMMAND_WHITELIST_RELOAD = "pocketmine.command.whitelist.reload";
|
||||
public const COMMAND_WHITELIST_REMOVE = "pocketmine.command.whitelist.remove";
|
||||
public const COMMAND_XP_OTHER = "pocketmine.command.xp.other";
|
||||
public const COMMAND_XP_SELF = "pocketmine.command.xp.self";
|
||||
public const GROUP_CONSOLE = "pocketmine.group.console";
|
||||
public const GROUP_OPERATOR = "pocketmine.group.operator";
|
||||
public const GROUP_USER = "pocketmine.group.user";
|
||||
|
@ -112,5 +112,7 @@ abstract class DefaultPermissions{
|
||||
self::registerPermission(new Permission(Names::COMMAND_WHITELIST_LIST, l10n::pocketmine_permission_command_whitelist_list()), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(Names::COMMAND_WHITELIST_RELOAD, l10n::pocketmine_permission_command_whitelist_reload()), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(Names::COMMAND_WHITELIST_REMOVE, l10n::pocketmine_permission_command_whitelist_remove()), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(Names::COMMAND_XP_OTHER, l10n::pocketmine_permission_command_xp_other()), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(Names::COMMAND_XP_SELF, l10n::pocketmine_permission_command_xp_self()), [$operatorRoot]);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\permission;
|
||||
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
@ -71,6 +72,10 @@ class PermissionManager{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Superseded by server chat broadcast channels
|
||||
* @see Server::subscribeToBroadcastChannel()
|
||||
*/
|
||||
public function subscribeToPermission(string $permission, PermissibleInternal $permissible) : void{
|
||||
if(!isset($this->permSubs[$permission])){
|
||||
$this->permSubs[$permission] = [];
|
||||
@ -78,6 +83,10 @@ class PermissionManager{
|
||||
$this->permSubs[$permission][spl_object_id($permissible)] = $permissible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Superseded by server chat broadcast channels
|
||||
* @see Server::unsubscribeFromBroadcastChannel()
|
||||
*/
|
||||
public function unsubscribeFromPermission(string $permission, PermissibleInternal $permissible) : void{
|
||||
if(isset($this->permSubs[$permission][spl_object_id($permissible)])){
|
||||
if(count($this->permSubs[$permission]) === 1){
|
||||
@ -88,6 +97,10 @@ class PermissionManager{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Superseded by server chat broadcast channels
|
||||
* @see Server::unsubscribeFromAllBroadcastChannels()
|
||||
*/
|
||||
public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{
|
||||
foreach(Utils::promoteKeys($this->permSubs) as $permission => $subs){
|
||||
if(count($subs) === 1 && isset($subs[spl_object_id($permissible)])){
|
||||
@ -99,6 +112,8 @@ class PermissionManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Superseded by server chat broadcast channels
|
||||
* @see Server::getBroadcastChannelSubscribers()
|
||||
* @return PermissibleInternal[]
|
||||
*/
|
||||
public function getPermissionSubscriptions(string $permission) : array{
|
||||
|
@ -57,6 +57,7 @@ use pocketmine\event\player\PlayerDisplayNameChangeEvent;
|
||||
use pocketmine\event\player\PlayerDropItemEvent;
|
||||
use pocketmine\event\player\PlayerEmoteEvent;
|
||||
use pocketmine\event\player\PlayerEntityInteractEvent;
|
||||
use pocketmine\event\player\PlayerEntityPickEvent;
|
||||
use pocketmine\event\player\PlayerExhaustEvent;
|
||||
use pocketmine\event\player\PlayerGameModeChangeEvent;
|
||||
use pocketmine\event\player\PlayerInteractEvent;
|
||||
@ -109,6 +110,7 @@ use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||
@ -190,6 +192,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
private const TAG_SPAWN_X = "SpawnX"; //TAG_Int
|
||||
private const TAG_SPAWN_Y = "SpawnY"; //TAG_Int
|
||||
private const TAG_SPAWN_Z = "SpawnZ"; //TAG_Int
|
||||
private const TAG_DEATH_WORLD = "DeathLevel"; //TAG_String
|
||||
private const TAG_DEATH_X = "DeathPositionX"; //TAG_Int
|
||||
private const TAG_DEATH_Y = "DeathPositionY"; //TAG_Int
|
||||
private const TAG_DEATH_Z = "DeathPositionZ"; //TAG_Int
|
||||
public const TAG_LEVEL = "Level"; //TAG_String
|
||||
public const TAG_LAST_KNOWN_XUID = "LastKnownXUID"; //TAG_String
|
||||
|
||||
@ -272,6 +278,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
private bool $respawnLocked = false;
|
||||
|
||||
private ?Position $deathPosition = null;
|
||||
|
||||
//TODO: Abilities
|
||||
protected bool $autoJump = true;
|
||||
protected bool $allowFlight = false;
|
||||
@ -390,6 +398,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_SPAWN_WORLD, ""))) instanceof World){
|
||||
$this->spawnPosition = new Position($nbt->getInt(self::TAG_SPAWN_X), $nbt->getInt(self::TAG_SPAWN_Y), $nbt->getInt(self::TAG_SPAWN_Z), $world);
|
||||
}
|
||||
if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_DEATH_WORLD, ""))) instanceof World){
|
||||
$this->deathPosition = new Position($nbt->getInt(self::TAG_DEATH_X), $nbt->getInt(self::TAG_DEATH_Y), $nbt->getInt(self::TAG_DEATH_Z), $world);
|
||||
}
|
||||
}
|
||||
|
||||
public function getLeaveMessage() : Translatable|string{
|
||||
@ -1031,6 +1042,30 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
public function getDeathPosition() : ?Position{
|
||||
if($this->deathPosition !== null && !$this->deathPosition->isValid()){
|
||||
$this->deathPosition = null;
|
||||
}
|
||||
return $this->deathPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Vector3|Position|null $pos
|
||||
*/
|
||||
public function setDeathPosition(?Vector3 $pos) : void{
|
||||
if($pos !== null){
|
||||
if($pos instanceof Position && $pos->world !== null){
|
||||
$world = $pos->world;
|
||||
}else{
|
||||
$world = $this->getWorld();
|
||||
}
|
||||
$this->deathPosition = new Position($pos->x, $pos->y, $pos->z, $world);
|
||||
}else{
|
||||
$this->deathPosition = null;
|
||||
}
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Position
|
||||
*/
|
||||
@ -1470,6 +1505,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canEat() : bool{
|
||||
return $this->isCreative() || parent::canEat();
|
||||
}
|
||||
|
||||
public function canBreathe() : bool{
|
||||
return $this->isCreative() || parent::canBreathe();
|
||||
}
|
||||
@ -1709,29 +1748,58 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
if($existingSlot !== -1){
|
||||
if($existingSlot < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setHeldItemIndex($existingSlot);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot);
|
||||
}
|
||||
}else{
|
||||
$firstEmpty = $this->inventory->firstEmpty();
|
||||
if($firstEmpty === -1){ //full inventory
|
||||
$this->inventory->setItemInHand($item);
|
||||
}elseif($firstEmpty < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setItem($firstEmpty, $item);
|
||||
$this->inventory->setHeldItemIndex($firstEmpty);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}
|
||||
$this->equipOrAddPickedItem($existingSlot, $item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function pickEntity(int $entityId) : bool{
|
||||
$entity = $this->getWorld()->getEntity($entityId);
|
||||
if($entity === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$item = $entity->getPickedItem();
|
||||
if($item === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$ev = new PlayerEntityPickEvent($this, $entity, $item);
|
||||
$existingSlot = $this->inventory->first($item);
|
||||
if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$this->equipOrAddPickedItem($existingSlot, $item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function equipOrAddPickedItem(int $existingSlot, Item $item) : void{
|
||||
if($existingSlot !== -1){
|
||||
if($existingSlot < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setHeldItemIndex($existingSlot);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot);
|
||||
}
|
||||
}else{
|
||||
$firstEmpty = $this->inventory->firstEmpty();
|
||||
if($firstEmpty === -1){ //full inventory
|
||||
$this->inventory->setItemInHand($item);
|
||||
}elseif($firstEmpty < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setItem($firstEmpty, $item);
|
||||
$this->inventory->setHeldItemIndex($firstEmpty);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a left-click (attack) action on the block.
|
||||
*
|
||||
@ -2302,6 +2370,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
unset($this->cursorInventory);
|
||||
unset($this->craftingGrid);
|
||||
$this->spawnPosition = null;
|
||||
$this->deathPosition = null;
|
||||
$this->blockBreakHandler = null;
|
||||
parent::destroyCycles();
|
||||
}
|
||||
@ -2343,6 +2412,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$nbt->setInt(self::TAG_SPAWN_Z, $spawn->getFloorZ());
|
||||
}
|
||||
|
||||
if($this->deathPosition !== null && $this->deathPosition->isValid()){
|
||||
$nbt->setString(self::TAG_DEATH_WORLD, $this->deathPosition->getWorld()->getFolderName());
|
||||
$nbt->setInt(self::TAG_DEATH_X, $this->deathPosition->getFloorX());
|
||||
$nbt->setInt(self::TAG_DEATH_Y, $this->deathPosition->getFloorY());
|
||||
$nbt->setInt(self::TAG_DEATH_Z, $this->deathPosition->getFloorZ());
|
||||
}
|
||||
|
||||
$nbt->setInt(self::TAG_GAME_MODE, GameModeIdMap::getInstance()->toId($this->gamemode));
|
||||
$nbt->setLong(self::TAG_FIRST_PLAYED, $this->firstPlayed);
|
||||
$nbt->setLong(self::TAG_LAST_PLAYED, (int) floor(microtime(true) * 1000));
|
||||
@ -2362,6 +2438,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
//main inventory and drops the rest on the ground.
|
||||
$this->removeCurrentWindow();
|
||||
|
||||
$this->setDeathPosition($this->getPosition());
|
||||
|
||||
$ev = new PlayerDeathEvent($this, $this->getDrops(), $this->getXpDropAmount(), null);
|
||||
$ev->call();
|
||||
|
||||
@ -2490,6 +2568,17 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
$properties->setPlayerFlag(PlayerMetadataFlags::SLEEP, $this->sleeping !== null);
|
||||
$properties->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $this->sleeping !== null ? BlockPosition::fromVector3($this->sleeping) : new BlockPosition(0, 0, 0));
|
||||
|
||||
if($this->deathPosition !== null && $this->deathPosition->world === $this->location->world){
|
||||
$properties->setBlockPos(EntityMetadataProperties::PLAYER_DEATH_POSITION, BlockPosition::fromVector3($this->deathPosition));
|
||||
//TODO: this should be updated when dimensions are implemented
|
||||
$properties->setInt(EntityMetadataProperties::PLAYER_DEATH_DIMENSION, DimensionIds::OVERWORLD);
|
||||
$properties->setByte(EntityMetadataProperties::PLAYER_HAS_DIED, 1);
|
||||
}else{
|
||||
$properties->setBlockPos(EntityMetadataProperties::PLAYER_DEATH_POSITION, new BlockPosition(0, 0, 0));
|
||||
$properties->setInt(EntityMetadataProperties::PLAYER_DEATH_DIMENSION, DimensionIds::OVERWORLD);
|
||||
$properties->setByte(EntityMetadataProperties::PLAYER_HAS_DIED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function sendData(?array $targets, ?array $data = null) : void{
|
||||
|
@ -36,6 +36,8 @@ use const DIRECTORY_SEPARATOR;
|
||||
/**
|
||||
* Provides resources from the given plugin directory on disk. The path may be prefixed with a specific access protocol
|
||||
* to enable special types of access.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
class DiskResourceProvider implements ResourceProvider{
|
||||
private string $file;
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
interface ResourceProvider{
|
||||
/**
|
||||
* Gets an embedded resource on the plugin file.
|
||||
|
@ -70,16 +70,17 @@ final class Promise{
|
||||
*
|
||||
* @phpstan-template TPromiseValue
|
||||
* @phpstan-template TKey of array-key
|
||||
* @phpstan-param non-empty-array<TKey, Promise<TPromiseValue>> $promises
|
||||
* @phpstan-param array<TKey, Promise<TPromiseValue>> $promises
|
||||
*
|
||||
* @phpstan-return Promise<array<TKey, TPromiseValue>>
|
||||
*/
|
||||
public static function all(array $promises) : Promise{
|
||||
if(count($promises) === 0){
|
||||
throw new \InvalidArgumentException("At least one promise must be provided");
|
||||
}
|
||||
/** @phpstan-var PromiseResolver<array<TKey, TPromiseValue>> $resolver */
|
||||
$resolver = new PromiseResolver();
|
||||
if(count($promises) === 0){
|
||||
$resolver->resolve([]);
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
$values = [];
|
||||
$toResolve = count($promises);
|
||||
$continue = true;
|
||||
|
@ -27,6 +27,7 @@ use pmmp\thread\Runnable;
|
||||
use pmmp\thread\ThreadSafe;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\thread\NonThreadSafeValue;
|
||||
use pocketmine\timings\Timings;
|
||||
use function array_key_exists;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
@ -67,7 +68,10 @@ abstract class AsyncTask extends Runnable{
|
||||
*/
|
||||
private static array $threadLocalStorage = [];
|
||||
|
||||
/** @phpstan-var ThreadSafeArray<int, string>|null */
|
||||
/**
|
||||
* @phpstan-var ThreadSafeArray<int, string>|null
|
||||
* @deprecated
|
||||
*/
|
||||
private ?ThreadSafeArray $progressUpdates = null;
|
||||
|
||||
private ThreadSafe|string|int|bool|null|float $result = null;
|
||||
@ -78,7 +82,14 @@ abstract class AsyncTask extends Runnable{
|
||||
public function run() : void{
|
||||
$this->result = null;
|
||||
|
||||
$this->onRun();
|
||||
$timings = Timings::getAsyncTaskRunTimings($this);
|
||||
$timings->startTiming();
|
||||
|
||||
try{
|
||||
$this->onRun();
|
||||
}finally{
|
||||
$timings->stopTiming();
|
||||
}
|
||||
|
||||
$this->finished = true;
|
||||
AsyncWorker::getNotifier()->wakeupSleeper();
|
||||
@ -153,6 +164,8 @@ abstract class AsyncTask extends Runnable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Call this method from {@link AsyncTask::onRun} (AsyncTask execution thread) to schedule a call to
|
||||
* {@link AsyncTask::onProgressUpdate} from the main thread with the given progress parameter.
|
||||
*
|
||||
@ -167,6 +180,7 @@ abstract class AsyncTask extends Runnable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @internal Only call from AsyncPool.php on the main thread
|
||||
*/
|
||||
public function checkProgressUpdates() : void{
|
||||
@ -179,6 +193,8 @@ abstract class AsyncTask extends Runnable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Called from the main thread after {@link AsyncTask::publishProgress} is called.
|
||||
* All {@link AsyncTask::publishProgress} calls should result in {@link AsyncTask::onProgressUpdate} calls before
|
||||
* {@link AsyncTask::onCompletion} is called.
|
||||
|
@ -26,8 +26,12 @@ namespace pocketmine\scheduler;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
abstract class Task{
|
||||
/** @phpstan-var TaskHandler<static>|null */
|
||||
private ?TaskHandler $taskHandler = null;
|
||||
|
||||
/**
|
||||
* @phpstan-return TaskHandler<static>|null
|
||||
*/
|
||||
final public function getHandler() : ?TaskHandler{
|
||||
return $this->taskHandler;
|
||||
}
|
||||
@ -36,6 +40,9 @@ abstract class Task{
|
||||
return Utils::getNiceClassName($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param TaskHandler<static>|null $taskHandler
|
||||
*/
|
||||
final public function setHandler(?TaskHandler $taskHandler) : void{
|
||||
if($this->taskHandler === null || $taskHandler === null){
|
||||
$this->taskHandler = $taskHandler;
|
||||
|
@ -26,6 +26,9 @@ namespace pocketmine\scheduler;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
|
||||
/**
|
||||
* @template TTask of Task
|
||||
*/
|
||||
class TaskHandler{
|
||||
protected int $nextRun;
|
||||
|
||||
@ -36,6 +39,9 @@ class TaskHandler{
|
||||
private string $taskName;
|
||||
private string $ownerName;
|
||||
|
||||
/**
|
||||
* @phpstan-param TTask $task
|
||||
*/
|
||||
public function __construct(
|
||||
protected Task $task,
|
||||
protected int $delay = -1,
|
||||
@ -66,6 +72,9 @@ class TaskHandler{
|
||||
$this->nextRun = $ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return TTask
|
||||
*/
|
||||
public function getTask() : Task{
|
||||
return $this->task;
|
||||
}
|
||||
|
@ -33,12 +33,12 @@ use pocketmine\utils\ReversePriorityQueue;
|
||||
class TaskScheduler{
|
||||
private bool $enabled = true;
|
||||
|
||||
/** @phpstan-var ReversePriorityQueue<int, TaskHandler> */
|
||||
/** @phpstan-var ReversePriorityQueue<int, TaskHandler<covariant Task>> */
|
||||
protected ReversePriorityQueue $queue;
|
||||
|
||||
/**
|
||||
* @var ObjectSet|TaskHandler[]
|
||||
* @phpstan-var ObjectSet<TaskHandler>
|
||||
* @phpstan-var ObjectSet<TaskHandler<covariant Task>>
|
||||
*/
|
||||
protected ObjectSet $tasks;
|
||||
|
||||
@ -51,18 +51,42 @@ class TaskScheduler{
|
||||
$this->tasks = new ObjectSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TTask of Task
|
||||
* @phpstan-param TTask $task
|
||||
*
|
||||
* @phpstan-return TaskHandler<TTask>
|
||||
*/
|
||||
public function scheduleTask(Task $task) : TaskHandler{
|
||||
return $this->addTask($task, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TTask of Task
|
||||
* @phpstan-param TTask $task
|
||||
*
|
||||
* @phpstan-return TaskHandler<TTask>
|
||||
*/
|
||||
public function scheduleDelayedTask(Task $task, int $delay) : TaskHandler{
|
||||
return $this->addTask($task, $delay, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TTask of Task
|
||||
* @phpstan-param TTask $task
|
||||
*
|
||||
* @phpstan-return TaskHandler<TTask>
|
||||
*/
|
||||
public function scheduleRepeatingTask(Task $task, int $period) : TaskHandler{
|
||||
return $this->addTask($task, -1, $period);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TTask of Task
|
||||
* @phpstan-param TTask $task
|
||||
*
|
||||
* @phpstan-return TaskHandler<TTask>
|
||||
*/
|
||||
public function scheduleDelayedRepeatingTask(Task $task, int $delay, int $period) : TaskHandler{
|
||||
return $this->addTask($task, $delay, $period);
|
||||
}
|
||||
@ -77,10 +101,19 @@ class TaskScheduler{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param TaskHandler<covariant Task> $task
|
||||
*/
|
||||
public function isQueued(TaskHandler $task) : bool{
|
||||
return $this->tasks->contains($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TTask of Task
|
||||
* @phpstan-param TTask $task
|
||||
*
|
||||
* @phpstan-return TaskHandler<TTask>
|
||||
*/
|
||||
private function addTask(Task $task, int $delay, int $period) : TaskHandler{
|
||||
if(!$this->enabled){
|
||||
throw new \LogicException("Tried to schedule task to disabled scheduler");
|
||||
@ -99,6 +132,11 @@ class TaskScheduler{
|
||||
return $this->handle(new TaskHandler($task, $delay, $period, $this->owner));
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TTask of Task
|
||||
* @phpstan-param TaskHandler<TTask> $handler
|
||||
* @phpstan-return TaskHandler<TTask>
|
||||
*/
|
||||
private function handle(TaskHandler $handler) : TaskHandler{
|
||||
if($handler->isDelayed()){
|
||||
$nextRun = $this->currentTick + $handler->getDelay();
|
||||
@ -128,7 +166,7 @@ class TaskScheduler{
|
||||
}
|
||||
$this->currentTick = $currentTick;
|
||||
while($this->isReady($this->currentTick)){
|
||||
/** @var TaskHandler $task */
|
||||
/** @phpstan-var TaskHandler<covariant Task> $task */
|
||||
$task = $this->queue->extract();
|
||||
if($task->isCancelled()){
|
||||
$this->tasks->remove($task);
|
||||
|
60
src/scheduler/TimingsCollectionTask.php
Normal file
60
src/scheduler/TimingsCollectionTask.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\scheduler;
|
||||
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
|
||||
/**
|
||||
* @phpstan-type Resolver PromiseResolver<list<string>>
|
||||
*/
|
||||
final class TimingsCollectionTask extends AsyncTask{
|
||||
private const TLS_KEY_RESOLVER = "resolver";
|
||||
|
||||
/**
|
||||
* @phpstan-param PromiseResolver<list<string>> $promiseResolver
|
||||
*/
|
||||
public function __construct(PromiseResolver $promiseResolver){
|
||||
$this->storeLocal(self::TLS_KEY_RESOLVER, $promiseResolver);
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
$this->setResult(TimingsHandler::printCurrentThreadRecords());
|
||||
}
|
||||
|
||||
public function onCompletion() : void{
|
||||
/**
|
||||
* @var string[] $result
|
||||
* @phpstan-var list<string> $result
|
||||
*/
|
||||
$result = $this->getResult();
|
||||
/**
|
||||
* @var PromiseResolver $promiseResolver
|
||||
* @phpstan-var PromiseResolver<list<string>> $promiseResolver
|
||||
*/
|
||||
$promiseResolver = $this->fetchLocal(self::TLS_KEY_RESOLVER);
|
||||
|
||||
$promiseResolver->resolve($result);
|
||||
}
|
||||
}
|
60
src/scheduler/TimingsControlTask.php
Normal file
60
src/scheduler/TimingsControlTask.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\scheduler;
|
||||
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
|
||||
final class TimingsControlTask extends AsyncTask{
|
||||
|
||||
private const ENABLE = 1;
|
||||
private const DISABLE = 2;
|
||||
private const RELOAD = 3;
|
||||
|
||||
private function __construct(
|
||||
private int $operation
|
||||
){}
|
||||
|
||||
public static function setEnabled(bool $enable) : self{
|
||||
return new self($enable ? self::ENABLE : self::DISABLE);
|
||||
}
|
||||
|
||||
public static function reload() : self{
|
||||
return new self(self::RELOAD);
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
if($this->operation === self::ENABLE){
|
||||
TimingsHandler::setEnabled(true);
|
||||
\GlobalLogger::get()->debug("Enabled timings");
|
||||
}elseif($this->operation === self::DISABLE){
|
||||
TimingsHandler::setEnabled(false);
|
||||
\GlobalLogger::get()->debug("Disabled timings");
|
||||
}elseif($this->operation === self::RELOAD){
|
||||
TimingsHandler::reload();
|
||||
\GlobalLogger::get()->debug("Reset timings");
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Invalid operation $this->operation");
|
||||
}
|
||||
}
|
||||
}
|
@ -30,12 +30,14 @@ use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\ServerboundPacket;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\scheduler\Task;
|
||||
use pocketmine\scheduler\TaskHandler;
|
||||
use function get_class;
|
||||
use function str_starts_with;
|
||||
|
||||
abstract class Timings{
|
||||
public const GROUP_MINECRAFT = "Minecraft";
|
||||
/** @deprecated No longer used */
|
||||
public const GROUP_BREAKDOWN = "Minecraft - Breakdown";
|
||||
|
||||
private static bool $initialized = false;
|
||||
@ -123,11 +125,16 @@ abstract class Timings{
|
||||
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskProgressUpdate = [];
|
||||
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskCompletion = [];
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskError = [];
|
||||
|
||||
private static TimingsHandler $asyncTaskWorkers;
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskRun = [];
|
||||
|
||||
public static function init() : void{
|
||||
if(self::$initialized){
|
||||
return;
|
||||
@ -187,11 +194,17 @@ abstract class Timings{
|
||||
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync);
|
||||
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync);
|
||||
|
||||
self::$asyncTaskWorkers = new TimingsHandler("Async Task Workers");
|
||||
|
||||
self::$playerCommand = new TimingsHandler("Player Command");
|
||||
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TTask of Task
|
||||
* @phpstan-param TaskHandler<TTask> $task
|
||||
*/
|
||||
public static function getScheduledTaskTimings(TaskHandler $task, int $period) : TimingsHandler{
|
||||
self::init();
|
||||
$name = "Task: " . $task->getTaskName();
|
||||
@ -339,6 +352,9 @@ abstract class Timings{
|
||||
return self::$asyncTaskCompletion[$taskClass];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated No longer used
|
||||
*/
|
||||
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskError[$taskClass])){
|
||||
@ -352,4 +368,18 @@ abstract class Timings{
|
||||
|
||||
return self::$asyncTaskError[$taskClass];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskRunTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskRun[$taskClass])){
|
||||
self::init();
|
||||
self::$asyncTaskRun[$taskClass] = new TimingsHandler(
|
||||
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Run",
|
||||
self::$asyncTaskWorkers,
|
||||
$group
|
||||
);
|
||||
}
|
||||
|
||||
return self::$asyncTaskRun[$taskClass];
|
||||
}
|
||||
}
|
||||
|
@ -23,20 +23,66 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\timings;
|
||||
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_merge;
|
||||
use function array_push;
|
||||
use function hrtime;
|
||||
use function implode;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* @phpstan-type CollectPromise Promise<list<string>>
|
||||
*/
|
||||
class TimingsHandler{
|
||||
private const FORMAT_VERSION = 2; //peak timings fix
|
||||
private const FORMAT_VERSION = 3; //thread timings collection
|
||||
|
||||
private static bool $enabled = false;
|
||||
private static int $timingStart = 0;
|
||||
|
||||
/** @return string[] */
|
||||
public static function printTimings() : array{
|
||||
/** @phpstan-var ObjectSet<\Closure(bool $enable) : void> */
|
||||
private static ?ObjectSet $toggleCallbacks = null;
|
||||
/** @phpstan-var ObjectSet<\Closure() : void> */
|
||||
private static ?ObjectSet $reloadCallbacks = null;
|
||||
/** @phpstan-var ObjectSet<\Closure() : list<CollectPromise>> */
|
||||
private static ?ObjectSet $collectCallbacks = null;
|
||||
|
||||
/**
|
||||
* @phpstan-template T of object
|
||||
* @phpstan-param ?ObjectSet<T> $where
|
||||
* @phpstan-param-out ObjectSet<T> $where
|
||||
* @phpstan-return ObjectSet<T>
|
||||
*/
|
||||
private static function lazyGetSet(?ObjectSet &$where) : ObjectSet{
|
||||
//workaround for phpstan bug - allows us to ignore 1 error instead of 6 without suppressing other errors
|
||||
return $where ??= new ObjectSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return ObjectSet<\Closure(bool $enable) : void>
|
||||
*/
|
||||
public static function getToggleCallbacks() : ObjectSet{ return self::lazyGetSet(self::$toggleCallbacks); }
|
||||
|
||||
/**
|
||||
* @phpstan-return ObjectSet<\Closure() : void>
|
||||
*/
|
||||
public static function getReloadCallbacks() : ObjectSet{ return self::lazyGetSet(self::$reloadCallbacks); }
|
||||
|
||||
/**
|
||||
* @phpstan-return ObjectSet<\Closure() : list<CollectPromise>>
|
||||
*/
|
||||
public static function getCollectCallbacks() : ObjectSet{ return self::lazyGetSet(self::$collectCallbacks); }
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public static function printCurrentThreadRecords() : array{
|
||||
$threadId = NativeThread::getCurrentThread()?->getThreadId();
|
||||
$groups = [];
|
||||
|
||||
foreach(TimingsRecord::getAll() as $timings){
|
||||
@ -49,7 +95,7 @@ class TimingsHandler{
|
||||
|
||||
$avg = $time / $count;
|
||||
|
||||
$group = $timings->getGroup();
|
||||
$group = $timings->getGroup() . ($threadId !== null ? " ThreadId: $threadId" : "");
|
||||
$groups[$group][] = implode(" ", [
|
||||
$timings->getName(),
|
||||
"Time: $time",
|
||||
@ -72,6 +118,15 @@ class TimingsHandler{
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private static function printFooter() : array{
|
||||
$result = [];
|
||||
|
||||
$result[] = "# Version " . Server::getInstance()->getVersion();
|
||||
$result[] = "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion();
|
||||
|
||||
@ -79,29 +134,95 @@ class TimingsHandler{
|
||||
|
||||
$sampleTime = hrtime(true) - self::$timingStart;
|
||||
$result[] = "Sample time $sampleTime (" . ($sampleTime / 1000000000) . "s)";
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This only collects timings from the main thread. Collecting timings from all threads is an async
|
||||
* operation, so it can't be done synchronously.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function printTimings() : array{
|
||||
$records = self::printCurrentThreadRecords();
|
||||
$footer = self::printFooter();
|
||||
|
||||
return [...$records, ...$footer];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects timings asynchronously, allowing timings from multiple threads to be aggregated into a single report.
|
||||
*
|
||||
* NOTE: You need to add a callback to collectCallbacks if you want to include timings from other threads. They
|
||||
* won't be automatically collected if you don't, since the main thread has no way to access them.
|
||||
*
|
||||
* This is an asynchronous operation, and the result is returned as a promise.
|
||||
* The caller must add a callback to the returned promise to get the complete timings report.
|
||||
*
|
||||
* @phpstan-return Promise<list<string>>
|
||||
*/
|
||||
public static function requestPrintTimings() : Promise{
|
||||
$thisThreadRecords = self::printCurrentThreadRecords();
|
||||
|
||||
$otherThreadRecordPromises = [];
|
||||
if(self::$collectCallbacks !== null){
|
||||
foreach(self::$collectCallbacks as $callback){
|
||||
$callbackPromises = $callback();
|
||||
array_push($otherThreadRecordPromises, ...$callbackPromises);
|
||||
}
|
||||
}
|
||||
|
||||
$resolver = new PromiseResolver();
|
||||
Promise::all($otherThreadRecordPromises)->onCompletion(
|
||||
function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{
|
||||
$resolver->resolve([...$thisThreadRecords, ...array_merge(...$promisedRecords), ...self::printFooter()]);
|
||||
},
|
||||
function() : void{
|
||||
throw new \AssertionError("This promise is not expected to be rejected");
|
||||
}
|
||||
);
|
||||
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
|
||||
public static function isEnabled() : bool{
|
||||
return self::$enabled;
|
||||
}
|
||||
|
||||
public static function setEnabled(bool $enable = true) : void{
|
||||
if($enable === self::$enabled){
|
||||
return;
|
||||
}
|
||||
self::$enabled = $enable;
|
||||
self::reload();
|
||||
self::internalReload();
|
||||
if(self::$toggleCallbacks !== null){
|
||||
foreach(self::$toggleCallbacks as $callback){
|
||||
$callback($enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getStartTime() : float{
|
||||
return self::$timingStart;
|
||||
}
|
||||
|
||||
public static function reload() : void{
|
||||
private static function internalReload() : void{
|
||||
TimingsRecord::reset();
|
||||
if(self::$enabled){
|
||||
self::$timingStart = hrtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static function reload() : void{
|
||||
self::internalReload();
|
||||
if(self::$reloadCallbacks !== null){
|
||||
foreach(self::$reloadCallbacks as $callback){
|
||||
$callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function tick(bool $measure = true) : void{
|
||||
if(self::$enabled){
|
||||
TimingsRecord::tick($measure);
|
||||
|
@ -67,6 +67,8 @@ use function is_nan;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function mb_check_encoding;
|
||||
use function mt_getrandmax;
|
||||
use function mt_rand;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
use function ob_start;
|
||||
@ -688,4 +690,12 @@ final class Utils{
|
||||
//jit not available
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random float between 0.0 and 1.0
|
||||
* Drop-in replacement for lcg_value()
|
||||
*/
|
||||
public static function getRandomFloat() : float{
|
||||
return mt_rand() / mt_getrandmax();
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ use pocketmine\ServerConfigGroup;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\utils\ReversePriorityQueue;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\biome\Biome;
|
||||
use pocketmine\world\biome\BiomeRegistry;
|
||||
use pocketmine\world\format\Chunk;
|
||||
@ -120,7 +121,6 @@ use function get_class;
|
||||
use function gettype;
|
||||
use function is_a;
|
||||
use function is_object;
|
||||
use function lcg_value;
|
||||
use function max;
|
||||
use function microtime;
|
||||
use function min;
|
||||
@ -1998,10 +1998,10 @@ class World implements ChunkManager{
|
||||
return null;
|
||||
}
|
||||
|
||||
$itemEntity = new ItemEntity(Location::fromObject($source, $this, lcg_value() * 360, 0), $item);
|
||||
$itemEntity = new ItemEntity(Location::fromObject($source, $this, Utils::getRandomFloat() * 360, 0), $item);
|
||||
|
||||
$itemEntity->setPickupDelay($delay);
|
||||
$itemEntity->setMotion($motion ?? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1));
|
||||
$itemEntity->setMotion($motion ?? new Vector3(Utils::getRandomFloat() * 0.2 - 0.1, 0.2, Utils::getRandomFloat() * 0.2 - 0.1));
|
||||
$itemEntity->spawnToAll();
|
||||
|
||||
return $itemEntity;
|
||||
@ -2018,9 +2018,9 @@ class World implements ChunkManager{
|
||||
$orbs = [];
|
||||
|
||||
foreach(ExperienceOrb::splitIntoOrbSizes($amount) as $split){
|
||||
$orb = new ExperienceOrb(Location::fromObject($pos, $this, lcg_value() * 360, 0), $split);
|
||||
$orb = new ExperienceOrb(Location::fromObject($pos, $this, Utils::getRandomFloat() * 360, 0), $split);
|
||||
|
||||
$orb->setMotion(new Vector3((lcg_value() * 0.2 - 0.1) * 2, lcg_value() * 0.4, (lcg_value() * 0.2 - 0.1) * 2));
|
||||
$orb->setMotion(new Vector3((Utils::getRandomFloat() * 0.2 - 0.1) * 2, Utils::getRandomFloat() * 0.4, (Utils::getRandomFloat() * 0.2 - 0.1) * 2));
|
||||
$orb->spawnToAll();
|
||||
|
||||
$orbs[] = $orb;
|
||||
@ -2173,19 +2173,25 @@ class World implements ChunkManager{
|
||||
|
||||
if($player !== null){
|
||||
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK);
|
||||
if($player->isSneaking()){
|
||||
$ev->setUseItem(false);
|
||||
$ev->setUseBlock($item->isNull()); //opening doors is still possible when sneaking if using an empty hand
|
||||
}
|
||||
if($player->isSpectator()){
|
||||
$ev->cancel(); //set it to cancelled so plugins can bypass this
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){
|
||||
if($ev->useBlock() && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
|
||||
if($result !== ItemUseResult::NONE){
|
||||
return $result === ItemUseResult::SUCCESS;
|
||||
if($ev->useItem()){
|
||||
$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
|
||||
if($result !== ItemUseResult::NONE){
|
||||
return $result === ItemUseResult::SUCCESS;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return false;
|
||||
|
35
src/world/sound/BottleEmptySound.php
Normal file
35
src/world/sound/BottleEmptySound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class BottleEmptySound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::BOTTLE_EMPTY, $pos, false)];
|
||||
}
|
||||
}
|
46
src/world/sound/GoatHornSound.php
Normal file
46
src/world/sound/GoatHornSound.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\item\GoatHornType;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
class GoatHornSound implements Sound{
|
||||
public function __construct(private GoatHornType $goatHornType){}
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(match($this->goatHornType){
|
||||
GoatHornType::PONDER => LevelSoundEvent::HORN_CALL0,
|
||||
GoatHornType::SING => LevelSoundEvent::HORN_CALL1,
|
||||
GoatHornType::SEEK => LevelSoundEvent::HORN_CALL2,
|
||||
GoatHornType::FEEL => LevelSoundEvent::HORN_CALL3,
|
||||
GoatHornType::ADMIRE => LevelSoundEvent::HORN_CALL4,
|
||||
GoatHornType::CALL => LevelSoundEvent::HORN_CALL5,
|
||||
GoatHornType::YEARN => LevelSoundEvent::HORN_CALL6,
|
||||
GoatHornType::DREAM => LevelSoundEvent::HORN_CALL7
|
||||
}, $pos, false)];
|
||||
}
|
||||
}
|
34
src/world/sound/IceBombHitSound.php
Normal file
34
src/world/sound/IceBombHitSound.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
final class IceBombHitSound implements Sound{
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::ICEBOMB_HIT, $pos, false)];
|
||||
}
|
||||
}
|
27
start.sh
27
start.sh
@ -44,6 +44,27 @@ fi
|
||||
|
||||
LOOPS=0
|
||||
|
||||
handle_exit_code() {
|
||||
local exitcode=$1
|
||||
if [ "$exitcode" -eq 134 ] || [ "$exitcode" -eq 139 ]; then #SIGABRT/SIGSEGV
|
||||
echo ""
|
||||
echo "ERROR: The server process was killed due to a critical error (code $exitcode) which could indicate a problem with PHP."
|
||||
echo "Updating your PHP binary is recommended."
|
||||
echo "If this keeps happening, please open a bug report."
|
||||
echo ""
|
||||
elif [ "$exitcode" -eq 143 ]; then #SIGKILL, maybe user intervention
|
||||
echo ""
|
||||
echo "WARNING: Server was forcibly killed!"
|
||||
echo "If you didn't kill the server manually, this probably means the server used too much memory and was killed by the system's OOM Killer."
|
||||
echo "Please ensure your system has enough available RAM."
|
||||
echo ""
|
||||
elif [ "$exitcode" -ne 0 ] && [ "$exitcode" -ne 137 ]; then #normal exit / SIGTERM
|
||||
echo ""
|
||||
echo "WARNING: Server did not shut down correctly! (code $exitcode)"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
set +e
|
||||
|
||||
if [ "$DO_LOOP" == "yes" ]; then
|
||||
@ -52,11 +73,15 @@ if [ "$DO_LOOP" == "yes" ]; then
|
||||
echo "Restarted $LOOPS times"
|
||||
fi
|
||||
"$PHP_BINARY" "$POCKETMINE_FILE" "$@"
|
||||
handle_exit_code $?
|
||||
echo "To escape the loop, press CTRL+C now. Otherwise, wait 5 seconds for the server to restart."
|
||||
echo ""
|
||||
sleep 5
|
||||
((LOOPS++))
|
||||
done
|
||||
else
|
||||
exec "$PHP_BINARY" "$POCKETMINE_FILE" "$@"
|
||||
"$PHP_BINARY" "$POCKETMINE_FILE" "$@"
|
||||
exitcode=$?
|
||||
handle_exit_code $exitcode
|
||||
exit $exitcode
|
||||
fi
|
||||
|
@ -801,7 +801,7 @@ parameters:
|
||||
path: ../../../src/scheduler/BulkCurlTask.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method getNextRun\\(\\) on array\\<string, int\\|pocketmine\\\\scheduler\\\\TaskHandler\\>\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\.$#"
|
||||
message: "#^Cannot call method getNextRun\\(\\) on array\\<string, int\\|pocketmine\\\\scheduler\\\\TaskHandler\\<covariant pocketmine\\\\scheduler\\\\Task\\>\\>\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\<covariant pocketmine\\\\scheduler\\\\Task\\>\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/scheduler/TaskScheduler.php
|
||||
|
||||
|
@ -20,6 +20,56 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/block/DoubleTallGrass.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:ACACIA_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:BIRCH_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CHERRY_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CRIMSON_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:DARK_OAK_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:JUNGLE_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:MANGROVE_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:OAK_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:SPRUCE_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:WARPED_SIGN\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/VanillaBlocks.php
|
||||
|
||||
-
|
||||
message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
@ -40,6 +90,11 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/plugin/PluginManager.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\<T of object\\> but returns pocketmine\\\\utils\\\\ObjectSet\\<object\\>\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/timings/TimingsHandler.php
|
||||
|
||||
-
|
||||
message: "#^Casting to int something that's already int\\.$#"
|
||||
count: 1
|
||||
|
@ -51,6 +51,7 @@ class BlockTypeIdsTest extends TestCase{
|
||||
foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){
|
||||
$expected = $block->getTypeId();
|
||||
$actual = $reflect->getConstant($name);
|
||||
self::assertNotFalse($actual, "VanillaBlocks::$name() does not have a BlockTypeIds constant");
|
||||
self::assertSame($expected, $actual, "VanillaBlocks::$name() does not match BlockTypeIds::$name");
|
||||
}
|
||||
}
|
||||
|
92
tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php
Normal file
92
tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?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\console;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function mt_rand;
|
||||
use function str_repeat;
|
||||
|
||||
final class ConsoleReaderChildProcessUtilsTest extends TestCase{
|
||||
|
||||
/**
|
||||
* @phpstan-return \Generator<int, array{string}, void, void>
|
||||
*/
|
||||
public static function commandStringProvider() : \Generator{
|
||||
yield ["stop"];
|
||||
yield ["pocketmine:status"];
|
||||
yield [str_repeat("s", 1000)];
|
||||
yield ["time set day"];
|
||||
yield ["give \"Steve\" golden_apple"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider commandStringProvider
|
||||
*/
|
||||
public function testCreateParseSymmetry(string $input) : void{
|
||||
$counterCreate = $counterParse = mt_rand();
|
||||
$message = ConsoleReaderChildProcessUtils::createMessage($input, $counterCreate);
|
||||
$parsedInput = ConsoleReaderChildProcessUtils::parseMessage($message, $counterParse);
|
||||
|
||||
self::assertSame($input, $parsedInput);
|
||||
}
|
||||
|
||||
public function testCreateMessage() : void{
|
||||
$counter = 0;
|
||||
|
||||
ConsoleReaderChildProcessUtils::createMessage("", $counter);
|
||||
self::assertSame(1, $counter, "createMessage should always bump the counter");
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return \Generator<int, array{string, bool}, void, void>
|
||||
*/
|
||||
public static function parseMessageProvider() : \Generator{
|
||||
$counter = 0;
|
||||
yield [ConsoleReaderChildProcessUtils::createMessage("", $counter), true];
|
||||
|
||||
yield ["", false]; //keepalive message, doesn't bump counter
|
||||
|
||||
$counter = 1;
|
||||
yield [ConsoleReaderChildProcessUtils::createMessage("", $counter), false]; //mismatched counter
|
||||
|
||||
$counter = 0;
|
||||
yield ["a" . ConsoleReaderChildProcessUtils::TOKEN_DELIMITER . "b", false]; //message with delimiter but not a valid IPC message
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parseMessageProvider
|
||||
*/
|
||||
public static function testParseMessage(string $message, bool $valid) : void{
|
||||
$counter = $oldCounter = 0;
|
||||
|
||||
$input = ConsoleReaderChildProcessUtils::parseMessage($message, $counter);
|
||||
if(!$valid){
|
||||
self::assertNull($input, "Result should be null on invalid message");
|
||||
self::assertSame($oldCounter, $counter, "Counter shouldn't be bumped on invalid message");
|
||||
}else{
|
||||
self::assertNotNull($input, "This was a valid message, expected a result");
|
||||
self::assertSame($oldCounter + 1, $counter, "Counter should be bumped on valid message parse");
|
||||
}
|
||||
}
|
||||
}
|
79
tests/phpunit/event/EventTest.php
Normal file
79
tests/phpunit/event/EventTest.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\event\fixtures\TestChildEvent;
|
||||
use pocketmine\event\fixtures\TestGrandchildEvent;
|
||||
use pocketmine\event\fixtures\TestParentEvent;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\Server;
|
||||
|
||||
final class EventTest extends TestCase{
|
||||
|
||||
private Plugin $mockPlugin;
|
||||
private PluginManager $pluginManager;
|
||||
|
||||
protected function setUp() : void{
|
||||
HandlerListManager::global()->unregisterAll();
|
||||
|
||||
//TODO: this is a really bad hack and could break any time if PluginManager decides to access its Server field
|
||||
//we really need to make it possible to register events without a Plugin or Server context
|
||||
$mockServer = $this->createMock(Server::class);
|
||||
$this->mockPlugin = self::createStub(Plugin::class);
|
||||
$this->mockPlugin->method('isEnabled')->willReturn(true);
|
||||
|
||||
$this->pluginManager = new PluginManager($mockServer, null);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() : void{
|
||||
HandlerListManager::global()->unregisterAll();
|
||||
}
|
||||
|
||||
public function testHandlerInheritance() : void{
|
||||
$expectedOrder = [
|
||||
TestGrandchildEvent::class,
|
||||
TestChildEvent::class,
|
||||
TestParentEvent::class
|
||||
];
|
||||
$actualOrder = [];
|
||||
|
||||
foreach($expectedOrder as $class){
|
||||
$this->pluginManager->registerEvent(
|
||||
$class,
|
||||
function(TestParentEvent $event) use (&$actualOrder, $class) : void{
|
||||
$actualOrder[] = $class;
|
||||
},
|
||||
EventPriority::NORMAL,
|
||||
$this->mockPlugin
|
||||
);
|
||||
}
|
||||
|
||||
$event = new TestGrandchildEvent();
|
||||
$event->call();
|
||||
|
||||
self::assertSame($expectedOrder, $actualOrder, "Expected event handlers to be called from most specific to least specific");
|
||||
}
|
||||
}
|
@ -24,6 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\event;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\event\fixtures\TestAbstractAllowHandleEvent;
|
||||
use pocketmine\event\fixtures\TestAbstractEvent;
|
||||
use pocketmine\event\fixtures\TestConcreteEvent;
|
||||
use pocketmine\event\fixtures\TestConcreteExtendsAbstractEvent;
|
||||
use pocketmine\event\fixtures\TestConcreteExtendsAllowHandleEvent;
|
||||
use pocketmine\event\fixtures\TestConcreteExtendsConcreteEvent;
|
||||
|
||||
class HandlerListManagerTest extends TestCase{
|
||||
|
||||
|
@ -21,7 +21,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
namespace pocketmine\event\fixtures;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
|
||||
/**
|
||||
* @allowHandle
|
@ -21,7 +21,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
namespace pocketmine\event\fixtures;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
|
||||
abstract class TestAbstractEvent extends Event{
|
||||
|
@ -21,8 +21,8 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pmmp\TesterPlugin\event;
|
||||
namespace pocketmine\event\fixtures;
|
||||
|
||||
class ChildEvent extends ParentEvent{
|
||||
class TestChildEvent extends TestParentEvent{
|
||||
|
||||
}
|
@ -21,7 +21,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
namespace pocketmine\event\fixtures;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
|
||||
class TestConcreteEvent extends Event{
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
namespace pocketmine\event\fixtures;
|
||||
|
||||
class TestConcreteExtendsAbstractEvent extends TestAbstractEvent{
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event;
|
||||
namespace pocketmine\event\fixtures;
|
||||
|
||||
class TestConcreteExtendsAllowHandleEvent extends TestAbstractAllowHandleEvent{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user