Skip to content

Instantly share code, notes, and snippets.

@dktapps
Last active November 11, 2025 20:39
Show Gist options
  • Select an option

  • Save dktapps/57a0a2ba161592d0e18577c4e93f6264 to your computer and use it in GitHub Desktop.

Select an option

Save dktapps/57a0a2ba161592d0e18577c4e93f6264 to your computer and use it in GitHub Desktop.
Demo of how a command tree for LuckPerms would be implemented using https://github.com/pmmp/PocketMine-MP/pull/6837 (not final)
<?php
declare(strict_types=1);
namespace dktapps\test;
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\OverloadBuilder;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\event\Listener;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\permission\Permission;
use pocketmine\permission\PermissionManager;
use pocketmine\plugin\PluginBase;
use function var_dump;
use const PHP_INT_MAX;
use const PHP_INT_MIN;
class Main extends PluginBase implements Listener{
private const DEMO_PERM = "dktapps.test.luckpermdemo";
public function onEnable() : void{
PermissionManager::getInstance()->addPermission(new Permission(self::DEMO_PERM));
PermissionManager::getInstance()->getPermission(DefaultPermissionNames::GROUP_OPERATOR)->addChild(self::DEMO_PERM, true);
$this->getServer()->getCommandMap()->register(new Command(
"dktapps",
"lp",
OverloadBuilder::make()
->executor(["sync"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("sync"))
->executor(["info"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("info"))
->executor(["editor"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("editor"))
->branch(["verbose"], function(OverloadBuilder $builder) : void{
$filter = new RawParameter("filter", "filter");
$builder
//TODO: due to lack of enums, these are currently forced to each be separate callbacks
//with enums we could just pass the subcommand directly to a single callback if desired
->executor(["on", $filter], self::DEMO_PERM, fn(CommandSender $sender, string $filter = "") => var_dump("verbose on $filter"))
->executor(["record", $filter], self::DEMO_PERM, fn(CommandSender $sender, string $filter = "") => var_dump("verbose record $filter"))
->executor(["off", $filter], self::DEMO_PERM, fn(CommandSender $sender, string $filter = "") => var_dump("verbose off $filter"))
->executor(["upload", $filter], self::DEMO_PERM, fn(CommandSender $sender, string $filter = "") => var_dump("verbose upload $filter"))
->branch(["command"], function(OverloadBuilder $builder) : void{
$commandParam = new RawParameter("command", "command");
$builder
->executor(["me", $commandParam], self::DEMO_PERM, fn(CommandSender $sender, string $command) => var_dump("verbose command me $command"))
->executor([
//TODO: no target param support yet, have to resolve target explicitly
new StringParameter("playerName", "player name"),
$commandParam
], self::DEMO_PERM, fn(CommandSender $sender, string $playerName, string $command) => var_dump("verbose command \"$playerName\" $command"));
});
})
->executor([
"tree",
new StringParameter("scope", "scope"),
new StringParameter("playerName", "player")
], self::DEMO_PERM, fn(CommandSender $sender, string $scope = ".", string $playerName = "") => var_dump("tree scope=$scope playerName=$playerName"))
->branch(["search"], function(OverloadBuilder $builder) : void{
$permissionParam = new StringParameter("permission", "permission");
$builder
->executor([$permissionParam], self::DEMO_PERM, fn(CommandSender $sender, string $permission) => var_dump("search, no comparison, permission=$permission"))
->executor([
new StringParameter("comparison", "comparison"),
$permissionParam
], self::DEMO_PERM, fn(CommandSender $sender, string $comparison, string $permission) => var_dump("search, comparison=$comparison, permission=$permission"));
})
->executor(["networksync"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("networksync"))
->branch(["import"], fn(OverloadBuilder $builder) => $builder
->branch([new StringParameter("filePath", "file")], fn(OverloadBuilder $builder) => $builder
//TODO: we don't support optional literals right now, so switching by --replace requires 2 overloads
//Not sure how that would work, not gonna lie...
->executor([], self::DEMO_PERM, fn(CommandSender $sender, string $filePath) => var_dump("importing from file=$filePath, not replacing"))
->executor(["--replace"], self::DEMO_PERM, fn(CommandSender $sender, string $filePath) => var_dump("importing from file=$filePath, replacing"))
)
->branch([new StringParameter("uploadCode", "code"), "--upload"], fn(OverloadBuilder $builder) => $builder
//TODO: we don't support optional literals right now, so switching by --replace requires 2 overloads
//Not sure how that would work, not gonna lie...
->executor([], self::DEMO_PERM, fn(CommandSender $sender, string $uploadCode) => var_dump("importing from uploadCode=$uploadCode, not replacing"))
->executor(["--replace"], self::DEMO_PERM, fn(CommandSender $sender, string $uploadCode) => var_dump("importing from uploadCode=$uploadCode, replacing"))
)
)
->branch(["export"], fn(OverloadBuilder $builder) => $builder
->executor([
"--upload",
new RawParameter("flags", "flags")
], self::DEMO_PERM, fn(CommandSender $sender, string $flags = "") => var_dump("export --upload flags=$flags (lmao you didn't think I was gonna overload this did you???)"))
->executor([
new StringParameter("filePath", "file"),
new RawParameter("flags", "flags")
], self::DEMO_PERM, fn(CommandSender $sender, string $file, string $flags = "") => var_dump("export file=$file flags=$flags (lmao you didn't think I was gonna overload this did you???)"))
)
->executor(["reloadconfig"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("reloadconfig"))
->branch(["bulkupdate", new StringParameter("dataType", "data type")], function(OverloadBuilder $builder) : void{
$constraintsParam = new StringParameter("constraints", "constraints");
$builder
->executor([
"update",
new StringParameter("field", "field"),
new StringParameter("value", "value"),
$constraintsParam
], self::DEMO_PERM, fn(CommandSender $sender, string $dataType, string $field, string $value, string $constraints = "") => var_dump("update dataType=$dataType field=$field value=$value constraints=$constraints"))
->executor([
"delete",
$constraintsParam
], self::DEMO_PERM, fn(CommandSender $sender, string $dataType, string $constraints = "") => var_dump("delete dataType=$dataType constraints=$constraints"));
})
->executor(["translations"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("translations info"))
->executor([
"creategroup",
new StringParameter("groupName", "name"),
//No idea what the bounds for this should be, but this will do for demo purposes
new IntRangeParameter("groupWeight", "weight", PHP_INT_MIN, PHP_INT_MAX),
new StringParameter("displayName", "display name")
], self::DEMO_PERM, fn(CommandSender $sender, string $groupName, int $groupWeight = 0, string $displayName = "") => var_dump("creategroup name=$groupName weight=$groupWeight displayNam=$displayName"))
->executor([
"deletegroup",
new StringParameter("groupName", "name")
], self::DEMO_PERM, fn(CommandSender $sender, string $groupName) => var_dump("deletegroup name=$groupName"))
->executor(["listgroups"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("listgroups"))
->executor([
"createtrack",
new StringParameter("trackName", "name")
], self::DEMO_PERM, fn(CommandSender $sender, string $trackName) => var_dump("createtrack $trackName"))
->executor([
"deletetrack",
new StringParameter("trackName", "name")
], self::DEMO_PERM, fn(CommandSender $sender, string $trackName) => var_dump("deletetrack $trackName"))
->executor(["listtracks"], self::DEMO_PERM, fn(CommandSender $sender) => var_dump("listtracks"))
->build(),
"Demo of LuckPerms command tree with new Overload API"
));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment