Merge branch 'minor-next' into feat/async-events

This commit is contained in:
Dylan K. Taylor 2024-11-20 15:43:17 +00:00
commit 4451770ca3
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
65 changed files with 2393 additions and 1447 deletions

View File

@ -7,13 +7,13 @@ assignees: ''
---
<!--- tell us what you want -->
## Description
<!--- Describe the problem you want to solve -->
## Problem description
<!--- explain why you want this and why it's a good idea -->
## Justification
<!--- Describe what changes you want to make to solve this problem -->
## Proposed solution
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
## Alternative methods
## Alternative solutions that don't require API changes

View File

@ -24,11 +24,9 @@
<!-- Suggest any actions to be done before/after merging this pull request -->
<!-- For example, future changes that this PR lays the groundwork for -->
## In-Game Testing
## Tests
<!--
GAMEPLAY FEATURE PRS MUST BE TESTED IN-GAME.
Include any screenshots or videos of in-game testing here.
If this PR affects gameplay or user experience in some way, it must be manually tested.
Include any screenshots or videos of manual testing here.
Any test plugin code should also be pasted here if it can't be adapted to a PHPUnit test.
If this isn't a gameplay PR, you can delete this section.
-->

View File

@ -13,30 +13,26 @@ on:
- reopened
- ready_for_review
permissions:
pull-requests: write
jobs:
approve:
name: Auto approve
dispatch:
name: Request approval
runs-on: ubuntu-latest
if: '! github.event.pull_request.draft'
steps:
- name: Check if PR author has write access
id: check-permission
uses: actions-cool/check-user-permission@v2
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
require: write
username: ${{ github.event.pull_request.user.login }}
#technically this would be fine for dependabot but generally bots don't count as team members
check-bot: true
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
owner: ${{ github.repository_owner }}
repositories: RestrictedActions
#TODO: Some way to avoid unnecessary repeated reviews would be nice here
- name: Approve PR if authorized
if: steps.check-permission.outputs.require-result == 'true' && steps.check-permission.outputs.check-result == 'false'
uses: juliangruber/approve-pull-request-action@v2
- name: Dispatch restricted action
uses: peter-evans/repository-dispatch@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number }}
token: ${{ steps.generate-token.outputs.token }}
repository: ${{ github.repository_owner }}/RestrictedActions
event-type: auto_approve_collaborator_pr
client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}" }'

View File

@ -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.21.0",
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
"pocketmine/nbt": "~1.0.0",

14
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b2fbf6e7a9d650341dc71fa4dd124681",
"content-hash": "476374fb3d22e26a97c1dea8c6736faf",
"packages": [
{
"name": "adhocore/json-comment",
@ -420,16 +420,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.19.6",
"version": "2.21.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e"
"reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e",
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e",
"url": "https://api.github.com/repos/pmmp/Language/zipball/fdba0f764d6281f64e5968dca94fdab96bf4e167",
"reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167",
"shasum": ""
},
"type": "library",
@ -437,9 +437,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.21.1"
},
"time": "2023-08-08T16:53:23+00:00"
"time": "2024-11-14T23:11:22+00:00"
},
{
"name": "pocketmine/log",

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
/**
* "Flowable" blocks are destroyed if water flows into the same space as the block. These blocks usually don't have any
@ -40,6 +41,11 @@ abstract class Flowable extends Transparent{
return false;
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return (!$this->canBeFlowedInto() || !$blockReplace instanceof Liquid) &&
parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
/**
* @return AxisAlignedBB[]
*/

View File

@ -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;
}
}

View File

@ -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

View File

@ -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,
});
}
}

View 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;
}
}

View 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;
}
}

View File

@ -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;
@ -128,7 +129,8 @@ class SimpleCommandMap implements CommandMap{
new TitleCommand(),
new TransferServerCommand(),
new VersionCommand(),
new WhitelistCommand()
new WhitelistCommand(),
new XpCommand(),
]);
}

View File

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

View File

@ -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.

View File

@ -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");

View 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;
}
}

View 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);
}
}
}

View 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;
}

View File

@ -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());
@ -483,6 +486,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(),

View File

@ -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;
@ -1560,6 +1561,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.
*/

View File

@ -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;
@ -92,6 +93,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']);

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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();
}
}

View 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()));
}
}

View File

@ -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);

View File

@ -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)
*/

View File

@ -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);

View File

@ -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{

View 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;
}
}

View File

@ -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; }
}

59
src/item/EndCrystal.php Normal file
View 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;
}
}

71
src/item/GoatHorn.php Normal file
View 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
View 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;
}

View File

@ -324,8 +324,10 @@ 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 FIRST_UNUSED_ITEM_ID = 20288;
public const FIRST_UNUSED_ITEM_ID = 20290;
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

View File

@ -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());

View File

@ -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()
@ -339,8 +342,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 +384,238 @@ 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("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("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 +623,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"));
}
}

View File

@ -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, []);
}
@ -1536,6 +1583,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 +2111,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, []);
}

View File

@ -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";
@ -336,6 +345,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 +460,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";

View File

@ -493,15 +493,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:
@ -529,9 +532,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){
@ -668,7 +671,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{
@ -682,7 +685,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;
@ -998,7 +1001,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;
}

View File

@ -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";

View File

@ -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]);
}
}

View File

@ -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;
@ -1709,29 +1710,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.
*

View File

@ -80,7 +80,7 @@ use function preg_match_all;
use function preg_replace;
use function shell_exec;
use function spl_object_id;
use function str_ends_with;
use function str_contains;
use function str_pad;
use function str_split;
use function str_starts_with;
@ -121,7 +121,7 @@ final class Utils{
*/
public static function getNiceClosureName(\Closure $closure) : string{
$func = new \ReflectionFunction($closure);
if(!str_ends_with($func->getName(), '{closure}')){
if(!str_contains($func->getName(), '{closure')){
//closure wraps a named function, can be done with reflection or fromCallable()
//isClosure() is useless here because it just tells us if $func is reflecting a Closure object

View File

@ -2037,7 +2037,7 @@ class World implements ChunkManager{
* @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
* @phpstan-param-out Item $item
*/
public function useBreakOn(Vector3 $vector, Item &$item = null, ?Player $player = null, bool $createParticles = false, array &$returnedItems = []) : bool{
public function useBreakOn(Vector3 $vector, ?Item &$item = null, ?Player $player = null, bool $createParticles = false, array &$returnedItems = []) : bool{
$vector = $vector->floor();
$chunkX = $vector->getFloorX() >> Chunk::COORD_BIT_SIZE;
@ -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;

View 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)];
}
}

View File

@ -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

View File

@ -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

View File

@ -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");
}
}

View 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");
}
}
}

View File

@ -0,0 +1,68 @@
<?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{
public function testHandlerInheritance() : void{
//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);
$mockPlugin = self::createStub(Plugin::class);
$mockPlugin->method('isEnabled')->willReturn(true);
$pluginManager = new PluginManager($mockServer, null);
$expectedOrder = [
TestGrandchildEvent::class,
TestChildEvent::class,
TestParentEvent::class
];
$actualOrder = [];
foreach($expectedOrder as $class){
$pluginManager->registerEvent(
$class,
function(TestParentEvent $event) use (&$actualOrder, $class) : void{
$actualOrder[] = $class;
},
EventPriority::NORMAL,
$mockPlugin
);
}
$event = new TestGrandchildEvent();
$event->call();
self::assertSame($expectedOrder, $actualOrder, "Expected event handlers to be called from most specific to least specific");
}
}

View File

