Last active
July 30, 2025 12:45
-
-
Save DavyCraft648/663450e8874e4c0adc8607d9c8a5fe15 to your computer and use it in GitHub Desktop.
Having problems with custom items and blocks on your server because you use a proxy like WaterdogPE? It can be solved with this plugin!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| declare(strict_types=1); | |
| namespace DavyCraft648\CustomiesProxySync; | |
| use customiesdevs\customies\block\BlockPalette; | |
| use customiesdevs\customies\block\CustomiesBlockFactory; | |
| use customiesdevs\customies\item\CustomiesItemFactory; | |
| use pmmp\thread\ThreadSafeArray; | |
| use pocketmine\block\Block; | |
| use pocketmine\data\bedrock\block\convert\BlockStateReader; | |
| use pocketmine\data\bedrock\block\convert\BlockStateWriter; | |
| use pocketmine\network\mcpe\convert\BlockStateDictionaryEntry; | |
| use pocketmine\network\mcpe\convert\TypeConverter; | |
| use pocketmine\network\mcpe\protocol\ProtocolInfo; | |
| use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; | |
| use pocketmine\scheduler\AsyncTask; | |
| use pocketmine\scheduler\ClosureTask; | |
| use Symfony\Component\Filesystem\Path; | |
| use function array_splice; | |
| use function count; | |
| use function defined; | |
| use function file_get_contents; | |
| use function file_put_contents; | |
| use function gzcompress; | |
| use function gzuncompress; | |
| use function igbinary_serialize; | |
| use function igbinary_unserialize; | |
| /** | |
| * @name CustomiesProxySync | |
| * @version 0.0.1 | |
| * @main DavyCraft648\CustomiesProxySync\CustomiesProxySync | |
| * @api 5.0.0 | |
| */ | |
| class CustomiesProxySync extends \pocketmine\plugin\PluginBase{ | |
| protected function onEnable() : void{ | |
| $config = $this->getConfig(); | |
| $config->setDefaults([ | |
| "type" => "save", // save, load | |
| "file" => Path::join($this->getDataFolder(), "data.dat") | |
| ]); | |
| if($config->hasChanged()){ | |
| $config->save(); | |
| } | |
| $customies = $this->getServer()->getPluginManager()->getPlugin("Customies"); | |
| $customies->getScheduler()->cancelAllTasks(); | |
| $this->getScheduler()->scheduleDelayedTask(new ClosureTask(function() use ($customies): void{ | |
| $customies->getScheduler()->cancelAllTasks(); | |
| if($this->getConfig()->get("type") === "save"){ | |
| $content = [ | |
| "blockPaletteEntries" => igbinary_serialize(CustomiesBlockFactory::getInstance()->getBlockPaletteEntries()), | |
| "customStates" => igbinary_serialize(BlockPalette::getInstance()->getCustomStates()), | |
| "itemTableEntries" => igbinary_serialize(CustomiesItemFactory::getInstance()->getItemTableEntries()) | |
| ]; | |
| file_put_contents($this->getConfig()->get("file"), gzcompress(igbinary_serialize($content))); | |
| }elseif($this->getConfig()->get("type") === "load"){ | |
| $content = igbinary_unserialize(gzuncompress(file_get_contents($this->getConfig()->get("file")))); | |
| $blockPaletteEntries = new \ReflectionProperty(CustomiesBlockFactory::class, "blockPaletteEntries"); | |
| $blockPaletteEntries->setValue(CustomiesBlockFactory::getInstance(), igbinary_unserialize($content["blockPaletteEntries"])); | |
| $pendingInsert = []; | |
| /** @var BlockStateDictionaryEntry $customState */ | |
| foreach(igbinary_unserialize($customStates = $content["customStates"]) as $customState){ | |
| $pendingInsert[$customState->getStateName()][] = $customState; | |
| } | |
| (new \ReflectionProperty(BlockPalette::getInstance(), "pendingInsert"))->setValue(BlockPalette::getInstance(), $pendingInsert); | |
| BlockPalette::getInstance()->sortStates(); | |
| $asyncPool = $this->getServer()->getAsyncPool(); | |
| /** @var \Closure $hook */ | |
| foreach((new \ReflectionProperty($asyncPool, "workerStartHooks"))->getValue($asyncPool) as $hook){ | |
| if((new \ReflectionFunction($hook))->getName() === "customiesdevs\customies\block\{closure}"){ | |
| $asyncPool->removeWorkerStartHook($hook); | |
| break; | |
| } | |
| } | |
| $blocks = (new \ReflectionProperty(CustomiesBlockFactory::getInstance(), "blockFuncs"))->getValue(CustomiesBlockFactory::getInstance()); | |
| $asyncPool->addWorkerStartHook(static function (int $worker) use ($asyncPool, $blocks, $customStates): void{ | |
| $asyncPool->submitTaskToWorker(new class($blocks, $customStates) extends AsyncTask{ | |
| private ThreadSafeArray $blockFuncs; | |
| private ThreadSafeArray $serializer; | |
| private ThreadSafeArray $deserializer; | |
| /** | |
| * @param Closure[] $blockFuncs | |
| * @phpstan-param array<string, array{(Closure(): Block), (Closure(BlockStateWriter): Block), (Closure(Block): BlockStateReader)}> $blockFuncs | |
| */ | |
| public function __construct(array $blockFuncs, private string $customStates){ | |
| $this->blockFuncs = new ThreadSafeArray(); | |
| $this->serializer = new ThreadSafeArray(); | |
| $this->deserializer = new ThreadSafeArray(); | |
| foreach($blockFuncs as $identifier => [$blockFunc, $serializer, $deserializer]){ | |
| $this->blockFuncs[$identifier] = $blockFunc; | |
| $this->serializer[$identifier] = $serializer; | |
| $this->deserializer[$identifier] = $deserializer; | |
| } | |
| } | |
| public function onRun() : void{ | |
| foreach($this->blockFuncs as $identifier => $blockFunc){ | |
| // We do not care about the model or creative inventory data in other threads since it is unused outside of | |
| // the main thread. | |
| try{ | |
| CustomiesBlockFactory::getInstance()->registerBlock($blockFunc, $identifier, serializer: $this->serializer[$identifier], deserializer: $this->deserializer[$identifier]); | |
| }catch(\Throwable $e){ | |
| //\GlobalLogger::get()->logException($e); | |
| } | |
| } | |
| $pendingInsert = []; | |
| /** @var BlockStateDictionaryEntry $customState */ | |
| foreach(igbinary_unserialize($this->customStates) as $customState){ | |
| $pendingInsert[$customState->getStateName()][] = $customState; | |
| } | |
| (new \ReflectionProperty(BlockPalette::getInstance(), "pendingInsert"))->setValue(BlockPalette::getInstance(), $pendingInsert); | |
| BlockPalette::getInstance()->sortStates(); | |
| } | |
| }, $worker); | |
| }); | |
| $cachedItemTableEntries = CustomiesItemFactory::getInstance()->getItemTableEntries(); | |
| $cachedCount = count($cachedItemTableEntries); | |
| $savedItemTableEntries = igbinary_unserialize($content["itemTableEntries"]); | |
| if($savedItemTableEntries === false || !$savedItemTableEntries[0] instanceof ItemTypeEntry){ | |
| throw new \RuntimeException("data.dat needs to be reset: ItemTableEntries must be an ItemTypeEntry"); | |
| } | |
| foreach(defined(ProtocolInfo::class . "::ACCEPTED_PROTOCOL") ? ProtocolInfo::ACCEPTED_PROTOCOL : [ProtocolInfo::CURRENT_PROTOCOL] as $protocol){ | |
| $typeConverter = TypeConverter::getInstance($protocol); | |
| $dictionary = $typeConverter->getItemTypeDictionary(); | |
| $reflection = new \ReflectionClass($dictionary); | |
| $intToString = $reflection->getProperty("intToStringIdMap"); | |
| $stringToInt = $reflection->getProperty("stringToIntMap"); | |
| $itemTypes = $reflection->getProperty("itemTypes"); | |
| /** @var string[] $intToStringValue */ | |
| $intToStringValue = $intToString->getValue($dictionary); | |
| /** @var int[] $stringToIntValue */ | |
| $stringToIntValue = $stringToInt->getValue($dictionary); | |
| $itemTypesValue = $itemTypes->getValue($dictionary); | |
| array_splice($itemTypesValue, -$cachedCount, $cachedCount); | |
| foreach($savedItemTableEntries as $entry){ | |
| $intToStringValue[$entry->getNumericId()] = $entry->getStringId(); | |
| $stringToIntValue[$entry->getStringId()] = $entry->getNumericId(); | |
| $itemTypesValue[] = $entry; | |
| } | |
| $intToString->setValue($dictionary, $intToStringValue); | |
| $stringToInt->setValue($dictionary, $stringToIntValue); | |
| $itemTypes->setValue($dictionary, $itemTypesValue); | |
| } | |
| (new \ReflectionProperty(CustomiesItemFactory::getInstance(), "itemTableEntries"))->setValue(CustomiesItemFactory::getInstance(), $savedItemTableEntries); | |
| } | |
| }), 2); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Having problems with custom items and blocks on your server because you use a proxy like WaterdogPE? It can be solved with this plugin!
You can use this plugin with either NetherGames pocketmine or pm vanilla, but custom blocks/items must be registered using https://github.com/DavyCraft648/Customies-NG.
How to use this?
hub,survival,gravity,parkour).pluginsfolder in all your servers (hub,survival,gravity,parkour).typeatplugin_data/CustomiesProxySync/config.ymltosavein thehubserver.hubserver.typeatplugin_data/CustomiesProxySync/config.ymltoloadagain in thehubserver.filelocation atplugin_data/CustomiesProxySync/config.yml:if your servers are on the same machine, just match the
filelocation to the config in thehubserver. If not, copy thedata.datfile that generated inplugin_data/CustomiesProxySyncon thehubserver to all servers (survival,gravity,parkour).survival,gravity,parkour).