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