It works fine as of for now but sometimes i loose Data on one Value only(1 out of 10000 tries)
I know theres DataStores2 but i like DataStores cause im more familiar.
--- Get Services Section ---
--- ---
local DataStoreWWE = game:GetService("DataStoreService"):GetDataStore("WinsBuxsDS")
local DataStoreBuxs = game:GetService("DataStoreService"):GetDataStore("Buxs")
local DataStoreElim = game:GetService("DataStoreService"):GetDataStore("Kills")
local DataStoreXP = game:GetService("DataStoreService"):GetDataStore("XP")
local DataStoreEquipped = game:GetService("DataStoreService"):GetDataStore("Equipped")
--- ---
--- Default Values ---
--- ---
local defaultwins = 0
local defaultbuxs = 0
local defaultelim = 0
local defaultxp = 0
local playersLeft = 0
local elimBonus = 10
local xpBonus = 10
local elim = 1
--- ---
--- Everything Else. ---
--- ---
game.Players.PlayerAdded:Connect(function(player)
playersLeft = playersLeft + 1
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Value = 0
wins.Parent = leaderstats
local buxs = Instance.new("IntValue")
buxs.Name = "Buxs"
buxs.Value = 0
buxs.Parent = leaderstats
local kills = Instance.new("IntValue")
kills.Name = "Kills"
kills.Value = 0
kills.Parent = leaderstats
local xp = Instance.new("IntValue")
xp.Name = "XP"
xp.Value = 0
xp.Parent = leaderstats
--local lvl = Instance.new("IntValue")
--lvl.Name = "Level"
--lvl.Value = 0
--xp.Parent = player
--lvl.Parent = player
local IntroTag = Instance.new('BoolValue')
IntroTag.Name = 'IntroTag'
IntroTag.Value = true
IntroTag.Parent = player
local AFK = Instance.new('BoolValue')
AFK.Name = 'AFK'
AFK.Value = false
AFK.Parent = player
local playerData = Instance.new("Folder")
playerData.Name = player.Name
playerData.Parent = game.ServerStorage.PlayerData
local equipped = Instance.new("StringValue")
equipped.Name = "Equipped"
equipped.Parent = playerData
local inventory = Instance.new("Folder")
inventory.Name = "Inventory"
inventory.Parent = playerData
player.CharacterAdded:Connect(function(character)
character.Humanoid.WalkSpeed = 16
character.Humanoid.Died:Connect(function()
local tag = character.Humanoid:FindFirstChild("creator")
if tag ~= nil then
local player = tag.Value
local leaderstatsElim = player:WaitForChild("leaderstats")
leaderstatsElim.Buxs.Value = leaderstatsElim.Buxs.Value + elimBonus
leaderstatsElim.Kills.Value = leaderstatsElim.Kills.Value + elim
leaderstatsElim.XP.Value = leaderstatsElim.XP.Value + xpBonus
end
--when ever someone dies this event will run
--if character.Humanoid and character.Humanoid:FindFirstChild("creator") then
--game.ReplicatedStorage.Status.Value = tostring(character.Humanoid.creator.Value.." KILLED "..player.Name)
--wait(0.6)
--end
if character:FindFirstChild("GameTag") then
character.GameTag:Remove()
end
wait(3)
player:LoadCharacter()
end)
end)
--[Loads and Saves the DataStore Values]--
local player_data,player_data1,player_dataElim,player_dataXP
local weaponsData
local equippedData
pcall(function()
player_data = DataStoreWWE:GetAsync(player.UserId.."-Wins")
player_data1 = DataStoreBuxs:GetAsync(player.UserId.."-Buxs")-- Ex. iSkeptical,22939,-Buxs
player_dataElim = DataStoreElim:GetAsync(player.UserId.."-Kills")
player_dataXP = DataStoreXP:GetAsync(player.UserId.."-XP")
end)
pcall(function()
weaponsData = DataStoreWWE:GetAsync(player.UserId.."-Weps")
end)
pcall(function()
equippedData = DataStoreEquipped:GetAsync(player.UserId.."-EquippedValue")
end)
if player_data ~= nil then
--player has saved data, load it in
wins.Value = player_data
buxs.Value = player_data1
kills.Value = player_dataElim
xp.Value = player_dataXP
print("loaded data for "..player.Name.."!")
else
--[New Player]--
wins.Value = defaultwins
buxs.Value = defaultbuxs
kills.Value = defaultelim
xp.Value = defaultxp
print("loading data baseline stats for "..player.Name.."")
end
if weaponsData then
for _, weapon in pairs (weaponsData) do
if game.ServerStorage.Items:FindFirstChild(weapon)then
local weaponClone = game.ServerStorage.Items[weapon]:Clone()
weaponClone.Parent = inventory
print(weapon.." loaded in!")
end
end
if equippedData then
equipped.Value = equippedData
player:WaitForChild("PlayerGui")
game.ReplicatedStorage.SendEquipped:FireClient(player,equippedData)
end
else
print("No weapon data")
end
end)
local bindableEvent = Instance.new("BindableEvent")
game.Players.PlayerRemoving:Connect(function(player)
pcall(function()
DataStoreWWE:SetAsync(player.UserId.."-Wins",player.leaderstats.Wins.Value)
DataStoreBuxs:SetAsync(player.UserId.."-Buxs",player.leaderstats.Buxs.Value)
DataStoreElim:SetAsync(player.UserId.."-Kills",player.leaderstats.Kills.Value)
DataStoreXP:SetAsync(player.UserId.."-XP",player.leaderstats.XP.Value)
print("Saved DS for "..player.Name.." ")
end)
pcall(function()
local weapons = game.ServerStorage.PlayerData[player.Name].Inventory:GetChildren()
local weaponsTable = {}
for _, v in pairs (weapons) do
table.insert(weaponsTable,v.Name)
end
DataStoreWWE:SetAsync(player.UserId.."-Weps",weaponsTable)
if game.ServerStorage.PlayerData[player.Name].Equipped.Value ~= nil then
local equippedVal = game.ServerStorage.PlayerData[player.Name].Equipped
DataStoreEquipped:SetAsync(player.UserId.."-EquippedValue",equippedVal.Value)
end
end)
playersLeft = playersLeft - 1
bindableEvent:Fire()
end)
game:BindToClose(function()
--this will be triggered upon shutdown.
while playersLeft > 0 do
bindableEvent.Event:Wait()
end
end)
Personally I like DataStore2 because it does most of this for you + there is no data loss really. Even though you are more familiar with DataStores I would still recommend switching. DataStore2 takes care of so much that it’s almost like you’re fluent from the get-go because it’s so simple to use and the amount of configuration is minimal.
You’re going to want to use Datastore2 instead of the regular datastores, especially if you’re opting for more than one datastore.
Why? because stuff like game:BindToClose() and PlayerRemoving are automatically done for you so that you won’t have to repetitively reuse the same code.
Also, throttling is almost completely non-existent, and since the new refactor came out, you can basically change whatever you don’t like about it without having to go through a hell of a mess. Plus, the API is easier to use once you actually learn it.
It took me around 30 minutes of trial and error (plus the documentation) to get the hang of it. You can check my first implementation of Datastore 2 via my game Site: B on my profile
Technically yes, since Datastore2 is practically using the regular datastores with a bunch of useful checks.
However, since the datastore :Get() and :Save() is easier to use, it means it creates a fixed name. For example, if you had a regular datastore "Currencies " … Player.UserId and if you did the same for the Datastore2, it would not work because the only argument is the datastore name and player itself, in the module it would automatically concatenate these values.
tl;dr
(not really, but if you’re determined to migrate you’re going to have to carefully edit Datastore2 in order to fit it)
I actually have a module that I’ve been told is nicer than DataStore2. It loads up faster, has full documentation, and has an API closer to DataStoreService. I have two examples setup on the Github page demonstrating how to use it without losing data. I’ve never had data loss either, even after performing three consecutive soft shutdowns on a game with around 200 players on. @oniich_n is using it in Astral Hearts in replacement of DataStore2 now, he can (hopefully) vouch for its reliability.
I’m also willing to help you convert your current code to use it, and I’m also happy to add features if they’re requested enough.
As for other things, I don’t personally like making more than one DataStore. I prefer to have at most two, plus however many the admin commands I have might add.
It’s pretty solid so far! The only issues I’m having involving code stem from my own incompetence, but RbxWeb has been great thus far. Only thing missing is a reliable backup and rollback feature, although I’m sure those can be implemented too.
@OutlookG@oniich_n@howmanysmaII@SmartNode@Intended_Pun@https_KingPie Is there a way to set up my current datastores script to make it error if someones data is lost?
Another way to word this: Is it possible to detect if someones data is overwritten by the defaults/ overwritten?
There is no way for you to tell if someone’s data was lost or not, the only other method that I can give you is via DataStore2’s method of backups, ensuring that you will always have an older version of the data available even if the latest copy is lost.
I suppose you could always set up two datastores using DataStore2 and have them save the same value. Then you can always compare between the two to see if they are equal to one another. However, this wouldn’t necessarily help you if there’s an external condition that caused the data to be lost since that might affect both values. However, on the off chance that there’s simply an error with one of the values saving - then the other would serve as both an indicator of data loss and also an accurate record of the player’s data since you’re saving it twice. Datastore2 is very reliable though, but if you want to be cautious this might be the way to do it.
afaik Datastore2 basically ends up creating endless backups and that was the whole controversy behind it. While there is technically no limit, if everybody did this, i’d expect some limitations to start applying. Be careful aobut this.
Correct me if im wrong if DataStores2 allows limited backups now?
I Updated this to help me see when a players data gets lost (it errors also when new data gets created) and instead of using the default values, should i just do wins.Value = wins.Value ?
else
error(player.Name.." S' data is maybe being overwritten!")
--[New Player]--
wins.Value = defaultwins
buxs.Value = defaultbuxs
kills.Value = defaultelim
xp.Value = defaultxp
print("loading data baseline stats for "..player.Name.."")
end
I’m just waiting for the day when Roblox can no longer afford servers to keep up with our poor uses of DataStores and heavier limits get imposed. Nothing against OP or innovations on the vanilla DataStore process, but I can just smell those limitations coming and it’s not a pleasant smell.
Nowadays, when you ask for feedback on DataStore uses, the majority of the responses you get back will be to try DataStore2. I mean go ahead but if you’re using DataStore2 poorly as well, it’s not going to change much for you.
A habit you and a majority of other newer or unfamiliar developers need to shake off is creating new DataStores for every key of data for a player. Not only is this a cheat out of best uses of DataStores and the learning process for them, but you’ll eventually realise how painful it is and then end up here with a thread on how to improve DataStore use.
The use of pcalls is, well, terrible. What you’ve done with them is no good. Protected calls should ideally be wrapping the actual method calls so you can receive direct errors based on a specific call, rather than a chunk of code stuffed in one. As well, you throw away the return values of pcalls for upvalues and assume truthy returns in your code, so that’s a bad plan there. I’ve put off on writing a tips thread for pcalls, but now I’m convinced that I need to write it.
What you’re doing with BindToClose; I don’t know if this is some kind of weird hack but this is not going to work. Once all the processes in BindToClose finish, your server’s gone. That also being said, I’m quite sure PlayerRemoving does not fire when BindToClose does. That means that you lose the opportunity to save player values because their instances are destroyed and thus by nature, their data (since it’s located in the player). You should look into creating a module-based cache or save your values in ServerStorage, while the values under the player only serve as displays for the leaderboard.