Do you ever wish, as the game creator, that you could easily give yourself special powers. For example, I like to increase my JumpPower and WalkSpeed. By raising these I am able to get around a large map much quicker while I am developing and testing the game. One way to accomplish this is with custom chat commands. If you have any interest in custom chat commands or superuser powers then I suggest you look at the following code.
I create a separate script for each command and place that script in ServerScriptService. The example below is named JumpPowerChatCommand:
local Players = game:GetService("Players")
-- Command to choose a jump power (note the trailing space)
local COMMAND = "/jumppower "
local function hasMatchingCommandName(text, command)
-- Note: string.sub(message, ...) is the same as text:sub(...)
return text:sub(1, command:len()):lower() == command:lower()
end
local function getPowerFromMessage(message)
local powerString = message:sub(COMMAND:len() + 1) -- Cut out the "60" from "/jumppower 60".
local power = tonumber(powerString)
return power
end
local function onPlayerChatted(player, message, recipient)
if hasMatchingCommandName(message, COMMAND) then
local power = getPowerFromMessage(message)
if power then
local character = player.Character or player.CharacterAdded:wait()
character.Humanoid.JumpPower = power
end
end
end
local function onPlayerAdded(player)
if player.UserId == game.CreatorId then -- This command is available only to the game creator.
player.Chatted:Connect(function(...)
onPlayerChatted(player, ...)
end)
end
end
for _, player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
Iāve tried to structure the code to maximize readability. And Iāve added some helpful comments. If you have any questions feel free to ask.
Split the message by " " (spaces), now we have {"jumppower" ,"60"}. Convert "60" to a number. Check if the message is "jumppower". If so, check if we have the 60 argument. If both are true, update the jumppower.
Cool, so you have separate scripts for each command?
I wonder if having too many scripts connecting to each chat message you send and scanning your chat messages would turn into performance issues. Maybe it wouldnāt but its something to consider.
Yes, I do use a separate script for each command. Keep in mind that I only have a handful of creator-only commands. And I decided that the code would be cleaner and more easily understood by separating them. I donāt think performance would be an issue the way I am coding this. For another example, here is my WalkSpeedChatCommand server Script:
local Players = game:GetService("Players")
-- Command to choose a walk speed (note the trailing space)
local COMMAND = "/walkspeed "
local function hasMatchingCommandName(text, command)
-- Note: string.sub(message, ...) is the same as text:sub(...)
return text:sub(1, command:len()):lower() == command:lower()
end
local function getSpeedFromMessage(message)
local speedString = message:sub(COMMAND:len() + 1) -- Cut out the "24" from "/walkspeed 24".
local speed = tonumber(speedString)
return speed
end
local function onPlayerChatted(player, message, recipient)
if hasMatchingCommandName(message, COMMAND) then
local speed = getSpeedFromMessage(message)
if speed then
local character = player.Character or player.CharacterAdded:wait()
character.Humanoid.WalkSpeed = speed
end
end
end
local function onPlayerAdded(player)
if player.UserId == game.CreatorId then -- This command is available only to the game creator.
player.Chatted:Connect(function(...)
onPlayerChatted(player, ...)
end)
end
end
for _, player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
Good questions, by the way. Thank you for asking them.
Yeah, iām sure you wouldnāt notice any problems with a handful of commands. I like this type of solution vs. the bloated admin systems that are popular. If I had to have one command like this itād probably be a player reset, which would be easy to make as you have done here. Right now, Iāve just been typing it into the console if i want to reset someone or so something āadminā. workspace.bozotrolling.Humanoid.Health = 0 take that!
And here is my script that allows me to switch teams in my team-based game:
local Players = game:GetService("Players")
local Teams = game:GetService("Teams")
game.Players.CharacterAutoLoads = false
-- Command to choose a team (note the trailing space)
local COMMAND = "/jointeam "
local function hasMatchingCommandName(text, command)
-- Note: string.sub(message, ...) is the same as text:sub(...)
return text:sub(1, command:len()):lower() == command:lower()
end
local function hasMatchingTeamName(text, name)
-- Let's check for case-insensitive partial matches, like "red" for "Red Robins".
return text:sub(1, name:len()):lower() == name:lower()
end
local function findTeamByName(name)
-- Return nil if no team found.
if Teams:FindFirstChild(name) then -- First, check for the exact name of a team.
return Teams[name]
end
for _, team in pairs(Teams:GetChildren()) do
if hasMatchingTeamName(team.Name, name) then
return team
end
end
end
local function getTeamFromMessage(message)
local teamName = message:sub(COMMAND:len() + 1) -- Cut out the "xyz" from "/jointeam xyz".
local team = findTeamByName(teamName)
return team
end
local function onPlayerChatted(player, message, recipient)
if hasMatchingCommandName(message, COMMAND) then
-- Matched "/JOINTEAM xyz" to our join command prefix "/jointeam "
local team = getTeamFromMessage(message)
if team then
player.Team = team
player.Neutral = false
else
player.Team = nil
player.Neutral = true
end
end
end
local function onPlayerAdded(player)
if player.UserId == game.CreatorId then -- This command is available only to the game creator.
player.Chatted:Connect(function(...)
onPlayerChatted(player, ...)
end)
end
end
for _, player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
If anyone has a better way of structuring these three scripts Iād love to see it.
Iād probably just suggest combining them, have one listener (one :connect) that parses the chat then decides which function to run. I guess you could get fancy with module scripts and such, but then if youāre going to do all that, you might as well get one of those more complex admin systems that others have created.
Exactly. I came up with this approach as a way to avoid using a big admin tool. On the other had, there is no way my way would be manageable for a complete admin system. Apples and oranges and donāt over-engineer. And donāt let āperfectionā be the enemy of āgood enoughā.
Iāve also coded these backdoor things using keybindings. But then I always forget what key is assigned to what command. So this chat approach just felt like a pretty good solution.
I tried it out. I like it. One thing I didnāt like was that everyone can see what youāre typing in. I was trying to figure a way around that and I kinda stumbled on a hacky way to do it. I know there are ways to tap into the chat system and have a function that prevents the ācommandsā from being sent as messagesā¦ but in the spirit of simplicity and with only your script, the workaround that delivers a similar result is to use the switch channel command instead of the plain slash. I change your code to:
local COMMAND = "/c walkspeed "
then I am able to type into the chat /c walkspeed 60
I get the error that there is no such channel, butā¦ the walkspeed function is executed and the walkspeed is changed, and, nobody seeās my chat.
I modified your script for a reset script that will reset any player by user name or displayname when I type /c reset Sir_Highness this happens:
I love it! Thanks for posting your version. I was willing to let others see my commands, but I could definitely see when you wouldnāt want the āmagicā to be seen. I suppose the next improvement would be to allow both options (/ and /c) to be possible for the same chat command. That way you would have a choice. After that we can make the whole thing object-oriented and then make a complete system admin framework.
Well, here is your first two scripts combined into one script with a configurable command dictionary, under 50 lines:
local Players = game:GetService("Players")
-- Command definition table, to add new cmd add in new Constand and also new dictionary entry
local CMDJUMP = "/jumppower"
local CMDSPEED = "/walkspeed"
local COMMANDS = {
[CMDJUMP] = CMDJUMP;
[CMDSPEED] = CMDSPEED;
}
local function onPlayerChatted(player, message, recipient)
local stringarray = string.split(message, " ")
local command = stringarray[1]
local argument = stringarray[2]
if argument and COMMANDS[command] then -- if command string exists in the command dictionary then...
if COMMANDS[command] == CMDJUMP then
local power = tonumber(argument)
if power then
local character = player.Character or player.CharacterAdded:wait()
character.Humanoid.JumpPower = power
end
end
if COMMANDS[stringarray[1]] == CMDSPEED then
local speed = tonumber(argument)
local character = player.Character or player.CharacterAdded:wait()
if speed then --make sure speed was entered as a valid number
character.Humanoid.WalkSpeed = speed
end
end
end
end
local function onPlayerAdded(player)
if player.UserId == game.CreatorId then -- This command is available only to the game creator.
player.Chatted:Connect(function(...)
onPlayerChatted(player, ...)
end)
end
end
for _, player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
It might make sense to break the commands out as functions to themselves after they are parsed if youāre going to get complicated with them.
Why do it in separate script if one script can do the big lifting?
plr.Chatted:Connect(function(msg)
--i didn't script admin commands for 1 year so I might do it wrong
if msgiscommand then
--do something
end
if msgisanothercommand then
--do something
end
end)
I believe 1 script is more efficient than multiple scripts.
Atleast use modulescripts
(Oh yeah the msg is command thing is just an example)
Yep, that is what I did in the script above your post. I put two of his commands into one script. I think I did it in a way that would be easy to add more.
Youāre confusing me, that is what I did. I did what you are saying, yet you are suggesting that what you say is preferable. I demonstrated a way to combine them, if he wants to add all his commands in that way, he can use my example and have them all ā¦ in a single script. As you have subsequently suggested.