Skip to content

Commit 3544e08

Browse files
committed
[sync] Update embedded LibCustom from standalone
1 parent 35eae86 commit 3544e08

3 files changed

Lines changed: 113 additions & 21 deletions

File tree

src/imperazim/custom/CustomManager.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@
3636

3737
final class CustomManager implements Listener {
3838

39-
private static Experiments $experiments;
39+
/** @internal accessed by packet handlers */
40+
public static Experiments $experiments;
4041

41-
/** @var ItemTypeEntry[] */
42-
private static array $cachedItemTable = [];
42+
/** @var ItemTypeEntry[] @internal */
43+
public static array $cachedItemTable = [];
4344

44-
/** @var BlockPaletteEntry[] */
45-
private static array $cachedBlockPalette = [];
45+
/** @var BlockPaletteEntry[] @internal */
46+
public static array $cachedBlockPalette = [];
4647

4748
public static function init(PluginBase $plugin): void {
4849
self::$experiments = new Experiments(['data_driven_items' => true], true);

src/imperazim/custom/block/CustomBlockFactory.php

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
use pocketmine\Server;
3131
use pocketmine\utils\SingletonTrait;
3232
use pocketmine\world\format\io\GlobalBlockStateHandlers;
33+
use function array_fill;
3334
use function array_map;
3435
use function array_reverse;
3536
use function hash;
37+
use function max;
3638
use function strcmp;
3739
use function usort;
3840

@@ -53,13 +55,53 @@ final class CustomBlockFactory {
5355

5456
/**
5557
* Adds a worker initialize hook to the async pool to sync the BlockFactory for every thread worker.
58+
* Serializes block data to a cache file so it can be loaded on worker threads without closures.
5659
*/
5760
public function addWorkerInitHook(string $cachePath): void {
61+
if (empty($this->blockFuncs)) {
62+
return;
63+
}
64+
65+
$cacheFile = $cachePath . DIRECTORY_SEPARATOR . 'block_cache.bin';
66+
@mkdir($cachePath, 0777, true);
67+
68+
// Build serializable cache: for each block, store the igbinary-serialized Block object
69+
// and the list of state entries (identifier + state count).
70+
$cache = [];
71+
foreach ($this->blockFuncs as $identifier => [$blockFunc, $serializer, $deserializer]) {
72+
try {
73+
$block = $blockFunc();
74+
$blockData = igbinary_serialize($block);
75+
if ($blockData === null) {
76+
continue;
77+
}
78+
// Count how many states this block has in the palette.
79+
$stateCount = 0;
80+
foreach ($this->blockPaletteEntries as $entry) {
81+
if ($entry->getName() === $identifier) {
82+
$stateCount++;
83+
}
84+
}
85+
$cache[$identifier] = [
86+
'blockData' => $blockData,
87+
'states' => array_fill(0, max(1, $stateCount), ['name' => $identifier]),
88+
];
89+
} 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.
92+
}
93+
}
94+
95+
if (empty($cache)) {
96+
return;
97+
}
98+
99+
file_put_contents($cacheFile, igbinary_serialize($cache));
100+
58101
$server = Server::getInstance();
59-
$blocks = $this->blockFuncs;
60102
$server->getAsyncPool()->addWorkerStartHook(
61-
static function (int $worker) use ($cachePath, $server, $blocks): void {
62-
$server->getAsyncPool()->submitTaskToWorker(new AsyncBlockRegistrationTask($blocks), $worker);
103+
static function (int $worker) use ($cacheFile, $server): void {
104+
$server->getAsyncPool()->submitTaskToWorker(new AsyncBlockRegistrationTask($cacheFile), $worker);
63105
}
64106
);
65107
}

src/imperazim/custom/task/AsyncBlockRegistrationTask.php

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,76 @@
33

44
namespace imperazim\custom\task;
55

6-
use imperazim\custom\block\CustomBlockFactory;
7-
use pmmp\thread\ThreadSafeArray;
6+
use pocketmine\block\Block;
7+
use pocketmine\block\RuntimeBlockStateRegistry;
8+
use pocketmine\data\bedrock\block\BlockStateData;
9+
use pocketmine\data\bedrock\block\convert\BlockStateReader;
10+
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
811
use pocketmine\scheduler\AsyncTask;
12+
use pocketmine\world\format\io\GlobalBlockStateHandlers;
913

1014
final class AsyncBlockRegistrationTask extends AsyncTask {
1115

12-
private ThreadSafeArray $blockFuncs;
16+
private string $cachePath;
1317

14-
/**
15-
* @param Closure[] $blockFuncs
16-
*/
17-
public function __construct(array $blockFuncs) {
18-
$this->blockFuncs = new ThreadSafeArray();
19-
foreach ($blockFuncs as $key => $func) {
20-
$this->blockFuncs[$key] = $func;
21-
}
18+
public function __construct(string $cachePath) {
19+
$this->cachePath = $cachePath;
2220
}
2321

2422
public function onRun(): void {
25-
foreach ($this->blockFuncs as $func) {
26-
$func();
23+
$raw = @file_get_contents($this->cachePath);
24+
if ($raw === false) {
25+
return;
26+
}
27+
/** @var array<string, array{blockData: string, states: list<array{name: string, statesNbt: string}>}> $cache */
28+
$cache = igbinary_unserialize($raw);
29+
if (!is_array($cache)) {
30+
return;
31+
}
32+
33+
$serializer = GlobalBlockStateHandlers::getSerializer();
34+
$deserializer = GlobalBlockStateHandlers::getDeserializer();
35+
$registry = RuntimeBlockStateRegistry::getInstance();
36+
37+
foreach ($cache as $identifier => $entry) {
38+
/** @var Block|null $block */
39+
$block = igbinary_unserialize($entry['blockData']);
40+
if (!$block instanceof Block) {
41+
continue;
42+
}
43+
44+
// Register the block in RuntimeBlockStateRegistry on this worker thread.
45+
try {
46+
$registry->register($block);
47+
} catch (\InvalidArgumentException) {
48+
// Already registered (shouldn't happen on fresh worker, but be safe).
49+
}
50+
51+
// Register the serializer — maps Block typeId to BlockStateData.
52+
if (!$serializer->isRegistered($block)) {
53+
if (count($entry['states']) === 1) {
54+
// Simple block, no permutations.
55+
$serializer->map($block, BlockStateData::current($identifier, []));
56+
} else {
57+
// Block with permutations — register a closure that builds state from block.
58+
$serializer->map($block, static function (Block $b) use ($identifier): BlockStateWriter {
59+
$writer = BlockStateWriter::create($identifier);
60+
if ($b instanceof \imperazim\custom\block\permutation\Permutable) {
61+
$b->serializeState($writer);
62+
}
63+
return $writer;
64+
});
65+
}
66+
}
67+
68+
// Register the deserializer — maps identifier string to Block factory.
69+
$deserializer->map($identifier, static function (BlockStateReader $in) use ($block, $identifier): Block {
70+
$b = clone $block;
71+
if ($b instanceof \imperazim\custom\block\permutation\Permutable) {
72+
$b->deserializeState($in);
73+
}
74+
return $b;
75+
});
2776
}
2877
}
2978
}

0 commit comments

Comments
 (0)