Server Addons allow you to extend and customize server functionality using custom Lua scripts. This page details the scripting environment, available API, and provides examples.
Server Addons run on a sandboxed version of Lua 5 with specific syntax extensions. Many Roblox engine functions (e.g., creating UI or accessing services like Players or ReplicatedStorage) and the full Luau feature set are not available. The sandbox supports compound assignment (+=, -=, /=, *=, ..=), the continue keyword, and basic type annotations on local variables, function parameters, and return values.
workspace.Part), but you cannot parent new Instances directly to Workspace. To add a new Instance to the map, you must use the f(Instance) function.
In Server Addons, text users can set is subject to a 1 minute (60-second) cooldown for Roblox compliance. Design addons to reuse prepared text-bearing instances rather than repeatedly creating instances and setting their text.
Cloning a prepared template reuses its existing text; it does not exempt new or changed text from the cooldown or filtering requirements. Existing filtering and host-presence requirements still apply. The separate Addon Commands registration limit is described in its API section; setToolCooldown controls tool use, not this text-setting cooldown.
You can use F3X to view a part's position, which is useful for coding logic that occurs when a player enters a specific region in the map.
An AI skill is a reusable set of instructions that gives your coding assistant context about a specific task. This skill helps an assistant create and debug Server Addons using SCP: Roleplay's sandbox, event format, and documented API.
It is a Markdown file for your AI assistant, not a Lua script to run in the game.
scp-roleplay-server-addons/SKILL.md in its skills directory and follow that assistant's instructions to enable it. Otherwise, attach the file or paste its contents into your conversation.Loading SKILL.md…
Loading the AI skill preview…
| Function | Description |
|---|---|
f(name : string / instance) |
Returns the first Instance in the map matching the given name. If an Instance is passed as the argument, it will be parented to the map. |
getTagged(tag) |
Returns all Instances in the map that have the specified tag. Tag is a string. |
tween(instance : instance, tweenInfo : tweeninfo, propertyTable : table) |
Creates and plays a tween. |
raycast(origin : Vector3, direction : Vector3, params : RaycastParams (default: nil), players : table (default: nil)) |
Performs a raycast operation and returns the result. If the ray hits a player, the RaycastResult.Instance property will be their username. |
| Function | Description |
|---|---|
kill(name : string) |
Kills the specified player. |
damage(name : string, amount : number) |
Damages the specified player by the given amount. |
heal(name : string) |
Heals the specified player to their maximum health. |
toggleRagdoll(player : string, on : bool (default: true)) |
Enables or disables ragdoll for the specified player. on defaults to true. |
getPlayers() |
Returns a list of the usernames of all players in the server. |
getPlayerHealth(name : string) |
Returns the specified player's current health. Returns -1 if the player is not spawned in. |
setPlayerHealth(name : string, amount : number) |
Sets the specified player's health. |
getPlayerMaxHealth(name : string) |
Returns the specified player's max health. Returns -1 if the player is not spawned in. |
setPlayerMaxHealth(name : string, amount : number) |
Sets the specified player's max health. |
getPlayerScore(name : string) |
Returns the specified player's score. |
setPlayerScore(name : string, score : number (default: 0)) |
Sets the specified player's score. |
getPlayerPosition(name : string) |
Returns the specified player's position (CFrame) in the map. |
setPlayerPosition(name : string, position : Vector3) |
Sets the specified player's position (Vector3 / CFrame). |
getPlayerKeycard(name : string) |
Returns the specified player's keycard level. (e.g. "L4"). |
getTools(name : string) |
Returns a list of the names of all tools in the specified player's inventory. |
getPlayerTool(name : string) |
Returns the name of the tool currently being equipped by the specified player. Also returns a secondary value with the position of the Muzzle part, or the position of the Handle part if there is no part named Muzzle in the tool. |
giveTool(name : string, tool : string) |
Gives the specified player the specified tool. It also supports the tool argument being a Tool, where in that case it will unanchor the Handle before giving it to the player (useful for making custom tools). |
removeTool(name : string, tool : string) |
Removes the specified tool from the specified player. |
getUserId(name : string) |
Returns the specified player's UserId. |
ownsGamepass(name : string, id : number) |
Returns whether or not the specified player owns the specified gamepass. |
ownsAsset(name : string, id : number) |
Returns whether or not the specified player owns the specified asset. |
getPlayerCredits(name : string) |
Returns the specified player's credits currency amount. |
getPlayerXP(name : string) |
Returns the specified player's XP amount. |
playEmote(target : string / Instance, id : number, loop : bool (default: false)) |
Plays the specified emote or animation on the specified player. You can pass a Humanoid, Animator, AnimationController or an Instance ascending a Humanoid as the target argument, allowing you to animate custom SCPs, for example. Returns a function that stops the animation and another that adjusts its speed, e.g.: stopFunction, adjustSpeedFunction = playEmote(...). |
hasTool(name : string, tool : string) |
Returns whether the specified player has the specified tool. |
getPlayerCurrentTool(name : string) |
Returns the name of the tool the player is currently using, or nil if none. |
getPlayerIsInGroup(name : string, id : number) |
Returns whether the specified player is in the specified group. |
getPlayerRankInGroup(name : string, id : number) |
Returns the player's rank number (0–255) in the specified group. |
getPlayerRoleInGroup(name : string, id : number) |
Returns the player's role in the specified group. |
getPlayerRolesInGroup(name : string, id : number) |
Returns the player's roles in the specified group. Returns an array with this format: { IsMember : bool, Roles : { Id : number, Name : string, Rank : number } } |
gunMagSize(name : string) |
Returns the number of bullets in a magazine for the specified gun. |
gunAddBullets(player : string, gun : string, amount : number) |
Adds the specified amount of bullets to the specified gun for the given player. |
gunAmmo(player : string, gun : string, mag : bool (default: false)) |
Returns the amount of ammo in the specified gun. Returns the amount of bullets left in the magazine if mag is true. |
isGun(name : string) |
Returns whether or not the specified string is a gun; also returns the weapon type. Example: local IsGun, IsPistol, IsShotgun = isGun("Golden Hawk") |
| Function | Description |
|---|---|
getTeams() |
Returns all teams in the game in the { name, color } format. (e.g. team[1] is the name, team[2] is the color). |
getTeamMembers(team : string / BrickColor) |
Returns the usernames of the members of the specified team. |
getTeam(name : string) |
Returns the specified player's team name and color. Example: local name, color = getTeam("PlayerName") |
setTeam(name : string, team : string / BrickColor) |
Sets the specified player's team. |
| Function | Description |
|---|---|
registerAddonCommand(id : string, title : string, callback : function) |
Adds a command button with a callback invoked when the command is selected. Titles are filtered and capped at 32 characters. See Addon Commands for details. |
registerAddonCommand(id : string, state : bool / nil) |
Pass false to disable the command, true to re-enable it, or nil to remove it. |
registerAddonCommand(whitelist : table) |
Expands access to the addon commands panel using a whitelist table of player names or user IDs. By default, only moderators and above can access the panel. |
Use the :addoncommands command to open the addon commands menu, which contains buttons registered by addons:
registerAddonCommand("id", "title", callback) adds a command button with a filtered title capped at 32 characters. The callback is invoked when the command is selected.registerAddonCommand("id", false) disables the command.registerAddonCommand("id", true) re-enables the command.registerAddonCommand("id", nil) removes the command.Using addon commands with arguments: You can also run a registered addon command with /command <id> [arguments]. For example, /command test 1 calls the test command's callback with the player first, followed by the supplied argument.
Argument privacy: Player-supplied argument values are hidden from print, warn, and error output, including error logs from the callback. This prevents command arguments from being used to communicate with other players. Use arguments for your command's logic, not for displaying or logging player-entered messages.
Existing commands take priority: Addon commands are checked only after all existing commands. If an addon ID matches an existing command, /command uses the existing command instead; it will not run the addon command.
By default, only moderators and above can access the panel. Expand this access with registerAddonCommand(whitelist), where whitelist is a table of player names or user IDs. You can implement your own restrictions and cooldowns in each command's callback.
For compliance reasons, registerAddonCommand is unavailable for 60 seconds after its latest batch. You can still assign multiple commands within the same execution frame.
Addon commands reset when the map changes, during loading and before addons are loaded. Register the commands needed for the new map when its addons load.
Example: register commands to turn ragdoll on and off.
registerAddonCommand("a", "ragdoll on", function(player) toggleRagdoll(player) end)
registerAddonCommand("b", "ragdoll off", function(player) toggleRagdoll(player, false) end)
| Function | Description |
|---|---|
announce(message : string, blue : bool (default: false)) |
Displays the specified message to everyone in the server. Alternatively, if the second argument is a string, then the first argument will be used to send the announcement message only to the specified player. |
title(message : string, color : color3 (default: color3.new(1, 1, 1)), gradientColor : color3 (default: nil)) |
Displays the specified title to everyone in the server. Alternatively, if the second argument is a string, then the first argument will be used to send the title message only to the specified player. |
subtitle(message : string, color : color3 (default: color3.new(1, 1, 1)), gradientColor : color3 (default: nil)) |
Displays the specified subtitle to everyone in the server. Alternatively, if the second argument is a string, then the first argument will be used to send the subtitle message only to the specified player. |
sideinfo(message : string, color : color3 (default: color3.new(1, 1, 1)), gradientColor : color3 (default: nil)) |
Displays the specified side info to everyone in the server. Alternatively, if the second argument is a string, then the first argument will be used to send the side info message only to the specified player. |
subsideinfo(message : string, color : color3 (default: color3.new(1, 1, 1)), gradientColor : color3 (default: nil)) |
Displays the specified sub side info to everyone in the server. Alternatively, if the second argument is a string, then the first argument will be used to send the sub side info message only to the specified player. |
| Function | Description |
|---|---|
rigSay(name : string, message : string) |
Sends the specified message as a chat bubble for the specified rigs (e.g. :load rig). |
rigMoveTo(name : string, position : vector / bool, callback : function (default: nil)) |
Makes the specified rigs move to the specified position (e.g. :load rig). Callback is an optional argument that gets called whenever the rig fires MoveToFinished. Sending true as the position argument will cause the rig to halt its movement. |
rigJump(name : string) |
Makes the specified rigs jump. |
setRigWalkSpeed(name : string, walkspeed: number) |
Sets the specified rigs' WalkSpeed. |
setRigJumpPower(name : string, jumppower: number) |
Sets the specified rigs' JumpPower. |
setRigHealth(name : string, health: number) |
Sets the specified rigs' health. |
setRigMaxHealth(name : string, maxhealth: number) |
Sets the specified rigs' max health. |
getRigHealth(name : string) |
Returns the specified rig's health. Will return nil if none exist with the specified name. |
| Function | Description |
|---|---|
openDoor(door : instance, front : bool (default: false)) |
Opens the specified door. You can pass the door's scanner as the instance. |
breakDoor(door : instance, front : bool (default: false)) |
Breaks the specified door. You can pass the door's scanner as the instance. |
playSound(sound : sound, player : string, attach : bool (default: false)) |
Plays the sound. If a player is specified, plays it for them; otherwise global. attach defaults to false. |
resetLights() |
Resets the map lights using the lights system. |
colorLights(r, g, b : number) |
Colors the map lights using the lights system. RGB values default to 0. |
disableLights(amount : number) |
Disables map lights in succession by the specified amount at a time. Amount defaults to 1. |
blackoutLights(enabled : bool, emergencyPower : bool) |
Enables or disables the map lights blackout state. Enabled defaults to true, EmergencyPower defaults to false. |
getClockTime() |
Returns the world time. |
setClockTime(time : number, updateAtmosphere : bool (default: true)) |
Changes the world time. |
setSignText(name : string, text : string) |
Changes the text of the first TextLabel found within the specified Instance in the map. |
setToolCooldown(tool : Instance, cooldown : number, activateOnEquip : bool (default: false)) |
Sets the cooldown for using the tool. If cooldown is specified as 0 then the cooldown is removed. When activateOnEquip is set to true, the tool will act as an ability and activate upon selection instead of equipping. |
tweenGetValue(alpha : number, easingStyle : Enum.EasingStyle, easingDirection : Enum.EasingDirection) |
Calculates a new alpha given an EasingStyle and EasingDirection. https://create.roblox.com/docs/reference/engine/classes/TweenService#summary-methods |
tweenSmoothDamp(current : any, target : any, velocity : any, smoothTime : number, maxSpeed : number, dt : number) |
Calculates a value simulating a critically damped spring. See TweenService summary methods. |
| Function | Description |
|---|---|
getHost() |
Returns the server host's UserId and Name, e.g.: local hostUserId, hostName = getHost(). |
runCommand(cmd : string) |
Executes the specified server command. Commands that broadcast text (for example announcements or titles) can only be run when the server host is present in the server. Returns whether or not the command was successful, and if it isn't successful, what the error was. |
http(url : string, method : string (default: "get"), headers : table (default: nil), body : string (default: nil), compress : Enum.HttpCompression (default: Enum.HttpCompression.None)) |
Performs an HTTP request. |
jsonEncode(content : table) |
Encodes a Lua table into a JSON string. |
jsonDecode(json : string) |
Decodes a JSON string into a Lua table. |
signal(name : string, ...) |
Sends a signal across all addons. |
signalAsync(name : string, ...) |
Sends a signal across all addons and awaits a response. |
signalReceived(name : string, callback : function) |
Sets up the response when receiving the specified signal. |
Events have to be handled uniquely due to the sandboxed environment. You can use didEventHappen(name) to determine whether an event has been triggered since the last check and to update a local cache.
To simplify this process, server addons provide a special event() function that can be used to listen for changes in the cache and execute a callback function:
This example only reads the callback fields; it does not print or display a message.
event("joined", function(data)
local When = data.When
local Value = data.Value
-- Value contains the player's name
end)
| Event Name | Data Type / Description |
|---|---|
joined |
string (player : string) - Occurs when a player joins the server. |
left |
string (player : string) - Occurs when a player leaves the server. |
spawned |
string (player : string) - Occurs when a player spawns. |
interaction |
table (player : string, name : string) - Occurs when a player interacts with a custom interaction part. |
lightswitch |
table (player : string, room : string, state : bool) - Occurs when a player interacts with a light switch. Includes the player's username, room name, and new state of the light (on/off). |
death |
string (player : string) - Occurs when a player dies. |
kill |
table (killer : string, victim : string) - Occurs when one player kills another. |
usedtool |
table (player : string, tool : string, clickposition : vector) - Occurs when the player clicks while holding a tool. |
mousedown |
table (player : string, tool : string, clickposition : vector) - Occurs when the player begins a click. |
mouseup |
table (player : string, tool : string, clickposition : vector) - Occurs when the player ends a click. |
Retrieves a part named "Part" from the map and makes it kill any player who touches it.
f("Part").Touched:Connect(function(Player)
if Player then
kill(Player)
end
end)
Listens for a custom interaction event and plays a sound globally when a part named "SoundPart" is triggered.
event("interaction", function(Data)
local Player = Data.Value[1]
local Interaction = Data.Value[2]
if Interaction == "SoundPart" then
local Sound = Instance.new("Sound")
Sound.SoundId = "rbxassetid://157636218"
playSound(Sound)
end
end)
Creates a simple custom Tool with a safe, hidden Handle and gives it to the first player in the server. This is a one-off creation example. For repeated grants, prepare a template once and clone it as described in User-settable text and templates; do not recreate and rename the Tool on every grant.
local function GiveTool(Player)
local Tool = Instance.new("Tool")
Tool.CanBeDropped = false
Tool.Name = "Custom Tool"
local Part = Instance.new("Part")
Part.Name = "Handle"
Part.Anchored = true
Part.CanCollide = false
Part.CFrame = CFrame.new(999, 9999, 999)
f(Tool)
Part.Parent = Tool
giveTool(Player, Tool)
end
GiveTool(getPlayers()[1])
Creates a simple custom Tool that changes the Handle color upon activation, with a tool-use cooldown of 1 second. This is separate from the text-setting cooldown. For repeated grants, use prepared templates rather than recreating and renaming the Tool on every grant.
local function GiveTool(Player)
local Tool = Instance.new("Tool")
Tool.CanBeDropped = false
Tool.Name = "Custom Tool"
local Part = Instance.new("Part")
Part.Name = "Handle"
Part.CanCollide = false
Part.CFrame = CFrame.new(999, 9999, 999)
Tool.Activated:Connect(function()
Part.Color = Color3.new(math.random(), math.random(), math.random())
end)
setToolCooldown(Tool, 1)
f(Tool)
Part.Parent = Tool
giveTool(Player, Tool)
end
GiveTool(getPlayers()[1])
Sets up a signal loop that works across multiple addons.
-- Addon 1
signalReceived("loop", function(n)
print("current number:", n)
end)
-- Addon 2
local n = 0
while true do
task.wait(1)
n = n + 1
signal("loop", n)
end
Makes use of signals to export variables and functions.
-- Addon 1
local var1 = "Hello world!"
function func()
return var1
end
signalReceived("var1", function() return var1 end)
signalReceived("func", function() return func end)
-- Addon 2
local var1 = signalAsync("var1")
local func = signalAsync("func")