Bedrock 1.21.60 (#6627)

Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
This commit is contained in:
Dries C 2025-02-16 21:51:53 +01:00 committed by GitHub
parent 9402a20ee3
commit 91ac64783f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 902 additions and 96 deletions

View File

@ -33,15 +33,15 @@
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"netresearch/jsonmapper": "~v5.0.0",
"pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
"pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50",
"pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
"pocketmine/bedrock-data": "~4.0.0+bedrock-1.21.60",
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~36.0.0+bedrock-1.21.60",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.7.0",
"pocketmine/locale-data": "~2.22.0",
"pocketmine/locale-data": "~2.24.0",
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
"pocketmine/nbt": "~1.0.0",

52
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a6aa0a34a230d325528d2e11f1439057",
"content-hash": "af7547291a131bfac6d7087957601325",
"packages": [
{
"name": "adhocore/json-comment",
@ -178,16 +178,16 @@
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "5.0.0",
"version": "5.1.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52"
"reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/2218512e4b91f5bfd09ef55f7a4c4b04e169e41a",
"reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a",
"shasum": ""
},
"type": "library",
@ -198,22 +198,22 @@
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.1.0"
},
"time": "2024-11-03T14:13:50+00:00"
"time": "2025-02-11T17:41:44+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.15.0+bedrock-1.21.50",
"version": "4.0.0+bedrock-1.21.60",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad"
"reference": "2e5f16ec2facac653f3f894f22eb831d880ba98e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/2e5f16ec2facac653f3f894f22eb831d880ba98e",
"reference": "2e5f16ec2facac653f3f894f22eb831d880ba98e",
"shasum": ""
},
"type": "library",
@ -224,9 +224,9 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.50"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.60"
},
"time": "2024-12-04T12:59:12+00:00"
"time": "2025-02-16T15:56:56+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
@ -256,16 +256,16 @@
},
{
"name": "pocketmine/bedrock-protocol",
"version": "35.0.3+bedrock-1.21.50",
"version": "36.0.0+bedrock-1.21.60",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c"
"reference": "2057de319c5c551001c2a544e08d1bc7727d9963"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c4d62581cb62d29ec426914c6b4d7e0ff835da9c",
"reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2057de319c5c551001c2a544e08d1bc7727d9963",
"reference": "2057de319c5c551001c2a544e08d1bc7727d9963",
"shasum": ""
},
"require": {
@ -296,9 +296,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.3+bedrock-1.21.50"
"source": "https://github.com/pmmp/BedrockProtocol/tree/36.0.0+bedrock-1.21.60"
},
"time": "2025-01-07T23:06:29+00:00"
"time": "2025-02-16T15:59:08+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -471,16 +471,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.22.1",
"version": "2.24.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49"
"reference": "6ec5e92c77a2102b2692763733e4763012facae9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/fa4e377c437391cfcfdedd08eea3a848eabd1b49",
"reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49",
"url": "https://api.github.com/repos/pmmp/Language/zipball/6ec5e92c77a2102b2692763733e4763012facae9",
"reference": "6ec5e92c77a2102b2692763733e4763012facae9",
"shasum": ""
},
"type": "library",
@ -488,9 +488,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.22.1"
"source": "https://github.com/pmmp/Language/tree/2.24.0"
},
"time": "2024-12-06T14:44:17+00:00"
"time": "2025-02-16T20:46:34+00:00"
},
{
"name": "pocketmine/log",
@ -2967,5 +2967,5 @@
"platform-overrides": {
"php": "8.1.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View File

@ -39,12 +39,11 @@ final class BedrockDataFiles{
public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json';
public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt';
public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json';
public const CREATIVEITEMS_JSON = BEDROCK_DATA_PATH . '/creativeitems.json';
public const ENTITY_ID_MAP_JSON = BEDROCK_DATA_PATH . '/entity_id_map.json';
public const ENTITY_IDENTIFIERS_NBT = BEDROCK_DATA_PATH . '/entity_identifiers.nbt';
public const ENUMS_PY = BEDROCK_DATA_PATH . '/enums.py';
public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json';
public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json';
public const PARTICLE_ID_MAP_JSON = BEDROCK_DATA_PATH . '/particle_id_map.json';
public const PROTOCOL_INFO_JSON = BEDROCK_DATA_PATH . '/protocol_info.json';
public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin';
public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json';

View File

@ -45,8 +45,8 @@ final class BlockStateData{
public const CURRENT_VERSION =
(1 << 24) | //major
(21 << 16) | //minor
(40 << 8) | //patch
(1); //revision
(60 << 8) | //patch
(33); //revision
public const TAG_NAME = "name";
public const TAG_STATES = "states";

View File

@ -59,6 +59,7 @@ final class BlockStateNames{
public const COVERED_BIT = "covered_bit";
public const CRACKED_STATE = "cracked_state";
public const CRAFTING = "crafting";
public const CREAKING_HEART_STATE = "creaking_heart_state";
public const DEAD_BIT = "dead_bit";
public const DEPRECATED = "deprecated";
public const DIRECTION = "direction";

View File

@ -56,6 +56,10 @@ final class BlockStateStringValues{
public const CRACKED_STATE_MAX_CRACKED = "max_cracked";
public const CRACKED_STATE_NO_CRACKS = "no_cracks";
public const CREAKING_HEART_STATE_AWAKE = "awake";
public const CREAKING_HEART_STATE_DORMANT = "dormant";
public const CREAKING_HEART_STATE_UPROOTED = "uprooted";
public const DRIPSTONE_THICKNESS_BASE = "base";
public const DRIPSTONE_THICKNESS_FRUSTUM = "frustum";
public const DRIPSTONE_THICKNESS_MERGE = "merge";

View File

@ -131,7 +131,7 @@ final class BlockStateDeserializerHelper{
//TODO: check if these need any special treatment to get the appropriate data to both halves of the door
return $block
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT))
->setFacing(Facing::rotateY($in->readLegacyHorizontalFacing(), false))
->setFacing($in->readCardinalHorizontalFacing())
->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT))
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}
@ -145,7 +145,7 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{
return $block
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setInWall($in->readBool(BlockStateNames::IN_WALL_BIT))
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}

View File

@ -100,7 +100,7 @@ final class BlockStateSerializerHelper{
public static function encodeDoor(Door $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
->writeLegacyHorizontalFacing(Facing::rotateY($block->getFacing(), true))
->writeCardinalHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::DOOR_HINGE_BIT, $block->isHingeRight())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
@ -112,7 +112,7 @@ final class BlockStateSerializerHelper{
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
return $out
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}

View File

@ -513,6 +513,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
AbilitiesLayer::LAYER_BASE,
array_fill(0, AbilitiesLayer::NUMBER_OF_ABILITIES, false),
0.0,
0.0,
0.0
)
])),

