Improving this Chat Command Processing Script

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 :sweat_smile:
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 command variable 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 use command instead of messageArguments[1]:sub(2) repeatedly.
  • Remove the first element of the messageArguments table since it isn’t needed anymore. We’re left with {"PlayerName", "Reason"} and our separate command variable containing a single string.
  • Check if the embedded commands table contains an index for command. If it does, run its function using the updated commandArguments table 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)
1 Like