Skip to Content
GuidesCommands and Key Mappings

Commands and key mappings

There are three ways to bind an action on a QBCore server, and they solve different problems. Using the wrong one is why keybinds end up hardcoded, unrebindable, or executable by anyone. The three are RegisterCommand, RegisterKeyMapping, and QBCore.Commands.Add. Each section below covers one, and Choosing between them is the summary table.

Primary sources

RegisterCommand

void REGISTER_COMMAND(char* commandName, func handler, BOOL restricted);
ParameterMeaning
commandNameThe command name, without a leading slash
handlerCalled as handler(source, args, rawCommand)
restrictedWhen true on a server command, requires the command.<name> ace

The native documentation is explicit that restricted is not used on the client side. Permissions can only be checked on the server, so a command that needs to be restricted has to be registered in a server script.

On the server, source is the player’s server ID, or 0 when the command came from the server console, an RCON client, or another resource.

-- server side RegisterCommand('ping', function(source, args, rawCommand) if source > 0 then -- a player ran it else -- console, RCON, or a resource end end, false)

RegisterKeyMapping

void REGISTER_KEY_MAPPING(char* commandString, char* description, char* defaultMapper, char* defaultParameter);
ParameterMeaning
commandStringThe command to execute. This is also the identifier of the binding
descriptionThe label shown in the FiveM settings menu
defaultMapperThe mapper ID for the default binding, for example keyboard
defaultParameterThe IO parameter ID, for example f3

The point of this native is that the default is only a default. Once the mapping is registered, the player can rebind it from the FiveM settings menu, and their choice survives restarts. A key handled by polling IsControlJustPressed in a loop cannot be rebound and does not appear in that menu.

Registering an alternate key for the same command uses the ~! prefix on the command string.

-- client side RegisterCommand('openinventory', function() TriggerEvent('myresource:client:openInventory') end, false) RegisterKeyMapping('openinventory', 'Open inventory', 'keyboard', 'TAB') RegisterKeyMapping('~!openinventory', 'Open inventory, alternate key', 'keyboard', 'i')

Hold to act, the plus and minus pair

For an action that should last only while the key is held, register two commands whose names differ only by a leading + and -, and map the + one. The runtime invokes +name on key down and -name on key up.

-- client side local handsUp = false RegisterCommand('+handsup', function() if handsUp then return end handsUp = true CreateThread(function() while handsUp do TaskHandsUp(PlayerPedId(), 250, PlayerPedId(), -1, true) Wait(200) end ClearPedTasks(PlayerPedId()) end) end, false) RegisterCommand('-handsup', function() handsUp = false end, false) RegisterKeyMapping('+handsup', 'Hands Up', 'keyboard', 'i')

The thread is created on key down and ends on key up, so nothing runs while the key is untouched. The wait is shorter than the task duration passed to TaskHandsUp, which keeps the animation from lapsing between refreshes. A permanent while true do Wait(0) end thread would do the same job at one check per frame for the lifetime of the resource.

qb-scoreboard uses the same + and - pair. With Config.Toggle = true it registers a single scoreboard command that flips state. With Config.Toggle = false it registers +scoreboard and -scoreboard and maps the + one, so the panel is only visible while the key is held.

Rules that trip people up

  1. Register the command first. RegisterKeyMapping binds a key to a command string. If no command by that name exists, pressing the key does nothing.
  2. Both calls belong in a client script for a key the player presses, because the key press happens on the client.
  3. The mapping is registered per resource. Restarting the resource re-registers it. Renaming the command creates a new binding and the player’s old rebind is orphaned.
  4. The description is what the player sees. Prefix it with your server or resource name if you register several, because the settings list is long.
  5. Do not poll for a key you have mapped. Pick one mechanism.
  6. A + command registered without its - partner leaves the state stuck on after the key is released.

QBCore.Commands.Add

QBCore.Commands.Add(name, help, arguments, argsrequired, callback, permission, ...)

This is the server-side wrapper around RegisterCommand. It adds ace permission registration, a cancellable QBCore:Server:PreCommandExecution event, an argument count check, and a registry at QBCore.Commands.List. The parameter table and what each of those does are on Commands reference. What matters when choosing it over the other two mechanisms is that it is a chat command with a permission level, not a key.

-- server side QBCore.Commands.Add('setjob', 'Set a player job', { { name = 'id', help = 'Player ID' }, { name = 'job', help = 'Job name' }, { name = 'grade', help = 'Job grade' }, }, true, function(source, args) -- args are strings, convert and validate before use local target = tonumber(args[1]) if not target then return end end, 'admin')

Because it defaults to 'user', omitting the permission produces an unrestricted command. If the command does anything privileged, either pass a permission level or check inside the callback. qb-towjob takes the second route: its /tow command is registered at the default level and the handler checks the caller’s job.

args always arrive as strings. Convert and range check them yourself. Several upstream resources call tonumber(args[n]) and use the result without a nil check, which raises a server-side Lua error on bad input rather than a helpful message.

Choosing between them

GoalUse
A rebindable key for a client actionRegisterCommand plus RegisterKeyMapping, both client side
A hold-to-act key+name and -name commands, map the + one
A chat command anyone can useQBCore.Commands.Add with the default permission
A chat command for staffQBCore.Commands.Add with 'admin' or your own level
A console or RCON commandRegisterCommand server side
An action triggered by proximity rather than a keyA target or zone resource, see qb-target
  • Admin commands for the command list a stock QBCore server ships with, and how permission levels are assigned
  • Commands reference for the stock command list itself, rather than how to register your own
  • Server functions for AddPermission, HasPermission, and GetPermission