View File

@ -0,0 +1,34 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory;
/**
* Available tabs in the creative inventory that an item can be displayed in.
*/
enum CreativeCategory{
case CONSTRUCTION;
case NATURE;
case EQUIPMENT;
case ITEMS;
}

View File

@ -0,0 +1,51 @@
<?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\inventory;
use pocketmine\item\Item;
use pocketmine\lang\Translatable;
use function strlen;
/**
* Info for an item group in the creative inventory menu.
*/
final class CreativeGroup{
/**
* @param Translatable|string $name Tooltip shown to the player on hover
* @param Item $icon Item shown when the group is collapsed
*/
public function __construct(
private readonly Translatable|string $name,
private readonly Item $icon
){
$nameLength = $name instanceof Translatable ? strlen($name->getText()) : strlen($name);
if($nameLength === 0){
throw new \InvalidArgumentException("Creative group name cannot be empty");
}
}
public function getName() : Translatable|string{ return $this->name; }
public function getIcon() : Item{ return clone $this->icon; }
}

View File

@ -24,21 +24,23 @@ declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\crafting\CraftingManagerFromDataHelper;
use pocketmine\crafting\json\ItemStackData;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\inventory\json\CreativeGroupData;
use pocketmine\item\Item;
use pocketmine\lang\Translatable;
use pocketmine\utils\DestructorCallbackTrait;
use pocketmine\utils\ObjectSet;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path;
use function array_filter;
use function array_map;
final class CreativeInventory{
use SingletonTrait;
use DestructorCallbackTrait;
/**
* @var Item[]
* @phpstan-var array<int, Item>
* @var CreativeInventoryEntry[]
* @phpstan-var array<int, CreativeInventoryEntry>
*/
private array $creative = [];
@ -47,17 +49,32 @@ final class CreativeInventory{
private function __construct(){
$this->contentChangedCallbacks = new ObjectSet();
$creativeItems = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile(
BedrockDataFiles::CREATIVEITEMS_JSON,
ItemStackData::class
);
foreach($creativeItems as $data){
$item = CraftingManagerFromDataHelper::deserializeItemStack($data);
if($item === null){
//unknown item
continue;
foreach([
"construction" => CreativeCategory::CONSTRUCTION,
"nature" => CreativeCategory::NATURE,
"equipment" => CreativeCategory::EQUIPMENT,
"items" => CreativeCategory::ITEMS,
] as $categoryId => $categoryEnum){
$groups = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile(
Path::join(\pocketmine\BEDROCK_DATA_PATH, "creative", $categoryId . ".json"),
CreativeGroupData::class
);
foreach($groups as $groupData){
$icon = $groupData->group_icon === null ? null : CraftingManagerFromDataHelper::deserializeItemStack($groupData->group_icon);
$group = $icon === null ? null : new CreativeGroup(
new Translatable($groupData->group_name),
$icon
);
$items = array_filter(array_map(static fn($itemStack) => CraftingManagerFromDataHelper::deserializeItemStack($itemStack), $groupData->items));
foreach($items as $item){
$this->add($item, $categoryEnum, $group);
}
}
$this->add($item);
}
}
@ -75,16 +92,28 @@ final class CreativeInventory{
* @phpstan-return array<int, Item>
*/
public function getAll() : array{
return Utils::cloneObjectArray($this->creative);
return array_map(fn(CreativeInventoryEntry $entry) => $entry->getItem(), $this->creative);
}
/**
* @return CreativeInventoryEntry[]
* @phpstan-return array<int, CreativeInventoryEntry>
*/
public function getAllEntries() : array{
return $this->creative;
}
public function getItem(int $index) : ?Item{
return isset($this->creative[$index]) ? clone $this->creative[$index] : null;
return $this->getEntry($index)?->getItem();
}
public function getEntry(int $index) : ?CreativeInventoryEntry{
return $this->creative[$index] ?? null;
}
public function getItemIndex(Item $item) : int{
foreach($this->creative as $i => $d){
if($item->equals($d, true, false)){
if($d->matchesItem($item)){
return $i;
}
}
@ -96,8 +125,8 @@ final class CreativeInventory{
* Adds an item to the creative menu.
* Note: Players who are already online when this is called will not see this change.
*/
public function add(Item $item) : void{
$this->creative[] = clone $item;
public function add(Item $item, CreativeCategory $category = CreativeCategory::ITEMS, ?CreativeGroup $group = null) : void{
$this->creative[] = new CreativeInventoryEntry($item, $category, $group);
$this->onContentChange();
}

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\inventory;
use pocketmine\item\Item;
final class CreativeInventoryEntry{
private readonly Item $item;
public function __construct(
Item $item,
private readonly CreativeCategory $category,
private readonly ?CreativeGroup $group = null
){
$this->item = clone $item;
}
public function getItem() : Item{ return clone $this->item; }
public function getCategory() : CreativeCategory{ return $this->category; }
public function getGroup() : ?CreativeGroup{ return $this->group; }
public function matchesItem(Item $item) : bool{
return $item->equals($this->item, checkDamage: true, checkCompound: false);
}
}

View File

@ -0,0 +1,38 @@
<?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\inventory\json;
use pocketmine\crafting\json\ItemStackData;
final class CreativeGroupData{
/** @required */
public string $group_name;
/** @required */
public ?ItemStackData $group_icon;
/**
* @var \pocketmine\crafting\json\ItemStackData[]
* @required
*/
public array $items;
}

View File

@ -897,6 +897,18 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_FROSTWALKER, []);
}
public static function enchantment_heavy_weapon_breach() : Translatable{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_HEAVY_WEAPON_BREACH, []);
}
public static function enchantment_heavy_weapon_density() : Translatable{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_HEAVY_WEAPON_DENSITY, []);
}
public static function enchantment_heavy_weapon_windburst() : Translatable{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_HEAVY_WEAPON_WINDBURST, []);
}
public static function enchantment_knockback() : Translatable{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_KNOCKBACK, []);
}
@ -1108,6 +1120,318 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_WARD_DESC, []);
}
public static function itemGroup_name_anvil() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_ANVIL, []);
}
public static function itemGroup_name_arrow() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_ARROW, []);
}
public static function itemGroup_name_axe() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_AXE, []);
}
public static function itemGroup_name_banner() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BANNER, []);
}
public static function itemGroup_name_banner_pattern() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BANNER_PATTERN, []);
}
public static function itemGroup_name_bed() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BED, []);
}
public static function itemGroup_name_boat() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BOAT, []);
}
public static function itemGroup_name_boots() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BOOTS, []);
}
public static function itemGroup_name_bundles() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BUNDLES, []);
}
public static function itemGroup_name_buttons() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_BUTTONS, []);
}
public static function itemGroup_name_candles() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CANDLES, []);
}
public static function itemGroup_name_chalkboard() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CHALKBOARD, []);
}
public static function itemGroup_name_chest() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CHEST, []);
}
public static function itemGroup_name_chestboat() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CHESTBOAT, []);
}
public static function itemGroup_name_chestplate() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CHESTPLATE, []);
}
public static function itemGroup_name_compounds() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_COMPOUNDS, []);
}
public static function itemGroup_name_concrete() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CONCRETE, []);
}
public static function itemGroup_name_concretePowder() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CONCRETEPOWDER, []);
}
public static function itemGroup_name_cookedFood() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_COOKEDFOOD, []);
}
public static function itemGroup_name_coral() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CORAL, []);
}
public static function itemGroup_name_coral_decorations() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CORAL_DECORATIONS, []);
}
public static function itemGroup_name_crop() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_CROP, []);
}
public static function itemGroup_name_door() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_DOOR, []);
}
public static function itemGroup_name_dye() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_DYE, []);
}
public static function itemGroup_name_enchantedBook() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_ENCHANTEDBOOK, []);
}
public static function itemGroup_name_fence() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_FENCE, []);
}
public static function itemGroup_name_fenceGate() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_FENCEGATE, []);
}
public static function itemGroup_name_firework() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_FIREWORK, []);
}
public static function itemGroup_name_fireworkStars() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_FIREWORKSTARS, []);
}
public static function itemGroup_name_flower() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_FLOWER, []);
}
public static function itemGroup_name_glass() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_GLASS, []);
}
public static function itemGroup_name_glassPane() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_GLASSPANE, []);
}
public static function itemGroup_name_glazedTerracotta() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_GLAZEDTERRACOTTA, []);
}
public static function itemGroup_name_goatHorn() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_GOATHORN, []);
}
public static function itemGroup_name_grass() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_GRASS, []);
}
public static function itemGroup_name_helmet() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_HELMET, []);
}
public static function itemGroup_name_hoe() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_HOE, []);
}
public static function itemGroup_name_horseArmor() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_HORSEARMOR, []);
}
public static function itemGroup_name_leaves() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_LEAVES, []);
}
public static function itemGroup_name_leggings() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_LEGGINGS, []);
}
public static function itemGroup_name_lingeringPotion() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_LINGERINGPOTION, []);
}
public static function itemGroup_name_log() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_LOG, []);
}
public static function itemGroup_name_minecart() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_MINECART, []);
}
public static function itemGroup_name_miscFood() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_MISCFOOD, []);
}
public static function itemGroup_name_mobEgg() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_MOBEGG, []);
}
public static function itemGroup_name_monsterStoneEgg() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_MONSTERSTONEEGG, []);
}
public static function itemGroup_name_mushroom() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_MUSHROOM, []);
}
public static function itemGroup_name_netherWartBlock() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_NETHERWARTBLOCK, []);
}
public static function itemGroup_name_ominousBottle() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_OMINOUSBOTTLE, []);
}
public static function itemGroup_name_ore() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_ORE, []);
}
public static function itemGroup_name_permission() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_PERMISSION, []);
}
public static function itemGroup_name_pickaxe() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_PICKAXE, []);
}
public static function itemGroup_name_planks() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_PLANKS, []);
}
public static function itemGroup_name_potion() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_POTION, []);
}
public static function itemGroup_name_pressurePlate() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_PRESSUREPLATE, []);
}
public static function itemGroup_name_products() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_PRODUCTS, []);
}
public static function itemGroup_name_rail() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_RAIL, []);
}
public static function itemGroup_name_rawFood() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_RAWFOOD, []);
}
public static function itemGroup_name_record() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_RECORD, []);
}
public static function itemGroup_name_sandstone() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SANDSTONE, []);
}
public static function itemGroup_name_sapling() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SAPLING, []);
}
public static function itemGroup_name_seed() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SEED, []);
}
public static function itemGroup_name_shovel() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SHOVEL, []);
}
public static function itemGroup_name_shulkerBox() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SHULKERBOX, []);
}
public static function itemGroup_name_sign() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SIGN, []);
}
public static function itemGroup_name_skull() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SKULL, []);
}
public static function itemGroup_name_slab() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SLAB, []);
}
public static function itemGroup_name_splashPotion() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SPLASHPOTION, []);
}
public static function itemGroup_name_stainedClay() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_STAINEDCLAY, []);
}
public static function itemGroup_name_stairs() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_STAIRS, []);
}
public static function itemGroup_name_stone() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_STONE, []);
}
public static function itemGroup_name_stoneBrick() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_STONEBRICK, []);
}
public static function itemGroup_name_sword() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_SWORD, []);
}
public static function itemGroup_name_trapdoor() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_TRAPDOOR, []);
}
public static function itemGroup_name_walls() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_WALLS, []);
}
public static function itemGroup_name_wood() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_WOOD, []);
}
public static function itemGroup_name_wool() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_WOOL, []);
}
public static function itemGroup_name_woolCarpet() : Translatable{
return new Translatable(KnownTranslationKeys::ITEMGROUP_NAME_WOOLCARPET, []);
}
public static function kick_admin() : Translatable{
return new Translatable(KnownTranslationKeys::KICK_ADMIN, []);
}

