valres/pmtoolbox

Best utilities libs for PocketMine-MP plugins.

Maintainers

Package info

github.com/Root-Productions/PM-Toolbox

pkg:composer/valres/pmtoolbox

Transparency log

Statistics

Installs: 68

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 0

0.1.0 2026-06-11 19:56 UTC

This package is not auto-updated.

Last update: 2026-07-07 17:51:30 UTC


README

PMToolBox is a small PocketMine-MP toolbox library. It provides utilities for manager-based plugin structure, task helpers and an optional RCON interface.

Setup

Register the toolbox from your plugin lifecycle:

use pocketmine\plugin\PluginBase;
use valres\toolbox\ToolboxLoader;

final class Main extends PluginBase {
    protected function onLoad(): void {
        ToolboxLoader::load($this);
    }

    protected function onEnable(): void {
        ToolboxLoader::enable($this);
    }

    protected function onDisable(): void {
        ToolboxLoader::disable();
    }
}

By default, managers are loaded from the manager directory under your plugin namespace.

If your managers are elsewhere:

ToolboxLoader::load($this, "modules");
ToolboxLoader::enable($this);

If you do not want to use the manager system:

ToolboxLoader::load($this, loadManagers: false);
ToolboxLoader::enable($this, enableManagers: false);

Managers

Create managers by extending BaseManager.

namespace myplugin\manager;

use valres\toolbox\manager\BaseManager;
use valres\toolbox\manager\attribute\Manager;
use valres\toolbox\manager\attribute\ManagerPriority;
use valres\toolbox\manager\attribute\ManagerPriorityEnum;

#[Manager(name: "Economy", version: "1.0.0")]
#[ManagerPriority(ManagerPriorityEnum::PRIORITY_HIGH)]
final class EconomyManager extends BaseManager {
    public function onLoad(): void {
        // Light startup work.
    }

    public function init(): void {
        // Enable-time setup.
    }

    public function save(): void {
        // Persist data before shutdown.
    }
}

Dependencies

Use ManagerDependsOn when a manager must be initialized after another one.

use valres\toolbox\manager\attribute\ManagerDependsOn;

#[ManagerDependsOn(["Economy"])]
final class ShopManager extends BaseManager {
    // ...
}

The resolver detects duplicate manager names, missing dependencies and circular dependencies.

Auto Register

Managers can auto-register commands and listeners from folders next to the manager class.

use valres\toolbox\manager\attribute\AutoRegister;
use valres\toolbox\manager\attribute\AutoRegisterAll;
use valres\toolbox\manager\attribute\AutoRegisterType;

#[AutoRegister(AutoRegisterType::COMMANDS)]
#[AutoRegister(AutoRegisterType::LISTENERS)]
final class GameplayManager extends BaseManager {
    // ...
}

#[AutoRegisterAll]
final class ProfileManager extends BaseManager {
    // Registers command/ and listener/.
}

Expected folder layout:

src/myplugin/manager/ProfileManager.php
src/myplugin/manager/command/ProfileCommand.php
src/myplugin/manager/listener/ProfileListener.php

Access

$economy = ToolboxLoader::getManager("Economy");
$shop = ToolboxLoader::getManagerOf(ShopManager::class);

if (ToolboxLoader::hasManager("Economy")) {
    // ...
}

You can also access the handler directly:

$handler = ToolboxLoader::getManagerHandler();

Each manager also behaves like a singleton automatically. You do not need to add a trait in your manager class:

$economy = EconomyManager::getInstance();

if (EconomyManager::hasInstance()) {
    EconomyManager::getInstance()->save();
}

The singleton instance is registered when the manager is discovered and reset when managers are disabled.

RCON

RCON is optional and can be started during enable:

use valres\toolbox\rcon\RconSettings;

ToolboxLoader::enable($this, new RconSettings(
    address: "0.0.0.0",
    port: 19132,
    password: "secret"
));

Or with the helper:

ToolboxLoader::enable($this, RconSettings::default("secret", 19132));

Camera

Register the camera helper once during enable. PMToolBox will send the vanilla camera presets when a player finishes initializing.

use valres\toolbox\camera\Camera;

protected function onEnable(): void {
    if (!Camera::isRegistered()) {
        Camera::register($this);
    }
}

Then build and send camera instructions fluently:

