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

Right, my point is i don’t have to do the whole:

CharInfoStore:GetTable({
        --dictionary stuff with values

})

Because when you deserialize and have the values such as what you had

for _, id in pairs(serialized) do
    local itemName = ItemsDictionary[id]
    deserialized[itemName] = true
end

The value of the itemName now = true, that doesn’t the job of the default? In the case of my data with many different forms of values i’ll just add the values in the initial dictionary, and index the value of the Deserialized name in the initial dictionary to get a default value, or else it puts the saved value.

My big problem right now is not the retrieving the name of the serialized data when deserializing, i understand that. My issue i don’t understand how to then apply the saved value of the serialized data. Should i serialize the value too? i just don’t understand that part and the github doesnt explain that.

It does.

But the GitHub thing is just an example–you can serialize/deserialize any way you want–the details don’t matter to DataStore2.

1 Like

Yeah i understand that part… i’m retrieving saved names, but i don’t understand how to retrieve the values of the names… i’m truly sorry if i’m being annoying, i’m just trying to understand. This is the code with serialization and deserialization that i’m playing with as an example, no need to disect the bottom its just indexing the type of values and making physical values and organizing accordingly.

local CharInfoStore = DataStore2("CharInfoStore"..key, Player)

local CharacterStats = Instance.new("Folder")
CharacterStats.Name = "CharacterStats"
CharacterStats.Parent = Player

local InitialDictionary = {
	"CharName",
	"CharDesc",
	"Aires",
	"CurrentLand",
	"Gender",
	"Skin Tone",
	"Face",
	"Full_Set",
	"Upper",
	"Lower",
	"Hair",
	"Head Wear",
	"Back",
	"BodyTypeScale",
	"DepthScale",
	"HeadScale",
	"HeightScale",
	"WidthScale",
}

local CharInfoData = {
	CharName = "";
	CharDesc = "";
	Aires = 30;
	["CurrentLand"] = "Bouldaron";
	["Gender"] = "Male";
	["Tool"] = "None";
	["Skin Tone"] = {255, 204, 153};
	["Face"] = "Man 1";
	["Full_Set"] = "Violet Velvet Robes";
	["Upper"] = "Default";
	["Lower"] = "Peasent Pants";
	["Hair"] = "Blonde Flowing Hair";
	['Head Wear'] = "None";
	["Back"] = "None";
	["BodyTypeScale"] = .03;
	["DepthScale"] = .8;
	["HeadScale"] = 1;
	["HeightScale"] = 1;
	["WidthScale"] = .8;
}

CharInfoStore:GetTable({
	CharName = "";
	CharDesc = "";
	Aires = 30;
	["CurrentLand"] = "Bouldaron";
	["Gender"] = "Male";
	["Tool"] = "None";
	["Skin Tone"] = {255, 204, 153};
	["Face"] = "Man 1";
	["Full_Set"] = "Violet Velvet Robes";
	["Upper"] = "Default";
	["Lower"] = "Peasent Pants";
	["Hair"] = "Blonde Flowing Hair";
	['Head Wear'] = "None";
	["Back"] = "None";
	["BodyTypeScale"] = .03;
	["DepthScale"] = .8;
	["HeadScale"] = 1;
	["HeightScale"] = 1;
	["WidthScale"] = .8;
})

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

	for _, id in pairs(serialized) do
		local itemName = InitialDictionary[id]
		deserialized[itemName] = CharInfoData[itemName]
	end
	
	return deserialized
end)

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

    for itemName in pairs(deserialized) do
        for itemId, name in pairs(InitialDictionary) do
            if name == itemName then
                table.insert(serialized, itemId)
            end
        end
    end

    return serialized
end)

local CharInfoInStore = CharInfoStore:Get()

for statname, statvalue in pairs(CharInfoInStore) do
	
	if type(statvalue) == 'number' then
		local intvalue = Instance.new("NumberValue")
		intvalue.Name = statname
		intvalue.Value = statvalue
		intvalue.Parent = CharacterStats
	elseif type(statvalue) == 'boolean' then
		local boolvalue = Instance.new("BoolValue")
		boolvalue.Name = statname
		boolvalue.Value = statvalue
		boolvalue.Parent =  CharacterStats
	elseif type(statvalue) == 'string' then
		local stringvalue = Instance.new("StringValue")
		stringvalue.Name = statname
		stringvalue.Value = statvalue
		stringvalue.Parent = CharacterStats
	elseif type(statvalue) == 'table' then
		local stringvalue = Instance.new("Color3Value")
		stringvalue.Name = statname
		stringvalue.Value = Color3.fromRGB(statvalue[1],statvalue[2],statvalue[3])
		stringvalue.Parent = CharacterStats
	end
end
2 Likes

Not sure what you mean here. When you serialize it, it should be a table. You can get the indexes from whatever you used to serialize it. You just retrieve the values from there. I’m not sure how to elaborate past the documentation.

2 Likes

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.

1 Like

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

1 Like

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: