Hi! This admin system will use a group to get the rank of the player, but I’m pretty sure you can also use datastores for this.
You will need basic scripting knowledge, nothing too advanced as I explain most things.
Let’s get right into this!
Create a serverscript, preferably named “Admin”, inside of ServerScriptService.
Now, create a modulescript inside of our serverscript and name it “Commands”.
We could do all the things in one script but we will break it up into two so it looks better. We will handle the chatted event in our serverscript and store the command functions inside of our commands modulescript. That way it’s all organized (you could create a seperate module for every command you are going to make but I don’t really like that).
First, we are going to define some variables.
local Players = game:GetService("Players")
local commands = require(script.Commands)
local prefix = ":"
If you don’t understand why I’m using :GetService() instead of simply a dot, it’s because if I renamed my Players service to “Clients” for example, it would error. game:GetService(“Players”) would still work. game.Players V.S. game:GetService("Players") Whats the difference? - #2 by TheNexusAvenger if you want to read more about it.
Now, let’s create our events.
Players.PlayerAdded:Connect(function(player)
player.Chatted:Connect(function(message)
end)
end)
As the name suggest, PlayerAdded executes the function connected to it whenever a player joins and passing that player as parameter. Same with Chatted, just that it executes the function passed to it, as the name suggest, whenever the player that just joined chats. The message the player sent is passed.
Now, we can check if the message was a command by checking if it starts with the prefix.
player.Chatted:Connect(function(message)
if string.sub(message, 1, #prefix) == prefix then
--his message starts with our prefix!
end
end)
string.sub is a function that returns the substring of the given string starting at, in our example, 1 and continuing until, in our case, the amount of characters of prefix.
string.sub examples
local var = "MyString"
print(string.sub(var, 1, 2)) --> "My"
local var = "Great!"
print(string.sub(var, 2, 3)) --> "re"
local var = "Bruh."
print(string.sub(var, 3, #var)) --> "uh."
Now, what we want to do is find the command that he wants to call.
What he just said could look like this: “:teleport player1 player2”
The way we can extract that teleport from that string is by again using string.sub to remove the prefix and another string function, string.split.
local messageWithoutPrefix = string.sub(message, #prefix + 1, #message)
Now, this might look a little bit more complicated than what we did before.
All this is saying is that it should start at the end of the prefix plus one, because of the space after the command, and end at the end of the message.
Now if we had “:teleport player1 player2” as command, it would become “teleport player1 player”.
All we have to do now is get the first word. Here is where we are going to use string.split.
The first argument it takes is the string we want to split, the second is by what we want to split it (seperator). It will return a table including the splits.
string.split examples
local var = "Hello World"
local splits = string.split(var, " ")
print("First split: " .. splits[1]) --> "First split: Hello"
print("Second split: " .. splits[2]) --> "Second split: World"
local var = "Hello,World"
local splits = string.split(var, ",")
print("First split: " .. splits[1]) --> "First split: Hello"
print("Second split: " .. splits[2]) --> "Second split: World"
local var = "Hello World"
local splits = string.split(var, "ll")
print("First split: " .. splits[1]) --> "First split: He"
print("Second split: " .. splits[2]) --> "Second split: o World"
local splits = string.split(messageWithoutPrefix, " ")
Now, knowing that, we can already get the first word (probably the command) by doing splits[1].
if string.sub(message, 1, #prefix) == prefix then
--his message starts with our prefix!
local messageWithoutPrefix = string.sub(message, #prefix + 1, #message)
local splits = string.split(messageWithoutPrefix, " ")
local firstWord = splits[1]
end
Great. What we want to do now is to check, if the command he is trying to execute really exists inside of our commands modulescript as function (maybe he made a typo).
if commands[firstWord] then
end
This is checking if there is something called the thing he said as first word inside of our commands modulescript (modulescripts return a table). → Don’t worry, this would not error if it doesn’t exist, it will simply return nil.
Alright, now, what we want to do is to just execute that function with the parameters the player that is trying to execute this command gave us. We already have all the words in his message because of our splits variable. What we don’t want to pass though is the first word, which is the command. So let’s remove that from our splits table, which is the table whose elements we want to pass as parameters to the function.
if commands[firstWord] then
table.remove(splits, 1)
end
We are almost done now. The only thing we have to do now is to execute the function inside of our commands modulescript.
commands[firstWord](player, table.unpack(splits))
We pass the player who executed this command as first parameter, so later in the module we can check if that player has a high enough rank. Next, we pass all the other words he said after the command, which are probably the arguments. We use table.unpack for that, we could pass the table too, but I like it better like this, so we don’t have to deal with unpacking the words in the modulescript.
table.unpack examples
local t = {
"firstElement",
"secondElement",
"thirdElement"
}
print(table.unpack(t)) --> firstElement secondElement thirdElement
local t = {
"firstElement",
"secondElement",
"thirdElement"
}
local firstVariable, secondVariable, thirdVariable = table.unpack(t)
print("Our first variable is: " .. firstVariable) --> Our first variable is: firstElement
print("Our second variable is: " .. secondVariable) --> Our second variable is: secondElement
print("Our third variable is: " .. thirdVariable) --> Our third variable is: thirdElement
Alright. The serverscript part is done.
Here is the entire script:
local Players = game:GetService("Players")
local commands = require(script.Commands)
local prefix = ":"
Players.PlayerAdded:Connect(function(player)
player.Chatted:Connect(function(message)
if string.sub(message, 1, #prefix) == prefix then
--his message starts with our prefix!
local messageWithoutPrefix = string.sub(message, #prefix + 1, #message)
local splits = string.split(messageWithoutPrefix, " ")
local firstWord = splits[1]
if commands[firstWord] then
table.remove(splits, 1)
commands[firstWord](player, table.unpack(splits))
end
end
end)
end)
You can now make functions inside of our modulescript and the names will be what the player has to say to execute them. A function that you almost always need is the getPlayerByName function. So even if the player passes something like “pla”, it will still return “Player1”.
local function getPlayerByPlayerName(name)
if name then
for i,v in ipairs(Players:GetPlayers()) do
if string.lower(string.sub(v.Name, 1, #name)) == string.lower(name) then
return v
end
end
end
end
This is nothing new, I’ve already talked about string.sub (examples above). string.lower is self explanatory. “pLaYeR1” → “player1”.
We will script some commands, you can add onto that. First, I will make a roleForRank table though. You can also use :GetRankInGroup() for this but I like this method better.
local ranksForRole = {
Guest = 1,
Mod = 2,
Admin = 3,
Owner = 4
}
We will also make a hasRank function, so we don’t have to do it manually everytimes.
local function hasRank(player, needed)
local rank = ranksForRole[player:GetRoleInGroup(groupId)]
if rank >= needed then
return true
end
end
First, let’s script a shutdown command. We basically want to iterate over every player and kick them with the reason the player has given. First, we want to check if the player’s rank is high enough though.
Because we made that function all we have to do is
function Commands.shutdown(plr, reason)
if hasRank(plr, 2) then
end
end
Now we need to get the reason. Remember how we seperated the arguments when firing a function? That means that our spaces are gone. We have to get them back.
local function getReasonFromDots(...)
local args = {...}
local str = ""
for i,v in ipairs(args) do
if i == 1 then
str = v
else
str = str .. " " .. v
end
end
if str == "" then
return "No reason given."
else
return str
end
end
We can use this function for it. … basically means every argument passed to it. We make a table out of them so we can loop through them.
reason = getReasonFromDots(...)
If no reason is passed, it will become “No reason given.”
Now all we have to do is kick every player.
function Commands.shutdown(plr, ...)
if hasRank(plr, 2) then
local reason = getStringFromDots(...)
for i,v in ipairs(Players:GetPlayers()) do
v:Kick("Server shutdown. Reason: " .. reason)
end
end
end
We are done!
Now let’s script a to command. Same thing as before, we check the rank.
function Commands.to(player, toPlayerName)
if hasRank(player, 2) then
end
end
But now, we have to get the player from the toPlayerName parameter (e.g: :to pla).
We’ve already defined a function that does that for us above. All we have to do is set the CFrame of our mod to the CFrame of the player he wants to go to with an offset of 5 on the z axis (optional).
function Commands.to(player, toPlayerName)
if hasRank(player, 2) then
local plrToTeleportTo = getPlayerByPlayerName(toPlayerName)
if plrToTeleportTo then
player.Character.HumanoidRootPart.CFrame = plrToTeleportTo.Character.HumanoidRootPart.CFrame * CFrame.new(0, 0, 5) --you can use CFrame.Angles to rotate the player too so he is looking at him
end
end
end
If we have a to command, let’s also script a bring command, shouldn’t be too different, just that now instead of setting our mod’s CFrame to the player’s CFrame, we set the player’s CFrame to our mod’s CFrame.
function Commands.bring(player, plrToBring)
if hasRank(player, 2) then
local plrToBring = getPlayerByPlayerName(plrToBring)
if plrToBring then
plrToBring.Character.HumanoidRootPart.CFrame = player.Character.HumanoidRootPart.CFrame * CFrame.new(0, 0, 5)
end
end
end
Now, let’s also script a kick command. This should be easier than to or bring.
function Commands.kick(player, plrToKick, reason)
if hasRank(player, 2) then
reason = getReasonFromDots(...)
local plrToKick = getPlayerByPlayerName(plrToKick)
if plrToKick then
plrToKick:Kick("You were kicked. Reason: " .. reason)
end
end
end
See how much the hasRank and getPlayerByPlayerName functions help us? We’d have to write the same thing again and again if we didn’t make them.
Alright, this will be a little bit more complicated.
We have to use string.find. It returns 2 things. StartsAt and endsAt (exactly what we use for string.sub btw).
string.find examples
local var = "lol :bring player"
local beginsAt, endsAt = string.find(var, ":")
print("Begins at: " .. beginsAt) --> --> "Begins at: 5"
print("Ends at: " .. endsAt) --> "Ends at: 5"
local var = "hello guys it's me!"
local beginsAt, endsAt = string.find(var, "guys")
print("Begins at: " .. beginsAt) --> --> "Begins at: 7"
print("Ends at: " .. endsAt) --> "Ends at: 10"
local var = "lolimgonnado:bring plr"
local beginsAt, endsAt = string.find(var, ":")
print("Begins at: " .. beginsAt) --> --> "Begins at: 13"
print("Ends at: " .. endsAt) --> "Ends at: 13"
We will replace our string.sub(message, 1, #prefix)
with string.find(message, prefix
.
If it returns anything, it means that the prefix was found in the message.
local Players = game:GetService("Players")
local commands = require(script.Commands)
local prefix = ":"
Players.PlayerAdded:Connect(function(player)
player.Chatted:Connect(function(message)
if string.find(message, prefix) then
local startsAt, endsAt = string.find(message, prefix)
local messageWithoutPrefix = string.sub(message, endsAt + 1, #message)
--"lol :bring pla" --> "bring pla"
local splits = string.split(messageWithoutPrefix, " ")
local command = splits[1]
if commands[command] then
table.remove(splits, 1)
commands[command](player, table.unpack(splits))
end
end
end)
end)