How to use DataStore2 - Data Store caching and data loss prevention

The *deserialized[itemname] = * part, is this the value that was saved? or is it a default value?

What should it equal to give me the saved value of, say… the “Skin Tone” or the “CharName”? The skin tone is its own table of 3 numbers, and the charname is a simple string value.

The parameter of :BeforeInitalGet is what was in the data store. The return is what :Get() will get.

Alright, i think we are just miscommunicating here, i don’t think i’m making my question clear enough for you, that’s my fault

yes i understand that, the :BeforeInitialGet() receives the store, then it runs through a dictionary of all the names that exist, and it transforms that serialized number into a string for the name, in your example then you have

local itemName = InitialDictionary[id] 
deserialized[itemName] = true 

so it adds to this new dictionary a name and a value of the name, but that deserialized[itemName] = true sends through the value as true, overriding the value that you may have wanted to be saved when you left before, say it was false… so when you rejoin the value is now true. And then it sends it through the return deserialized and the :Get() receives it. But, with all the experimenting i’m doing, i can not seem to get the value that was originally saved.

For example i have a name value- [“CharacterName”] = “Bob the builder”- and if i wasn’t doing the serialization it does infact save, but when i serialize it, it saves the [“CharacterName”] as a number, and then when i deserialize it when i rejoin, it isnt recieving “Bob the builder”.

Originally when i had the deserialized[itemName] = true, it was passing [“CharacterName”] = true, if i dont have that being placed into the deserialized table then Get() wont receive anything.

What would help me is if you explained how your example of your True/false inventory serialization/deserialization indexed the saved value of the “Cool Sword” and delivered it into the value, it might help me understand how to fix my issue.

I will reiterate, i understand :BeforeInitialGet() runs the first time that the :Get() is called and the :BeforeInitialGet() runs through the saved serialization of the names which are indexed as numbers, and it runs through another table that indexes the String names of the saved serialized id’s, which are then passed through the return and sent through the Get(). I understand all of that, but how do i apply the saved value of “Bob the builder”.

In the context of the example, this is the data that’s expected. I think you’re looking way too much at the example and not just thinking about what the methods do. How about this: assume we have this data structure:

{ Name = "Manelin", Coins = 20, Level = 1 }

This is the data we want to work with when writing code, but it’s not the data we want to save. You would want to write out the format you want to serialize it as. So we’d have a line somewhere like:

local dataMap = { "Name", "Coins", "Level"}

Then, we can do:

local dataStore = DataStore2("PlayerData", player)

dataStore:BeforeInitialGet(function(serialized)
	local deserialized = {}

	for index, value in pairs(serialized) do
		deserialized[dataMap[index]] = value
	end

	return deserialized
end)

dataStore:BeforeSave(function(deserialized)
	local serialized = {}

	for name, value in pairs(deserialized) do
		for mapIndex, mapName in pairs(dataMap) do
			if mapName == name then
				serialized[mapIndex] = value
			end
		end
	end

	return serialized
end)
1 Like

That worked! :slight_smile: Thankyou so much for your patience it is always appreciated! You truly are a fabulous person for taking the time to deal with my nonsense! I think i understand enough now to figure out the dictionaries within dictionaries. Thankyou again!

3 Likes

I am using this datastore for my game, and its Amazing! I have a question about this and I also made a topic on it if you want to check it out: click here

If you don’t wont to read it its basically just about how would I make this coin collecting system with rebirths and game pass multipliers(I am an intermediate scripter btw)

Please if you don’t mind tell me a way on how to do this. Thank You

Since :Increment() uses:

function DataStore:Increment(value, defaultValue)
	self:Set(self:Get(defaultValue) + value)
end

What value is the parameter defaultvalue when you leave the second argument empty when using :Increment()?

So if I would do CoinsDataStore:Increment(50), and no coinsdata could be found, will the defaultvalue be 0 and incremented with 50? or will the defaultvalue be a nil value and result in an error when incrementing nil with 50?

