Now before we start, this is my first ever post on the Community Tutorials category, and my first time making a tutorial as well, so sorry if it doesn’t make much sense, haha
Now that you’ve (probably) read that, don’t expect this to be much of a versatile tutorial.
Today we’re going to be making your own simple ban system using Roblox’s built-in service DataStoreService. So without further ado, let’s get started.
In this tutorial we’ll be making a custom ban command/ban system, of course. With the reason being saved, so that the user sees it every time they attempt to enter the game while still banned.
I’m going to be making a server/general/regular/whatever script and putting it in ServerScriptService. I’ll then be naming it jojo
for better organization.
We’ll start the script off by indexing/getting the DataStoreService and the Players service, which should be a fast and easy process. After, we’ll want to get our “BanStore”.
-------------
--- INDEX ---
-------------
--// SERVICES
local DDS = game:GetService("DataStoreService");
local Players = game:GetService("Players");
--// DECLARABLES/VARIABLES
local BanStore = DDS:GetDataStore("BanStore", 2) --scope is optional
"What is the number next to the "BanStore" string?
That is what we call a “Scope”, think of it as like a certain folder. While scopes are very optional, I’d prefer using them for better organization and usage in the feature, and for less confusion of the script.
Setting up the functions
Now that we have the DataStore and the Players service, and we’ve created a new DataStore, we’ll then have to set up the functions for the ban before the main script, for better organization.
We’ll first want to start up with a function that sets the ban for the player. We’ll make sure to wrap this in a pcall
in case we run into any errors that break the entire script, like the player not actually existing and all that good stuff.
local function SetBan(Player, Reason, Duration)
local Success, Error = pcall(function() --Protect the call in case the player specified doesn't exist, the player isn't found, or stuff like that
BanStore:SetAsync(tostring(Player.UserId), {BanStart = os.time(), BanDuration = (Duration * secondsInADay), BanReason = Reason});
end)
Player:Kick(Reason);
if not Success then
warn("Not successful."); --for debugging
end
end
It is a good practice to use pcalls when it comes to situations like these, just in case you run into a script-breaking error.
Now that that’s done, I think something’s missing. Yeah, something’s not right. Of course it’s obvious, the player isn’t banned - they can still join, and that is because we haven’t set up a function that searches for any key entry/data of the player in the BanStore when they join. So let’s set that up, shall we?
local function GetBan(Player)
local Success, Result = pcall(function()
return BanStore:GetAsync(tostring(Player.UserId, "TempBan");
end)
if Success then --see if datastore request was successful
if Result then --check if any data for the player was found
if Result.BanStart + Result.BanDuration < os.time() then --check if temp ban duration is over
return true;
else
return false;
end
end
end
end
All right, that’s out of the way, we’re making some good time. And it only took two functions, awesome.
Now let’s initialize the command.
INTIALIZATION
Of course anyone experienced could easily guess it. We’ll be using the .Chatted
event with a selected prefix which you can change to anything you want.
----------------------
--- INITIALIZATION ---
----------------------
Players.PlayerAdded:Connect(function(plr)
plr.Chatted:Connect(function(message)
--empty
end)
end)
But wait! I think you forgot something. Yeah, that’s right.
We wouldn’t really want to have every player of the game have access to the command, would we? So we should restrict this to the selected. But since this is just a tutorial, I won’t really be going over that, haha.
Players.PlayerAdded:Connect(function(plr)
if plr.UserId == game.CreatorId then --restrict this command only to the owner
end
end)
Easy, we’ve restricted the command to only a few lines and words, now let’s get to the main part which you probably don’t know how to do if you aren’t experienced with string patterns and all that.
Players.PlayerAdded:Connect(function(plr)
if plr.UserId == game.CreatorId then --restrict this command only to the owner
plr.Chatted:Connect(function(message)
if string.sub(message, 1, #"/ban"):lower() == "/ban" then
local args = string.split(message, " "); --split string message via space, and iterate them into a table
local numbers = "%d" or "%d%d"; -- string pattern to look for duration/numbers
local duration = string.sub(message, string.find(message, numbers));
local playerToBan = Players:FindFirstChild(args[2]); --search for player via specified playername in command
local reason = string.sub(message, string.len("/ban " .. args[2] .. " " .. duration) + 1); --search for reason
end
end)
end
end)
More info about string patterns can be found here.
Great! Now you have access to each important element of the message. Now all we have to do is check if all these even exist.
Players.PlayerAdded:Connect(function(plr)
if plr.UserId == game.CreatorId then --restrict this command only to the owner
plr.Chatted:Connect(function(message)
if string.sub(message, 1, #"/ban"):lower() == "/ban" then
local args = string.split(message, " "); --split string message via space, and iterate them into a table
local numbers = "%d" or "%d%d"; -- string pattern to look for duration/numbers
local duration = string.sub(message, string.find(message, numbers));
local playerToBan = Players:FindFirstChild(args[2]); --search for player via specified playername in command
local reason = string.sub(message, string.len("/ban " .. args[2] .. " " .. duration) + 1); --search for reason
if playerToBan then
if duration then
if reason then
SetBan(playerToBan, reason, duration);
print("Player has been kicked!");
end
end
end
end
end)
end
end)
Now we are done with the chat command, but you probably forgot one thing - that’s right. We forgot to add in GetBan. We’ll be using that before the owner if statement, since, it’s not like the owner can get banned or have any entry data, right? haha.
local IsBanned = GetBan(plr);
if IsBanned ~= nil then
plr:Kick(IsBanned);
else
print("Player's ban has been lifted.");
end
And now we are done. Just in a few lines. (Notify me if I missed something or got something wrong).
v Check out the full code below v
Full Code
-------------
--- INDEX ---
-------------
--// SERVICES
local DDS = game:GetService("DataStoreService");
local Players = game:GetService("Players");
--// DECLARABLES/VARIABLES
local BanStore = DDS:GetDataStore("BanStore", 2) --scope is optional
local secondsInADay = 86400
-----------------
--- FUNCTIONS ---
-----------------
local function SetBan(Player, Reason, Duration)
local Success, Error = pcall(function() --Protect the call in case the player doesn't exist or the player wasn't found or anything that may cause an error that breaks the whole script;
BanStore:SetAsync(tostring(Player.UserId), {BanStart = os.time(), BanDuration = (Duration * secondsInADay), BanReason = Reason});
end)
Player:Kick(Reason);
if not Success then --for debugging
warn("Not successful.")
end
end
local function GetBan(Player)
local Success, Result = pcall(function()
return BanStore:GetAsync(tostring(Player.UserId), "TempBan");
end)
if Success then --See if DataStore request was successful or not in order to prevent running into any errors
if Result then --see if ban data exists for the specified player or not
if Result.BanStart + Result.BanDuration > os.time() then --see if ban duration has passed
return nil;
else
return Result.BanReason;
end
end
end
end
----------------------
--- INITIALIZATION ---
----------------------
Players.PlayerAdded:Connect(function(plr)
local IsBanned = GetBan(plr);
if IsBanned ~= nil then
plr:Kick(IsBanned);
else
print("Player's ban has been lifted.");
end
if plr.UserId == game.CreatorId then --restrict this command only to the owner
plr.Chatted:Connect(function(message)
if string.sub(message, 1, #"/ban"):lower() == "/ban" then
local args = string.split(message, " "); --split string message via space, and iterate them into a table
local numbers = "%d" or "%d%d"; -- string pattern to look for duration/numbers
local duration = string.sub(message, string.find(message, numbers));
local playerToBan = Players:FindFirstChild(args[2]); --search for player via specified playername in command
local reason = string.sub(message, string.len("/ban " .. args[2] .. " " .. duration) + 1); --search for reason
if playerToBan then
if duration then
if reason then
SetBan(playerToBan, reason, duration);
print("Player has been banned!");
end
end
end
end
end)
end
end)
Now, don’t abuse your power!
I’d also like to remind you that duration is in days, not seconds, in case anyone misunderstands.
EDIT #1: Before anyone tells me it’s not working, when testing it on Roblox Studio it wouldn’t really work. Comment out the part where it checks if the player’s ID is the same as the game creator’s ID, and it should work upon testing.