Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Use Closures for registering blocks #37

Merged
merged 6 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: A PocketMine-MP plugin that implements support for custom blocks, i

main: customiesdevs\customies\Customies
src-namespace-prefix: customiesdevs\customies
version: 1.0.7
version: 1.1.0
api: 4.0.0

authors:
Expand Down
65 changes: 31 additions & 34 deletions src/block/CustomiesBlockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@

namespace customiesdevs\customies\block;

use Closure;
use customiesdevs\customies\item\CreativeInventoryInfo;
use customiesdevs\customies\item\CustomiesItemFactory;
use customiesdevs\customies\task\AsyncRegisterBlocksTask;
use customiesdevs\customies\world\LegacyBlockIdToStringIdMap;
use InvalidArgumentException;
use OutOfRangeException;
use pocketmine\block\Block;
use pocketmine\block\BlockBreakInfo;
use pocketmine\block\BlockFactory;
use pocketmine\block\BlockIdentifier;
use pocketmine\inventory\CreativeInventory;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
Expand All @@ -25,7 +24,6 @@
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\Server;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
use ReflectionClass;
use RuntimeException;
use SplFixedArray;
Expand All @@ -40,10 +38,10 @@ final class CustomiesBlockFactory {
private const NEW_BLOCK_FACTORY_SIZE = 2048 << Block::INTERNAL_METADATA_BITS;

/**
* @var Block[]
* @phpstan-var array<string, Block>
* @var Closure[]
* @phpstan-var array<string, Closure(int): Block>
*/
private array $customBlocks = [];
private array $blockFuncs = [];
/** @var BlockPaletteEntry[] */
private array $blockPaletteEntries = [];
/** @var R12ToCurrentBlockMapEntry[] */
Expand All @@ -68,10 +66,10 @@ public function increaseBlockFactoryLimits(): void {
$array->setSize(self::NEW_BLOCK_FACTORY_SIZE);
$property->setValue($instance, $array);
}
$instance->light = SplFixedArray::fromArray(array_merge($instance->light->toArray(), array_fill(count($instance->light), self::NEW_BLOCK_FACTORY_SIZE, 0)));
$instance->lightFilter = SplFixedArray::fromArray(array_merge($instance->lightFilter->toArray(), array_fill(count($instance->lightFilter), self::NEW_BLOCK_FACTORY_SIZE, 1)));
$instance->blocksDirectSkyLight = SplFixedArray::fromArray(array_merge($instance->blocksDirectSkyLight->toArray(), array_fill(count($instance->blocksDirectSkyLight), self::NEW_BLOCK_FACTORY_SIZE, false)));
$instance->blastResistance = SplFixedArray::fromArray(array_merge($instance->blastResistance->toArray(), array_fill(count($instance->blastResistance), self::NEW_BLOCK_FACTORY_SIZE, 0.0)));
$instance->light = SplFixedArray::fromArray(array_merge($instance->light->toArray(), array_fill(count($instance->light), self::NEW_BLOCK_FACTORY_SIZE, 0)));
$instance->lightFilter = SplFixedArray::fromArray(array_merge($instance->lightFilter->toArray(), array_fill(count($instance->lightFilter), self::NEW_BLOCK_FACTORY_SIZE, 1)));
$instance->blocksDirectSkyLight = SplFixedArray::fromArray(array_merge($instance->blocksDirectSkyLight->toArray(), array_fill(count($instance->blocksDirectSkyLight), self::NEW_BLOCK_FACTORY_SIZE, false)));
$instance->blastResistance = SplFixedArray::fromArray(array_merge($instance->blastResistance->toArray(), array_fill(count($instance->blastResistance), self::NEW_BLOCK_FACTORY_SIZE, 0.0)));
}

