Things you need to know before doing this tutorial
- Basics of programming on Roblox
- How to use the Data Store 2 module made by @Kampfkarren → How to use DataStore2 - Data Store caching and data loss prevention
- String matching
- Module scripts
The things listed above will not be explained, as I expect you know them if you read on
Things this tutorial will teach you
- How to use the
ChatService:RegisterProcessCommandsFunction()
function - How to make a command parsing function
- How to find command targets
If you have anything to add or if you have any questions, please reply, I’ll try to get back to you as soon as possible
If you find any grammatical errors that I haven’t found yet please let me know
What is the Chat Service?
When you open studio and go into your explorer, you’ll see something called “SoundService
” underneath that is another service called “Chat
”. This is not the Chat Service, the Chat Service is inside of the Chat
. You may realize that there are no objects inside of Chat
. That’s because the Chat Service is automatically added to Chat
when the game runs.
How do we access the Chat Service?
As I said above, the Chat Service is added to the Chat
during a play test (or in a public or private server), so to begin this tutorial, initiate a play test. When you look in the explorer you will now see the Chat
now has some objects inside of it.
There are multiple different objects here, but all we need to focus on is the ChatServiceRunner
and the ChatModules
, but there’s no ChatService
… so where is it? The ChatService
is located inside of the ChatServiceRunner
along with some other module scripts.
Manipulating the Chat Service
To begin messing around with the ChatService
you need to select the ChatModules
folder and use the Ctrl + C shortcut to copy it. Once you have copied the ChatModules
you may end the play test and paste the ChatModules
into the Chat
using the Ctrl + V shortcut. Putting this new folder into the Chat
will overwrite the original. Create a new module script inside of the ChatModules
so we can begin. Inside of the the module write the following code:
function Run(ChatService)
--Hopefully you noticed that "ChatService" parameter
--This is parameter is the same thing as requiring the Chat Service module
--If it's the same, why are we doing this inside of the ChatModules?
--We're doing it like this because all module scripts inside of the ChatModules
--Are required and then the returned value of the module script is called
--Since we're returning a function from this module, that function will be called and the ChatService parameter will be filled in automatically
end
return Run
Now that we understand how we can require the ChatService
, we can can start working with the ChatService:RegisterProcessCommandsFunction
function.
function Run(ChatService)
local function isCommand(speakerName, message, channelName)
--The speaker name is the name of the speaker
--The message is the message the speaker sent
--The channel name is the name of the chat channel the message was sent on
print(speakerName)
print(message)
print(channelName)
if message:lower() == "testing" then
return true
end
return false
--When you say /e [name of emote] into the chat
--It plays the emote and stops the message from being sent
--That's what we're doing here
--When this function returns true the message doesn't get sent, when it returns false it does get sent
end
ChatService:RegisterProcessCommandsFunction("cmd", isCommand)
--This function calls and auto fills the parameters of the isCommand function we created
end
return Run
Now that we know how to let the ChatService
know we have sent a command, we can now get to the fun part, making commands!
The Fun Part
local commands = {}
We’re going to leave that table blank because we’ll be creating a function that adds commands to it
local commands = {}
-- [[ Services ]] --
local ServerScriptService = game:GetService("ServerScriptService")
local PlayersService = game:GetService("Players")
local Settings = {
Prefix = ";"; -- Symbol that lets the script know the message is a command
ChannelName = "Admin"; -- Name of the chat channel we'll be creating to send admin commands on
DebugMode = false; -- Set to true when making new commands so it's easier to identify errors
DefaultPerm = 0; -- If a player is not on the admins list, this is what level of permission they'll have
Admins = {
-- Dictionary of user ids and the rank the player with that user id will be receiving (rank must be a number)
[game.CreatorId] = math.huge;
[0] = 1;
}
}
local DataStore2 = require(1936396537)
-- *** MAKE SURE YOU HAVE THE DATASTORE2 MODULE IN YOUR GAME *** --
function SendMessageToClient(data, speakerName)
local ChatService = require(ServerScriptService.ChatServiceRunner.ChatService)
-- The ChatService can also be found in the ServerScriptService
local Speaker = ChatService:GetSpeaker(speakerName)
-- The speaker is another module script in the ChatServiceRunner that has functions related to the speaker and some other things
local extraData = {data.ChatColor} -- Sets the color of the message
Speaker:SendSystemMessage(data.Text, Settings.ChannelName, extraData)
-- Sends a private message to the speaker
end
-- Function to send error messages to the person who attempted to use the command
function GetSpeakerPerms(speakerName)
local playerObj = PlayersService[speakerName] -- Finds the actual player object
local permission = DataStore2("perms", playerObj):Get(Settings.DefaultPerm)
return permission
end
--Gets the permission level of the speaker
function SetSpeakerPerms(speakerName, level)
local playerObj = PlayersService[speakerName]
local permission = DataStore2("perms", playerObj)
permission:Set(tonumber(level))
end
--Sets the permission level of the speaker
function StartSpeaker(speakerName)
local playerObj = PlayersService[speakerName]
local perms = GetSpeakerPerms(speakerName)
if perms <= -1 then
playerObj:Kick("You are banned from this place")
elseif Settings.Admins[playerObj.UserId]then
SetSpeakerPerms(speakerName,Settings.Admins[playerObj.UserId])
end
end
function GetTarget(player, msg)
local msgl = msg:lower()
local ts = {} -- Targets table
if msgl == "all" then
--// Loop through all players and add them to the targets table
for i, v in pairs(PlayersService:GetPlayers()) do
table.insert(ts, v)
end
elseif msgl == "others" then
--// Loops through all players and only adds them to the targets table if they aren't the player
for i, v in pairs(PlayersService:GetPlayers()) do
if v.Name ~= player.Name then
table.insert(ts, v)
end
end
elseif msgl == "me" then
--// Loops through all players and only adds them to the targets table if they are the player
for i, v in pairs(PlayersService:GetPlayers()) do
if v.Name == player.Name then
table.insert(ts, v)
end
end
else
--// Loops through all players and only adds them to the targets table if a portion of their name was in the msg
-- for example
-- if i wanted to admin somebody named "Jerry"
-- I could say
-- ;admin jer
for i, v in pairs(PlayersService:GetPlayers()) do
if v.Name:lower():sub(1, #msgl) == msg:lower() then
table.insert(ts, v)
end
end
end
return ts
end
-- If speaker is on the admins list this will admin them, if they're banned this will ban them
function BindCommand(data)
commands[data.name] = data
end
-- Adds a new command to the commands table
function BindCommands()
-- For this tutorial I'll just be making a simple kill command, you can of course make more
BindCommand({name = "kill", perm = 1, func = function(speaker, args)
local commandTargets = GetTarget(speaker, args[1] or "me")
if #commandTargets == 0 then
-- No target was specifie so we can't do anything
SendMessageToClient({
Text = "No targets specified";
ChatColor = Color3.new(1, 0, 0)
}, speaker.Name)
return -- End command
end
for _, target in pairs(commandTargets) do
-- Loop through targets table
local targetPerm = GetSpeakerPerms(target.Name)
local speakerPerm = GetSpeakerPerms(speaker.Name)
if targetPerm >= speakerPerm and speaker.Name ~= target.Name then
-- Since a kill command can be seen as abusive to use
-- I made it so people of lower ranks can't use it on higher ranks or people of the same rank
SendMessageToClient({
Text = "You cannot use this command on this player";
ChatColor = Color3.new(1, 0, 0)
}, speaker.Name)
return -- End command
end
if target.Character then
target.Character:BreakJoints() -- Kill
end
end
end})
--[[
Make sure your table looks something like this
{
name = "command_name",
perm = number,
func = function(speaker, args)
-- random code
end
}
]]
end
-- Binds all commands at once
function Run(ChatService)
spawn(BindCommands) -- Bind all the commands
local AdminChannel = ChatService:AddChannel(Settings.ChannelName)
-- Add a new channel for admin commands
AdminChannel.WelcomeMessage = ""
AdminChannel.AutoJoin = true
-- Make all speakers join the channel by default
AdminChannel.SpeakerJoined:Connect(StartSpeaker)
-- When a new speaker is added to the channel it calls the StartSpeaker function
local function ParseCommand(speakerName, message, channelName)
local isCommand = message:match("^"..Settings.Prefix)
-- Pattern that returns true if the prefix starts off the message
if isCommand then
local speaker = ChatService:GetSpeaker(speakerName) -- Requires the speaker module from the speaker module in the ChatServiceRunner
local perms = GetSpeakerPerms(speakerName) -- Get speaker's permission level
local messageWithoutPrefix = message:sub(#Settings.Prefix+1,#message) -- Get all characters after the prefix
local command = nil -- The command the player is trying to execute (we haven't found that yet)
local args = {} -- Table of arguments
-- Arguments are words after the command
-- So let's say the command was
-- ;fly jerry
-- jerry would be the 1st argument
for word in messageWithoutPrefix:gmatch("[%w%p]+") do
-- Loops through a table of words inside of the message
if command ~= nil then
table.insert(args, word)
else
command = word:lower()
end
end
-- Identify the command and get the arguments
local properCommand = command:sub(1,1):upper() .. command:sub(2,#command):lower()
-- This converts something like "fLy" into "Fly"
if commands[command] then
-- Command exists
local commandPerm = commands[command].perm
if commandPerm > perms then
-- Player does not have permission to use this command
SendMessageToClient({
Text = "You do not have access to this command";
ChatColor = Color3.new(1, .5, 0)
}, speakerName)
return false
else
-- Player has access to the command
if Settings.DebugMode then
-- Only shows output of command when DebugMode is on
-- I'd turn it on if you're creating new commands and need to test them
local executed, err = pcall(function()
return commands[command].func(PlayersService[speakerName], args)
end)
if executed then
SendMessageToClient({
Text = "\"" .. properCommand .. "\" ran successfully";
ChatColor = Color3.new(0, 1, 0)
}, speakerName)
return true
else
-- An error accured when executing
SendMessageToClient({
Text = "\"" .. properCommand .. "\" experienced an issue: " .. err;
ChatColor = Color3.new(1, 0, 0)
})
return false
end
else
-- DebugMode is disabled so we just execute the command
pcall(commands[command].func, PlayersService[speakerName], args)
return true
end
end
else
-- Command doesn't exist
SendMessageToClient({
Text = "\"" .. properCommand .. "\" doesn't exist!";
ChatColor = Color3.new(1, 0, 0)
}, speakerName)
return false
end
end
return false
end
ChatService:RegisterProcessCommandsFunction("cmd", ParseCommand)
end
return Run
I may do more tutorials like this in the future and create a module for this
There’s more about the Lua Chat System here → Lua Chat System if you’re interested