This, though it’s of course not a great solution. There’s an issue filed adjacent to this one here: Add proper error when calling :Increment() with no default or cached value · Issue #10 · Kampfkarren/Roblox · GitHub

1 Like

So is the main reason why most examples (including yours) do not include the defaultvalue in :Increment() because you use the :Get() somewhere before you use :Increment()?

Hello, I’m having a little problem with SaveAll:

local DS = require(1936396537) --DataStore2

local DSnames = {"Coins", "FruitInventory"} --etc
--all datastore names to be combined in "Main"

for _, DSname in pairs(DSnames) do
	DS.Combine("Main", DSname)
end

The above code combines some datastores, works fine; though when I try:

DS:SaveAll(Player)

or

DS.SaveAll(Player)

it errors that SaveAll isn’t a function of DS.


Am I doing something wrong? The only way I worked around this is using:

DS("Main", Player):Save()

Before I try to answer this, since I am not the best scripter (I am like an inter mediate one), I kinda don’t know what you mean in your replies.

The parameter defaultvalue equals 0, if that’s what you mean.

So if I would do CoinsDataStore:Increment(50), and no coinsdata could be found, will the defaultvalue be 0 and incremented with 50?

yes it would. all I want to know is how I would make a rebirth system in my game that multipliers.
My current script for collecting a coin works, and all I want to know if there is an easier script for it using both rebirth and gamepass multipliers. I made this collect script using the video from alvinbloxs datastore2 video, and other videos. This is my leaderstat script in the ServerScriptService:

local dM = 0-- this is the default coins and gems


local dLev = 0 -- this is the default level
local dXP = 0 -- this is the default XP for leveling up

local xpToLevelUp = function(level)-- this is for my level system
	return 100 + level * 5
	
end

-------------------------------


------------------------------
game.Players.PlayerAdded:connect(function(plr)
	local coinDT = dS2("Coins",plr)
	local gemDt = dS2("Gems", plr)
	local levelDt = dS2("Level", plr)
	local xpDt = dS2("XP", plr)

 local stats = Instance.new("Folder",plr)

 stats.Name = "leaderstats"



 local money = Instance.new("IntValue", stats)

 money.Name = "Coins"

local gems = Instance.new("IntValue",stats); gems.Name = "Gems"

local pLevel = Instance.new("IntValue",stats);pLevel.Name = "Level" 

local xp = Instance.new("IntValue",stats);xp.Name = "XP"


----------------------------
  
--saving coins
local function coinUp(UpValue)

	money.Value = coinDT:Get(UpValue)
end

	coinUp(dM)
	coinDT:OnUpdate(coinUp)
	
	
	--------------------------
	-- saving gems
local function gemUp(UpGemValue)

	gems.Value = gemDt:Get(UpGemValue)
	
end

	gemUp(dM)
	gemDt:OnUpdate(gemUp)
	
	
	
----------------------------	

-- saving level and xp
	
local function updateLevel(level)
	plr.leaderstats.Level.Value = level
end	

local function updateXP(xp)
	if xp >= xpToLevelUp(levelDt:Get(dLev)) then
		xpDt:Increment(xpToLevelUp(levelDt:Get(dLev)) * -1)
			levelDt:Increment(1)
			game.ReplicatedStorage.Events.GuiEvents.LevelUpGui:FireClient(plr)
	else
		plr.leaderstats.XP.Value = xp
	end
end

	updateLevel(levelDt:Get(dLev))
	updateXP(xpDt:Get(dXP))
	
	
	
	
	levelDt:OnUpdate(updateLevel)
	xpDt:OnUpdate(updateXP)
	

	
	
	
end) 

So again I all want to know is an easier way to make a collect script with rebirth and game pass multipliers, and how I would add a rebirth system to the script above. thx

Yes, that’s what I follow in my own code as well.

Your DataStore2 isn’t up to date. The free model is no longer updated (Roblox has a bug preventing me from doing so), please go to the GitHub releases and download from there.

