Skip to content

Commit 936499f

Browse files
committed
[sync] Update embedded LibCustom from standalone
1 parent c741708 commit 936499f

9 files changed

Lines changed: 141 additions & 137 deletions

src/imperazim/custom/CustomManager.php

Lines changed: 21 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,103 +4,57 @@
44
namespace imperazim\custom;
55

66
use imperazim\custom\block\CustomBlockFactory;
7-
use imperazim\custom\item\CustomItemFactory;
87
use imperazim\custom\item\ItemComponentsHolder;
9-
use imperazim\custom\registry\ReflectionBridge;
10-
use imperazim\packet\handler\PacketHandler;
11-
use imperazim\packet\LibPacket;
128
use pocketmine\block\inventory\ChestInventory;
139
use pocketmine\block\inventory\DoubleChestInventory;
1410
use pocketmine\block\inventory\EnderChestInventory;
1511
use pocketmine\block\inventory\FurnaceInventory;
1612
use pocketmine\block\inventory\HopperInventory;
1713
use pocketmine\block\inventory\ShulkerBoxInventory;
1814
use pocketmine\block\ItemFrame;
19-
use pocketmine\event\EventPriority;
2015
use pocketmine\event\inventory\InventoryTransactionEvent;
2116
use pocketmine\event\Listener;
2217
use pocketmine\event\player\PlayerDropItemEvent;
2318
use pocketmine\event\player\PlayerInteractEvent;
19+
use pocketmine\event\server\DataPacketSendEvent;
2420
use pocketmine\inventory\PlayerInventory;
25-
use pocketmine\network\mcpe\NetworkSession;
26-
use pocketmine\network\mcpe\protocol\ItemRegistryPacket;
2721
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
2822
use pocketmine\network\mcpe\protocol\StartGamePacket;
2923
use pocketmine\network\mcpe\protocol\types\BlockPaletteEntry;
3024
use pocketmine\network\mcpe\protocol\types\Experiments;
31-
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
3225
use pocketmine\plugin\PluginBase;
3326
use pocketmine\Server;
34-
use function array_merge;
35-
use function count;
3627

