valres / pmtoolbox
Best utilities libs for PocketMine-MP plugins.
Requires
- php: >=8.1
- ext-sockets: *
- opis/closure: ^4.4
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());