View File

@ -194,6 +194,9 @@ final class KnownTranslationKeys{
public const ENCHANTMENT_FIRE = "enchantment.fire";
public const ENCHANTMENT_FISHINGSPEED = "enchantment.fishingSpeed";
public const ENCHANTMENT_FROSTWALKER = "enchantment.frostwalker";
public const ENCHANTMENT_HEAVY_WEAPON_BREACH = "enchantment.heavy_weapon.breach";
public const ENCHANTMENT_HEAVY_WEAPON_DENSITY = "enchantment.heavy_weapon.density";
public const ENCHANTMENT_HEAVY_WEAPON_WINDBURST = "enchantment.heavy_weapon.windburst";
public const ENCHANTMENT_KNOCKBACK = "enchantment.knockback";
public const ENCHANTMENT_LOOTBONUS = "enchantment.lootBonus";
public const ENCHANTMENT_LOOTBONUSDIGGER = "enchantment.lootBonusDigger";
@ -245,6 +248,84 @@ final class KnownTranslationKeys{
public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc";
public const ITEM_RECORD_WAIT_DESC = "item.record_wait.desc";
public const ITEM_RECORD_WARD_DESC = "item.record_ward.desc";
public const ITEMGROUP_NAME_ANVIL = "itemGroup.name.anvil";
public const ITEMGROUP_NAME_ARROW = "itemGroup.name.arrow";
public const ITEMGROUP_NAME_AXE = "itemGroup.name.axe";
public const ITEMGROUP_NAME_BANNER = "itemGroup.name.banner";
public const ITEMGROUP_NAME_BANNER_PATTERN = "itemGroup.name.banner_pattern";
public const ITEMGROUP_NAME_BED = "itemGroup.name.bed";
public const ITEMGROUP_NAME_BOAT = "itemGroup.name.boat";
public const ITEMGROUP_NAME_BOOTS = "itemGroup.name.boots";
public const ITEMGROUP_NAME_BUNDLES = "itemGroup.name.bundles";
public const ITEMGROUP_NAME_BUTTONS = "itemGroup.name.buttons";
public const ITEMGROUP_NAME_CANDLES = "itemGroup.name.candles";
public const ITEMGROUP_NAME_CHALKBOARD = "itemGroup.name.chalkboard";
public const ITEMGROUP_NAME_CHEST = "itemGroup.name.chest";
public const ITEMGROUP_NAME_CHESTBOAT = "itemGroup.name.chestboat";
public const ITEMGROUP_NAME_CHESTPLATE = "itemGroup.name.chestplate";
public const ITEMGROUP_NAME_COMPOUNDS = "itemGroup.name.compounds";
public const ITEMGROUP_NAME_CONCRETE = "itemGroup.name.concrete";
public const ITEMGROUP_NAME_CONCRETEPOWDER = "itemGroup.name.concretePowder";
public const ITEMGROUP_NAME_COOKEDFOOD = "itemGroup.name.cookedFood";
public const ITEMGROUP_NAME_CORAL = "itemGroup.name.coral";
public const ITEMGROUP_NAME_CORAL_DECORATIONS = "itemGroup.name.coral_decorations";
public const ITEMGROUP_NAME_CROP = "itemGroup.name.crop";
public const ITEMGROUP_NAME_DOOR = "itemGroup.name.door";
public const ITEMGROUP_NAME_DYE = "itemGroup.name.dye";
public const ITEMGROUP_NAME_ENCHANTEDBOOK = "itemGroup.name.enchantedBook";
public const ITEMGROUP_NAME_FENCE = "itemGroup.name.fence";
public const ITEMGROUP_NAME_FENCEGATE = "itemGroup.name.fenceGate";
public const ITEMGROUP_NAME_FIREWORK = "itemGroup.name.firework";
public const ITEMGROUP_NAME_FIREWORKSTARS = "itemGroup.name.fireworkStars";
public const ITEMGROUP_NAME_FLOWER = "itemGroup.name.flower";
public const ITEMGROUP_NAME_GLASS = "itemGroup.name.glass";
public const ITEMGROUP_NAME_GLASSPANE = "itemGroup.name.glassPane";
public const ITEMGROUP_NAME_GLAZEDTERRACOTTA = "itemGroup.name.glazedTerracotta";
public const ITEMGROUP_NAME_GOATHORN = "itemGroup.name.goatHorn";
public const ITEMGROUP_NAME_GRASS = "itemGroup.name.grass";
public const ITEMGROUP_NAME_HELMET = "itemGroup.name.helmet";
public const ITEMGROUP_NAME_HOE = "itemGroup.name.hoe";
public const ITEMGROUP_NAME_HORSEARMOR = "itemGroup.name.horseArmor";
public const ITEMGROUP_NAME_LEAVES = "itemGroup.name.leaves";
public const ITEMGROUP_NAME_LEGGINGS = "itemGroup.name.leggings";
public const ITEMGROUP_NAME_LINGERINGPOTION = "itemGroup.name.lingeringPotion";
public const ITEMGROUP_NAME_LOG = "itemGroup.name.log";
public const ITEMGROUP_NAME_MINECART = "itemGroup.name.minecart";
public const ITEMGROUP_NAME_MISCFOOD = "itemGroup.name.miscFood";
public const ITEMGROUP_NAME_MOBEGG = "itemGroup.name.mobEgg";
public const ITEMGROUP_NAME_MONSTERSTONEEGG = "itemGroup.name.monsterStoneEgg";
public const ITEMGROUP_NAME_MUSHROOM = "itemGroup.name.mushroom";
public const ITEMGROUP_NAME_NETHERWARTBLOCK = "itemGroup.name.netherWartBlock";
public const ITEMGROUP_NAME_OMINOUSBOTTLE = "itemGroup.name.ominousBottle";
public const ITEMGROUP_NAME_ORE = "itemGroup.name.ore";
public const ITEMGROUP_NAME_PERMISSION = "itemGroup.name.permission";
public const ITEMGROUP_NAME_PICKAXE = "itemGroup.name.pickaxe";
public const ITEMGROUP_NAME_PLANKS = "itemGroup.name.planks";
public const ITEMGROUP_NAME_POTION = "itemGroup.name.potion";
public const ITEMGROUP_NAME_PRESSUREPLATE = "itemGroup.name.pressurePlate";
public const ITEMGROUP_NAME_PRODUCTS = "itemGroup.name.products";
public const ITEMGROUP_NAME_RAIL = "itemGroup.name.rail";
public const ITEMGROUP_NAME_RAWFOOD = "itemGroup.name.rawFood";
public const ITEMGROUP_NAME_RECORD = "itemGroup.name.record";
public const ITEMGROUP_NAME_SANDSTONE = "itemGroup.name.sandstone";
public const ITEMGROUP_NAME_SAPLING = "itemGroup.name.sapling";
public const ITEMGROUP_NAME_SEED = "itemGroup.name.seed";
public const ITEMGROUP_NAME_SHOVEL = "itemGroup.name.shovel";
public const ITEMGROUP_NAME_SHULKERBOX = "itemGroup.name.shulkerBox";
public const ITEMGROUP_NAME_SIGN = "itemGroup.name.sign";
public const ITEMGROUP_NAME_SKULL = "itemGroup.name.skull";
public const ITEMGROUP_NAME_SLAB = "itemGroup.name.slab";
public const ITEMGROUP_NAME_SPLASHPOTION = "itemGroup.name.splashPotion";
public const ITEMGROUP_NAME_STAINEDCLAY = "itemGroup.name.stainedClay";
public const ITEMGROUP_NAME_STAIRS = "itemGroup.name.stairs";
public const ITEMGROUP_NAME_STONE = "itemGroup.name.stone";
public const ITEMGROUP_NAME_STONEBRICK = "itemGroup.name.stoneBrick";
public const ITEMGROUP_NAME_SWORD = "itemGroup.name.sword";
public const ITEMGROUP_NAME_TRAPDOOR = "itemGroup.name.trapdoor";
public const ITEMGROUP_NAME_WALLS = "itemGroup.name.walls";
public const ITEMGROUP_NAME_WOOD = "itemGroup.name.wood";
public const ITEMGROUP_NAME_WOOL = "itemGroup.name.wool";
public const ITEMGROUP_NAME_WOOLCARPET = "itemGroup.name.woolCarpet";
public const KICK_ADMIN = "kick.admin";
public const KICK_ADMIN_REASON = "kick.admin.reason";
public const KICK_REASON_CHEAT = "kick.reason.cheat";

View File

@ -690,7 +690,7 @@ class InventoryManager{
}
public function syncCreative() : void{
$this->session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache($this->player->getCreativeInventory()));
$this->session->sendDataPacket(CreativeInventoryCache::getInstance()->buildPacket($this->player->getCreativeInventory(), $this->session));
}
/**

View File

@ -1058,7 +1058,7 @@ class NetworkSession{
];
$layers = [
new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, $for->getFlightSpeedMultiplier(), 0.1),
new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, $for->getFlightSpeedMultiplier(), 1, 0.1),
];
if(!$for->hasBlockCollision()){
//TODO: HACK! In 1.19.80, the client starts falling in our faux spectator mode when it clips into a
@ -1068,7 +1068,7 @@ class NetworkSession{
$layers[] = new AbilitiesLayer(AbilitiesLayer::LAYER_SPECTATOR, [
AbilitiesLayer::ABILITY_FLYING => true,
], null, null);
], null, null, null);
}
$this->sendDataPacket(UpdateAbilitiesPacket::create(new AbilitiesData(

View File

@ -23,23 +23,30 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\cache;
use pocketmine\inventory\CreativeCategory;
use pocketmine\inventory\CreativeInventory;
use pocketmine\lang\Translatable;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeGroupEntry;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeItemEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\utils\SingletonTrait;
use function is_string;
use function spl_object_id;
use const PHP_INT_MIN;
final class CreativeInventoryCache{
use SingletonTrait;
/**
* @var CreativeContentPacket[]
* @phpstan-var array<int, CreativeContentPacket>
* @var CreativeInventoryCacheEntry[]
* @phpstan-var array<int, CreativeInventoryCacheEntry>
*/
private array $caches = [];
public function getCache(CreativeInventory $inventory) : CreativeContentPacket{
private function getCacheEntry(CreativeInventory $inventory) : CreativeInventoryCacheEntry{
$id = spl_object_id($inventory);
if(!isset($this->caches[$id])){
$inventory->getDestructorCallbacks()->add(function() use ($id) : void{
@ -48,7 +55,7 @@ final class CreativeInventoryCache{
$inventory->getContentChangedCallbacks()->add(function() use ($id) : void{
unset($this->caches[$id]);
});
$this->caches[$id] = $this->buildCreativeInventoryCache($inventory);
$this->caches[$id] = $this->buildCacheEntry($inventory);
}
return $this->caches[$id];
}
@ -56,14 +63,91 @@ final class CreativeInventoryCache{
/**
* Rebuild the cache for the given inventory.
*/
private function buildCreativeInventoryCache(CreativeInventory $inventory) : CreativeContentPacket{
$entries = [];
private function buildCacheEntry(CreativeInventory $inventory) : CreativeInventoryCacheEntry{
$categories = [];
$groups = [];
$typeConverter = TypeConverter::getInstance();
//creative inventory may have holes if items were unregistered - ensure network IDs used are always consistent
foreach($inventory->getAll() as $k => $item){
$entries[] = new CreativeContentEntry($k, $typeConverter->coreItemStackToNet($item));
$nextIndex = 0;
$groupIndexes = [];
$itemGroupIndexes = [];
foreach($inventory->getAllEntries() as $k => $entry){
$group = $entry->getGroup();
$category = $entry->getCategory();
if($group === null){
$groupId = PHP_INT_MIN;
}else{
$groupId = spl_object_id($group);
unset($groupIndexes[$category->name][PHP_INT_MIN]); //start a new anonymous group for this category
}
//group object may be reused by multiple categories
if(!isset($groupIndexes[$category->name][$groupId])){
$groupIndexes[$category->name][$groupId] = $nextIndex++;
$categories[] = $category;
$groups[] = $group;
}
$itemGroupIndexes[$k] = $groupIndexes[$category->name][$groupId];
}
return CreativeContentPacket::create($entries);
//creative inventory may have holes if items were unregistered - ensure network IDs used are always consistent
$items = [];
foreach($inventory->getAllEntries() as $k => $entry){
$items[] = new CreativeItemEntry(
$k,
$typeConverter->coreItemStackToNet($entry->getItem()),
$itemGroupIndexes[$k]
);
}
return new CreativeInventoryCacheEntry($categories, $groups, $items);
}
public function buildPacket(CreativeInventory $inventory, NetworkSession $session) : CreativeContentPacket{
$player = $session->getPlayer() ?? throw new \LogicException("Cannot prepare creative data for a session without a player");
$language = $player->getLanguage();
$forceLanguage = $player->getServer()->isLanguageForced();
$typeConverter = $session->getTypeConverter();
$cachedEntry = $this->getCacheEntry($inventory);
$translate = function(Translatable|string $translatable) use ($session, $language, $forceLanguage) : string{
if(is_string($translatable)){
$message = $translatable;
}elseif(!$forceLanguage){
[$message,] = $session->prepareClientTranslatableMessage($translatable);
}else{
$message = $language->translate($translatable);
}
return $message;
};
$groupEntries = [];
foreach($cachedEntry->categories as $index => $category){
$group = $cachedEntry->groups[$index];
$categoryId = match ($category) {
CreativeCategory::CONSTRUCTION => CreativeContentPacket::CATEGORY_CONSTRUCTION,
CreativeCategory::NATURE => CreativeContentPacket::CATEGORY_NATURE,
CreativeCategory::EQUIPMENT => CreativeContentPacket::CATEGORY_EQUIPMENT,
CreativeCategory::ITEMS => CreativeContentPacket::CATEGORY_ITEMS
};
if($group === null){
$groupEntries[] = new CreativeGroupEntry($categoryId, "", ItemStack::null());
}else{
$groupIcon = $group->getIcon();
//TODO: HACK! In 1.21.60, Workaround glitchy behaviour when an item is used as an icon for a group it
//doesn't belong to. Without this hack, both instances of the item will show a +, but neither of them
//will actually expand the group work correctly.
$groupIcon->getNamedTag()->setInt("___GroupBugWorkaround___", $index);
$groupName = $group->getName();
$groupEntries[] = new CreativeGroupEntry(
$categoryId,
$translate($groupName),
$typeConverter->coreItemStackToNet($groupIcon)
);
}
}
return CreativeContentPacket::create($groupEntries, $cachedEntry->items);
}
}

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\network\mcpe\cache;
use pocketmine\inventory\CreativeCategory;
use pocketmine\inventory\CreativeGroup;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeItemEntry;
final class CreativeInventoryCacheEntry{
/**
* @param CreativeCategory[] $categories
* @param CreativeGroup[]|null[] $groups
* @param CreativeItemEntry[] $items
*
* @phpstan-param list<CreativeCategory> $categories
* @phpstan-param list<CreativeGroup|null> $groups
* @phpstan-param list<CreativeItemEntry> $items
*/
public function __construct(
public readonly array $categories,
public readonly array $groups,
public readonly array $items,
){
//NOOP
}
}

View File

@ -23,10 +23,15 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\convert;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use function base64_decode;
use function is_array;
use function is_bool;
use function is_int;
@ -41,12 +46,15 @@ final class ItemTypeDictionaryFromDataHelper{
throw new AssumptionFailedError("Invalid item list format");
}
$emptyNBT = new CacheableNbt(new CompoundTag());
$nbtSerializer = new LittleEndianNbtSerializer();
$params = [];
foreach(Utils::promoteKeys($table) as $name => $entry){
if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){
if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"], $entry["version"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"]) || !is_int($entry["version"]) || !(is_string($componentNbt = $entry["component_nbt"] ?? null) || $componentNbt === null)){
throw new AssumptionFailedError("Invalid item list format");
}
$params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"]);
$params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"], $entry["version"], $componentNbt === null ? $emptyNBT : new CacheableNbt($nbtSerializer->read(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => base64_decode($componentNbt, true)))->mustGetCompoundTag()));
}
return new ItemTypeDictionary($params);
}

View File

@ -28,6 +28,7 @@ use pocketmine\network\mcpe\cache\CraftingDataCache;
use pocketmine\network\mcpe\cache\StaticPacketCache;
use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\ItemRegistryPacket;
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\StartGamePacket;
@ -110,9 +111,11 @@ class PreSpawnPacketHandler extends PacketHandler{
new NetworkPermissions(disableClientSounds: true),
[],
0,
$typeConverter->getItemTypeDictionary()->getEntries(),
));
$this->session->getLogger()->debug("Sending items");
$this->session->sendDataPacket(ItemRegistryPacket::create($typeConverter->getItemTypeDictionary()->getEntries()));
$this->session->getLogger()->debug("Sending actor identifiers");
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getAvailableActorIdentifiers());

View File

@ -51,12 +51,12 @@ use function time;
class BedrockWorldData extends BaseNbtWorldData{
public const CURRENT_STORAGE_VERSION = 10;
public const CURRENT_STORAGE_NETWORK_VERSION = 748;
public const CURRENT_STORAGE_NETWORK_VERSION = 776;
public const CURRENT_CLIENT_VERSION_TARGET = [
1, //major
21, //minor
40, //patch
1, //revision
60, //patch
33, //revision
0 //is beta
];

View File

@ -35,6 +35,7 @@ use pocketmine\crafting\json\SmithingTrimRecipeData;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\item\BlockItemIdMap;
use pocketmine\data\bedrock\item\ItemTypeNames;
use pocketmine\inventory\json\CreativeGroupData;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
@ -48,15 +49,17 @@ use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket;
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
use pocketmine\network\mcpe\protocol\ItemRegistryPacket;
use pocketmine\network\mcpe\protocol\PacketPool;
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\StartGamePacket;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeGroupEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\network\mcpe\protocol\types\recipe\ComplexAliasItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
@ -134,6 +137,19 @@ class ParserPacketHandler extends PacketHandler{
return base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($statePropertiesTag)));
}
/**
* @param ItemStackData[] $items
*/
private function creativeGroupEntryToJson(CreativeGroupEntry $entry, array $items) : CreativeGroupData{
$data = new CreativeGroupData();
$data->group_name = $entry->getCategoryName();
$data->group_icon = $entry->getIcon()->getId() === 0 ? null : $this->itemStackToJson($entry->getIcon());
$data->items = $items;
return $data;
}
private function itemStackToJson(ItemStack $itemStack) : ItemStackData{
if($itemStack->getId() === 0){
throw new InvalidArgumentException("Cannot serialize a null itemstack");
@ -234,31 +250,68 @@ class ParserPacketHandler extends PacketHandler{
}
public function handleStartGame(StartGamePacket $packet) : bool{
$this->itemTypeDictionary = new ItemTypeDictionary($packet->itemTable);
echo "updating legacy item ID mapping table\n";
$table = [];
foreach($packet->itemTable as $entry){
$table[$entry->getStringId()] = [
"runtime_id" => $entry->getNumericId(),
"component_based" => $entry->isComponentBased()
];
}
ksort($table, SORT_STRING);
file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n");
foreach(Utils::promoteKeys($packet->levelSettings->experiments->getExperiments()) as $name => $experiment){
echo "Experiment \"$name\" is " . ($experiment ? "" : "not ") . "active\n";
}
return true;
}
public function handleItemRegistry(ItemRegistryPacket $packet) : bool{
$this->itemTypeDictionary = new ItemTypeDictionary($packet->getEntries());
echo "updating legacy item ID mapping table\n";
$emptyNBT = new CompoundTag();
$table = [];
foreach($packet->getEntries() as $entry){
$table[$entry->getStringId()] = [
"runtime_id" => $entry->getNumericId(),
"component_based" => $entry->isComponentBased(),
"version" => $entry->getVersion(),
];
$componentNBT = $entry->getComponentNbt()->getRoot();
if(!$componentNBT->equals($emptyNBT)){
$table[$entry->getStringId()]["component_nbt"] = base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($componentNBT)));
}
}
ksort($table, SORT_STRING);
file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n");
echo "updating item registry\n";
$items = array_map(function(ItemTypeEntry $entry) : array{
return self::objectToOrderedArray($entry);
}, $packet->getEntries());
file_put_contents($this->bedrockDataPath . '/item_registry.json', json_encode($items, JSON_PRETTY_PRINT) . "\n");
return true;
}
public function handleCreativeContent(CreativeContentPacket $packet) : bool{
echo "updating creative inventory data\n";
$items = array_map(function(CreativeContentEntry $entry) : array{
return self::objectToOrderedArray($this->itemStackToJson($entry->getItem()));
}, $packet->getEntries());
file_put_contents($this->bedrockDataPath . '/creativeitems.json', json_encode($items, JSON_PRETTY_PRINT) . "\n");
$groupItems = [];
foreach($packet->getItems() as $itemEntry){
$groupItems[$itemEntry->getGroupId()][] = $this->itemStackToJson($itemEntry->getItem());
}
static $typeMap = [
CreativeContentPacket::CATEGORY_CONSTRUCTION => "construction",
CreativeContentPacket::CATEGORY_NATURE => "nature",
CreativeContentPacket::CATEGORY_EQUIPMENT => "equipment",
CreativeContentPacket::CATEGORY_ITEMS => "items",
];
$groupCategories = [];
foreach(Utils::promoteKeys($packet->getGroups()) as $groupId => $group){
$category = $typeMap[$group->getCategoryId()] ?? throw new PacketHandlingException("Unknown creative category ID " . $group->getCategoryId());
//FIXME: objectToOrderedArray might mess with the order of groupItems
//this isn't a problem right now because it's a list, but could cause problems in the future
$groupCategories[$category][] = self::objectToOrderedArray($this->creativeGroupEntryToJson($group, $groupItems[$groupId]));
}
foreach(Utils::promoteKeys($groupCategories) as $category => $categoryGroups){
file_put_contents($this->bedrockDataPath . '/creative/' . $category . '.json', json_encode($categoryGroups, JSON_PRETTY_PRINT) . "\n");
}
return true;
}