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

If you search “datastore editor datastore2” you should have found this: Does CrazyMan's DataStore Editor work for DataStore2? - #6 by MFCmaster1234.

1 Like

Hi, is it possible to use OrderedDataStores with this module?

I believe you need to use your own separate ordered data stores:

It works BUT, you need to use a seperate datastore as @Grargror said and doesn’t need to be that complicated:
https://gyazo.com/b72c32cc287523776ee3c8ebe471bafd

There is also a tutorial on how to create leaderboards if you need to, its in community resources I think.

My currency gui works but doesn’t update when I respawn do you know why?

replicatedStorage.UpdateClientCurrency.OnClientEvent:Connect(function(amount)
script.Parent.Text = "B$" .. amount
end)

Im currently trying to save a players play time. The problem is, it does not seem to save whatever I try. Lemme explain how my system looks. As soon as the player joins, os.time() is saved in a dictonary under the players UserId. The dictionary should look like this: {983190238 = 931290831902} (Numbers aren’t correct). Next thing Im doing is connecting a Players.PlayerRemoving event. This simply subtracts the join time value from os.time(). My script looks like this:

local Players = game:GetService("Players")
local SStorage = game:GetService("ServerStorage")

local Modules = SStorage:WaitForChild("Modules")
local DataStore2 = require(Modules.DataStore2)

local JoinTimes = {}

DataStore2.Combine("Data","Coins","PlayTime")

local function LoadLeaderstats(Player)
	local PlayTimeData = DataStore2("PlayTime",Player)
	
	JoinTimes[Player.UserId] = os.time()

	local PlayTime = Instance.new("IntValue")
	PlayTime.Name = "PlayTime"
	PlayTime.Parent = Player
        PlayTime.Value = PlayTimeData:Get(0)
end

local function OnPlayerRemoving(Player)
	if JoinTimes[Player.UserId] then
		local PlayTime = os.time() - JoinTimes[Player.UserId]
		local PlayTimeData = DataStore2("PlayTime",Player)
		
		JoinTimes[Player.UserId] = nil
		PlayTimeData:Get()
		PlayTimeData:Increment(PlayTime)
		print(PlayTimeData:Get())
	end
end

Players.PlayerAdded:Connect(LoadLeaderstats)
Players.PlayerRemoving:Connect(OnPlayerRemoving)

What I assume is that this is a timing problem, as DataStore2 probably also connects to a PlayerRemoving event. I still dont want to do something like:

while wait(1) do
    PlayTimeData:Increment(1)
end

Did I make any mistakes in my code or is what I assume right? If so, is there another way to cope with this?

By the way, I would like to thank you for the effort you put into writing the module. It really helps me to prevent data loss (till now) and spares me the effort to write my own one. :happy3:

Thanks in advance

:crown: Nova_MrRoyal :crown:

2 Likes

I’m not sure you completely understand DataStore2. Why are you storing a dictionary of the player’s user ID? The PlayTime data store is exclusive to the player, that’s why you pass player to the DataStore2 function in the first place. You should refactor this so it just saves the number.

I got a question, is it compatible if i placed in LeaderboardV3.

			if kills.Value == kills +1 then
			local e = DataStore2("points",humanoid)
			e:Increment(100)
LeaderboardV3

print(“Leaderboard script version 3.00 loaded”)
local ServerScriptService = game:GetService(“ServerScriptService”)
local DataStore2 = require(ServerScriptService.DataStore2)

function onPlayerEntered(newPlayer)

local stats = Instance.new("IntValue")
stats.Name = "leaderstats"

local points = Instance.new("IntValue")
points.Name = "Points"
points.Value = 0

local kills = Instance.new("IntValue")
kills.Name = "Kills"
kills.Value = 0

local deaths = Instance.new("IntValue")
deaths.Name = "Deaths"
deaths.Value = 0

kills.Parent = stats
deaths.Parent = stats
points.Parent = stats

-- VERY UGLY HACK
-- Will this leak threads?
-- Is the problem even what I think it is (player arrived before character)?
while true do
	if newPlayer.Character ~= nil then break end
	wait(5)
end

local humanoid = newPlayer.Character.Humanoid

humanoid.Died:connect(function() onHumanoidDied(humanoid, newPlayer) end )

-- start to listen for new humanoid
newPlayer.Changed:connect(function(property) onPlayerRespawn(property, newPlayer) end )


stats.Parent = newPlayer

end

function Send_DB_Event_Died(victim, killer)
– killer may be nil
local killername = “no one”
if killer ~= nil then killername = killer.Name end
print("DIED EVENT: ", victim.Name, " KILLED by ", killername)

if shared["deaths"] ~= nil then 
	shared["deaths"](victim, killer)
	print("SENT DB DEATH EVENT")
end

end

function Send_DB_Event_Kill(killer, victim)
print("KILL EVENT. ", killer.Name, " BLOXXED ", victim.Name)
if shared[“kills”] ~= nil then
shared[“kills”](killer, victim)
print(“SENT DB KILL EVENT”)
end
end

