Does it save in the game itself, though? Because maybe then it is just a studio bug or something.
Make sure to publish the place before testing it.
I did, in game it still doesn’t save. Super weird
There might be something wrong with getting the data. In the saving, it works just fine.
Also in the data saving, remove the before, after, and data being saved print statements. They aren’t necessary anymore.
Go back up to where the data loads. This is all of that same code, just instead it has an extra print that tells you what the data is. Please show me what that says.
local Data
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
Data = DataStore:GetAsync(Player.UserId) -- Get Data
end)
if success and Data ~= nil then --// Checks if the pcall was successful and if the data is not equal to nil. If it can receive a successful call, but the user has no data, then it will still run just fine, but it will error when you try to apply the nil data to the value/property of something
print("Successfully loaded data for "..Player.Name)
print("This is the data that was loaded:", Data) --// The line I added. Tell me what this prints out
Minutes.Value = Data
else
warn(errorMessage)
end
I also moved the line Minutes.Value = Data
into that if statement. You should do the same and remove the same line a little farther down in that PlayerAdded event. It is better to have it in the if statement.
Well, load into the game again and tell me if it prints “This is the data being loaded: 0”
It should because it says that it loaded “5”, meaning it did save.
There is probably an issue with setting the value of the Minutes stat.
yet my leaderstats say 0?
Okay, so now I know it is indeed saving and loading. There is most definitely an issue with setting the value.
I think with how you are using ProfileService and the basic DataStoreService together at the same time, both are interfering with each other. What I would do is probably remove ProfileService from your code entirely. Delete the module and start a clean, server script (inside of ServerScriptService.)
I can guide you on how to. However, it is getting pretty late for me. I’m going to go to bed, but as soon as I wake up and get through my morning routine, I will help you out. If anyone else offers help before then, feel free to take their help. I don’t mind if you do.
Sounds good ill remove everything tomorrow. Let me know what you can help and ill get on!
Hey hey! I’m ready whenever you can help me guide on how to transfer everything!
It shouldn’t be very hard to do. There are quite a few major changes that need to be made, though. If you still have that DataStore script, delete it and create a new one. Also get rid of the ProfileService module since you won’t need it anymore.
A mostly ideal DataStore script would look something like this
-- >>: Variables
local Players = game:GetService("Players")
local dataStoreService = game:GetService("DataStoreService")
local httpService = game:GetService("HttpService")
local dataStore = dataStoreService:GetDataStore("DataStore")
local defaultTable = {
Cash = 0;
Gems = 0;
Wins = 0;
Deaths = 0;
EquippedWeapon = "ClassicSword";
OwnedWeapons = {
"ClassicSword";
"TimeBomb";
"RocketLauncher";
"Slingshot";
"Trowel";
};
}
-- >>: Functions
local function PlayerAdded(player: Player)
-- // Stats
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local cash = Instance.new("IntValue")
cash.Name = "Cash"
cash.Value = defaultTable.Cash
cash.Parent = leaderstats
local gems = Instance.new("IntValue")
gems.Name = "Gems"
gems.Value = defaultTable.Gems
gems.Parent = leaderstats
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Value = defaultTable.Wins
wins.Parent = leaderstats
local deaths = Instance.new("IntValue")
deaths.Name = "Deaths"
deaths.Value = defaultTable.Deaths
deaths.Parent = leaderstats
local equippedWeapon = Instance.new("StringValue")
deaths.Name = "EquippedWeapon"
deaths.Value = defaultTable.EquippedWeapon
deaths.Parent = player
local ownedWeapons = Instance.new("Folder")
ownedWeapons.Name = "OwnedWeapons"
ownedWeapons.Parent = player
--// Loading Data
local encoded
local dataTable
local success, errorMessage = pcall(function()
encoded = dataStore:GetAsync("dataTable-"..player.UserId)
end)
if success then
if encoded ~= nil then
dataTable = httpService:JSONDecode(encoded)
if dataTable ~= nil then
cash.Value = dataTable.Cash
gems.Value = dataTable.Gems
wins.Value = dataTable.Wins
deaths.Value = dataTable.Deaths
equippedWeapon.Value = dataTable.EquippedWeapon
for i, v in pairs(dataTable.OwnedWeapons) do
local stringValue = Instance.new("StringValue")
stringValue.Name = v
stringValue.Parent = ownedWeapons
end
end
end
else
if errorMessage then
warn(errorMessage, player.Name, player.UserId)
elseif encoded == nil then
warn("Data for "..player.Name.." is nil!", player.UserId)
end
end
end
local function PlayerRemoving(player: Player)
local dataTable = {
Cash = player.leaderstats.Cash.Value;
Gems = player.leaderstats.Gems.Value;
Wins = player.leaderstats.Wins.Value;
Deaths = player.leaderstats.Deaths.Value;
EquippedWeapon = player.EquippedWeapon.Value;
OwnedWeapons = {};
}
for _, v in pairs(player.EquippedWeapons:GetChildren()) do
table.insert(dataTable.OwnedWeapons, v.Name)
end
local encoded = httpService:JSONEncode(dataTable)
local success, errorMessage = pcall(function()
dataStore:SetAsync("dataTable-"..player.UserId, encoded)
end)
end
-- >>: Events
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
-- >>: Bind To Close
game:BindToClose(function()
task.wait(60)
end)
This structure is also the same that I will guide you on creating.
Alright, just deleted the Profile service scripts and make a new fresh one with the code you provided
Do not. There are a couple bugs I noticed with it, as well as you should create a new structure in general. As well as it isn’t written with the same stats you had earlier.
I will be back in a few minutes after I fix the bugs with it.
Ahh I see, ill wait for you then! I’m sorry im not the best at coding I mostly got everything from youtube
Here is an actually working version of what I originally wrote. This one still isn’t written to have the same stats and features as yours originally did, but this is just the general structure for a basic DataStore
-- >>: Variables
local Players = game:GetService("Players")
local dataStoreService = game:GetService("DataStoreService")
local httpService = game:GetService("HttpService")
local dataStore = dataStoreService:GetDataStore("DataStore")
local defaultTable = {
Cash = 0;
Gems = 0;
Wins = 0;
Deaths = 0;
EquippedWeapon = "ClassicSword";
OwnedWeapons = {
"ClassicSword";
};
} --// A default table that stores all of the default values for any stats in your game. This is used if the user has no data, or if the data fails to load/decode
-- >>: Functions
local function PlayerAdded(player: Player)
-- // Stats
local leaderstats = Instance.new("Folder") --// Creates a leaderstats folder
leaderstats.Name = "leaderstats" --// Names it
leaderstats.Parent = player --// Sets its parent
local cash = Instance.new("IntValue") --// Creates a Cash stat
cash.Name = "Cash" --// Names it
cash.Value = defaultTable.Cash --// Sets its value
cash.Parent = leaderstats --// Sets its parent
local gems = Instance.new("IntValue") --// Creates a Gems stat
gems.Name = "Gems" --// Names it
gems.Value = defaultTable.Gems --// Sets its value
gems.Parent = leaderstats --// Sets its parent
local wins = Instance.new("IntValue") --// Creates a Wins stat
wins.Name = "Wins" --// Names it
wins.Value = defaultTable.Wins --// Sets its value
wins.Parent = leaderstats --// Sets its parent
local deaths = Instance.new("IntValue") --// Creates a Deaths stat
deaths.Name = "Deaths" --// Names it
deaths.Value = defaultTable.Deaths --// Sets its value
deaths.Parent = leaderstats --// Sets its parent
local equippedWeapon = Instance.new("StringValue") --// Creates an EquippedWeapon stat
equippedWeapon.Name = "EquippedWeapon" --// Names it
equippedWeapon.Value = defaultTable.EquippedWeapon --// Sets its value
equippedWeapon.Parent = player --// Sets its parent
local ownedWeapons = Instance.new("Folder") --// Creates an OwnedWeapons folder
ownedWeapons.Name = "OwnedWeapons" --// Names it
ownedWeapons.Parent = player --// Sets its parent
--// Loading Data
local encoded --// Creates a new, empty variable that will be set in the pcall
local dataTable --// Creates a new, empty variable that will be set to the table that is decoded version of the JSONEncoded string. So instead of a string, it is an actual table
local success, errorMessage = pcall(function() --// Creates a safety net in case the API call fails so the entire script won't break
encoded = dataStore:GetAsync("dataTable-"..player.UserId) --// Gets the saved, encoded string from the player's DataStore
end)
if success then --// Only runs if it was successful
if encoded ~= nil then --// Only runs if the "encoded" variable is not equal to nil. It will only be equal to nil if something goes wrong in the pcall
dataTable = httpService:JSONDecode(encoded) --// Uses HttpService to decode the encoded version of the player's DataTable
if dataTable ~= nil then --// Only runs if the dataTable is not equal to nil. It will only be equal to nil if there is an error while decoding the encoded table.
print("Successfully loaded data table for "..player.Name, dataTable) --// Prints out that the data loading was a success, and will then proceed to set the stats with the values inside of the table
else --// Only gets ran when the dataTable variable is equal to nil, which only happens when there is an error decoding the encoded table
warn("Data table for "..player.Name.." failed to be decoded") --// Prints out that the data table could not be decoded
dataTable = defaultTable --// Sets the dataTable variable to the default data table defined at the top of the script
end
else --// Only runs when the encoded variable is equal to nil. This will only happen when the player has no saved data
warn("There is no saved data table for "..player.Name) --// Prints out that the player has no saved data
dataTable = defaultTable --// Sets the dataTable variable to the default data table defined at the top of the script
end
cash.Value = dataTable.Cash --// Sets the value of "Cash" to the value in the data table
gems.Value = dataTable.Gems --// Sets the value of "Gems" to the value in the data table
wins.Value = dataTable.Wins --// Sets the value of "Wins" to the value in the data table
deaths.Value = dataTable.Deaths --// Sets the value of "Deaths" to the value in the data table
equippedWeapon.Value = dataTable.EquippedWeapon --// Sets the value of "EquippedWeapon" to the value in the data table
for i, v in pairs(dataTable.OwnedWeapons) do --// Loops through every single item inside of the OwnedWeapons table in the data table, which will repeat the code below for every item in the table
local stringValue = Instance.new("StringValue") --// Creates a new string value
stringValue.Name = v --// Sets its name to the string in the OwnedWeapons table
stringValue.Parent = ownedWeapons --// Sets its parent to the OwnedWeapons folder inside of the player
end
else --// Only runs when the pcall is not successful. Could be a wide range of many errors
if errorMessage then --// If there is an error message, it will print it out. When a pcall is not successful, it will always have an error message anyway
warn(errorMessage, player.Name, player.UserId) --// Prints out the error message, player name, and player UserId
elseif encoded == nil then --// Only runs when the encoded value is equal to nil and the errorMessage is nil. This should never happen because there is always an error message when pcalls fail
warn("Data for "..player.Name.." is nil!", player.UserId) --// Prints that the data for the player is nil
end
end
end
local function PlayerRemoving(player: Player)
local success, errorMessage = pcall(function() --// Creates a new pcall. A safety net for if the API call fails so it won't break the entire script
local dataTable = {
Cash = player.leaderstats.Cash.Value;
Gems = player.leaderstats.Gems.Value;
Wins = player.leaderstats.Wins.Value;
Deaths = player.leaderstats.Deaths.Value;
EquippedWeapon = player.EquippedWeapon.Value;
OwnedWeapons = {};
} --// Creates a new data table that will be encoded and saved. This table includes all of the values
for _, v in pairs(player.OwnedWeapons:GetChildren()) do --// Similarly to loading the data, it will make a loop that will repeat the code below for every item. The only difference is instead of creating a StringValue inside of the OwnedWeapons folder from every string in the table, it creates a string in the table for every StringValue in the OwnedWeapons folder
table.insert(dataTable.OwnedWeapons, v.Name) --// Inserts a string with the same name for the StringValue it is currently on
end
local encoded = httpService:JSONEncode(dataTable) --// Using HttpService to encode the table, converting it into a string to be saved. You should always do this because eventually a table will get larger than four megabytes, and each DataStore can only hold four megabytes.
--// Everything done above should be in the pcall as well, not just the SetAsync() call. If there isn't a value created inside of the player when they leave, or something is missing that is required in the data table, if it errors, it will break the entire script for every player, causing every player in the server to lose their data
dataStore:SetAsync("dataTable-"..player.UserId, encoded) --// Saves
end)
if success then --// Only runs when the pcall is successful
print("Successfully saved data for "..player.Name, player.UserId) --// Prints that the data saving was a success
else --// Only runs when the pcall fails. It could fail for a wide range of reasons
warn(errorMessage) --// Prints the error message
end
end
-- >>: Events
Players.PlayerAdded:Connect(PlayerAdded) --// Runs the PlayerAdded function when a player is joining the game
Players.PlayerRemoving:Connect(PlayerRemoving) --// Runs the PlayerRemoving function when a player is leaving the game
-- >>: Bind To Close
game:BindToClose(function() --// Only runs the code inside when the last player of the server is leaving. This is so the data has enough time to save, because if the server closes before it can fully save, it will cause data loss
task.wait(60) --// Waits 60 seconds before the server closes when the last player leaves the game
end)
I wrote this just so you can get a general idea on what it should look like, and I added comments to clarify if you don’t know what something does.
Thank you I’ll take a look at it when I’m available. In the leaderstat script I had previously was there anything special that happened for the donations that I should be aware about? Like firing anything or stuff? Or is it just a leaderstat that displays how much I’ve raised / donated and saves it
It was the latter of the two. Only a leaderstat.