Is there a way to improve my DataStores (Im Open to DataStores2)

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.

1 Like

I mean @Kampfkarren did a good job on making DataStore2 easy to use. I didn’t understand it at first but Kampfkarren graciously explained it to me.

After getting used to it, I think I honestly prefer its setup more than vanilla DataStores. Highly recommend the module.

2 Likes

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

The only thing is, can i transfer my existing datastore data to datastores2?

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.

Github
Roblox Module

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.

4 Likes

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.

2 Likes

I would use DataStores2. AlvinBlox even made a tutorial on how to use it (as well as the guide on the devfourms.) https://www.youtube.com/watch?v=hBfMfB0BwGA

1 Like

@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.

You can’t tell if something is overwritten by defaults, however you can instead PREVENT the player’s data from being overwritten.

This was discussed during RDC in one of the breakout sessions, and is good general practice overall.

1 Like

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.

So if i check on the PlayerRemoving Section, by seeing if the get async = nil then would it not overrite data?

1 Like

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

So if you throw an error like that, the rest of the code won’t run. Your logic is solid though. Maybe swap out error for a warn instead.

1 Like

i didnt know when you did an error it would stop the script hmm.

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.

Thanks for attending my TED Talk.

1 Like

So about the pcalls, what would you suggest where i should put them?