/**
Expand All @@ -80,8 +78,8 @@ public function increaseBlockFactoryLimits(): void {
* can result in massive issues with almost every block showing as the wrong thing and causing lag to clients.
*/
public function addWorkerInitHook(): void {
$blocks = serialize($this->customBlocks);
$server = Server::getInstance();
$blocks = $this->blockFuncs;
$server->getAsyncPool()->addWorkerStartHook(static function (int $worker) use ($server, $blocks): void {
$server->getAsyncPool()->submitTaskToWorker(new AsyncRegisterBlocksTask($blocks), $worker);
});
Expand Down Expand Up @@ -109,16 +107,15 @@ public function getBlockPaletteEntries(): array {

/**
* Register a block to the BlockFactory and all the required mappings.
* @phpstan-param class-string $className
* @phpstan-param (Closure(int): Block) $blockFunc
*/
public function registerBlock(string $className, string $identifier, string $name, BlockBreakInfo $breakInfo, ?Model $model = null, ?CreativeInventoryInfo $creativeInfo = null): void {
if($className !== Block::class) {
Utils::testValidInstance($className, Block::class);
public function registerBlock(Closure $blockFunc, string $identifier, ?Model $model = null, ?CreativeInventoryInfo $creativeInfo = null): void {
$id = $this->getNextAvailableId();
$block = $blockFunc($id);
if(!$block instanceof Block) {
throw new InvalidArgumentException("Class returned from closure is not a Block");
}

/** @var Block $block */
$block = new $className(new BlockIdentifier($this->getNextAvailableId(), 0), $name, $breakInfo);

if(BlockFactory::getInstance()->isRegistered($block->getId())) {
throw new InvalidArgumentException("Block with ID " . $block->getId() . " is already registered");
}
Expand All @@ -131,20 +128,20 @@ public function registerBlock(string $className, string $identifier, string $nam
BlockPalette::getInstance()->insertState($blockState);

$propertiesTag = CompoundTag::create();
$components = CompoundTag::create()
->setTag("minecraft:light_emission", CompoundTag::create()
->setByte("emission", $block->getLightLevel()))
->setTag("minecraft:block_light_filter", CompoundTag::create()
->setByte("lightLevel", $block->getLightFilter()))
->setTag("minecraft:destructible_by_mining", CompoundTag::create()
->setFloat("value", $block->getBreakInfo()->getHardness()))//Says seconds_to_destroy in docs
->setTag("minecraft:destructible_by_explosion", CompoundTag::create()
->setFloat("value", $block->getBreakInfo()->getBlastResistance()))//Uses explosion_resistance in docs
->setTag("minecraft:friction", CompoundTag::create()
->setFloat("value", $block->getFrictionFactor()))
->setTag("minecraft:flammable", CompoundTag::create()
->setInt("catch_chance_modifier", $block->getFlameEncouragement())
->setInt("destroy_chance_modifier", $block->getFlammability()));
$components = CompoundTag::create()
->setTag("minecraft:light_emission", CompoundTag::create()
->setByte("emission", $block->getLightLevel()))
->setTag("minecraft:block_light_filter", CompoundTag::create()
->setByte("lightLevel", $block->getLightFilter()))
->setTag("minecraft:destructible_by_mining", CompoundTag::create()
->setFloat("value", $block->getBreakInfo()->getHardness()))//Says seconds_to_destroy in docs
->setTag("minecraft:destructible_by_explosion", CompoundTag::create()
->setFloat("value", $block->getBreakInfo()->getBlastResistance()))//Uses explosion_resistance in docs
->setTag("minecraft:friction", CompoundTag::create()
->setFloat("value", $block->getFrictionFactor()))
->setTag("minecraft:flammable", CompoundTag::create()
->setInt("catch_chance_modifier", $block->getFlameEncouragement())
->setInt("destroy_chance_modifier", $block->getFlammability()));

if($model !== null) {
foreach($model->toNBT() as $tagName => $tag){
Expand All @@ -164,15 +161,15 @@ public function registerBlock(string $className, string $identifier, string $nam

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

$this->customBlocks[$identifier] = $block;
$this->blockFuncs[$identifier] = $blockFunc;
LegacyBlockIdToStringIdMap::getInstance()->registerMapping($identifier, $block->getId());
}

/**
* Returns the next available custom block id, an exception will be thrown if the block factory is full.
*/
private function getNextAvailableId(): int {
$id = 1000 + count($this->customBlocks);
$id = 1000 + count($this->blockFuncs);
if($id > (self::NEW_BLOCK_FACTORY_SIZE / 16)) {
throw new OutOfRangeException("All custom block ids are used up");
}
Expand Down
14 changes: 13 additions & 1 deletion src/block/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,19 @@ public function toNBT(): array {
->setTag("materials", $materials),
"minecraft:geometry" => CompoundTag::create()
->setString("value", $this->geometry),
"minecraft:pick_collision" => CompoundTag::create()
"minecraft:collision_box" => CompoundTag::create()
->setByte("enabled", 1)
->setTag("origin", new ListTag([
new FloatTag($this->origin->getX()),
new FloatTag($this->origin->getY()),
new FloatTag($this->origin->getZ())
]))
->setTag("size", new ListTag([
new FloatTag($this->size->getX()),
new FloatTag($this->size->getY()),
new FloatTag($this->size->getZ())
])),
"minecraft:selection_box" => CompoundTag::create()
->setByte("enabled", 1)
->setTag("origin", new ListTag([
new FloatTag($this->origin->getX()),
Expand Down
24 changes: 17 additions & 7 deletions src/task/AsyncRegisterBlocksTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@

namespace customiesdevs\customies\task;

use Closure;
use customiesdevs\customies\block\CustomiesBlockFactory;
use pocketmine\block\Block;
use pocketmine\scheduler\AsyncTask;
use Threaded;

final class AsyncRegisterBlocksTask extends AsyncTask {

public function __construct(private string $blocks) {
private Threaded $blockFuncs;

/**
* @param Closure[] $blockFuncs
* @phpstan-param array<string, Closure(int): Block> $blockFuncs
*/
public function __construct(array $blockFuncs) {
$this->blockFuncs = new Threaded();
foreach($blockFuncs as $identifier => $blockFunc) {
$this->blockFuncs[$identifier] = $blockFunc;
}
}

public function onRun(): void {
/** @phpstan-var array<string, Block> $blocks */
$blocks = unserialize($this->blocks);
foreach($blocks as $identifier => $block){
/** @phpstan-var class-string $className */
$className = get_class($block);
CustomiesBlockFactory::getInstance()->registerBlock($className, $identifier, $block->getName(), $block->getBreakInfo());
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.
CustomiesBlockFactory::getInstance()->registerBlock($blockFunc, $identifier);
}
CustomiesBlockFactory::getInstance()->registerCustomRuntimeMappings();
}
Expand Down