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

Keep your pcalls in the same place, but eliminate some of the dependencies in your code (such as upvalues) and make use of what pcalls return. This allows you to do error handling as well.

-- Simplified pcall example, you may not understand the direct-wrap method
local success, data = pcall(function ()
    return DataStoreWWE:GetAsync(player.userId.."-Wins")
end)

This allows you to handle cases of

  • Custom error handling
    • Data store failures
    • Actions based on returned values
  • Validation
    • Checking if returned data is nil or updated
    • Performing updates based on returns
if not success then
    warn("DataStore: Failure to perform GetAsync request:", data)
else
    if data then
        -- Overwrite current values with saved ones
    else
        -- Handle the case of no data being found
    end
end

Most importantly, before you think about what to do with pcalls, you need to rewrite your DataStore structure to use only a single DataStore with all the values to be saved in a table rather than as individual DataStores. The fundamental flaw to this structure is the way you’ve set up your DataStores; once that has been corrected, you can think about the rest of your code.

If your game isn’t public yet and no one has data saved to those DataStores yet, you can make the change fairly invisible by announcing a data wipe as the game’s development progresses further. If your game already has data in it, then you’re in a pinch. You will need to set up a system where players can merge old data over to the new system.

As far as mergers go, I suggest a one-use merger option that incorporates UpdateAsync. Here is a reference template, though you will need to pursue further discussion on Scripting Support if you want to implement this (a topic such as “How do I merge data from old DataStores to a new one?” would do - try specifying without DataStore2’s merge, otherwise most of your responses will just be “DataStore2”).

  • When the request is processed, prevent any further requests from that player altogether.

  • Perform a GetAsync request on all your DataStores.

    • You have around 6 GetAsync requests to call per player.

    • Keep in mind DataStore Limits. The lucky part is that you fall just under the limits. You’ll need about 7 requests per player - 6 to get old data, 1 for the UpdateAsync call.

  • Increment player’s stats accordingly. Do not set, as this is a merger situation.

    • A note: if a player has nil data for all 6 of those GetAsync requests, drop the request. You can use an eighth GetAsync request at the start to check if players already have data saved, but this is pushing the limits literally.

    • This harkens back to my cache strategy where you don’t use values directly under the player as data values, only as display values. Actual data should be in values under ServerStorage or cached in a session data tracking ModuleScript.

  • Call RemoveAsync on all those 6 DataStores to clear the player’s data from the old stores.

    • Again, lucky you fall under limits. You need 7 write requests - 6 for RemoveAsync, 1 for UpdateAsync when the merge completes.
  • When the merge is complete, push the combined data to UpdateAsync to update player data. Make sure the new data is in a table format. After that, you should be good.

The above is my go-to, but obviously it’s not something that should be followed down to the word. Work around your inefficiency as needed.

Why would i want to remove the data that already exists? Also would it look like this for the pcall?
Also would remove the these?

local player_data,player_data1,player_dataElim,player_dataXP
	local weaponsData 
	local equippedData
 local success, data = pcall(function ()
    return DataStoreWWE:GetAsync(player.userId.."-Wins")
    return DataStoreBuxs:GetAsync(player.userId.."-Buxs")
    return DataStoreElim:GetAsync(player.UserId.."-Kills")
    return DataStoreXP:GetAsync(player.UserId.."-XP")
  end)
end)
     if not success then 
        warn("DataStore: Failure to perform GetAsync request:", data)
else
if data then -- Would i do SetAsync then? or Get Async
    -- Overwrite current values with saved ones
else  -- Would i use SetAsync instead of doing the default values?
    DataStoreWWE:SetAsync(player.UserId..("-Buxs", player.leaderstats.Buxs.Value = 0)
end
end

Read my post again and carefully.

No, because the call would terminate at the first return value and return only the data retrieved from DataStoreWWE. Each DataStore needs to be wrapped in a pcall. What I provided is sample code, so you shouldn’t try bruteforcing it into your current code by copy-pasting it.

As well, again, the fundamental flaw in your DataStore system is that you’re using multiple DataStores.

You can’t do this in a SetAsync call. You can only pass the value that’s to be saved to the key. This would throw an error as well due to the way you’ve written it, which is incorrect.

SetAsync(player.UserId.."-Buxs", 0)

Im Confused, would it work like this:

local success, data = pcall(function()
return DataStoreWWE:GetAsync(player.UserId.."Wins")


end)
local success1, data = pcall(function()
return DataStoreElim:GetAsync(player.UserId.."Kills")
end)

but since your saying at max i should have 2 DataStores, Could it be like this?

local DataStore = game:GetService("DataStoreService") -- This is for making it shorter (obviously) 

local StatsStore = DataStore:GetDataStore('MainStats')
local EquippedData = DataStore:GetDataStore('Equipped')

then im having trouble with this part: How Could i make all the local player_data,player_data1, Ect. to local Player_data for all the MainStats?

local player_data,player_data1,player_data2,player_data3
	local weaponsData 
	local equippedData
	
	pcall(function()
		player_data = StatsStore:GetAsync(player.UserId.."-Wins")
		player_data1 = StatsStore:GetAsync(player.UserId.."-Buxs")-- Ex. iSkeptical,22939,-Buxs
		player_data2 = StatsStore:GetAsync(player.UserId.."-Kills")
		player_data3 = StatsStore:GetAsync(player.UserId.."-XP")
	  end)
	pcall(function()
		weaponsData = OwnedEquippedData:GetAsync(player.UserId.."-Weps")
	  end)
	pcall(function()
		equippedData = OwnedEquippedData: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_data2
		xp.Value = player_data3
		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 you’re only using normal data stores, then 1 data store should be enough.

Realistically you only need multiple datastores if you’re either over the limit of how much data you can store (which almost never happens), or you’re using OrderedDataStores.

You can store all the players info in a table, without having to use multiple keys in a datastore or using multiple datastores.

local DataStoreService = game:GetService"DataStoreService"
local MainDataStore = DataStoreService:GetDataStore"Main"

Using multiple keys is essentially as bad as using multiple datastores.

local success,playerdata = pcall(MainDataStore.GetAsync,MainDataStore,player.UserId)
if success then
    playerdata = playerdata or {--[[include the defaults here]]}
    --do what you want with the data
else
    --data failed to load, might want to do something (ex: notify user, stop data from saving)
end
1 Like

So i could use 2 keys then. One for the stats and for equipped and weapons owns.

I dont understand the (MainDataStore.GetAsync,MainDataStore,player.UserId)
what does that even do? Is it the same as the local StatsStore or is it what its suppose to be?

You’re missing the point.

Instead of using multiple keys, simply use one, this way you’re only using one GetAsync call instead of multiple. (you aren’t using OrderedDataStores, so its pointless to use multiple keys)

I used a different name for the datastore.

It gets the data from the datastore associated with the UserId. (wrapped in pcall)

If you don’t understand why it works, here is an explanation.

pcall accepts multiple arguments, the first argument is the function, and the others are the arguments to be passed onto that function.
ex:

pcall(print,5,2)
--> 5   2

So MainDataStore, and the player’s ID are passed through to MainDataStore.GetAsync

  • MainDataStore is passed to tell it to get data from that DataStore
  • UserId is passed to tell what key to get info from

Without pcall

MainDataStore.GetAsync(MainDataStore,player.UserId)

This is the same thing as

MainDataStore:GetAsync(player.UserId)

This simply avoids creating a pointless anonymous function.

1 Like