Creating Commands
Your first command¶
HD Admin commands are plain ModuleScripts. To create your own, add a ModuleScript under HD Admin > Config > Commands (folders are fine if you want to group things) and return an array of command tables. One module can hold as many commands as you like:
--!strict
local modules = script:FindFirstAncestor("HD Admin").Core.MainModule.Value.Modules
local Task = require(modules.Objects.Task)
local commands: Task.Commands = {
{
name = "Shield",
aliases = {"Bubble"},
tags = {"PackMod"}, -- (1)!
args = {"Player"},
run = function(task: Task.Class, args: {any})
local target = unpack(args)
local character = target.Character
if not character then
return
end
task:keep("UntilTargetRespawns") -- (2)!
local field = task.janitor:add(Instance.new("ForceField")) -- (3)!
field.Parent = character
end
},
}
return commands
- Tags bind the command to roles. The default Mod role inherits everything tagged
PackMod, so your Mods can use this straight away. - Keeps the task alive after
runreturns, until the target respawns. - Anything added to the janitor is destroyed when the task ends, which is what makes
;unshieldwork without any teardown code.
That's a complete command. name is what players type after the prefix, so ;shield me wraps you in a forcefield and ;unshield me removes it. aliases are alternative names that behave identically. args lists the arguments the command accepts by name; each one is resolved for you and passed into run in order. run is then called with a task, the object that manages the command's lifetime.
Roles gain commands through their inheritCommandsWithTags array, and senior roles pick tagged commands up too through inheritCommandsFromJuniorRoles. See Roles for more.
Command fields¶
Every field except name is optional. These are the ones you'll reach for most:
1.
name{required}
What players type after the prefix to run the command. It doubles as the undo name, so Shield is ended with ;unshield.
2.
aliases{optional}
Alternative names that run the same command, like {"Bubble"}.
3.
undoAliases{optional}
Names that undo the command instead of running it. The built-in NoClip has undoAliases = {"Clip"}.
4.
tags{optional}
Binds the command to roles. A role can use any command whose tags overlap its inheritCommandsWithTags array. The built-in packs use tags like "PackMod" and "PackAdmin".
5.
order{optional}
Sorts the Commands page. Lower numbers appear first.
6.
groups{optional}
Commands in the same group undo each other when another one runs. Fly and NoClip share the "Flight" group, so starting one ends the other.
7.
args{optional}
An array of argument names, resolved in order and passed into run. Use built-in names like "Player", "Number" and "SoundId", or your own custom arguments (see below).
8.
cooldown{optional}
Seconds that must pass after the command finishes before it can be run again.
9.
autoPreview{optional}
Opens the command in the Run page instead of executing straight away. The built-in ban command uses this so details are confirmed first.
10.
hide{optional}
Hides the command from the Commands page.
11.
disableModifiers{optional}
Silently strips any Modifiers from the call and hides the Modifiers picker on the Run page.
12.
disableGlobalModifiers{optional}
Rejects the global, perm and join modifiers for this command. Useful when a command should never reach beyond the current server.
The task object¶
run receives a task, created fresh for every target. It handles cleanup, persistence and live updates so you don't have to.
task:keep(persistence) keeps the task alive after run returns (tasks are otherwise destroyed immediately). Your options are "UntilTargetRespawns", "UntilTargetLeaves", "UntilCallerRespawns", "UntilCallerLeaves" and "Indefinitely".
task.janitor:add(item) registers instances, connections or functions for cleanup. Everything added is destroyed when the task ends, which is what makes ;unshield and command groups work without you writing any teardown code.
task:getArg(name, default) and task:setArg(name, value) read and write arguments by name. When the player didn't provide the argument, getArg falls back to default and remembers it.
task.client:run(player, ...) invokes your matching client command on that player's device (next section).
task:redo(player, callback) calls callback immediately, then again every time the player respawns while the task is alive. Handy for effects that would otherwise vanish on death.
Client commands¶
Some commands need code on the player's device, like flight, which reads input every frame. Add a child ModuleScript named Client to your command module and return entries whose names match your server commands:
local modules = script:FindFirstAncestor("HD Admin").Core.MainModule.Value.Modules
local Task = require(modules.Objects.Task)
local clientCommands: Task.ClientCommands = {
{
name = "Fly",
run = function(task: Task.Class, speed: number)
-- This runs on the target's device
end,
},
}
return clientCommands
The server half then hands over whenever it likes:
run = function(task: Task.Class, args: {any})
local target = unpack(args)
task:keep("UntilTargetRespawns")
task.client:run(target, task:getArg("FlightSpeed", 50))
end
Info
You don't replicate anything yourself. The framework detects any child module named Client and ships it to players automatically.
Custom arguments¶
Built-in argument names cover most cases. When you need your own, register them inside HD Admin > Config > Args:
return function(Args)
return {
["Scale"] = Args.createAliasOf("Number", {
minValue = 0.1,
maxValue = 4,
stepAmount = 0.1,
defaultValue = 2,
logarithmic = true,
}),
}
end
This creates a Scale argument based on Number with its own minimum, maximum, step and default, ready to use in any command's args. Everything you register here also appears in argument autocompletes, and displayName lets you relabel an argument in menus.
Warning
Make sure the name you choose doesn't match any of HD Admin's built-in arguments.
Selling commands¶
Custom commands are yours to sell. Tag them with something unique, then on the role you'd like to sell, add your gamepass or asset to giveToProducts and the tag to inheritCommandsWithTags:
giveToProducts = {
{type = "GamePass", id = 123456},
},
inheritCommandsWithTags = {"VIP"},
Anyone who owns the product receives the role automatically, along with every command tagged "VIP". This is how creators earn Robux from their commands. See Roles for the full breakdown of role configuration.