@ -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{

View File

@ -21,7 +21,9 @@
declare(strict_types=1);
namespace pocketmine\event;
namespace pocketmine\event\fixtures;
use pocketmine\event\Event;
/**
* @allowHandle

View File

@ -21,7 +21,9 @@
declare(strict_types=1);
namespace pocketmine\event;
namespace pocketmine\event\fixtures;
use pocketmine\event\Event;
abstract class TestAbstractEvent extends Event{

View File

@ -21,8 +21,8 @@
declare(strict_types=1);
namespace pmmp\TesterPlugin\event;
namespace pocketmine\event\fixtures;
class ChildEvent extends ParentEvent{
class TestChildEvent extends TestParentEvent{
}

View File

@ -21,7 +21,9 @@
declare(strict_types=1);
namespace pocketmine\event;
namespace pocketmine\event\fixtures;
use pocketmine\event\Event;
class TestConcreteEvent extends Event{

View File

@ -21,7 +21,7 @@
declare(strict_types=1);
namespace pocketmine\event;
namespace pocketmine\event\fixtures;
class TestConcreteExtendsAbstractEvent extends TestAbstractEvent{

View File

@ -21,7 +21,7 @@
declare(strict_types=1);
namespace pocketmine\event;
namespace pocketmine\event\fixtures;
class TestConcreteExtendsAllowHandleEvent extends TestAbstractAllowHandleEvent{

View File

@ -21,7 +21,7 @@
declare(strict_types=1);
namespace pocketmine\event;
namespace pocketmine\event\fixtures;
class TestConcreteExtendsConcreteEvent extends TestConcreteEvent{

View File

@ -21,8 +21,8 @@
declare(strict_types=1);
namespace pmmp\TesterPlugin\event;
namespace pocketmine\event\fixtures;
class ParentEvent extends \pocketmine\event\Event{
class TestGrandchildEvent extends TestChildEvent{
}

View File

@ -21,8 +21,10 @@
declare(strict_types=1);
namespace pmmp\TesterPlugin\event;
namespace pocketmine\event\fixtures;
class GrandchildEvent extends ChildEvent{
use pocketmine\event\Event;
class TestParentEvent extends Event{
}

View File

@ -54,6 +54,7 @@ class ItemTypeIdsTest extends TestCase{
}
$expected = $item->getTypeId();
$actual = $reflect->getConstant($name);
self::assertNotFalse($actual, "VanillaItems::$name() does not have an ItemTypeIds constant");
self::assertSame($expected, $actual, "VanillaItems::$name() type ID does not match ItemTypeIds::$name");
}
}

View File

@ -1,88 +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 pmmp\TesterPlugin;
use pmmp\TesterPlugin\event\ChildEvent;
use pmmp\TesterPlugin\event\GrandchildEvent;
use pmmp\TesterPlugin\event\ParentEvent;
use pocketmine\event\EventPriority;
use function implode;
final class EventHandlerInheritanceTest extends Test{
private const EXPECTED_ORDER = [
GrandchildEvent::class,
ChildEvent::class,
ParentEvent::class
];
/** @var string[] */
private array $callOrder = [];
public function getName() : string{
return "Event Handler Inheritance Test";
}
public function getDescription() : string{
return "Tests that child events are correctly passed to parent event handlers";
}
public function run() : void{
$plugin = $this->getPlugin();
$plugin->getServer()->getPluginManager()->registerEvent(
ParentEvent::class,
function(ParentEvent $event) : void{
$this->callOrder[] = ParentEvent::class;
},
EventPriority::NORMAL,
$plugin
);
$plugin->getServer()->getPluginManager()->registerEvent(
ChildEvent::class,
function(ChildEvent $event) : void{
$this->callOrder[] = ChildEvent::class;
},
EventPriority::NORMAL,
$plugin
);
$plugin->getServer()->getPluginManager()->registerEvent(
GrandchildEvent::class,
function(GrandchildEvent $event) : void{
$this->callOrder[] = GrandchildEvent::class;
},
EventPriority::NORMAL,
$plugin
);
$event = new GrandchildEvent();
$event->call();
if($this->callOrder === self::EXPECTED_ORDER){
$this->setResult(Test::RESULT_OK);
}else{
$plugin->getLogger()->error("Expected order: " . implode(", ", self::EXPECTED_ORDER) . ", got: " . implode(", ", $this->callOrder));
$this->setResult(Test::RESULT_FAILED);
}
}
}

View File

@ -57,7 +57,6 @@ class Main extends PluginBase implements Listener{
}), 10);
$this->waitingTests = [
new EventHandlerInheritanceTest($this),
new AsyncEventInheritanceTest($this),
new AsyncEventConcurrencyTest($this),
new AsyncEventPriorityTest($this)