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

You don’t need pcalls, and you can use the same data store.

2 Likes

My script is erroring.

Error message: 17:42:42.322 - ServerScriptService.save_script:92: attempt to index nil with ‘Get’
Other messages:
17:42:47.663 - Data store main was not saved as it was not updated.

player left, saved main

Both messages happen at the same time.

DataStore2 = require(game:GetService("ServerScriptService").DataStore2)
ServerStorage = game:GetService("ServerStorage")
Players = game:GetService("Players")

default_omega_coins, default_likes, default_wins = 0, 0, 0

default_bunker_terrain_position = {
	CFrame.new(26.927, -33.383, -435.507),
	CFrame.new(26.927, 23.365, -572.788),
	CFrame.new(164.479, 23.365, -434.153),
	CFrame.new(-110.625, 23.365, -435.236),
	CFrame.new(25.844, 23.365, -297.955)
}
default_bunker_terrain_size = {
	Vector3.new(277.27, 8.143, 277.27),
	Vector3.new(277.27, 108.309, 2.708),
	Vector3.new(2.166, 108.309, 274.562),
	Vector3.new(2.166, 108.309, 272.396),
	Vector3.new(275.104, 108.309, 2.166)
}
default_bunker_terrain_rotation = {
	CFrame.Angles(0, 0, 0),
	CFrame.Angles(0, 0, 0),
	CFrame.Angles(0, 0, 0),
	CFrame.Angles(0, 0, 0),
	CFrame.Angles(0, 0, 0)
}
default_bunker_materials = {
	"Mud",
	"Rock",
	"Rock",
	"Rock",
	"Rock"
}

default_object_name, default_object_position, default_object_rotation = {}, {}, {}

DataStore2.Combine("main", "Wins", "Ωmega Coins", "Likes", "object_name", "object_position", "object_rotation", "terrain-position", "terrain_rotation", "terrain_size", "terrain_materials")

Players.PlayerAdded:Connect(function(Player)
	winsStore = DataStore2("Wins", Player)
	coinStore = DataStore2("Ωmega Coins", Player)
	likesStore = DataStore2("Likes", Player)
	
	objectnameStore = DataStore2("object_name", Player)
	objectpositionStore = DataStore2("object_position", Player)
	objectrotationStore = DataStore2("object_rotation", Player)
	
	terrainpositionStore = DataStore2("terrain-position", Player)
	terrainrotationStore = DataStore2("terrain_rotation", Player)
	terrainsizeStore = DataStore2("terrain_size", Player)
	terrainmaterialsStore = DataStore2("terrain_materials", Player)
	
	local leaderstats = Instance.new("Folder") do
		leaderstats.Name = "leaderstats"
	end
	local wins = Instance.new("IntValue") do
		wins.Name = "Wins"
		wins.Parent = leaderstats
	end
	local omega_coins = Instance.new("IntValue") do
		omega_coins.Name = "Ωmega $"
		omega_coins.Parent = leaderstats
	end
	local likes = Instance.new("IntValue") do
		likes.Name = "Likes"
		likes.Parent = leaderstats
	end
	leaderstats.Parent = Player
	function save_to_model(folder_name, target_location, CFrameoffset, pos,size,rot,mat)
	local newMap = ServerStorage.empty_map:Clone() do
		newMap.Name = folder_name
	end
	
	local terrain = newMap.terrain:WaitForChild("cubes")
	for i, v in pairs(terrainmaterialsStore:Get(default_bunker_materials)) do
		local Part = Instance.new("Part") do
			Part.Name = v
			Part.CFrame = pos[i]
			Part.Size = size[i]
			Part.Parent = terrain
		end
	end
	
	newMap.Parent = target_location
end
	
	
end)


save_to_model("MrGuyROBLOX's Dungeon", game.Workspace.maps, 0, 0, 0, terrainpositionStore:Get(default_bunker_terrain_position), terrainsizeStore:Get(default_bunker_terrain_size), terrainrotationStore:Get(default_bunker_terrain_rotation,terrainmaterialsStore)) -- test function



Edit: Solved.

1 Like

What is line 92?

Hi, this probably has been asked before but how would I use a plugin like Crazyman32’s Datastore Editor to edit people’s datastores saved in the default format of DataStore2?

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?