Hey Guys, I have decided just to make my own datastore, but I am encountering an error I don’t quite understand. My Code keeps Erroring out(through a Pcall Function because) with Unable to cast value to function
Here is my code:
Code
local DataStoreService = game:GetService("DataStoreService")
local Bans = DataStoreService:GetDataStore("BanDataStore")
local playerData = DataStoreService:GetDataStore(game.ServerStorage:WaitForChild("datastore").DatastoreName.Value)
game.Players.PlayerAdded:Connect(function(player)
local key = player.UserId.."-"..player.Name
local DataGot
local success, current = pcall(function()
DataGot = playerData:GetAsync(key)
return DataGot
end)
if success then
print("Stats:", DataGot)
if DataGot == nil then
for i, stat in pairs(player.leaderstats:GetChildren())do
stat.Value = 100
end
return
else
for i, stat in pairs(DataGot)do
print(stat.Value)
end
end
end
local success, current = pcall(function()
local banned = Bans:GetAsync(key)
if banned == false then
return
elseif banned == true then
player:Kick("You have been Banned!")
end
end)
if success then
print("Player is not Banned")
end
end)
game.ReplicatedStorage.Events.LoadLeaderstats.OnServerEvent:Connect(function(player,stat,value)
game.ReplicatedStorage.Events.LoadLeaderstats:FireAllClients(player,stat,value)
end)
game.Players.PlayerRemoving:Connect(function(player)
local sats = player.leaderstats:GetChildren()
local key = player.UserId.."-"..player.Name
for i, stat in pairs(sats)do
local stater = stats[stat.Name]
stater = stat.Value
print(stater)
end
for key, value in pairs(stats) do
print(key, value)
end
local success, update = pcall(function()
return playerData:UpdateAsync(key, stats) --This is Causing the Error
end)
if success then
print(player.Name.." Data Is Saved")
else
print(player.Name .. ": Error! Data was not Saved")
warn(update) --This is what is sending the Error
end
end)
Pretty much, I am trying to save a table that can be expanded or shortened at anytime.
If you Guys could it help, It would mean a lot!
First, the code is extremely messy, you misspelled “stats” and put “sats” instead.
Instead of your current method, why not try something like this;
--player removing
local DataObject = player.leaderstats -- example
local Data = { }
for i,v in pairs(DataObject:GetChildren()) do
Data[v.Name] = Data.Value
end
--save data here , simply saving the table
Your current method doesn’t really make a ton of sense, try something like what I have just listed.
So I added your code into a respected place, and it is still throwing the error.
Also sorry it is Messy as I usually code and then after making sure everything works, I clean it up.
Also I misspelled stats to sats just for a quick filler until I clean it up, I would of changed it to leaderStats
Here is the Changed Remove Function:
game.Players.PlayerRemoving:Connect(function(player)
local Key = player.UserId .."-".. player.Name
local DataObject = player.leaderstats
local Data = {}
for i,v in pairs(DataObject:GetChildren()) do
Data[v.Name] = Data.Value
end
local success, err = pcall(function()
return playerData:UpdateAsync(Key, Data)
end)
if success then
print(player.Name.." Data Is Saved")
else
print(player.Name .. ": Error! Data was not Saved")
warn(err)
end
end)
You understand you cannot return a datastore being saved, instead, just return true. This is mainly where your error begins, sorry I didn’t make this clear earlier.
btw, this isn’t how you should be using pcall.
pcall returns two things, whether or not it succeeded and the result from the function.
if it’s a success, the result is what was returned from the function, and if it isn’t a success, the result is an error.
so that section of your code can be changed to this:
local success, result = pcall(function()
return playerData:GetAsync(key)
end)
if success then
-- use result as you would with "DataGot"
end
If you want a separate thing solely for error, you might want to use xpcall instead.
UpdateAsync accepts two arguments; one is the key you want to save data to, and the second is a function that has its own parameter. This parameter contains the old data from the last previous save. What you want to do is to update your data inside this function and return the updated data.
local Data = -- your data
playerData:UpdateAsync(Key, function(OldData)
if not OldData or OldData.Id == Data.Id then
Data.Id = Data.Id + 1
return Data
else
return nil
end
end)
The reason why you would want the OldData is so you don’t accidently save something you don’t want to, such as Default data received due to a GetAsync failure. You don’t seem to save “Id” or what “version” you are saving, so it’s redundant using UpdateAsync in this case, though it is certainly more effective at saving data compared to SetAsync.
When you call Data[v.Name] it automatically puts it in the table, as you’re setting what that table is called and what it will hold in the table. It will work, and I currently use this method constantly for my data saving and loading. I do not see the point of using a method like that, it doesn’t seem too different, everyone has a different coding style, and appreciation for coding, but please do not claim something does not work when it does.
Correct me if I’m wrong, but I could’ve sworn that if you save data like that, it would error once you load it. But, you learn something every day! (oh no, I swore, don’t tell my teacher)
I did not wrap it in pcalls, which is usually crucial for DataStore calls. So yes, you would need pcalls for sure.
@VegetationBush as long as you have pcalls and are calling these DataStore calls periodically you should be good It won’t error on GetAsync, don’t worry.
pcall is basically a protected calling method. It allows you to safely run something that might have an error, and then lets you handle the error if there is one.
The reason they’re needed when using Datastore is because it makes an http request, which may fail, and you don’t want to cause an error in your script because of this.
however, I prefer xpcall because it’s easier to handle the error and the error is seperate from the returned result. here’s an example:
local success, result = xpcall(function()
return 4 -- just an example
end, function(err)
print(debug.traceback(err)) -- gets the stack trace
end)
now, it prints the error + traceback if it fails. then success would be false and result would be nil. However, if it’s a success, success would be true and result would be 4 (which is what happens in this example)
So I inputed your code in and tested it and tried debugging and all and it won’t even run through the function inside the function for UpdateAsync.
Here is the code:
game.Players.PlayerRemoving:Connect(function(player)
local Key = player.UserId .."-".. player.Name
local Data = player.leaderstats:GetChildren()
local success, result = pcall(function()
playerData:UpdateAsync(Key, function(OldData)
if not OldData or OldData.Id == Data.Id then
Data.Id = Data.Id + 1
print("Saving")
return Data
else
print("Data is the Same ID")
return OldData
end
end)
end)
if success then
print(player.Name.. " Player Data has been Saved")
else
print(player.Name " Player data has not been Saved")
warn("Error Code: "..result )
end
end)
A little late, but the problem may be that it is not saving in time, which in that case you need to consider adding a BindToClose to determine when the server shuts down, disconnect the PlayerRemoving event, loop through the remaining players list and save data for each player in a seperate thread using coroutines.
local Players = game:GetService("Players")
local Removing(Player)
local Key = player.UserId .."-".. player.Name
local Data = player.leaderstats:GetChildren()
local success, result = pcall(function()
playerData:UpdateAsync(Key, function(OldData)
if not OldData or OldData.Id == Data.Id then
Data.Id = Data.Id + 1
print("Saving")
return Data
else
print("Data is the Same ID")
return OldData
end
end)
end)
if success then
print(player.Name.. " Player Data has been Saved")
else
print(player.Name " Player data has not been Saved")
warn("Error Code: "..result )
end
end
local Conn = Players.PlayerAdded:Connect(Removing)
game:BindToClose(function()
Conn:Disconnect()
for _, Player in pairs(Players:GetPlayers()) do
coroutine.resume(coroutine.create(function()
Removing(Player)
end))
end
if game:GetService("RunService"):IsStudio() then
wait(3)
else
wait(30)
end
end)