use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\types\camera\CameraSetInstructionEaseType;
use valres\toolbox\camera\Camera;
use valres\toolbox\camera\CameraPresets;

Camera::set(CameraPresets::free())
    ->ease(1.0, CameraSetInstructionEaseType::OUT_CUBIC)
    ->position(new Vector3(100, 80, 100))
    ->facing($player->getPosition())
    ->send($player);

Camera::fade()
    ->time(0.5, 1.0, 0.5)
    ->rgb(0, 0, 0)
    ->send($player);

Camera::clear()->send($player);

Sequences are useful when a plugin wants to group multiple instructions:

Camera::sequence()
    ->add(Camera::shake(0.35, 0.75)->rotational())
    ->add(Camera::fov(65)->ease(0.4))
    ->send($player);

Data-Driven UI

PMToolBox includes an experimental DDUI helper inspired by Minecraft Bedrock's Data-Driven UI model and Joshet18/Data-DrivenUI. It is registered automatically by ToolboxLoader::enable().

use valres\toolbox\form\ddui\DduiManager;
use valres\toolbox\form\ddui\DduiObservable;

$status = DduiObservable::string("Ready");
$enabled = DduiObservable::bool(true, clientWritable: true);

DduiManager::customForm("Settings")
    ->label($status)
    ->toggle("Enabled", $enabled)
    ->button("Refresh", function() use ($status): void {
        $status->set("Updated at " . date("H:i:s"));
    }, closeOnClick: false)
    ->send($player);

For simple dialogs:

DduiManager::messageBox("Confirm", "Delete this item?")
    ->button1("Delete")
    ->button2("Cancel")
    ->onSelect(function($player, int $selection): void {
        if ($selection === 1) {
            // Delete item.
        }
    })
    ->send($player);

Supported DduiCustomForm controls are labels, spacers, dividers, text fields, toggles, dropdowns, sliders and buttons. DduiObservable values can be updated server-side while the screen is open; client-writable observables are updated when the player changes the matching control.

Packets

PMToolBox includes a small packet API inspired by LibPacket/SimplePacketHandler: register handlers for specific packet classes instead of writing long instanceof chains.

Monitor

Use a monitor when you only want to observe packets. Return values are ignored.

use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\LoginPacket;
use valres\toolbox\ToolboxLoader;

$monitor = ToolboxLoader::createPacketMonitor();

$monitor->monitorIncoming(function(LoginPacket $packet, NetworkSession $session): void {
    // Debug or inspect the packet.
});

Interceptor

Use an interceptor when you want to cancel or modify packets before normal handling. Return false to cancel the packet event.

use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use valres\toolbox\ToolboxLoader;

$interceptor = ToolboxLoader::createPacketInterceptor();

$interceptor->interceptIncoming(function(AdventureSettingsPacket $packet, NetworkSession $session): bool {
    return true; // false cancels the DataPacketReceiveEvent.
});

Handler Classes

use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\LoginPacket;
use pocketmine\network\mcpe\protocol\Packet;
use valres\toolbox\packet\PacketHandlerInterface;

final class LoginPacketHandler implements PacketHandlerInterface {
    public function getPacketIds(): array {
        return [LoginPacket::class];
    }

    public function handle(Packet $packet, NetworkSession $session): bool {
        return true;
    }
}

ToolboxLoader::createPacketInterceptor()->registerIncoming(new LoginPacketHandler());

Attributes

You can also register public methods with attributes.

use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\LoginPacket;
use valres\toolbox\packet\attribute\IncomingPacket;
use valres\toolbox\packet\attribute\OutgoingPacket;
use valres\toolbox\packet\attribute\PacketHandler;
use valres\toolbox\packet\PacketDirection;

final class PacketHandlers {
    #[IncomingPacket(LoginPacket::class)]
    public function onLogin(LoginPacket $packet, NetworkSession $session): bool {
        return true;
    }

    #[OutgoingPacket]
    public function onAnyOutgoing(LoginPacket $packet, NetworkSession $session): void {
    }

    #[PacketHandler(PacketDirection::INCOMING, LoginPacket::class)]
    public function onLoginWithGenericAttribute(LoginPacket $packet, NetworkSession $session): bool {
        return true;
    }
}

ToolboxLoader::createPacketInterceptor()->registerAnnotated(new PacketHandlers());