This isn’t true–I already answered their question with the correct answer.

2 Likes

I’m getting this error;
attempt to perform arithmetic on a nil value

on this line:
MassStore:Increment(1)

above that:

  local MassStore = DataStore2("Mass", player)
  wait(.9)
  MassStore:Increment(1)

if I print MassStore:Get() it prints nil, but I set the value to 0. :confused:

It prints nil, and I’m unable to increment it, yeah.

EDIT: :Set() works, but Increment() doesn’t.

1 Like

Increment requires a number value, nil is not a number value.

Maybe you tried to set the value to zero earlier, maybe not successfully.

local MassStore = DataStore2("Mass", player)
if MassStore == nil then MassStore = 0 end

Adding a conditional check on the returned value allows you to set it to an appropriate number value (in this case zero) so that you can then proceed to use Increment.

Okay, I’ll do that. Thanks. :slight_smile:

The code they provided won’t work, DataStore2 never returns nil and if it did that code would set the variable to 0, not to a store.

Use the second argument of Increment if you can’t guarantee the data will exist by then.

MassStore:Increment(1, 0)
1 Like

Thanks for correcting me. Not to argue, but would it be more valid to do MassStore:Set(0) so that it is still a DataStore2?

Also @sebi210:

Curious, when specifically are you printing and getting a nil value, since DataStore never return nil?

:Get() can return nil. DataStore2() can not.

Yes.

So, I’ve been kind of against using DataStore2 because of the fact I feel like it’s somewhat inefficient. I put together something, and I was curious If this could be a fix for that.

local DataStore2 = require(...)
local Players = game:GetService("Players")
local MarketPlaceService = game:GetService("MarketPlaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Data = {
    Coins = 0,
    Inventory = {},
    XP = 0,
    Level = 1
}

function recurseTable(tbl, func) -- just a for loop, originally used as a means of easily recursing nested tables but changed it because i have no use for that
    for index, value in next, tbl do 
        --if typeof(value) == 'table' then -- realised i don’t need this, typing this quickly.
            --recurseTable(value, func)
        --else
            func(index, value)
        --end
    end
end

local DataList = {}
recurseTable(Data, function(index, value)
    table.insert(DataList, index)
end)

DataStore2.Combine("DATA", unpack(DataList))

local function playerAdded(player)
    recurseTable(Data, function(index, value)
        local DataThingy = DataStore2(player, index)

        local function callRemote(value)
            ReplicatedStorage.Events:FindFirstChild(index):FireClient(player, value)
        end

        callRemote(DataThingy:Get(value))
        DataThingy:OnUpdate(callRemote)
    end)
end

Players.PlayerAdded:Connect(playerAdded)

for _, player in next, Players:GetPlayers() do
    playerAdded(player)
end

-- marketplace example
local CashProducts = {}
CashProducts[123123] = 100
function MarketPlaceService.ProcessReceipt(receipt)
    local playerId = receipt.PlayerId
    local player = Players:GetPlayerByUserId(playerId)
    if CashProducts[receipt.ProductId] then
        local coins = DataStore2('Coins', player)
        coins:Increment(CashProducts[receipt.ProductId])
        return Enum.ProductPurchaseDecision.PurchaseGranted
    end
end

ReplicatedStorage.BuyProduct.OnServerEvent:connect(function(player, productName)
    if not Products[productName] then -- product doesn't exist
         return -- end it here
    end -- goo goo gaga
    local coinStore = DataStore2("Coins", player)
    local productPrice = Products[productName].Price

    if coinStore:Get(Data.Coins) >= productPrice then
        print("Buying product", productName)
        coinStore:Increment(-productPrice)
    end
end)

Yeah it’s a little messy, but I’m just giving a general idea of what I’d do. Obviously I’d use, OOP and stuff but I feel like this could be a fix.

1 Like

What exactly is this fixing? This just looks like mostly normal DataStore2 usage to me.

2 Likes