Okay so the table has data at the time of saving. So then something might be going wrong with SetAsync
. The documentation shows the key should be a string but you’re passing in a number. Not sure if would affect it but try using a string instead?
I Tested This Code in my game and it work
Module Script :
local ValidReasons = {
"Inapropriate Audio",
"Detain Abuse",
"Admin Abuse",
"Blocking",
"Trolling",
"Btools Abuse",
"Fake Donation",
"Exploiting",
"Break Roblox TOS",
"Moderation Bypass",
}
-- Services
local DatastoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local PlayersServ = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Warning Datastore
local WarningDataStore = DatastoreService:GetDataStore("Warnings")
-- NumberOfWarningsRequiredForBan
local RequiredWarnings = 4
-- Store Warnings in a roblox table
local ActiveWarningListForPlayers = {}
local System = {}
local function WarnUser(UserId:number,Reason:string,sender:string)
if not table.find(ValidReasons,Reason) then
return
end
local self = System
local warningsamount = 0
for i,v in ActiveWarningListForPlayers[UserId] do
warningsamount +=1
end
warningsamount += 1
ActiveWarningListForPlayers[UserId][warningsamount] = {
Sender = sender,
ReasonGiven = Reason
}
if warningsamount >= RequiredWarnings then
ActiveWarningListForPlayers[UserId] = {}
-- Ban the player temporary with banasync
PlayersServ:BanAsync({
UserIds = {UserId},
ApplyToUniverse = true,
Duration = 250,
DisplayReason = "You have reachen the maximum amount of warnings allowed, Your last warning is "..Reason..". If you think this is a mistake please contact our support team",
PrivateReason = "Banned for max warning by admin for "..Reason,
ExcludeAltAccounts = true
})
--warn("Player has been banned for "..reason.." by admin")
self:SaveWarnings(UserId)
if PlayersServ:GetPlayerByUserId(UserId) then
PlayersServ:GetPlayerByUserId(UserId):Kick( "A moderation action had been taken, please rejoin to review it.")
end
task.wait(2)
ActiveWarningListForPlayers[UserId] = nil
return
end
--if PlayersServ:GetPlayerByUserId(UserId) then
-- ReplicatedStorage.RemoteEvents.Warnings:FireClient(PlayersServ:GetPlayerByUserId(UserId),sender,Reason)
--end
end
function System:Warn(UserId:number,Reason:string,sender:string)
task.spawn(WarnUser,UserId,Reason,sender)
end
function System:RemoveWarn(UserId:number,warnnumber:number)
if not ActiveWarningListForPlayers[UserId][warnnumber] then
return
end
local amountofwarns = 0
for i,v in ActiveWarningListForPlayers[UserId] do
amountofwarns += 1
end
if amountofwarns == 0 then
return
end
local lastwarns = {}
local afterwarns = {}
for i,v in ActiveWarningListForPlayers[UserId] do
if i < warnnumber then
lastwarns[i] = v
end
if i > warnnumber then
afterwarns[i] = v
end
end
-- Reclassify the warn by removing 1 from every i in afterwarns then put them back
ActiveWarningListForPlayers[UserId] = {}
for i,v in lastwarns do
ActiveWarningListForPlayers[UserId][i] = v
end
for i,v in afterwarns do
ActiveWarningListForPlayers[UserId][i-1] = v
end
-- Avoid memory leaks
lastwarns = nil
afterwarns = nil
end
function System:GetWarnings(UserId:number)
-- Load from json to the table
local Success, Data = pcall(function()
return WarningDataStore:GetAsync(UserId)
end)
if Success and Data then
ActiveWarningListForPlayers[UserId] = HttpService:JSONDecode(Data)
print(Data)
end
if Success and not Data then
ActiveWarningListForPlayers[UserId] = {}
end
return ActiveWarningListForPlayers[UserId]
end
function System:SaveWarnings(UserId:number)
if not ActiveWarningListForPlayers[UserId] then
warn("Save Operation failed. Warnings wasnt on the roblox table which might cause dataloss")
return
end
-- Use json to convert into string
local Success, Error = pcall(function()
WarningDataStore:SetAsync(UserId,HttpService:JSONEncode(ActiveWarningListForPlayers[UserId]))
end)
if not Success then
warn("Failed to Save Warnings for "..UserId.." | Reason: "..Error)
end
end
function System:GetAllWarns()
return ActiveWarningListForPlayers
end
function System:GetPLayerWarns(UserId : number)
return ActiveWarningListForPlayers[UserId]
end
return System
ServerScript :
-- Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Modules
local ModerationModule = require(ReplicatedStorage.ModuleScript)
Players.PlayerAdded:Connect(function(player)
ModerationModule:GetWarnings(player.UserId)
task.wait(5)
warn("we Warn him")
ModerationModule:Warn(player.UserId, "Exploiting", "Icke")
task.wait(0.5)
warn(ModerationModule:GetAllWarns())
end)
Players.PlayerRemoving:Connect(function(player)
ModerationModule:SaveWarnings(player.UserId)
end)
Console Log :
All Date from the DataStore
00:54:36.563 [{“ReasonGiven”:“Exploiting”,“Sender”:“Icke”}] - Server - ModuleScript:140
**Data after Warn **
00:54:42.087 ▼ {
[2492399418] = ▼ {
[1] = ▼ {
[“ReasonGiven”] = “Exploiting”,
[“Sender”] = “Icke”
},
[2] = ▼ {
[“ReasonGiven”] = “Exploiting”,
[“Sender”] = “Icke”
}
}
} - Server - Script:14
I just removed the RankingSystem and triggert it from the server script. How do you want to trigger it. From Client or Server Side?
Obviously server side. I don’t want exploiters to cause mass warning in the server and having a protection allowing to warn abusers.
Then you can use this moduleScript which can be in ReplicatedStorage because its asked if we are sending it from a server.
local ValidReasons = {
"Inapropriate Audio",
"Detain Abuse",
"Admin Abuse",
"Blocking",
"Trolling",
"Btools Abuse",
"Fake Donation",
"Exploiting",
"Break Roblox TOS",
"Moderation Bypass",
}
-- Services
local DatastoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local PlayersServ = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
-- Warning Datastore
local WarningDataStore = DatastoreService:GetDataStore("Warnings")
-- NumberOfWarningsRequiredForBan
local RequiredWarnings = 4
-- Store Warnings in a roblox table
local ActiveWarningListForPlayers = {}
local System = {}
local function WarnUser(UserId:number,Reason:string,sender:string)
if not table.find(ValidReasons,Reason) then
return
end
local self = System
local warningsamount = 0
for i,v in ActiveWarningListForPlayers[UserId] do
warningsamount +=1
end
warningsamount += 1
ActiveWarningListForPlayers[UserId][warningsamount] = {
Sender = sender,
ReasonGiven = Reason
}
if warningsamount >= RequiredWarnings then
ActiveWarningListForPlayers[UserId] = {}
-- Ban the player temporary with banasync
PlayersServ:BanAsync({
UserIds = {UserId},
ApplyToUniverse = true,
Duration = 250,
DisplayReason = "You have reachen the maximum amount of warnings allowed, Your last warning is "..Reason..". If you think this is a mistake please contact our support team",
PrivateReason = "Banned for max warning by admin for "..Reason,
ExcludeAltAccounts = true
})
--warn("Player has been banned for "..reason.." by admin")
self:SaveWarnings(UserId)
if PlayersServ:GetPlayerByUserId(UserId) then
PlayersServ:GetPlayerByUserId(UserId):Kick( "A moderation action had been taken, please rejoin to review it.")
end
task.wait(2)
ActiveWarningListForPlayers[UserId] = nil
return
end
--if PlayersServ:GetPlayerByUserId(UserId) then
-- ReplicatedStorage.RemoteEvents.Warnings:FireClient(PlayersServ:GetPlayerByUserId(UserId),sender,Reason)
--end
end
function System:Warn(UserId:number,Reason:string,sender:string)
if RunService:IsClient() then warn(UserId .. " try to exploit the game") return end
task.spawn(WarnUser,UserId,Reason,sender)
end
function System:RemoveWarn(UserId:number,warnnumber:number)
if RunService:IsClient() then warn(UserId .. " try to exploit the game") return end
if not ActiveWarningListForPlayers[UserId][warnnumber] then
return
end
local amountofwarns = 0
for i,v in ActiveWarningListForPlayers[UserId] do
amountofwarns += 1
end
if amountofwarns == 0 then
return
end
local lastwarns = {}
local afterwarns = {}
for i,v in ActiveWarningListForPlayers[UserId] do
if i < warnnumber then
lastwarns[i] = v
end
if i > warnnumber then
afterwarns[i] = v
end
end
-- Reclassify the warn by removing 1 from every i in afterwarns then put them back
ActiveWarningListForPlayers[UserId] = {}
for i,v in lastwarns do
ActiveWarningListForPlayers[UserId][i] = v
end
for i,v in afterwarns do
ActiveWarningListForPlayers[UserId][i-1] = v
end
-- Avoid memory leaks
lastwarns = nil
afterwarns = nil
end
function System:GetWarnings(UserId:number)
-- Load from json to the table
if RunService:IsClient() then warn(UserId .. " try to exploit the game") return end
local Success, Data = pcall(function()
return WarningDataStore:GetAsync(UserId)
end)
if Success and Data then
ActiveWarningListForPlayers[UserId] = HttpService:JSONDecode(Data)
print(Data)
end
if Success and not Data then
ActiveWarningListForPlayers[UserId] = {}
end
return ActiveWarningListForPlayers[UserId]
end
function System:SaveWarnings(UserId:number)
if RunService:IsClient() then warn(UserId .. " try to exploit the game") return end
if not ActiveWarningListForPlayers[UserId] then
warn("Save Operation failed. Warnings wasnt on the roblox table which might cause dataloss")
return
end
-- Use json to convert into string
local Success, Error = pcall(function()
WarningDataStore:SetAsync(UserId,HttpService:JSONEncode(ActiveWarningListForPlayers[UserId]))
end)
if not Success then
warn("Failed to Save Warnings for "..UserId.." | Reason: "..Error)
end
end
function System:GetAllWarns()
return ActiveWarningListForPlayers
end
function System:GetPLayerWarns(UserId : number)
return ActiveWarningListForPlayers[UserId]
end
return System
Now you can Just add it in a ServerScript and just call the Warn function or hear on a remoteFunctions from the Server to Trigger the Function (if you have a warn GUI where the Admin can type all stuff).
Yea I also just took your original scripts and ran it in my place, and it’s working fine. I’m not sure tbh why you’re seeing issues with loading
Just realised it worked fine in game but not in studio and i dont know why