function onHumanoidDied(humanoid, player)
local stats = player:findFirstChild(“leaderstats”)
if stats ~= nil then
local deaths = stats:findFirstChild(“Deaths”)
deaths.Value = deaths.Value + 1

	-- do short dance to try and find the killer

	local killer = getKillerOfHumanoidIfStillInGame(humanoid)

	Send_DB_Event_Died(player, killer)
	handleKillCount(humanoid, player)
end

end

function onPlayerRespawn(property, player)
– need to connect to new humanoid

if property == "Character" and player.Character ~= nil then
	local humanoid = player.Character.Humanoid
		local p = player
		local h = humanoid
		humanoid.Died:connect(function() onHumanoidDied(h, p) end )
end

end

function getKillerOfHumanoidIfStillInGame(humanoid)
– returns the player object that killed this humanoid
– returns nil if the killer is no longer in the game

-- check for kill tag on humanoid - may be more than one - todo: deal with this
local tag = humanoid:findFirstChild("creator")

-- find player with name on tag
if tag ~= nil then
	
	local killer = tag.Value
	if killer.Parent ~= nil then -- killer still in game
		return killer
	end
end

return nil

end

function handleKillCount(humanoid, player)

local killer = getKillerOfHumanoidIfStillInGame(humanoid)
if killer ~= nil then
	local stats = killer:findFirstChild("leaderstats")
	if stats ~= nil then
		local kills = stats:findFirstChild("Kills")
		local points = stats:findFirstChild("Points")
		
		if killer ~= player then
			kills.Value = kills.Value + 1
			points.Value = points.Value +1
			if kills.Value == kills +1 then
			local e = DataStore2("points",humanoid)
			e:Increment(100)	
			end
		else
			kills.Value = kills.Value - 0
			
		end
		Send_DB_Event_Kill(killer, player)
	end
end

end

game.Players.ChildAdded:connect(onPlayerEntered)

1 Like

Hello, just wanted to ask if I’m understanding this correctly:

local ValueStore = DataStore2(Data, Player)
local Value = ValueStore:Get()
return Value[Data], Value, ValueStore -- Gets the value from data (a table)

local AchievementStore, Value, ValueStore = DataHandler.GrabData("DataThingy", Player, "Data thing from table")
AchievementStore["Data thing from table"] = true -- sets to true
ValueStore:Set(Value) -- Sets/saves it?

AchievementsStore:Get(DataTable["The data table"]) -- Is found inside main/playeradded, gets data from table?

Or is this wrong/a better way of doing so?

You need to set value, not the data store itself.

2 Likes

Is there a way for me to see the total length of the jsonencoded DATA table when I’m using this module? I want to see how much storage i’m using right now.

1 Like

Here’s my post.

I’m unsure what you mean. I think I am saving a number. I want to save a player’s play time. Therefore I need 2 times, namely JoinTime and LeaveTime, to calculate how long the player played. After that I’m increasing the current play time of the player by the time I calculated. It still does not seem to save,altough I can’t find a mistake.

My explanation in the post linked above might be misleading :uhh: Is there something wrong in my code?

2 Likes

You’re saving a table with the player’s user id. You should just save a number.

1 Like

I have recieved a message from roblox “Right To Erasure” meaning that I have to erase a player’s data from my datastores.

Is there a way to do this through datastore2?

I’ve read posts saying you can use a plugin Crazyman has made;

Will this affect or conflict with any data saved by datastore2? I know Datastore2 internally uses normal datastores but I want to be sure.

1 Like

Saw this post a bit too late, this has worked!

I am new to using Datastore2.

there is a function - Increment(value, defaultValue)

My question is, what is this default value used for; if when a player joins their value is set to the value defined by :Get(defaultValue) making the Increment() function’s defaultValue redundant?

Or is that defaultValue actually the default value to increment the value with when no Increment(Value) parameter is specified?

It is for if you haven’t called Get with a default parameter yet, it’s not always necessary.

1 Like

Is it necessary to combine all keys every time I attempt to index Datastore2?
Or is it that unless I am adding extra keys I should not call Datastore2.Combine again, after already combining keys once in a script ?

The only thing you need to make sure of is that before you call DataStore2("key", player), "key" is combined.

2 Likes

Thank you.

Is this the correct way to save through a function using Datastore 2 ?

function dataFunctions.UpdateStats(player,data, SaveAll )
             if player.leaderstats then 
             local cont = player.leaderstats
             if SaveAll == true then
                   for _,value in ipairs(cont:GetChildren()) do
                   local store = dataStore2(value,player) -- maybe tostring(value)
             store:Save() -- or store:Save(player)??
         end
      else 
           store = dataStore2(data,player)
           store:Save()
   end
      else warn("leaderstats not loaded")                             
 end 


   function dataFunctions.CreateStats(player)

The parameter data would be passed as for example, “coins” so when I run the function it should create a store

datastore2(coins, player)

Where does it explain how to use :Save() and SaveAsync() with Ds2, unless what I’m doing is right?

Will I need to loop constantly after a set interval to save the cached data for every player myself? Or should I only do this by using :bind to close

1 Like