Completely new to DataStores, need help

Heyo, so I’ve been trying to construct a DataStore for a little bit but I’ve been having some trouble.

Here’s what I’m trying to create:

I figure I need to store two arrays, one for abilities and one for money (money array isn’t exactly necessary but I plan on possibly adding another currency later). I’ve been consulting this so far and I’m still lost.

My code is likely nowhere near right, but here’s what I tried:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

--[[Set PowerUp]]--
local SetPowerUpEvent = ReplicatedStorage.SetPowerUp

local function ChangePowerUp(player, power, value) 
	pcall(function()
		playerPowers:UpdateAsync(player, {power, value}) --this is where i'd reference the specific powerup in the ability array and change the boolean
	end)
	if(value == true) then
		print(player.Name .. " now has the " .. power)
	else
		print(player.Name .. " no longer has the " .. power)
	end
end

SetPowerUpEvent.OnServerEvent:Connect(ChangePowerUp)

--[[Datastores]]--
local function onPlayerAdded(player)
	local scope = player.UserId
	
	pcall(function()
		playerPowers = DataStoreService:GetDataStore("Data", scope) --setting up the datastore?
		if(playerPowers == nil) then
			playerData = {} --things within the datastore
			playerData.Abilities = {}
			playerData.Abilities.DoubleJump = false
			playerData.Abilities.Fling = false
			playerData.Abilities.FeatherFall = false
			playerData.Abilities.Charge = false
			playerData.Moolah = 0
		end
		print(playerData.Abilities.DoubleJump) --trying to check the value
	end)
end
 
Players.PlayerAdded:Connect(onPlayerAdded)

Basically, I need help setting up the DataStore with arrays and then I need help with how to properly update the values within the arrays within it.

3 Likes

I see a lot of weird stuff going on here. Let’s get started!


Firstly you want to figure out exactly what your data is going to be (and look like) before you do anything. In your situation, it’d probably look like this:

{ -- imagine this main table is your DataStore
    Abilities = {Sprint = false, DoubleJump = false},
    Money = {Cash = 0}
}

Next you want to apply this in all of your code. To modify Abilities or Money, you will need to do DataStore:SetAsync("Name", Value). To apply this to your ChangePowerUp function:

local function ChangePowerUp(Player, PowerName, NewValue)
    local DataStore = DataStoreService:GetDataStore("Data", Player.UserId) -- gets the player's DataStore
    local CurrentAbilities = DataStore:GetAsync("Abilities") -- gets their abilities

    pcall(function()
        CurrentAbilities[PowerName] = NewValue -- sets their ability
        DataStore:SetAsync("Abilities", CurrentAbilities) -- uploads their abilities to the DataStore
    end)
end

As you can see, it’s a little different. In comparison to this code, let’s see what you did wrong:

  • playerPowers is a global that updates with every player that joins, meaning if Player2 joins after Player1, the script will always use Player2’s DataStore, no matter if your code is meant to be relevant to Player1.
  • Player objects cannot be used as keys for any DataStore things
  • UpdateAsync takes a function as an argument, not a value (you’re probably confusing it with SetAsync)
  • The entire structure is wrong based on what you seem to want your DataStore to work like

Don’t worry about making mistakes - everybody does, you’re not stupid or anything. Hopefully this information helps :wink:


So, this new code I provided seems good, but it can end up being lots of DataStore requests (you can only have NumPlayers * 10 per minute). You don’t want to be failing to save player data because you’ve reached the limit. That could cause serious issues.

Part of the way to fixing this is by noticing that some of these DataStore requests are redundant. For example, the first two in that code can be put into a globally-accessible table connected to their players. This will reduce the number of requests that need to be made, making your DataStore system more viable:

local PlayerAbilities = {} -- global table for abilities
local PlayerDataStores = {} -- global table for DataStores

games.Players.PlayerAdded:Connect(function(Player)
    PlayerDataStores[Player] = DataStoreService:GetDataStore("Data", Player.UserId) -- save their DataStore
    PlayerAbilities[Player] = PlayerDataStores[Player]:GetAsync("Abilities") -- save their abilities
end

Now a player’s DataStores and abilities can be accessed throughout the script without making any DataStore requests (e.g. PlayerAbilities[Player]). This will also make your code run faster, as it won’t have to wait for the DataStore requests to do their thing.


A big issue I see here is just the general design - while it may seem a little complex at first, I believe the following solution is simpler & easier to use (in the long run) than the way you are using now. It would also potentially lower the number of DataStore requests that need to be made.

Currently the way you are doing it is a bit weird. You’re making lots of different DataStore requests (first you get the DataStore for every player, then for every player you get a single piece of data using their data store, then you get their second pieces, then their third pieces… etc., then you save their pieces individually).

That’s no good. Why not instead just make it so every player has their data be a single table? Something like this maybe?

local DefaultData = {
    Abilities = {DoubleJump = false, Sprint = false}
    Money = {Cash = 0}
}

And then, you’d only to do one get request and one set request to retrieve & update a player’s data:

local DataStore = DataStoreService:GetDataStore("Data") -- get a 'global' DataStore that has the data for every player in it

game.Players.PlayerAdded:Connect(function(Player)
    local Key = Player.UserId -- cause it gets used more than once
    local Data = Player:GetAsync(Key)

    if Data == nil then -- if the player has no existing data
        Data = DefaultData -- the table used previously
    end

    Data.Abilities.DoubleJump = true -- change the double jump value
    DataStore:SetAsync(Key, Data) -- save their data in one go
end)

As you can see, the way of doing things seems a bit simpler. Saving just requires you to send the data with the player’s key (in this case, their UserId) – the same goes for getting data, too.

Modifying just requires you to change the table, and not touch the DataStore at all (and if you did want to, you saw from the previous sentence how easy it is).


Hopefully this was helpful :slight_smile:

12 Likes

I honestly had no clue as to what I was doing, so I appreciate this very much! I have to sleep for now, but I’ll @ you (if you’re fine with that) with any questions when I try to make the appropriate changes tomorrow.

1 Like

Hi again @EmeraldSlash

So I’ve got one last question, hopefully you don’t mind the @. :pray:

Is there a proper way to have a failsafe if you somehow do exceed the data limit?

1 Like

Use pcalls - they’ll catch any errors.

1 Like