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 --> <!--- Describe the problem you want to solve -->
## Description ## Problem description
<!--- explain why you want this and why it's a good idea --> <!--- Describe what changes you want to make to solve this problem -->
## Justification ## Proposed solution
<!--- (optional) describe alternative methods you've explored to achieve your goal --> <!--- (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 --> <!-- Suggest any actions to be done before/after merging this pull request -->
<!-- For example, future changes that this PR lays the groundwork for --> <!-- For example, future changes that this PR lays the groundwork for -->
## In-Game Testing ## Tests
<!-- <!--
GAMEPLAY FEATURE PRS MUST BE TESTED IN-GAME. If this PR affects gameplay or user experience in some way, it must be manually tested.
Include any screenshots or videos of in-game testing here. 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. 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 - reopened
- ready_for_review - ready_for_review
permissions:
pull-requests: write
jobs: jobs:
approve: dispatch:
name: Auto approve name: Request approval
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: '! github.event.pull_request.draft'
steps: steps:
- name: Check if PR author has write access - name: Generate access token
id: check-permission id: generate-token
uses: actions-cool/check-user-permission@v2 uses: actions/create-github-app-token@v1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
require: write private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
username: ${{ github.event.pull_request.user.login }} owner: ${{ github.repository_owner }}
#technically this would be fine for dependabot but generally bots don't count as team members repositories: RestrictedActions
check-bot: true
#TODO: Some way to avoid unnecessary repeated reviews would be nice here - name: Dispatch restricted action
uses: peter-evans/repository-dispatch@v3
- 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
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} token: ${{ steps.generate-token.outputs.token }}
number: ${{ github.event.pull_request.number }} 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/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0", "pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.7.0", "pocketmine/errorhandler": "^0.7.0",
"pocketmine/locale-data": "~2.19.0", "pocketmine/locale-data": "~2.21.0",
"pocketmine/log": "^0.4.0", "pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0", "pocketmine/math": "~1.0.0",
"pocketmine/nbt": "~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", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "b2fbf6e7a9d650341dc71fa4dd124681", "content-hash": "476374fb3d22e26a97c1dea8c6736faf",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -420,16 +420,16 @@
}, },
{ {
"name": "pocketmine/locale-data", "name": "pocketmine/locale-data",
"version": "2.19.6", "version": "2.21.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/Language.git", "url": "https://github.com/pmmp/Language.git",
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e" "reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e", "url": "https://api.github.com/repos/pmmp/Language/zipball/fdba0f764d6281f64e5968dca94fdab96bf4e167",
"reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e", "reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -437,9 +437,9 @@
"description": "Language resources used by PocketMine-MP", "description": "Language resources used by PocketMine-MP",
"support": { "support": {
"issues": "https://github.com/pmmp/Language/issues", "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", "name": "pocketmine/log",

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\math\AxisAlignedBB; 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 * "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; 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[] * @return AxisAlignedBB[]
*/ */

View File

@ -24,60 +24,20 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\MultiAnySupportTrait;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\World; use pocketmine\world\World;
use function array_key_first;
use function count; use function count;
use function shuffle; use function shuffle;
class GlowLichen extends Transparent{ class GlowLichen extends Transparent{
use MultiAnySupportTrait;
/** @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;
}
public function getLightLevel() : int{ public function getLightLevel() : int{
return 7; return 7;
@ -102,39 +62,11 @@ class GlowLichen extends Transparent{
return true; 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 : []; * @return int[]
$availableFaces = $this->getAvailableFaces(); */
protected function getInitialPlaceFaces(Block $blockReplace) : array{
if(count($availableFaces) === 0){ return $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
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);
}
}
} }
private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{ private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{
@ -261,17 +193,4 @@ class GlowLichen extends Transparent{
public function getFlammability() : int{ public function getFlammability() : int{
return 100; 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{ class Sugarcane extends Flowable{
use AgeableTrait; use AgeableTrait;
use StaticSupportTrait; use StaticSupportTrait {
onNearbyBlockChange as onSupportBlockChange;
}
public const MAX_AGE = 15; public const MAX_AGE = 15;
@ -97,7 +99,13 @@ class Sugarcane extends Flowable{
} }
public function onRandomTick() : void{ 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){ if($this->age === self::MAX_AGE){
$this->grow($this->position); $this->grow($this->position);
}else{ }else{
@ -123,4 +131,23 @@ class Sugarcane extends Flowable{
return false; 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\VanillaCommand;
use pocketmine\command\defaults\VersionCommand; use pocketmine\command\defaults\VersionCommand;
use pocketmine\command\defaults\WhitelistCommand; use pocketmine\command\defaults\WhitelistCommand;
use pocketmine\command\defaults\XpCommand;
use pocketmine\command\utils\CommandStringHelper; use pocketmine\command\utils\CommandStringHelper;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
@ -128,7 +129,8 @@ class SimpleCommandMap implements CommandMap{
new TitleCommand(), new TitleCommand(),
new TransferServerCommand(), new TransferServerCommand(),
new VersionCommand(), 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 cli_set_process_title;
use function count; use function count;
use function dirname; use function dirname;
use function feof;
use function fwrite; 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'; 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'); @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 */ /** @phpstan-var ThreadSafeArray<int, string> $channel */
$channel = new ThreadSafeArray(); $channel = new ThreadSafeArray();
@ -75,15 +73,15 @@ $thread = new class($channel) extends NativeThread{
}; };
$thread->start(NativeThread::INHERIT_NONE); $thread->start(NativeThread::INHERIT_NONE);
while(!feof($socket)){ while(true){
$line = $channel->synchronized(function() use ($channel) : ?string{ $line = $channel->synchronized(function() use ($channel) : ?string{
if(count($channel) === 0){ if(count($channel) === 0){
$channel->wait(1_000_000); $channel->wait(1_000_000);
} }
$line = $channel->shift(); return $channel->shift();
return $line;
}); });
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 //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 //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. //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 base64_encode;
use function fgets; use function fgets;
use function fopen; use function fopen;
use function mt_rand;
use function preg_replace; use function preg_replace;
use function proc_close; use function proc_close;
use function proc_open; use function proc_open;
use function proc_terminate; use function proc_terminate;
use function rtrim;
use function sprintf; use function sprintf;
use function stream_select; 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 function trim;
use const PHP_BINARY; 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 * 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. * communication.
*/ */
final class ConsoleReaderChildProcessDaemon{ final class ConsoleReaderChildProcessDaemon{
public const TOKEN_DELIMITER = ":";
public const TOKEN_HASH_ALGO = "xxh3";
private \PrefixedLogger $logger; private \PrefixedLogger $logger;
/** @var resource */ /** @var resource */
private $subprocess; private $subprocess;
/** @var resource */ /** @var resource */
private $socket; private $socket;
private int $commandTokenSeed;
public function __construct( public function __construct(
\Logger $logger \Logger $logger
){ ){
$this->logger = new \PrefixedLogger($logger, "Console Reader Daemon"); $this->logger = new \PrefixedLogger($logger, "Console Reader Daemon");
$this->commandTokenSeed = mt_rand();
$this->prepareSubprocess(); $this->prepareSubprocess();
} }
private function prepareSubprocess() : void{ 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 //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 :( //the path to avoid the problem. This is an abysmally shitty hack, but here we are :(
$sub = Utils::assumeNotFalse(proc_open( $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"), 2 => fopen("php://stderr", "w"),
], ],
$pipes $pipes
), "Something has gone horribly wrong"); ), "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->subprocess = $sub;
$this->socket = $client; $this->socket = $pipes[1];
} }
private function shutdownSubprocess() : void{ private function shutdownSubprocess() : void{
@ -104,7 +101,6 @@ final class ConsoleReaderChildProcessDaemon{
//the first place). //the first place).
proc_terminate($this->subprocess); proc_terminate($this->subprocess);
proc_close($this->subprocess); proc_close($this->subprocess);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
} }
public function readLine() : ?string{ public function readLine() : ?string{
@ -112,13 +108,27 @@ final class ConsoleReaderChildProcessDaemon{
$w = null; $w = null;
$e = null; $e = null;
if(stream_select($r, $w, $e, 0, 0) === 1){ if(stream_select($r, $w, $e, 0, 0) === 1){
$command = fgets($this->socket); $line = fgets($this->socket);
if($command === false){ if($line === false){
$this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)"); $this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)");
$this->shutdownSubprocess(); $this->shutdownSubprocess();
$this->prepareSubprocess(); $this->prepareSubprocess();
return null; 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("#\\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"); $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\block\VanillaBlocks as Blocks;
use pocketmine\data\bedrock\CompoundTypeIds; use pocketmine\data\bedrock\CompoundTypeIds;
use pocketmine\data\bedrock\DyeColorIdMap; use pocketmine\data\bedrock\DyeColorIdMap;
use pocketmine\data\bedrock\GoatHornTypeIdMap;
use pocketmine\data\bedrock\item\ItemTypeNames as Ids; use pocketmine\data\bedrock\item\ItemTypeNames as Ids;
use pocketmine\data\bedrock\item\SavedItemData as Data; use pocketmine\data\bedrock\item\SavedItemData as Data;
use pocketmine\data\bedrock\MedicineTypeIdMap; use pocketmine\data\bedrock\MedicineTypeIdMap;
@ -38,6 +39,7 @@ use pocketmine\data\bedrock\PotionTypeIdMap;
use pocketmine\data\bedrock\SuspiciousStewTypeIdMap; use pocketmine\data\bedrock\SuspiciousStewTypeIdMap;
use pocketmine\item\Banner; use pocketmine\item\Banner;
use pocketmine\item\Dye; use pocketmine\item\Dye;
use pocketmine\item\GoatHorn;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\Medicine; use pocketmine\item\Medicine;
use pocketmine\item\Potion; use pocketmine\item\Potion;
@ -230,6 +232,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::EMERALD, Items::EMERALD()); $this->map1to1Item(Ids::EMERALD, Items::EMERALD());
$this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK()); $this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK());
$this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE()); $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::ENDER_PEARL, Items::ENDER_PEARL());
$this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE()); $this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE());
$this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); $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()) 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( $this->map1to1ItemWithMeta(
Ids::MEDICINE, Ids::MEDICINE,
Items::MEDICINE(), Items::MEDICINE(),

View File

@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityMotionEvent;
use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\EntityRegainHealthEvent;
use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntitySpawnEvent;
use pocketmine\event\entity\EntityTeleportEvent; use pocketmine\event\entity\EntityTeleportEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\math\Vector2; use pocketmine\math\Vector2;
@ -1560,6 +1561,13 @@ abstract class Entity{
$this->hasSpawned = []; $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. * 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\bedrock\PotionTypeIds;
use pocketmine\data\SavedDataLoadingException; use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\EntityDataHelper as Helper; use pocketmine\entity\EntityDataHelper as Helper;
use pocketmine\entity\object\EndCrystal;
use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\ExperienceOrb;
use pocketmine\entity\object\FallingBlock; use pocketmine\entity\object\FallingBlock;
use pocketmine\entity\object\ItemEntity; use pocketmine\entity\object\ItemEntity;
@ -92,6 +93,10 @@ final class EntityFactory{
return new Egg(Helper::parseLocation($nbt, $world), null, $nbt); return new Egg(Helper::parseLocation($nbt, $world), null, $nbt);
}, ['Egg', 'minecraft:egg']); }, ['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{ $this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{
return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt); return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt);
}, ['ThrownEnderpearl', 'minecraft:ender_pearl']); }, ['ThrownEnderpearl', 'minecraft:ender_pearl']);

View File

@ -26,6 +26,7 @@ namespace pocketmine\entity;
use pocketmine\entity\animation\SquidInkCloudAnimation; use pocketmine\entity\animation\SquidInkCloudAnimation;
use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
@ -124,4 +125,8 @@ class Squid extends WaterAnimal{
VanillaItems::INK_SAC()->setCount(mt_rand(1, 3)) 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; namespace pocketmine\entity;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
@ -87,6 +89,10 @@ class Villager extends Living implements Ageable{
return $this->baby; return $this->baby;
} }
public function getPickedItem() : ?Item{
return VanillaItems::VILLAGER_SPAWN_EGG();
}
protected function syncNetworkData(EntityMetadataCollection $properties) : void{ protected function syncNetworkData(EntityMetadataCollection $properties) : void{
parent::syncNetworkData($properties); parent::syncNetworkData($properties);
$properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); $properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby);

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\entity; namespace pocketmine\entity;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use function mt_rand; use function mt_rand;
@ -65,4 +66,8 @@ class Zombie extends Living{
//TODO: check for equipment and whether it's a baby //TODO: check for equipment and whether it's a baby
return 5; 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\EntityBlockChangeEvent;
use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Item;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
@ -194,6 +195,10 @@ class FallingBlock extends Entity{
return $nbt; return $nbt;
} }
public function getPickedItem() : ?Item{
return $this->block->asItem();
}
protected function syncNetworkData(EntityMetadataCollection $properties) : void{ protected function syncNetworkData(EntityMetadataCollection $properties) : void{
parent::syncNetworkData($properties); parent::syncNetworkData($properties);

View File

@ -28,6 +28,7 @@ use pocketmine\entity\Entity;
use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\EntitySizeInfo;
use pocketmine\entity\Location; use pocketmine\entity\Location;
use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing; 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) * 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; namespace pocketmine\entity\object;
use pocketmine\block\VanillaBlocks;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\EntitySizeInfo;
use pocketmine\entity\Explosive; use pocketmine\entity\Explosive;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\EntityPreExplodeEvent; use pocketmine\event\entity\EntityPreExplodeEvent;
use pocketmine\item\Item;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds; 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{ protected function syncNetworkData(EntityMetadataCollection $properties) : void{
parent::syncNetworkData($properties); parent::syncNetworkData($properties);

View File

@ -28,6 +28,7 @@ use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\entity\Living; use pocketmine\entity\Living;
use pocketmine\entity\Location; use pocketmine\entity\Location;
use pocketmine\entity\object\EndCrystal;
use pocketmine\event\entity\EntityCombustByEntityEvent; use pocketmine\event\entity\EntityCombustByEntityEvent;
use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByChildEntityEvent;
use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent;
@ -96,7 +97,7 @@ abstract class Projectile extends Entity{
} }
public function canCollideWith(Entity $entity) : bool{ 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{ 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 Vector3 $touchVector;
protected bool $useItem = true;
protected bool $useBlock = true;
public function __construct( public function __construct(
Player $player, Player $player,
protected Item $item, protected Item $item,
@ -73,4 +76,28 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
public function getFace() : int{ public function getFace() : int{
return $this->blockFace; 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 SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285;
public const PITCHER_POD = 20286; public const PITCHER_POD = 20286;
public const NAME_TAG = 20287; 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; 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)); $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){ foreach(SuspiciousStewType::cases() as $suspiciousStewType){
$prefix = fn(string $name) => strtolower($suspiciousStewType->name) . "_" . $name; $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_book", fn() => Items::ENCHANTED_BOOK());
$result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE());
$result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE()); $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("ender_pearl", fn() => Items::ENDER_PEARL());
$result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE()); $result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE());
$result->register("eye_armor_trim_smithing_template", fn() => Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); $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_berries", fn() => Items::GLOW_BERRIES());
$result->register("glow_ink_sac", fn() => Items::GLOW_INK_SAC()); $result->register("glow_ink_sac", fn() => Items::GLOW_INK_SAC());
$result->register("glowstone_dust", fn() => Items::GLOWSTONE_DUST()); $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_axe", fn() => Items::GOLDEN_AXE());
$result->register("gold_boots", fn() => Items::GOLDEN_BOOTS()); $result->register("gold_boots", fn() => Items::GOLDEN_BOOTS());
$result->register("gold_chestplate", fn() => Items::GOLDEN_CHESTPLATE()); $result->register("gold_chestplate", fn() => Items::GOLDEN_CHESTPLATE());

View File

@ -33,11 +33,12 @@ use pocketmine\entity\Zombie;
use pocketmine\inventory\ArmorInventory; use pocketmine\inventory\ArmorInventory;
use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags;
use pocketmine\item\ItemIdentifier as IID; use pocketmine\item\ItemIdentifier as IID;
use pocketmine\item\ItemTypeIds as Ids;
use pocketmine\item\VanillaArmorMaterials as ArmorMaterials; use pocketmine\item\VanillaArmorMaterials as ArmorMaterials;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\utils\CloningRegistryTrait; use pocketmine\utils\CloningRegistryTrait;
use pocketmine\world\World; use pocketmine\world\World;
use function is_int;
use function mb_strtoupper;
use function strtolower; use function strtolower;
/** /**
@ -157,6 +158,7 @@ use function strtolower;
* @method static EnchantedBook ENCHANTED_BOOK() * @method static EnchantedBook ENCHANTED_BOOK()
* @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE() * @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE()
* @method static EnderPearl ENDER_PEARL() * @method static EnderPearl ENDER_PEARL()
* @method static EndCrystal END_CRYSTAL()
* @method static ExperienceBottle EXPERIENCE_BOTTLE() * @method static ExperienceBottle EXPERIENCE_BOTTLE()
* @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE()
* @method static Item FEATHER() * @method static Item FEATHER()
@ -171,6 +173,7 @@ use function strtolower;
* @method static Item GLOWSTONE_DUST() * @method static Item GLOWSTONE_DUST()
* @method static GlowBerries GLOW_BERRIES() * @method static GlowBerries GLOW_BERRIES()
* @method static Item GLOW_INK_SAC() * @method static Item GLOW_INK_SAC()
* @method static GoatHorn GOAT_HORN()
* @method static GoldenApple GOLDEN_APPLE() * @method static GoldenApple GOLDEN_APPLE()
* @method static Axe GOLDEN_AXE() * @method static Axe GOLDEN_AXE()
* @method static Armor GOLDEN_BOOTS() * @method static Armor GOLDEN_BOOTS()
@ -339,8 +342,29 @@ final class VanillaItems{
//NOOP //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); self::_registryRegister($name, $item);
return $item;
} }
/** /**
@ -360,242 +384,238 @@ final class VanillaItems{
self::registerTierToolItems(); self::registerTierToolItems();
self::registerSmithingTemplates(); 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("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
self::register("amethyst_shard", new Item(new IID(Ids::AMETHYST_SHARD), "Amethyst Shard")); self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard"));
self::register("apple", new Apple(new IID(Ids::APPLE), "Apple")); self::register("apple", fn(IID $id) => new Apple($id, "Apple"));
self::register("arrow", new Arrow(new IID(Ids::ARROW), "Arrow")); self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow"));
self::register("baked_potato", new BakedPotato(new IID(Ids::BAKED_POTATO), "Baked Potato")); self::register("baked_potato", fn(IID $id) => new BakedPotato($id, "Baked Potato"));
self::register("bamboo", new Bamboo(new IID(Ids::BAMBOO), "Bamboo")); self::register("bamboo", fn(IID $id) => new Bamboo($id, "Bamboo"));
self::register("banner", new Banner(new IID(Ids::BANNER), Blocks::BANNER(), Blocks::WALL_BANNER())); self::register("banner", fn(IID $id) => new Banner($id, Blocks::BANNER(), Blocks::WALL_BANNER()));
self::register("beetroot", new Beetroot(new IID(Ids::BEETROOT), "Beetroot")); self::register("beetroot", fn(IID $id) => new Beetroot($id, "Beetroot"));
self::register("beetroot_seeds", new BeetrootSeeds(new IID(Ids::BEETROOT_SEEDS), "Beetroot Seeds")); self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds"));
self::register("beetroot_soup", new BeetrootSoup(new IID(Ids::BEETROOT_SOUP), "Beetroot Soup")); self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup"));
self::register("birch_sign", new ItemBlockWallOrFloor(new IID(Ids::BIRCH_SIGN), Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN()));
self::register("blaze_powder", new Item(new IID(Ids::BLAZE_POWDER), "Blaze Powder")); self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder"));
self::register("blaze_rod", new BlazeRod(new IID(Ids::BLAZE_ROD), "Blaze Rod")); self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod"));
self::register("bleach", new Item(new IID(Ids::BLEACH), "Bleach")); self::register("bleach", fn(IID $id) => new Item($id, "Bleach"));
self::register("bone", new Item(new IID(Ids::BONE), "Bone")); self::register("bone", fn(IID $id) => new Item($id, "Bone"));
self::register("bone_meal", new Fertilizer(new IID(Ids::BONE_MEAL), "Bone Meal")); self::register("bone_meal", fn(IID $id) => new Fertilizer($id, "Bone Meal"));
self::register("book", new Book(new IID(Ids::BOOK), "Book", [EnchantmentTags::ALL])); self::register("book", fn(IID $id) => new Book($id, "Book", [EnchantmentTags::ALL]));
self::register("bow", new Bow(new IID(Ids::BOW), "Bow", [EnchantmentTags::BOW])); self::register("bow", fn(IID $id) => new Bow($id, "Bow", [EnchantmentTags::BOW]));
self::register("bowl", new Bowl(new IID(Ids::BOWL), "Bowl")); self::register("bowl", fn(IID $id) => new Bowl($id, "Bowl"));
self::register("bread", new Bread(new IID(Ids::BREAD), "Bread")); self::register("bread", fn(IID $id) => new Bread($id, "Bread"));
self::register("brick", new Item(new IID(Ids::BRICK), "Brick")); self::register("brick", fn(IID $id) => new Item($id, "Brick"));
self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket")); self::register("bucket", fn(IID $id) => new Bucket($id, "Bucket"));
self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot")); self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot"));
self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal")); self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal"));
self::register("cherry_sign", new ItemBlockWallOrFloor(new IID(Ids::CHERRY_SIGN), Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, 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_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide"));
self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia")); self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia"));
self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate")); self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate"));
self::register("chemical_benzene", new Item(new IID(Ids::CHEMICAL_BENZENE), "Benzene")); self::register("chemical_benzene", fn(IID $id) => new Item($id, "Benzene"));
self::register("chemical_boron_trioxide", new Item(new IID(Ids::CHEMICAL_BORON_TRIOXIDE), "Boron Trioxide")); self::register("chemical_boron_trioxide", fn(IID $id) => new Item($id, "Boron Trioxide"));
self::register("chemical_calcium_bromide", new Item(new IID(Ids::CHEMICAL_CALCIUM_BROMIDE), "Calcium Bromide")); self::register("chemical_calcium_bromide", fn(IID $id) => new Item($id, "Calcium Bromide"));
self::register("chemical_calcium_chloride", new Item(new IID(Ids::CHEMICAL_CALCIUM_CHLORIDE), "Calcium Chloride")); self::register("chemical_calcium_chloride", fn(IID $id) => new Item($id, "Calcium Chloride"));
self::register("chemical_cerium_chloride", new Item(new IID(Ids::CHEMICAL_CERIUM_CHLORIDE), "Cerium Chloride")); self::register("chemical_cerium_chloride", fn(IID $id) => new Item($id, "Cerium Chloride"));
self::register("chemical_charcoal", new Item(new IID(Ids::CHEMICAL_CHARCOAL), "Charcoal")); self::register("chemical_charcoal", fn(IID $id) => new Item($id, "Charcoal"));
self::register("chemical_crude_oil", new Item(new IID(Ids::CHEMICAL_CRUDE_OIL), "Crude Oil")); self::register("chemical_crude_oil", fn(IID $id) => new Item($id, "Crude Oil"));
self::register("chemical_glue", new Item(new IID(Ids::CHEMICAL_GLUE), "Glue")); self::register("chemical_glue", fn(IID $id) => new Item($id, "Glue"));
self::register("chemical_hydrogen_peroxide", new Item(new IID(Ids::CHEMICAL_HYDROGEN_PEROXIDE), "Hydrogen Peroxide")); self::register("chemical_hydrogen_peroxide", fn(IID $id) => new Item($id, "Hydrogen Peroxide"));
self::register("chemical_hypochlorite", new Item(new IID(Ids::CHEMICAL_HYPOCHLORITE), "Hypochlorite")); self::register("chemical_hypochlorite", fn(IID $id) => new Item($id, "Hypochlorite"));
self::register("chemical_ink", new Item(new IID(Ids::CHEMICAL_INK), "Ink")); self::register("chemical_ink", fn(IID $id) => new Item($id, "Ink"));
self::register("chemical_iron_sulphide", new Item(new IID(Ids::CHEMICAL_IRON_SULPHIDE), "Iron Sulphide")); self::register("chemical_iron_sulphide", fn(IID $id) => new Item($id, "Iron Sulphide"));
self::register("chemical_latex", new Item(new IID(Ids::CHEMICAL_LATEX), "Latex")); self::register("chemical_latex", fn(IID $id) => new Item($id, "Latex"));
self::register("chemical_lithium_hydride", new Item(new IID(Ids::CHEMICAL_LITHIUM_HYDRIDE), "Lithium Hydride")); self::register("chemical_lithium_hydride", fn(IID $id) => new Item($id, "Lithium Hydride"));
self::register("chemical_luminol", new Item(new IID(Ids::CHEMICAL_LUMINOL), "Luminol")); self::register("chemical_luminol", fn(IID $id) => new Item($id, "Luminol"));
self::register("chemical_magnesium_nitrate", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_NITRATE), "Magnesium Nitrate")); self::register("chemical_magnesium_nitrate", fn(IID $id) => new Item($id, "Magnesium Nitrate"));
self::register("chemical_magnesium_oxide", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_OXIDE), "Magnesium Oxide")); self::register("chemical_magnesium_oxide", fn(IID $id) => new Item($id, "Magnesium Oxide"));
self::register("chemical_magnesium_salts", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_SALTS), "Magnesium Salts")); self::register("chemical_magnesium_salts", fn(IID $id) => new Item($id, "Magnesium Salts"));
self::register("chemical_mercuric_chloride", new Item(new IID(Ids::CHEMICAL_MERCURIC_CHLORIDE), "Mercuric Chloride")); self::register("chemical_mercuric_chloride", fn(IID $id) => new Item($id, "Mercuric Chloride"));
self::register("chemical_polyethylene", new Item(new IID(Ids::CHEMICAL_POLYETHYLENE), "Polyethylene")); self::register("chemical_polyethylene", fn(IID $id) => new Item($id, "Polyethylene"));
self::register("chemical_potassium_chloride", new Item(new IID(Ids::CHEMICAL_POTASSIUM_CHLORIDE), "Potassium Chloride")); self::register("chemical_potassium_chloride", fn(IID $id) => new Item($id, "Potassium Chloride"));
self::register("chemical_potassium_iodide", new Item(new IID(Ids::CHEMICAL_POTASSIUM_IODIDE), "Potassium Iodide")); self::register("chemical_potassium_iodide", fn(IID $id) => new Item($id, "Potassium Iodide"));
self::register("chemical_rubbish", new Item(new IID(Ids::CHEMICAL_RUBBISH), "Rubbish")); self::register("chemical_rubbish", fn(IID $id) => new Item($id, "Rubbish"));
self::register("chemical_salt", new Item(new IID(Ids::CHEMICAL_SALT), "Salt")); self::register("chemical_salt", fn(IID $id) => new Item($id, "Salt"));
self::register("chemical_soap", new Item(new IID(Ids::CHEMICAL_SOAP), "Soap")); self::register("chemical_soap", fn(IID $id) => new Item($id, "Soap"));
self::register("chemical_sodium_acetate", new Item(new IID(Ids::CHEMICAL_SODIUM_ACETATE), "Sodium Acetate")); self::register("chemical_sodium_acetate", fn(IID $id) => new Item($id, "Sodium Acetate"));
self::register("chemical_sodium_fluoride", new Item(new IID(Ids::CHEMICAL_SODIUM_FLUORIDE), "Sodium Fluoride")); self::register("chemical_sodium_fluoride", fn(IID $id) => new Item($id, "Sodium Fluoride"));
self::register("chemical_sodium_hydride", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDRIDE), "Sodium Hydride")); self::register("chemical_sodium_hydride", fn(IID $id) => new Item($id, "Sodium Hydride"));
self::register("chemical_sodium_hydroxide", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDROXIDE), "Sodium Hydroxide")); self::register("chemical_sodium_hydroxide", fn(IID $id) => new Item($id, "Sodium Hydroxide"));
self::register("chemical_sodium_hypochlorite", new Item(new IID(Ids::CHEMICAL_SODIUM_HYPOCHLORITE), "Sodium Hypochlorite")); self::register("chemical_sodium_hypochlorite", fn(IID $id) => new Item($id, "Sodium Hypochlorite"));
self::register("chemical_sodium_oxide", new Item(new IID(Ids::CHEMICAL_SODIUM_OXIDE), "Sodium Oxide")); self::register("chemical_sodium_oxide", fn(IID $id) => new Item($id, "Sodium Oxide"));
self::register("chemical_sugar", new Item(new IID(Ids::CHEMICAL_SUGAR), "Sugar")); self::register("chemical_sugar", fn(IID $id) => new Item($id, "Sugar"));
self::register("chemical_sulphate", new Item(new IID(Ids::CHEMICAL_SULPHATE), "Sulphate")); self::register("chemical_sulphate", fn(IID $id) => new Item($id, "Sulphate"));
self::register("chemical_tungsten_chloride", new Item(new IID(Ids::CHEMICAL_TUNGSTEN_CHLORIDE), "Tungsten Chloride")); self::register("chemical_tungsten_chloride", fn(IID $id) => new Item($id, "Tungsten Chloride"));
self::register("chemical_water", new Item(new IID(Ids::CHEMICAL_WATER), "Water")); self::register("chemical_water", fn(IID $id) => new Item($id, "Water"));
self::register("chorus_fruit", new ChorusFruit(new IID(Ids::CHORUS_FRUIT), "Chorus Fruit")); self::register("chorus_fruit", fn(IID $id) => new ChorusFruit($id, "Chorus Fruit"));
self::register("clay", new Item(new IID(Ids::CLAY), "Clay")); self::register("clay", fn(IID $id) => new Item($id, "Clay"));
self::register("clock", new Clock(new IID(Ids::CLOCK), "Clock")); self::register("clock", fn(IID $id) => new Clock($id, "Clock"));
self::register("clownfish", new Clownfish(new IID(Ids::CLOWNFISH), "Clownfish")); self::register("clownfish", fn(IID $id) => new Clownfish($id, "Clownfish"));
self::register("coal", new Coal(new IID(Ids::COAL), "Coal")); self::register("coal", fn(IID $id) => new Coal($id, "Coal"));
self::register("cocoa_beans", new CocoaBeans(new IID(Ids::COCOA_BEANS), "Cocoa Beans")); self::register("cocoa_beans", fn(IID $id) => new CocoaBeans($id, "Cocoa Beans"));
self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass", [EnchantmentTags::COMPASS])); self::register("compass", fn(IID $id) => new Compass($id, "Compass", [EnchantmentTags::COMPASS]));
self::register("cooked_chicken", new CookedChicken(new IID(Ids::COOKED_CHICKEN), "Cooked Chicken")); self::register("cooked_chicken", fn(IID $id) => new CookedChicken($id, "Cooked Chicken"));
self::register("cooked_fish", new CookedFish(new IID(Ids::COOKED_FISH), "Cooked Fish")); self::register("cooked_fish", fn(IID $id) => new CookedFish($id, "Cooked Fish"));
self::register("cooked_mutton", new CookedMutton(new IID(Ids::COOKED_MUTTON), "Cooked Mutton")); self::register("cooked_mutton", fn(IID $id) => new CookedMutton($id, "Cooked Mutton"));
self::register("cooked_porkchop", new CookedPorkchop(new IID(Ids::COOKED_PORKCHOP), "Cooked Porkchop")); self::register("cooked_porkchop", fn(IID $id) => new CookedPorkchop($id, "Cooked Porkchop"));
self::register("cooked_rabbit", new CookedRabbit(new IID(Ids::COOKED_RABBIT), "Cooked Rabbit")); self::register("cooked_rabbit", fn(IID $id) => new CookedRabbit($id, "Cooked Rabbit"));
self::register("cooked_salmon", new CookedSalmon(new IID(Ids::COOKED_SALMON), "Cooked Salmon")); self::register("cooked_salmon", fn(IID $id) => new CookedSalmon($id, "Cooked Salmon"));
self::register("cookie", new Cookie(new IID(Ids::COOKIE), "Cookie")); self::register("cookie", fn(IID $id) => new Cookie($id, "Cookie"));
self::register("copper_ingot", new Item(new IID(Ids::COPPER_INGOT), "Copper Ingot")); self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot"));
self::register("coral_fan", new CoralFan(new IID(Ids::CORAL_FAN))); self::register("coral_fan", fn(IID $id) => new CoralFan($id));
self::register("crimson_sign", new ItemBlockWallOrFloor(new IID(Ids::CRIMSON_SIGN), Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, 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("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN()));
self::register("diamond", new Item(new IID(Ids::DIAMOND), "Diamond")); self::register("diamond", fn(IID $id) => new Item($id, "Diamond"));
self::register("disc_fragment_5", new Item(new IID(Ids::DISC_FRAGMENT_5), "Disc Fragment (5)")); self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)"));
self::register("dragon_breath", new Item(new IID(Ids::DRAGON_BREATH), "Dragon's Breath")); self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath"));
self::register("dried_kelp", new DriedKelp(new IID(Ids::DRIED_KELP), "Dried Kelp")); self::register("dried_kelp", fn(IID $id) => new DriedKelp($id, "Dried Kelp"));
//TODO: add interface to dye-colour objects //TODO: add interface to dye-colour objects
self::register("dye", new Dye(new IID(Ids::DYE), "Dye")); self::register("dye", fn(IID $id) => new Dye($id, "Dye"));
self::register("echo_shard", new Item(new IID(Ids::ECHO_SHARD), "Echo Shard")); self::register("echo_shard", fn(IID $id) => new Item($id, "Echo Shard"));
self::register("egg", new Egg(new IID(Ids::EGG), "Egg")); self::register("egg", fn(IID $id) => new Egg($id, "Egg"));
self::register("emerald", new Item(new IID(Ids::EMERALD), "Emerald")); self::register("emerald", fn(IID $id) => new Item($id, "Emerald"));
self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book", [EnchantmentTags::ALL])); self::register("enchanted_book", fn(IID $id) => new EnchantedBook($id, "Enchanted Book", [EnchantmentTags::ALL]));
self::register("enchanted_golden_apple", new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE), "Enchanted Golden Apple")); self::register("enchanted_golden_apple", fn(IID $id) => new GoldenAppleEnchanted($id, "Enchanted Golden Apple"));
self::register("ender_pearl", new EnderPearl(new IID(Ids::ENDER_PEARL), "Ender Pearl")); self::register("end_crystal", fn(IID $id) => new EndCrystal($id, "End Crystal"));
self::register("experience_bottle", new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE), "Bottle o' Enchanting")); self::register("ender_pearl", fn(IID $id) => new EnderPearl($id, "Ender Pearl"));
self::register("feather", new Item(new IID(Ids::FEATHER), "Feather")); self::register("experience_bottle", fn(IID $id) => new ExperienceBottle($id, "Bottle o' Enchanting"));
self::register("fermented_spider_eye", new Item(new IID(Ids::FERMENTED_SPIDER_EYE), "Fermented Spider Eye")); self::register("feather", fn(IID $id) => new Item($id, "Feather"));
self::register("fire_charge", new FireCharge(new IID(Ids::FIRE_CHARGE), "Fire Charge")); self::register("fermented_spider_eye", fn(IID $id) => new Item($id, "Fermented Spider Eye"));
self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod", [EnchantmentTags::FISHING_ROD])); self::register("fire_charge", fn(IID $id) => new FireCharge($id, "Fire Charge"));
self::register("flint", new Item(new IID(Ids::FLINT), "Flint")); self::register("fishing_rod", fn(IID $id) => new FishingRod($id, "Fishing Rod", [EnchantmentTags::FISHING_ROD]));
self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL])); self::register("flint", fn(IID $id) => new Item($id, "Flint"));
self::register("ghast_tear", new Item(new IID(Ids::GHAST_TEAR), "Ghast Tear")); self::register("flint_and_steel", fn(IID $id) => new FlintSteel($id, "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL]));
self::register("glass_bottle", new GlassBottle(new IID(Ids::GLASS_BOTTLE), "Glass Bottle")); self::register("ghast_tear", fn(IID $id) => new Item($id, "Ghast Tear"));
self::register("glistering_melon", new Item(new IID(Ids::GLISTERING_MELON), "Glistering Melon")); self::register("glass_bottle", fn(IID $id) => new GlassBottle($id, "Glass Bottle"));
self::register("glow_berries", new GlowBerries(new IID(Ids::GLOW_BERRIES), "Glow Berries")); self::register("glistering_melon", fn(IID $id) => new Item($id, "Glistering Melon"));
self::register("glow_ink_sac", new Item(new IID(Ids::GLOW_INK_SAC), "Glow Ink Sac")); self::register("glow_berries", fn(IID $id) => new GlowBerries($id, "Glow Berries"));
self::register("glowstone_dust", new Item(new IID(Ids::GLOWSTONE_DUST), "Glowstone Dust")); self::register("glow_ink_sac", fn(IID $id) => new Item($id, "Glow Ink Sac"));
self::register("gold_ingot", new Item(new IID(Ids::GOLD_INGOT), "Gold Ingot")); self::register("glowstone_dust", fn(IID $id) => new Item($id, "Glowstone Dust"));
self::register("gold_nugget", new Item(new IID(Ids::GOLD_NUGGET), "Gold Nugget")); self::register("goat_horn", fn(IID $id) => new GoatHorn($id, "Goat Horn"));
self::register("golden_apple", new GoldenApple(new IID(Ids::GOLDEN_APPLE), "Golden Apple")); self::register("gold_ingot", fn(IID $id) => new Item($id, "Gold Ingot"));
self::register("golden_carrot", new GoldenCarrot(new IID(Ids::GOLDEN_CARROT), "Golden Carrot")); self::register("gold_nugget", fn(IID $id) => new Item($id, "Gold Nugget"));
self::register("gunpowder", new Item(new IID(Ids::GUNPOWDER), "Gunpowder")); self::register("golden_apple", fn(IID $id) => new GoldenApple($id, "Golden Apple"));
self::register("heart_of_the_sea", new Item(new IID(Ids::HEART_OF_THE_SEA), "Heart of the Sea")); self::register("golden_carrot", fn(IID $id) => new GoldenCarrot($id, "Golden Carrot"));
self::register("honey_bottle", new HoneyBottle(new IID(Ids::HONEY_BOTTLE), "Honey Bottle")); self::register("gunpowder", fn(IID $id) => new Item($id, "Gunpowder"));
self::register("honeycomb", new Item(new IID(Ids::HONEYCOMB), "Honeycomb")); self::register("heart_of_the_sea", fn(IID $id) => new Item($id, "Heart of the Sea"));
self::register("ink_sac", new Item(new IID(Ids::INK_SAC), "Ink Sac")); self::register("honey_bottle", fn(IID $id) => new HoneyBottle($id, "Honey Bottle"));
self::register("iron_ingot", new Item(new IID(Ids::IRON_INGOT), "Iron Ingot")); self::register("honeycomb", fn(IID $id) => new Item($id, "Honeycomb"));
self::register("iron_nugget", new Item(new IID(Ids::IRON_NUGGET), "Iron Nugget")); self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac"));
self::register("jungle_sign", new ItemBlockWallOrFloor(new IID(Ids::JUNGLE_SIGN), Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot"));
self::register("lapis_lazuli", new Item(new IID(Ids::LAPIS_LAZULI), "Lapis Lazuli")); self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget"));
self::register("lava_bucket", new LiquidBucket(new IID(Ids::LAVA_BUCKET), "Lava Bucket", Blocks::LAVA())); self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN()));
self::register("leather", new Item(new IID(Ids::LEATHER), "Leather")); self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli"));
self::register("magma_cream", new Item(new IID(Ids::MAGMA_CREAM), "Magma Cream")); self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA()));
self::register("mangrove_sign", new ItemBlockWallOrFloor(new IID(Ids::MANGROVE_SIGN), Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); self::register("leather", fn(IID $id) => new Item($id, "Leather"));
self::register("medicine", new Medicine(new IID(Ids::MEDICINE), "Medicine")); self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream"));
self::register("melon", new Melon(new IID(Ids::MELON), "Melon")); self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN()));
self::register("melon_seeds", new MelonSeeds(new IID(Ids::MELON_SEEDS), "Melon Seeds")); self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine"));
self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket")); self::register("melon", fn(IID $id) => new Melon($id, "Melon"));
self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart")); self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds"));
self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew")); self::register("milk_bucket", fn(IID $id) => new MilkBucket($id, "Milk Bucket"));
self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag")); self::register("minecart", fn(IID $id) => new Minecart($id, "Minecart"));
self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell")); self::register("mushroom_stew", fn(IID $id) => new MushroomStew($id, "Mushroom Stew"));
self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick")); self::register("name_tag", fn(IID $id) => new NameTag($id, "Name Tag"));
self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz")); self::register("nautilus_shell", fn(IID $id) => new Item($id, "Nautilus Shell"));
self::register("nether_star", new Item(new IID(Ids::NETHER_STAR), "Nether Star")); self::register("nether_brick", fn(IID $id) => new Item($id, "Nether Brick"));
self::register("netherite_ingot", new class(new IID(Ids::NETHERITE_INGOT), "Netherite Ingot") extends Item{ 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; } 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; } 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("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
self::register("painting", new PaintingItem(new IID(Ids::PAINTING), "Painting")); self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting"));
self::register("paper", new Item(new IID(Ids::PAPER), "Paper")); self::register("paper", fn(IID $id) => new Item($id, "Paper"));
self::register("phantom_membrane", new Item(new IID(Ids::PHANTOM_MEMBRANE), "Phantom Membrane")); self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane"));
self::register("pitcher_pod", new PitcherPod(new IID(Ids::PITCHER_POD), "Pitcher Pod")); self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod"));
self::register("poisonous_potato", new PoisonousPotato(new IID(Ids::POISONOUS_POTATO), "Poisonous Potato")); self::register("poisonous_potato", fn(IID $id) => new PoisonousPotato($id, "Poisonous Potato"));
self::register("popped_chorus_fruit", new Item(new IID(Ids::POPPED_CHORUS_FRUIT), "Popped Chorus Fruit")); self::register("popped_chorus_fruit", fn(IID $id) => new Item($id, "Popped Chorus Fruit"));
self::register("potato", new Potato(new IID(Ids::POTATO), "Potato")); self::register("potato", fn(IID $id) => new Potato($id, "Potato"));
self::register("potion", new Potion(new IID(Ids::POTION), "Potion")); self::register("potion", fn(IID $id) => new Potion($id, "Potion"));
self::register("prismarine_crystals", new Item(new IID(Ids::PRISMARINE_CRYSTALS), "Prismarine Crystals")); self::register("prismarine_crystals", fn(IID $id) => new Item($id, "Prismarine Crystals"));
self::register("prismarine_shard", new Item(new IID(Ids::PRISMARINE_SHARD), "Prismarine Shard")); self::register("prismarine_shard", fn(IID $id) => new Item($id, "Prismarine Shard"));
self::register("pufferfish", new Pufferfish(new IID(Ids::PUFFERFISH), "Pufferfish")); self::register("pufferfish", fn(IID $id) => new Pufferfish($id, "Pufferfish"));
self::register("pumpkin_pie", new PumpkinPie(new IID(Ids::PUMPKIN_PIE), "Pumpkin Pie")); self::register("pumpkin_pie", fn(IID $id) => new PumpkinPie($id, "Pumpkin Pie"));
self::register("pumpkin_seeds", new PumpkinSeeds(new IID(Ids::PUMPKIN_SEEDS), "Pumpkin Seeds")); self::register("pumpkin_seeds", fn(IID $id) => new PumpkinSeeds($id, "Pumpkin Seeds"));
self::register("rabbit_foot", new Item(new IID(Ids::RABBIT_FOOT), "Rabbit's Foot")); self::register("rabbit_foot", fn(IID $id) => new Item($id, "Rabbit's Foot"));
self::register("rabbit_hide", new Item(new IID(Ids::RABBIT_HIDE), "Rabbit Hide")); self::register("rabbit_hide", fn(IID $id) => new Item($id, "Rabbit Hide"));
self::register("rabbit_stew", new RabbitStew(new IID(Ids::RABBIT_STEW), "Rabbit Stew")); self::register("rabbit_stew", fn(IID $id) => new RabbitStew($id, "Rabbit Stew"));
self::register("raw_beef", new RawBeef(new IID(Ids::RAW_BEEF), "Raw Beef")); self::register("raw_beef", fn(IID $id) => new RawBeef($id, "Raw Beef"));
self::register("raw_chicken", new RawChicken(new IID(Ids::RAW_CHICKEN), "Raw Chicken")); self::register("raw_chicken", fn(IID $id) => new RawChicken($id, "Raw Chicken"));
self::register("raw_copper", new Item(new IID(Ids::RAW_COPPER), "Raw Copper")); self::register("raw_copper", fn(IID $id) => new Item($id, "Raw Copper"));
self::register("raw_fish", new RawFish(new IID(Ids::RAW_FISH), "Raw Fish")); self::register("raw_fish", fn(IID $id) => new RawFish($id, "Raw Fish"));
self::register("raw_gold", new Item(new IID(Ids::RAW_GOLD), "Raw Gold")); self::register("raw_gold", fn(IID $id) => new Item($id, "Raw Gold"));
self::register("raw_iron", new Item(new IID(Ids::RAW_IRON), "Raw Iron")); self::register("raw_iron", fn(IID $id) => new Item($id, "Raw Iron"));
self::register("raw_mutton", new RawMutton(new IID(Ids::RAW_MUTTON), "Raw Mutton")); self::register("raw_mutton", fn(IID $id) => new RawMutton($id, "Raw Mutton"));
self::register("raw_porkchop", new RawPorkchop(new IID(Ids::RAW_PORKCHOP), "Raw Porkchop")); self::register("raw_porkchop", fn(IID $id) => new RawPorkchop($id, "Raw Porkchop"));
self::register("raw_rabbit", new RawRabbit(new IID(Ids::RAW_RABBIT), "Raw Rabbit")); self::register("raw_rabbit", fn(IID $id) => new RawRabbit($id, "Raw Rabbit"));
self::register("raw_salmon", new RawSalmon(new IID(Ids::RAW_SALMON), "Raw Salmon")); self::register("raw_salmon", fn(IID $id) => new RawSalmon($id, "Raw Salmon"));
self::register("record_11", new Record(new IID(Ids::RECORD_11), RecordType::DISK_11, "Record 11")); self::register("record_11", fn(IID $id) => new Record($id, RecordType::DISK_11, "Record 11"));
self::register("record_13", new Record(new IID(Ids::RECORD_13), RecordType::DISK_13, "Record 13")); self::register("record_13", fn(IID $id) => new Record($id, RecordType::DISK_13, "Record 13"));
self::register("record_5", new Record(new IID(Ids::RECORD_5), RecordType::DISK_5, "Record 5")); self::register("record_5", fn(IID $id) => new Record($id, RecordType::DISK_5, "Record 5"));
self::register("record_blocks", new Record(new IID(Ids::RECORD_BLOCKS), RecordType::DISK_BLOCKS, "Record Blocks")); self::register("record_blocks", fn(IID $id) => new Record($id, RecordType::DISK_BLOCKS, "Record Blocks"));
self::register("record_cat", new Record(new IID(Ids::RECORD_CAT), RecordType::DISK_CAT, "Record Cat")); self::register("record_cat", fn(IID $id) => new Record($id, RecordType::DISK_CAT, "Record Cat"));
self::register("record_chirp", new Record(new IID(Ids::RECORD_CHIRP), RecordType::DISK_CHIRP, "Record Chirp")); self::register("record_chirp", fn(IID $id) => new Record($id, RecordType::DISK_CHIRP, "Record Chirp"));
self::register("record_far", new Record(new IID(Ids::RECORD_FAR), RecordType::DISK_FAR, "Record Far")); self::register("record_far", fn(IID $id) => new Record($id, RecordType::DISK_FAR, "Record Far"));
self::register("record_mall", new Record(new IID(Ids::RECORD_MALL), RecordType::DISK_MALL, "Record Mall")); self::register("record_mall", fn(IID $id) => new Record($id, RecordType::DISK_MALL, "Record Mall"));
self::register("record_mellohi", new Record(new IID(Ids::RECORD_MELLOHI), RecordType::DISK_MELLOHI, "Record Mellohi")); self::register("record_mellohi", fn(IID $id) => new Record($id, RecordType::DISK_MELLOHI, "Record Mellohi"));
self::register("record_otherside", new Record(new IID(Ids::RECORD_OTHERSIDE), RecordType::DISK_OTHERSIDE, "Record Otherside")); self::register("record_otherside", fn(IID $id) => new Record($id, RecordType::DISK_OTHERSIDE, "Record Otherside"));
self::register("record_pigstep", new Record(new IID(Ids::RECORD_PIGSTEP), RecordType::DISK_PIGSTEP, "Record Pigstep")); self::register("record_pigstep", fn(IID $id) => new Record($id, RecordType::DISK_PIGSTEP, "Record Pigstep"));
self::register("record_stal", new Record(new IID(Ids::RECORD_STAL), RecordType::DISK_STAL, "Record Stal")); self::register("record_stal", fn(IID $id) => new Record($id, RecordType::DISK_STAL, "Record Stal"));
self::register("record_strad", new Record(new IID(Ids::RECORD_STRAD), RecordType::DISK_STRAD, "Record Strad")); self::register("record_strad", fn(IID $id) => new Record($id, RecordType::DISK_STRAD, "Record Strad"));
self::register("record_wait", new Record(new IID(Ids::RECORD_WAIT), RecordType::DISK_WAIT, "Record Wait")); self::register("record_wait", fn(IID $id) => new Record($id, RecordType::DISK_WAIT, "Record Wait"));
self::register("record_ward", new Record(new IID(Ids::RECORD_WARD), RecordType::DISK_WARD, "Record Ward")); self::register("record_ward", fn(IID $id) => new Record($id, RecordType::DISK_WARD, "Record Ward"));
self::register("redstone_dust", new Redstone(new IID(Ids::REDSTONE_DUST), "Redstone")); self::register("redstone_dust", fn(IID $id) => new Redstone($id, "Redstone"));
self::register("rotten_flesh", new RottenFlesh(new IID(Ids::ROTTEN_FLESH), "Rotten Flesh")); self::register("rotten_flesh", fn(IID $id) => new RottenFlesh($id, "Rotten Flesh"));
self::register("scute", new Item(new IID(Ids::SCUTE), "Scute")); self::register("scute", fn(IID $id) => new Item($id, "Scute"));
self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears", [EnchantmentTags::SHEARS])); self::register("shears", fn(IID $id) => new Shears($id, "Shears", [EnchantmentTags::SHEARS]));
self::register("shulker_shell", new Item(new IID(Ids::SHULKER_SHELL), "Shulker Shell")); self::register("shulker_shell", fn(IID $id) => new Item($id, "Shulker Shell"));
self::register("slimeball", new Item(new IID(Ids::SLIMEBALL), "Slimeball")); self::register("slimeball", fn(IID $id) => new Item($id, "Slimeball"));
self::register("snowball", new Snowball(new IID(Ids::SNOWBALL), "Snowball")); self::register("snowball", fn(IID $id) => new Snowball($id, "Snowball"));
self::register("spider_eye", new SpiderEye(new IID(Ids::SPIDER_EYE), "Spider Eye")); self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye"));
self::register("splash_potion", new SplashPotion(new IID(Ids::SPLASH_POTION), "Splash Potion")); self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion"));
self::register("spruce_sign", new ItemBlockWallOrFloor(new IID(Ids::SPRUCE_SIGN), Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN()));
self::register("spyglass", new Spyglass(new IID(Ids::SPYGLASS), "Spyglass")); self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass"));
self::register("steak", new Steak(new IID(Ids::STEAK), "Steak")); self::register("steak", fn(IID $id) => new Steak($id, "Steak"));
self::register("stick", new Stick(new IID(Ids::STICK), "Stick")); self::register("stick", fn(IID $id) => new Stick($id, "Stick"));
self::register("string", new StringItem(new IID(Ids::STRING), "String")); self::register("string", fn(IID $id) => new StringItem($id, "String"));
self::register("sugar", new Item(new IID(Ids::SUGAR), "Sugar")); self::register("sugar", fn(IID $id) => new Item($id, "Sugar"));
self::register("suspicious_stew", new SuspiciousStew(new IID(Ids::SUSPICIOUS_STEW), "Suspicious Stew")); self::register("suspicious_stew", fn(IID $id) => new SuspiciousStew($id, "Suspicious Stew"));
self::register("sweet_berries", new SweetBerries(new IID(Ids::SWEET_BERRIES), "Sweet Berries")); self::register("sweet_berries", fn(IID $id) => new SweetBerries($id, "Sweet Berries"));
self::register("torchflower_seeds", new TorchflowerSeeds(new IID(Ids::TORCHFLOWER_SEEDS), "Torchflower Seeds")); self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds"));
self::register("totem", new Totem(new IID(Ids::TOTEM), "Totem of Undying")); self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying"));
self::register("warped_sign", new ItemBlockWallOrFloor(new IID(Ids::WARPED_SIGN), Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
self::register("water_bucket", new LiquidBucket(new IID(Ids::WATER_BUCKET), "Water Bucket", Blocks::WATER())); self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER()));
self::register("wheat", new Item(new IID(Ids::WHEAT), "Wheat")); self::register("wheat", fn(IID $id) => new Item($id, "Wheat"));
self::register("wheat_seeds", new WheatSeeds(new IID(Ids::WHEAT_SEEDS), "Wheat Seeds")); self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds"));
self::register("writable_book", new WritableBook(new IID(Ids::WRITABLE_BOOK), "Book & Quill")); self::register("writable_book", fn(IID $id) => new WritableBook($id, "Book & Quill"));
self::register("written_book", new WrittenBook(new IID(Ids::WRITTEN_BOOK), "Written Book")); self::register("written_book", fn(IID $id) => new WrittenBook($id, "Written Book"));
foreach(BoatType::cases() as $type){ foreach(BoatType::cases() as $type){
//boat type is static, because different types of wood may have different properties //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){ self::register(strtolower($type->name) . "_boat", fn(IID $id) => new Boat($id, $type->getDisplayName() . " Boat", $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));
} }
} }
private static function registerSpawnEggs() : void{ 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{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Zombie(Location::fromObject($pos, $world, $yaw, $pitch)); 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{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Squid(Location::fromObject($pos, $world, $yaw, $pitch)); 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{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Villager(Location::fromObject($pos, $world, $yaw, $pitch)); return new Villager(Location::fromObject($pos, $world, $yaw, $pitch));
} }
@ -603,87 +623,87 @@ final class VanillaItems{
} }
private static function registerTierToolItems() : void{ private static function registerTierToolItems() : void{
self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE])); self::register("diamond_axe", fn(IID $id) => new Axe($id, "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("golden_axe", fn(IID $id) => new Axe($id, "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("iron_axe", fn(IID $id) => new Axe($id, "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("netherite_axe", fn(IID $id) => new Axe($id, "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("stone_axe", fn(IID $id) => new Axe($id, "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("wooden_axe", fn(IID $id) => new Axe($id, "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("diamond_hoe", fn(IID $id) => new Hoe($id, "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("golden_hoe", fn(IID $id) => new Hoe($id, "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("iron_hoe", fn(IID $id) => new Hoe($id, "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("netherite_hoe", fn(IID $id) => new Hoe($id, "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("stone_hoe", fn(IID $id) => new Hoe($id, "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("wooden_hoe", fn(IID $id) => new Hoe($id, "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("diamond_pickaxe", fn(IID $id) => new Pickaxe($id, "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("golden_pickaxe", fn(IID $id) => new Pickaxe($id, "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("iron_pickaxe", fn(IID $id) => new Pickaxe($id, "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("netherite_pickaxe", fn(IID $id) => new Pickaxe($id, "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("stone_pickaxe", fn(IID $id) => new Pickaxe($id, "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("wooden_pickaxe", fn(IID $id) => new Pickaxe($id, "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("diamond_shovel", fn(IID $id) => new Shovel($id, "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("golden_shovel", fn(IID $id) => new Shovel($id, "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("iron_shovel", fn(IID $id) => new Shovel($id, "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("netherite_shovel", fn(IID $id) => new Shovel($id, "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("stone_shovel", fn(IID $id) => new Shovel($id, "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("wooden_shovel", fn(IID $id) => new Shovel($id, "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("diamond_sword", fn(IID $id) => new Sword($id, "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("golden_sword", fn(IID $id) => new Sword($id, "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("iron_sword", fn(IID $id) => new Sword($id, "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("netherite_sword", fn(IID $id) => new Sword($id, "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("stone_sword", fn(IID $id) => new Sword($id, "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("wooden_sword", fn(IID $id) => new Sword($id, "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD]));
} }
private static function registerArmorItems() : void{ 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("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", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [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", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [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", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [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", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [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", 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("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("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", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [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", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [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", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [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", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [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", 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("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("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", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [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", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [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", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [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", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [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", 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("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", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [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("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", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [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", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [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", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [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", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [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", 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("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{ 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("netherite_upgrade_smithing_template", fn(IID $id) => new Item($id, "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("coast_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("dune_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("eye_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("host_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("raiser_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("rib_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("sentry_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("shaper_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("silence_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("snout_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("spire_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("tide_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("vex_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("ward_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("wayfinder_armor_trim_smithing_template", fn(IID $id) => new Item($id, "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("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, []); 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{ public static function death_attack_anvil(Translatable|string $param0) : Translatable{
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ANVIL, [ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ANVIL, [
0 => $param0, 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{ public static function death_attack_generic(Translatable|string $param0) : Translatable{
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [
0 => $param0, 0 => $param0,
@ -1025,6 +1056,14 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_CHIRP_DESC, []); 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{ public static function item_record_far_desc() : Translatable{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_FAR_DESC, []); return new Translatable(KnownTranslationKeys::ITEM_RECORD_FAR_DESC, []);
} }
@ -1045,6 +1084,14 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_PIGSTEP_DESC, []); 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{ public static function item_record_stal_desc() : Translatable{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_STAL_DESC, []); return new Translatable(KnownTranslationKeys::ITEM_RECORD_STAL_DESC, []);
} }
@ -1536,6 +1583,14 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_WHITELIST_DESCRIPTION, []); 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{ public static function pocketmine_crash_archive(Translatable|string $param0, Translatable|string $param1) : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_CRASH_ARCHIVE, [ return new Translatable(KnownTranslationKeys::POCKETMINE_CRASH_ARCHIVE, [
0 => $param0, 0 => $param0,
@ -2056,6 +2111,14 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE, []); 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{ public static function pocketmine_permission_group_console() : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_CONSOLE, []); 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_SUCCESS = "commands.whitelist.remove.success";
public const COMMANDS_WHITELIST_REMOVE_USAGE = "commands.whitelist.remove.usage"; public const COMMANDS_WHITELIST_REMOVE_USAGE = "commands.whitelist.remove.usage";
public const COMMANDS_WHITELIST_USAGE = "commands.whitelist.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_ANVIL = "death.attack.anvil";
public const DEATH_ATTACK_ARROW = "death.attack.arrow"; public const DEATH_ATTACK_ARROW = "death.attack.arrow";
public const DEATH_ATTACK_ARROW_ITEM = "death.attack.arrow.item"; 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_FALL = "death.attack.fall";
public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock"; public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock";
public const DEATH_ATTACK_FIREWORKS = "death.attack.fireworks"; 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_GENERIC = "death.attack.generic";
public const DEATH_ATTACK_INFIRE = "death.attack.inFire"; public const DEATH_ATTACK_INFIRE = "death.attack.inFire";
public const DEATH_ATTACK_INWALL = "death.attack.inWall"; 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_BLOCKS_DESC = "item.record_blocks.desc";
public const ITEM_RECORD_CAT_DESC = "item.record_cat.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_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_FAR_DESC = "item.record_far.desc";
public const ITEM_RECORD_MALL_DESC = "item.record_mall.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_MELLOHI_DESC = "item.record_mellohi.desc";
public const ITEM_RECORD_OTHERSIDE_DESC = "item.record_otherside.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_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_STAL_DESC = "item.record_stal.desc";
public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc"; public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc";
public const ITEM_RECORD_WAIT_DESC = "item.record_wait.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_SERVERSOFTWAREVERSION = "pocketmine.command.version.serverSoftwareVersion";
public const POCKETMINE_COMMAND_VERSION_USAGE = "pocketmine.command.version.usage"; public const POCKETMINE_COMMAND_VERSION_USAGE = "pocketmine.command.version.usage";
public const POCKETMINE_COMMAND_WHITELIST_DESCRIPTION = "pocketmine.command.whitelist.description"; 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_ARCHIVE = "pocketmine.crash.archive";
public const POCKETMINE_CRASH_CREATE = "pocketmine.crash.create"; public const POCKETMINE_CRASH_CREATE = "pocketmine.crash.create";
public const POCKETMINE_CRASH_ERROR = "pocketmine.crash.error"; 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_LIST = "pocketmine.permission.command.whitelist.list";
public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_RELOAD = "pocketmine.permission.command.whitelist.reload"; 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_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_CONSOLE = "pocketmine.permission.group.console";
public const POCKETMINE_PERMISSION_GROUP_OPERATOR = "pocketmine.permission.group.operator"; public const POCKETMINE_PERMISSION_GROUP_OPERATOR = "pocketmine.permission.group.operator";
public const POCKETMINE_PERMISSION_GROUP_USER = "pocketmine.permission.group.user"; public const POCKETMINE_PERMISSION_GROUP_USER = "pocketmine.permission.group.user";

View File

@ -493,15 +493,18 @@ class InGamePacketHandler extends PacketHandler{
$blockPos = $data->getBlockPosition(); $blockPos = $data->getBlockPosition();
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){ $this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos);
$this->onFailedBlockAction($vBlockPos, $data->getFace()); //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; return true;
case UseItemTransactionData::ACTION_BREAK_BLOCK: case UseItemTransactionData::ACTION_BREAK_BLOCK:
$blockPos = $data->getBlockPosition(); $blockPos = $data->getBlockPosition();
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
if(!$this->player->breakBlock($vBlockPos)){ if(!$this->player->breakBlock($vBlockPos)){
$this->onFailedBlockAction($vBlockPos, null); $this->syncBlocksNearby($vBlockPos, null);
} }
return true; return true;
case UseItemTransactionData::ACTION_CLICK_AIR: 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){ if($blockPos->distanceSquared($this->player->getLocation()) < 10000){
$blocks = $blockPos->sidesArray(); $blocks = $blockPos->sidesArray();
if($face !== null){ if($face !== null){
@ -668,7 +671,7 @@ class InGamePacketHandler extends PacketHandler{
} }
public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{ public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{
return false; //TODO return $this->player->pickEntity($packet->actorUniqueId);
} }
public function handlePlayerAction(PlayerActionPacket $packet) : bool{ public function handlePlayerAction(PlayerActionPacket $packet) : bool{
@ -682,7 +685,7 @@ class InGamePacketHandler extends PacketHandler{
case PlayerAction::START_BREAK: case PlayerAction::START_BREAK:
self::validateFacing($face); self::validateFacing($face);
if(!$this->player->attackBlock($pos, $face)){ if(!$this->player->attackBlock($pos, $face)){
$this->onFailedBlockAction($pos, $face); $this->syncBlocksNearby($pos, $face);
} }
break; break;
@ -998,7 +1001,7 @@ class InGamePacketHandler extends PacketHandler{
$lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ()); $lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ());
if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){ if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){
if(!$lectern->onPageTurn($packet->page)){ if(!$lectern->onPageTurn($packet->page)){
$this->onFailedBlockAction($lectern->getPosition(), null); $this->syncBlocksNearby($lectern->getPosition(), null);
} }
return true; return true;
} }

View File

@ -84,6 +84,8 @@ final class DefaultPermissionNames{
public const COMMAND_WHITELIST_LIST = "pocketmine.command.whitelist.list"; public const COMMAND_WHITELIST_LIST = "pocketmine.command.whitelist.list";
public const COMMAND_WHITELIST_RELOAD = "pocketmine.command.whitelist.reload"; public const COMMAND_WHITELIST_RELOAD = "pocketmine.command.whitelist.reload";
public const COMMAND_WHITELIST_REMOVE = "pocketmine.command.whitelist.remove"; 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_CONSOLE = "pocketmine.group.console";
public const GROUP_OPERATOR = "pocketmine.group.operator"; public const GROUP_OPERATOR = "pocketmine.group.operator";
public const GROUP_USER = "pocketmine.group.user"; 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_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_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_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\PlayerDropItemEvent;
use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEmoteEvent;
use pocketmine\event\player\PlayerEntityInteractEvent; use pocketmine\event\player\PlayerEntityInteractEvent;
use pocketmine\event\player\PlayerEntityPickEvent;
use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\event\player\PlayerExhaustEvent;
use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerGameModeChangeEvent;
use pocketmine\event\player\PlayerInteractEvent; use pocketmine\event\player\PlayerInteractEvent;
@ -1709,29 +1710,58 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
if($existingSlot !== -1){ $this->equipOrAddPickedItem($existingSlot, $item);
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);
}
}
} }
return true; 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. * 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 preg_replace;
use function shell_exec; use function shell_exec;
use function spl_object_id; use function spl_object_id;
use function str_ends_with; use function str_contains;
use function str_pad; use function str_pad;
use function str_split; use function str_split;
use function str_starts_with; use function str_starts_with;
@ -121,7 +121,7 @@ final class Utils{
*/ */
public static function getNiceClosureName(\Closure $closure) : string{ public static function getNiceClosureName(\Closure $closure) : string{
$func = new \ReflectionFunction($closure); $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() //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 //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) * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
* @phpstan-param-out Item $item * @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(); $vector = $vector->floor();
$chunkX = $vector->getFloorX() >> Chunk::COORD_BIT_SIZE; $chunkX = $vector->getFloorX() >> Chunk::COORD_BIT_SIZE;
@ -2173,19 +2173,25 @@ class World implements ChunkManager{
if($player !== null){ if($player !== null){
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK); $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()){ if($player->isSpectator()){
$ev->cancel(); //set it to cancelled so plugins can bypass this $ev->cancel(); //set it to cancelled so plugins can bypass this
} }
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ 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; return true;
} }
$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems); if($ev->useItem()){
if($result !== ItemUseResult::NONE){ $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
return $result === ItemUseResult::SUCCESS; if($result !== ItemUseResult::NONE){
return $result === ItemUseResult::SUCCESS;
}
} }
}else{ }else{
return false; 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 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 set +e
if [ "$DO_LOOP" == "yes" ]; then if [ "$DO_LOOP" == "yes" ]; then
@ -52,11 +73,15 @@ if [ "$DO_LOOP" == "yes" ]; then
echo "Restarted $LOOPS times" echo "Restarted $LOOPS times"
fi fi
"$PHP_BINARY" "$POCKETMINE_FILE" "$@" "$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 "To escape the loop, press CTRL+C now. Otherwise, wait 5 seconds for the server to restart."
echo "" echo ""
sleep 5 sleep 5
((LOOPS++)) ((LOOPS++))
done done
else else
exec "$PHP_BINARY" "$POCKETMINE_FILE" "$@" "$PHP_BINARY" "$POCKETMINE_FILE" "$@"
exitcode=$?
handle_exit_code $exitcode
exit $exitcode
fi fi

View File

@ -20,6 +20,56 @@ parameters:
count: 1 count: 1
path: ../../../src/block/DoubleTallGrass.php 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\\.$#" message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#"
count: 1 count: 1

View File

@ -51,6 +51,7 @@ class BlockTypeIdsTest extends TestCase{
foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){ foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){
$expected = $block->getTypeId(); $expected = $block->getTypeId();
$actual = $reflect->getConstant($name); $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"); 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; namespace pocketmine\event;
use PHPUnit\Framework\TestCase; 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{ class HandlerListManagerTest extends TestCase{

View File

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

View File

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

View File

@ -21,8 +21,8 @@
declare(strict_types=1); 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); declare(strict_types=1);
namespace pocketmine\event; namespace pocketmine\event\fixtures;
use pocketmine\event\Event;
class TestConcreteEvent extends Event{ class TestConcreteEvent extends Event{

View File

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

View File

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

View File

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

View File

@ -21,8 +21,8 @@
declare(strict_types=1); 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); 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(); $expected = $item->getTypeId();
$actual = $reflect->getConstant($name); $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"); 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); }), 10);
$this->waitingTests = [ $this->waitingTests = [
new EventHandlerInheritanceTest($this),
new AsyncEventInheritanceTest($this), new AsyncEventInheritanceTest($this),
new AsyncEventConcurrencyTest($this), new AsyncEventConcurrencyTest($this),
new AsyncEventPriorityTest($this) new AsyncEventPriorityTest($this)