3728
final class CustomManager implements Listener {
3829

39-
/** @internal accessed by packet handlers */
30+
/** @internal */
4031
public static Experiments $experiments;
4132

42-
/** @var ItemTypeEntry[] @internal */
43-
public static array $cachedItemTable = [];
44-
45-
/** @var BlockPaletteEntry[] @internal */
46-
public static array $cachedBlockPalette = [];
33+
/** @var BlockPaletteEntry[] */
34+
private array $cachedBlockPalette = [];
4735

4836
public static function init(PluginBase $plugin): void {
4937
self::$experiments = new Experiments(['data_driven_items' => true], true);
38+
Server::getInstance()->getPluginManager()->registerEvents(new self(), $plugin);
39+
}
5040

51-
$interceptor = LibPacket::createInterceptor($plugin, EventPriority::NORMAL);
52-
53-
$interceptor->registerOutgoing(new class extends PacketHandler {
54-
public function __construct() {
55-
parent::__construct([ItemRegistryPacket::class]);
56-
}
57-
58-
public function handle($packet, NetworkSession $session): bool {
59-
if ($packet instanceof ItemRegistryPacket) {
60-
if (CustomManager::$cachedItemTable === []) {
61-
CustomManager::$cachedItemTable = CustomItemFactory::getInstance()->getItemTableEntries();
62-
}
63-
if (count(CustomManager::$cachedItemTable) > 0) {
64-
ReflectionBridge::mergeItemRegistryEntries($packet, CustomManager::$cachedItemTable);
65-
}
66-
}
67-
return true;
68-
}
69-
});
70-
71-
$interceptor->registerOutgoing(new class extends PacketHandler {
72-
public function __construct() {
73-
parent::__construct([StartGamePacket::class]);
74-
}
75-
76-
public function handle($packet, NetworkSession $session): bool {
77-
if ($packet instanceof StartGamePacket) {
78-
if (CustomManager::$cachedBlockPalette === []) {
79-
CustomManager::$cachedBlockPalette = CustomBlockFactory::getInstance()->getBlockPaletteEntries();
80-
}
81-
$packet->levelSettings->experiments = CustomManager::$experiments;
82-
if (count(CustomManager::$cachedBlockPalette) > 0) {
83-
$packet->blockPalette = array_merge($packet->blockPalette, CustomManager::$cachedBlockPalette);
84-
}
85-
}
86-
return true;
87-
}
88-
});
89-
90-
$interceptor->registerOutgoing(new class extends PacketHandler {
91-
public function __construct() {
92-
parent::__construct([ResourcePackStackPacket::class]);
93-
}
94-
95-
public function handle($packet, NetworkSession $session): bool {
96-
if ($packet instanceof ResourcePackStackPacket) {
97-
$packet->experiments = CustomManager::$experiments;
41+
/**
42+
* Intercepts outgoing packets to inject custom block palette and experiments.
43+
* Custom items are injected into the ItemTypeDictionary directly (like Customies),
44+
* so ItemRegistryPacket naturally includes them without interception.
45+
*/
46+
public function onDataPacketSend(DataPacketSendEvent $event): void {
47+
foreach ($event->getPackets() as $packet) {
48+
if ($packet instanceof StartGamePacket) {
49+
if ($this->cachedBlockPalette === []) {
50+
$this->cachedBlockPalette = CustomBlockFactory::getInstance()->getBlockPaletteEntries();
9851
}
99-
return true;
52+
$packet->levelSettings->experiments = self::$experiments;
53+
$packet->blockPalette = $this->cachedBlockPalette;
54+
} elseif ($packet instanceof ResourcePackStackPacket) {
55+
$packet->experiments = self::$experiments;
10056
}
101-
});
102-
103-
Server::getInstance()->getPluginManager()->registerEvents(new self(), $plugin);
57+
}
10458
}
10559

10660
public function onDrop(PlayerDropItemEvent $event): void {

src/imperazim/custom/block/CustomBlockFactory.php

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
namespace imperazim\custom\block;
55

66
use Closure;
7-
use imperazim\custom\block\component\DestructibleByMiningComponent;
8-
use imperazim\custom\block\component\FrictionComponent;
9-
use imperazim\custom\block\component\LightDampeningComponent;
10-
use imperazim\custom\block\component\LightEmissionComponent;
117
use imperazim\custom\block\permutation\Permutable;
128
use imperazim\custom\block\permutation\Permutation;
139
use imperazim\custom\block\permutation\PermutationHelper;
@@ -58,7 +54,20 @@ final class CustomBlockFactory {
5854
* Serializes block data to a cache file so it can be loaded on worker threads without closures.
5955
*/
6056
public function addWorkerInitHook(string $cachePath): void {
61-
if (empty($this->blockFuncs)) {
57+
// Collect custom block state data so workers can re-sort their BlockStateDictionary.
58+
// This MUST happen even if no block objects can be serialized, because the main thread
59+
// already re-sorted its dictionary (which shifts ALL runtime IDs including vanilla blocks).
60+
// Workers must apply the same re-sort or chunk encoding will use wrong runtime IDs.
61+
$customStates = [];
62+
foreach (BlockPaletteManager::getInstance()->getCustomStates() as $state) {
63+
$customStates[] = [
64+
'name' => $state->getStateName(),
65+
'rawProperties' => $state->getRawStateProperties(),
66+
'meta' => $state->getMeta(),
67+
];
68+
}
69+
70+
if (empty($this->blockFuncs) && empty($customStates)) {
6271
return;
6372
}
6473

@@ -67,35 +76,29 @@ public function addWorkerInitHook(string $cachePath): void {
6776

6877
// Build serializable cache: for each block, store the igbinary-serialized Block object
6978
// and the list of state entries (identifier + state count).
70-
$cache = [];
79+
$blocks = [];
7180
foreach ($this->blockFuncs as $identifier => [$blockFunc, $serializer, $deserializer]) {
7281
try {
7382
$block = $blockFunc();
7483
$blockData = igbinary_serialize($block);
7584
if ($blockData === null) {
7685
continue;
7786
}
78-
// Count how many states this block has in the palette.
7987
$stateCount = 0;
8088
foreach ($this->blockPaletteEntries as $entry) {
8189
if ($entry->getName() === $identifier) {
8290
$stateCount++;
8391
}
8492
}
85-
$cache[$identifier] = [
93+
$blocks[$identifier] = [
8694
'blockData' => $blockData,
8795
'states' => array_fill(0, max(1, $stateCount), ['name' => $identifier]),
8896
];
8997
} catch (\Throwable) {
90-
// Block cannot be serialized (e.g. anonymous class) — skip gracefully.
91-
// The block will appear as INFO_UPDATE on async-encoded chunks.
9298
}
9399
}
94100

95-
if (empty($cache)) {
96-
return;
97-
}
98-
101+
$cache = ['blocks' => $blocks, 'states' => $customStates];
99102
file_put_contents($cacheFile, igbinary_serialize($cache));
100103

101104
$server = Server::getInstance();
@@ -145,15 +148,9 @@ public function register(
145148
CustomItemFactory::getInstance()->registerBlockItem($identifier, $block);
146149
$this->customBlocks[$identifier] = $block;
147150

148-
// Build base component NBT from block properties.
151+
// Build component NBT — only include components the block explicitly provides.
149152
$propertiesTag = CompoundTag::create();
150-
$components = CompoundTag::create()
151-
->setTag('minecraft:light_emission', (new LightEmissionComponent($block->getLightLevel()))->toNbt())
152-
->setTag('minecraft:light_dampening', (new LightDampeningComponent($block->getLightFilter()))->toNbt())
153-
->setTag('minecraft:destructible_by_mining', (new DestructibleByMiningComponent($block->getBreakInfo()->getHardness()))->toNbt())
154-
->setTag('minecraft:friction', (new FrictionComponent(1 - $block->getFrictionFactor()))->toNbt());
155-
156-
// Merge any BlockComponent instances the block may carry.
153+
$components = CompoundTag::create();
157154
if ($block instanceof BlockComponentsHolder) {
158155
foreach ($block->getBlockComponents() as $component) {
159156
$components->setTag($component->getName(), $component->toNbt());
@@ -216,12 +213,32 @@ public function register(
216213
GlobalBlockStateHandlers::getSerializer()->map($block, $serializer);
217214
GlobalBlockStateHandlers::getDeserializer()->map($identifier, $deserializer);
218215

216+
// Map creative info to Bedrock string format (always included, defaults to "items")
217+
$categoryStr = 'items';
218+
$groupStr = '';
219219
if ($creative !== null && $creative->category !== null) {
220+
$categoryStr = match ($creative->category) {
221+
CreativeCategory::CONSTRUCTION => 'construction',
222+
CreativeCategory::NATURE => 'nature',
223+
CreativeCategory::EQUIPMENT => 'equipment',
224+
CreativeCategory::ITEMS => 'items',
225+
};
226+
if ($creative->group !== null) {
227+
$name = $creative->group->getName();
228+
$groupStr = is_string($name) ? $name : $name->getText();
229+
}
220230
CreativeInventory::getInstance()->add($block->asItem(), $creative->category, $creative->group);
221231
}
222232

233+
$components->setTag('minecraft:creative_category', CompoundTag::create()
234+
->setString('category', $categoryStr)
235+
->setString('group', $groupStr));
236+
223237
$propertiesTag
224238
->setTag('components', $components)
239+
->setTag('menu_category', CompoundTag::create()
240+
->setString('category', $categoryStr)
241+
->setString('group', $groupStr))
225242
->setInt('molangVersion', 1);
226243

227244
$this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($propertiesTag));

src/imperazim/custom/block/component/GeometryComponent.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,33 @@
88

99
final class GeometryComponent implements BlockComponent {
1010

11+
private CompoundTag $boneVisibility;
12+
1113
public function __construct(
12-
private string $identifier,
13-
) {}
14+
private string $identifier = 'minecraft:geometry.full_block',
15+
) {
16+
$this->boneVisibility = CompoundTag::create();
17+
}
1418

1519
public function getName(): string {
1620
return 'minecraft:geometry';
1721
}
1822

1923
public function toNbt(): CompoundTag {
2024
return CompoundTag::create()
25+
->setTag('bone_visibility', $this->boneVisibility)
26+
->setString('culling', '')
2127
->setString('identifier', $this->identifier);
2228
}
29+
30+
public function addBoneVisibility(string $boneName, bool|string $visibility): self {
31+
if (is_string($visibility)) {
32+
$this->boneVisibility->setTag($boneName, CompoundTag::create()
33+
->setString('expression', $visibility)
34+
->setShort('version', 12));
35+
} else {
36+
$this->boneVisibility->setFloat($boneName, $visibility ? 1 : 0);
37+
}
38+
return $this;
39+
}
2340
}

src/imperazim/custom/block/component/LightDampeningComponent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ public function getName(): string {
2323

2424
public function toNbt(): CompoundTag {
2525
return CompoundTag::create()
26-
->setInt('lightLevel', $this->level);
26+
->setByte('lightLevel', $this->level);
2727
}
2828
}

src/imperazim/custom/block/component/LightEmissionComponent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ public function getName(): string {
2323

2424
public function toNbt(): CompoundTag {
2525
return CompoundTag::create()
26-
->setInt('emission', $this->emission);
26+
->setByte('emission', $this->emission);
2727
}
2828
}

src/imperazim/custom/block/component/MaterialInstancesComponent.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function toNbt(): CompoundTag {
2626
$materialsTag->setTag($material->target, $material->toNbt());
2727
}
2828
return CompoundTag::create()
29+
->setTag('mappings', CompoundTag::create())
2930
->setTag('materials', $materialsTag);
3031
}
3132
}

src/imperazim/custom/item/CustomItemFactory.php

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,9 @@
77
use InvalidArgumentException;
88
use imperazim\custom\registry\ReflectionBridge;
99
use pocketmine\block\Block;
10-
use pocketmine\data\bedrock\item\BlockItemIdMap;
1110
use pocketmine\data\bedrock\item\SavedItemData;
12-
use pocketmine\inventory\CreativeCategory;
1311
use pocketmine\inventory\CreativeInventory;
1412
use pocketmine\item\Item;
15-
use pocketmine\item\ItemIdentifier;
16-
use pocketmine\item\ItemTypeIds;
1713
use pocketmine\item\StringToItemParser;
1814
use pocketmine\nbt\tag\CompoundTag;
1915
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
@@ -48,36 +44,39 @@ public function getItemTableEntries(): array {
4844
* @param Closure(): Item $factory
4945
*/
5046
public function register(string $identifier, Closure $factory, ?CreativeInventoryInfo $creative = null): void {
51-
$itemId = ItemTypeIds::newId();
52-
5347
$item = $factory();
5448
if (!$item instanceof Item) {
5549
throw new InvalidArgumentException('Factory must return an Item instance');
5650
}
57-
58-
ReflectionBridge::injectItemMapping($identifier, $itemId);
51+
$itemId = $item->getTypeId();
5952

6053
GlobalItemDataHandlers::getDeserializer()->map($identifier, static fn(): Item => clone $item);
6154
GlobalItemDataHandlers::getSerializer()->map($item, static fn(): SavedItemData => new SavedItemData($identifier));
6255

6356
StringToItemParser::getInstance()->register($identifier, static fn(): Item => clone $item);
6457

65-
$componentNbt = ($item instanceof ItemComponentsHolder)
58+
$componentBased = $item instanceof ItemComponentsHolder;
59+
$componentNbt = $componentBased
6660
? $item->getComponentNbt()
67-
: CompoundTag::create()->setTag('components', CompoundTag::create());
61+
: CompoundTag::create();
6862

69-
$this->itemTableEntries[$identifier] = new ItemTypeEntry(
63+
if ($componentBased) {
64+
$componentNbt
65+
->setInt('id', $itemId)
66+
->setString('name', $identifier);
67+
}
68+
69+
$entry = new ItemTypeEntry(
7070
$identifier,
7171
$itemId,
72-
true,
73-
1,
74-
new CacheableNbt(
75-
$componentNbt
76-
->setInt('id', $itemId)
77-
->setString('name', $identifier)
78-
)
72+
$componentBased,
73+
$componentBased ? 1 : 0,
74+
new CacheableNbt($componentNbt)
7975
);
8076

77+
$this->itemTableEntries[$identifier] = $entry;
78+
ReflectionBridge::injectItemMapping($identifier, $itemId, $entry);
79+
8180
$this->registeredItems[$identifier] = $item;
8281

8382
if ($creative !== null && $creative->category !== null) {
@@ -87,15 +86,16 @@ public function register(string $identifier, Closure $factory, ?CreativeInventor
8786

8887
public function registerBlockItem(string $identifier, Block $block): void {
8988
$itemId = $block->getIdInfo()->getBlockTypeId();
90-
ReflectionBridge::injectItemMapping($identifier, $itemId);
9189
StringToItemParser::getInstance()->registerBlock($identifier, static fn(): Block => clone $block);
92-
$this->itemTableEntries[$identifier] = new ItemTypeEntry(
90+
$entry = new ItemTypeEntry(
9391
$identifier,
9492
$itemId,
9593
false,
96-
0,
97-
new CacheableNbt(new CompoundTag())
94+
2,
95+
new CacheableNbt(CompoundTag::create())
9896
);
97+
$this->itemTableEntries[$identifier] = $entry;
98+
ReflectionBridge::injectItemMapping($identifier, $itemId, $entry);
9999
ReflectionBridge::injectBlockItemMapping($identifier);
100100
}
101101
}

0 commit comments

Comments
 (0)