XP System DataStore

My xp system shows up in the leaderstats but when I change the xp for me to level up nothing happens.

My code is as follows:

1 Like

Unsure if it’ll apply to you but I recommend making it Case-Sensitive.

When grabbing the data from Line 16, Line 20 and Line 24, I see that you put, “userId”

Maybe try changing it to “UserId” since that’s the Case-Sensitive version in the Player Properties?

Edit: Keep in mind, there are other lines where the same may apply such as at the bottom of the script.

2 Likes

If you ever need to find out why a certain piece of code is not running and console is silent, then either step through the code using debugging tools or (if you’re lazy like me) just shove print statements into your code and look at where they stop showing up.

10 Likes

To clarify, Roblox used to use ‘camelCase’ for method property names, but it now uses ‘PascalCase’. camelCase names are simply for legacy support and I’m pretty sure they just redirect to the PascalCase version – that being said, you should always be using PascalCase now.


By looking at your code, does the XP value even change on the server? Be aware that, due to FE, any changes made from a client will not be replicated to the server aside from a few exceptions.

I recommend you use the printing debugging technique and just print everything – make sure the Changed event is firing, check what the conditions are, etc.

Also, in your if statement, you aren’t using the variables for the xp objects, which is generally considered inefficient and bad practice. You don’t need to wait for the xp objects again – you already have them saved to variables.

2 Likes

Roblox still uses camelCase in properties of game.Players.LocalPlayer apparently. Maybe they use camelCase in some areas and PascalCase in others?

Are you trying to change the colour level inside of studio test? If so, studio closes before SetAsync can fire meaning that your data wont save unless you have a BindToClose in your game script.

However, separate issue BindToClose freezes studio currently and I’m not sure whether it saves or not.

This means that you are going to either have to go into a game and use the devconsole to increase your XP or have a button to press to save your data before you press stop testing.

If I were you, I’d have a table for all of your data and make the key the UserId. What you’re doing is going to take up a lot of DataStore requests in an actual server with a decent amount of players. Also I recommend wrapping your save and load functions into a pcall loop incase it fails.

Here is how I create a data template

function DataTemplate()
    local temp = {
        
        Coins = 0,
        
        EquipGun = "Default",
        EquipKnife = "Default",
        
        OwnsPremium = false,
        OwnsRadio = false,
        
        Level = 1,
        XP = 0,
        NeedXP = 100,
        
        Wins = 0,
        Kills = 0,
        
        Skins = {
            
        }
        
    }
    return temp
end

Now I recommend a pcall loop to make sure it gets saved/loaded.

Here is some code taken directly from an old game of mine. Not the prettiest code, but it gets the job done.

function SaveData(Plr)
    
    if not DataIsLoaded[Plr.Name] then
        warn(Plr.Name .. " | Data was not saved as it was not loaded")
        return
    end
    local tempD = DataTemplate()
    
    local data = GetData(Plr.Name)
    if data then
    
        tempD["Coins"] = data.Currencies.Coins.Value
        
        tempD["EquipGun"] = data.Equips.Gun.Value
        tempD["EquipKnife"] = data.Equips.Knife.Value
        
        tempD["Level"] = data.LevelData.Level.Value
        tempD["XP"] = data.LevelData.CurrentXP.Value
        tempD["NeedXP"] = data.LevelData.NeededXP.Value
        
        tempD["OwnsPremium"] = data.Gamepasses.Premium.Value
        tempD["OwnsRadio"] = data.Gamepasses.Radio.Value
        
        tempD["Wins"] = data.Totals.Wins.Value
        tempD["Kills"] = data.Totals.Kills.Value
        
        for i,v in pairs(data.Skins:GetChildren()) do
            local skint = SkinTempl()
            skint["Skin"] = v.Skin.Value
            skint["Wep"] = v.Wep.Value
            skint["Copies"] = v.Copies.Value
            table.insert(tempD["Skins"],#tempD["Skins"]+1,skint)
        end
        
        local Pcalled = false
        repeat
            Pcalled = pcall(function()
                PlayerDS:SetAsync(Plr.UserId .. "_data2",tempD)
            end)
            if not Pcalled then
                warn(Plr.Name .. " | Data failed to save, retrying")
            end
            wait(1)
        until Pcalled
        warn(Plr.Name .. " | Data successfully saved")
    end
    
end

I personally recomend you redo your method of saving and getting data. You aren’t going to get far with it.

4 Likes

I’m not one for trying to lay it on thick when it comes to replying to issues, but this is… quite the mess. Three data stores for three separate stats? That’s a yikes from me.

It looks like your save system in general needs to be completely redone, sorry to spill the beans. There’s a couple of inherent issues with your current script:

  • It would really help if you just index the DataStoreService as a variable so that you don’t have to keep writing game:GetService("DataStoreService").
  • The three data stores that I mentioned earlier, typically isn’t good practice - try using scopes or saving your data in raw/JSON-encoded tables to a single data store.
  • Your SaveData() function can be changed since you’ve already pre-defined your DataStores. You don’t need the save function to go back and get the DataStoreService, get the DataStore then use SetAsync.
  • I generally do not recommend using SetAsync unless you need to force data to stick, typically after a player leaves or purchases something that affects stats. Try switching to UpdateAsync.
  • There’s no failure redundancy or retries. If someone’s data fails to load for whatever reason, you set their stats back to base values. When they leave, their data gets wiped and replaced with stats they’ve earned from the game session with only base stats. People probably won’t come back after that.
  • Why are you using tostring on a player’s UserId?
  • Saving data every time a stat changes is an excellent way to exhaust your allowed budget of DataStore requests per minute, resulting in extremely faulty data management. Things won’t save often and they won’t for everyone.
  • Keep your PlayerRemoving function out of PlayerAdded.

Using this script won’t get you places. It’d be hurtful to retain this knowledge as you gradually improve your area of scripting and it wouldn’t do your player base any good. If you need support on making a new data store script, there’s the developer hub, tutorials and support categories available for your usage - people would be happy to point you in the right direction.

9 Likes

Thanks for all your help! I ended up rewriting the entire system and it is working now.

3 Likes

It’s definitely not the standard. I know of no Player properties that are exclusively camelCase. It would be incredibly stupid of the Roblox engineers to use an inconsistent naming format – like I said, camelCase is for legacy support only now.

1 Like