Hi all, I’m working on a solo project to create a working demo of chat command processing. Before moving forward to things like Player Ranks, and moving over to using the Chat Service, I’d like to ensure the foundation of the message handling is the ‘best’ it can be.
Right now, there is a list of embedded commands, indicated by a name, that run a particular function when it is called from the table. The code itself is fairly self-documenting, hopefully the comments explain any possible confusion ![]()
It’s not that I’m not fond with the way this works, but I have a gut feeling that I’m missing something sort of obvious that could make this… neater, perhaps easier to be expanded on in the future?
The goal is to, given a player’s chat message, organize this string into a valid command, working arguments, verifying the command is properly prefixed, and ensuring the command is indexed from a table, in a neat and efficient manner in order to be processed by a command handler’s function.
Running through the code, we get these steps when the player sends ";kick PlayerName Reason":
- Check if the player’s sent message starts with
;. If not, return. - Split the message into a table using spaces as a delimiter. Now we have
{";kick", "PlayerName", "Reason"} - Assign a
commandvariable to the first element of this newly created table, starting at the second index, to exclude the prefix. We now have a separate variable containing just the name of the command:"kick", and a table of the rest of the whole message:{";kick", "PlayerName", "Reason"}– Is this necessary? I did it for readability purposes, so I could usecommandinstead ofmessageArguments[1]:sub(2)repeatedly. - Remove the first element of the
messageArgumentstable since it isn’t needed anymore. We’re left with{"PlayerName", "Reason"}and our separatecommandvariable containing a single string. - Check if the embedded
commandstable contains an index forcommand. If it does, run its function using the updatedcommandArgumentstable as the argument. Processing the command itself will be handled there (I’m not concerned about this… yet) - Expected (and currently working) output is
"Kicking PlayerName for reason: Reason"
-- Script: ServerScriptService/ChatCommands.lua
-- TODO: Hierarchical Staff ranks (Jr/Mod, Admin, Owner, etc. have access to different commands)
-- TODO: Command Argument checking (e.g. Kick command must contain at least two arguments, optionally a third)
-- TODO: Touch ups (:lower(), variable renaming, command feedback (GUI warnings/success messages, etc)
-- TODO: Convert to Chat service instead of Player.Chatted, using RegisterChatCallback to hide command usage from public chat.
-- TODO: Put the commands and prefix in a separate ModuleScript, this is purely for demonstrative purposes.
local prefix = ";"
local commands = {
-- Example command: kick
["kick"] = function(args)
local name = args[1]
local reason = table.concat(args, " ", 2)
print(string.format("Kicking %s for reason: %s", name, reason))
-- Kick player here
end
}
game.Players.PlayerAdded:Connect(function(player)
player.Chatted:Connect(function(msg)
-- Leave if the message doesn't start with the prefix - it isn't a command
if msg:sub(1, 1) ~= prefix then
return
end
-- Split the player's chat message by spaces
local commandArguments = msg:split(" ")
-- First element is the command name
-- Start at the second character to exclude the prefix
local command = commandArguments[1]:sub(2)
-- Remove the command now that it's saved elsewhere
-- We're left with the command parameters as a table and the command as a string
--[[ e.g.
{
command = "kick", (string command)
arguments = { (table commandArguments)
"<playerName>",
"<additional argument>",
"<additional argument>"
}
}
]]--
table.remove(commandArguments, 1)
-- If the command exists in the table, run its function and pass in the table of parameters
if commands[command] then
commands[command](commandArguments)
end
end)
end)