Trouble with datastore 2 saving a value change

You can write your topic however you want, but you need to answer these questions:

  1. Trying to get Datastore 2 to save stringvalue of the name agent or jeepbase when i equip it to slot 1-5


updated to rbxm, file no longer requiring asset id

  1. googled the error tried many diff variations i been trying to get this value to save for an entire week so depressing working on the same thing everyday and getting no were. Seems like thats all i ever do in roblox

Datastore2

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

local defaultCashValue = 0
local towerTable = {}
local equipmentTable = {}

game.Players.PlayerAdded:Connect(function(player)
	
	print ("Data Loaded")
	local CashDataStore = DataStore2("CashDataStore", player)
	local TowersDataStore = DataStore2("TowersDataStore", player)
	local EquipmentsDatastore = DataStore2("EquipmentDatastore", player)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local equipment = Instance.new("Folder")
	equipment.Name = "Equipment"
	equipment.Parent = player
	
	local EquipmentStats = Instance.new("IntValue", equipment)
	EquipmentStats.Name = "EquipmentStats"
	EquipmentStats.Value = 0
	
	local W1 = Instance.new("StringValue",equipment)
			W1.Name = "W1"
			W1.Value = "None"

			local W2 = Instance.new("StringValue",equipment)
			W2.Name = "W2"
			W2.Value = "None"

			local W3 = Instance.new("StringValue",equipment)
			W3.Name = "W3"
			W3.Value = "None"

			local W4 = Instance.new("StringValue",equipment)
			W4.Name = "W4"
			W4.Value = "None"

			local W5 = Instance.new("StringValue",equipment)
			W5.Name = "W5"
			W5.Value = "None"
	
	
	local Cash = Instance.new ("IntValue")
	Cash.Parent = leaderstats
	Cash.Name = "Cash"
	
	local diamonds = Instance.new("IntValue")
	diamonds.Name = "Money"
	diamonds.Value = 1000-- starting money
	diamonds.Parent = leaderstats
	
	local Folder = Instance.new("Folder")
	Folder.Parent = player
	Folder.Name = "Towers"
	
	local function cashUpdate(value)
		Cash.Value = CashDataStore:Get(value)
	end
	
	local function equipmentUpdate(value)
		equipmentTable = EquipmentsDatastore:Get(value)
		Folder:ClearAllChildren()
		for _, equipment in ipairs(equipmentTable) do
		local EquipmentValue = Instance.new("Folder")
			EquipmentValue.Parent = Folder
			EquipmentValue.Name = equipment
		end
	end
	
	
	local function towerUpdate(value)
		towerTable = TowersDataStore:Get(value)
		Folder:ClearAllChildren()
		for _, tower in ipairs(towerTable) do
			local TowerValue = Instance.new("Folder")
			TowerValue.Parent = Folder
			TowerValue.Name = tower
		end
	end
	
	
	cashUpdate(defaultCashValue)
	towerUpdate(towerTable)
	equipmentUpdate(equipmentTable)
	
	CashDataStore:OnUpdate(cashUpdate)
	TowersDataStore:OnUpdate(towerUpdate)
	EquipmentsDatastore:OnUpdate(equipmentUpdate)
	
end)

game.ReplicatedStorage.DataUpdate.OnServerEvent:Connect(function(player, value, DataType)
	local DataStore = DataStore2(DataType, player)
	
	if DataType ~= "EquipmentsDataStore" then --or DataType ~= "EquipmentsDataStore" then
		DataStore:Increment(value, defaultCashValue)
	else
		local Towers = player.Towers
		local Tower = Instance.new("BoolValue")
		Tower.Name = value
		Tower.Parent = Towers
		local TowerTable = {}
		for _, v in ipairs(Towers:GetChildren()) do
			table.insert(TowerTable, v.Name)
		end
		local Equipments = player.Equipment
		local Equipment = Instance.new("StringValue")
		Equipment.Name = value
		Equipment.Parent = Equipment
		local EquipmentTable = {}
		for _, v in ipairs(Equipment:GetChildren()) do
			table.insert(EquipmentTable, v.Name)
		end

		DataStore:Set(EquipmentTable)
		DataStore:Set(TowerTable)
	end
	 	


	
end)

Local script on a image button in my backpack

wait(1)

local equipmentTable = {"Agent" ,"JeepBase"}
--local W1 = game.Players.LocalPlayer.Equipment.W1
--local eq = game.Players.LocalPlayer.Equipment

script.Parent.MouseButton1Click:Connect(function()
	game.ReplicatedStorage.DataUpdate:FireServer("equipmentTable")
end)

DATASTORE2

--[[
	DataStore2: A wrapper for data stores that caches, saves player's data, and uses berezaa's method of saving data.
	Use require(1936396537) to have an updated version of DataStore2.

	DataStore2(dataStoreName, player) - Returns a DataStore2 DataStore

	DataStore2 DataStore:
	- Get([defaultValue])
	- Set(value)
	- Update(updateFunc)
	- Increment(value, defaultValue)
	- BeforeInitialGet(modifier)
	- BeforeSave(modifier)
	- Save()
	- SaveAsync()
	- OnUpdate(callback)
	- BindToClose(callback)

	local coinStore = DataStore2("Coins", player)

	To give a player coins:

	coinStore:Increment(50)

	To get the current player's coins:

	coinStore:Get()
--]]

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

local Constants = require(script.Constants)
local Promise = require(script.Promise)
local SavingMethods = require(script.SavingMethods)
local Settings = require(script.Settings)
local TableUtil = require(script.TableUtil)
local Verifier = require(script.Verifier)

local SaveInStudioObject = ServerStorage:FindFirstChild("SaveInStudio")
local SaveInStudio = SaveInStudioObject and SaveInStudioObject.Value

local function clone(value)
	if typeof(value) == "table" then
		return TableUtil.clone(value)
	else
		return value
	end
end

--DataStore object
local DataStore = {}

--Internal functions
function DataStore:Debug(...)
	if self.debug then
		print(...)
	end
end

function DataStore:_GetRaw()
	if self.getRawPromise then
		return self.getRawPromise
	end

	self.getRawPromise = self.savingMethod:Get():andThen(function(value)
		self.value = value
		self:Debug("value received")
		self.haveValue = true
	end):finally(function()
		self.getting = false
	end)

	return self.getRawPromise
end

function DataStore:_Update(dontCallOnUpdate)
	if not dontCallOnUpdate then
		for _,callback in pairs(self.callbacks) do
			callback(self.value, self)
		end
	end

	self.haveValue = true
	self.valueUpdated = true
end

--Public functions

function DataStore:Get(defaultValue, dontAttemptGet)
	if dontAttemptGet then
		return self.value
	end

	local backupCount = 0

	if not self.haveValue then
		while not self.haveValue do
			local success, error = self:_GetRaw():await()

			if not success then
				if self.backupRetries then
					backupCount = backupCount + 1

					if backupCount >= self.backupRetries then
						self.backup = true
						self.haveValue = true
						self.value = self.backupValue
						break
					end
				end

				self:Debug("Get returned error:", error)
			end
		end

		if self.value ~= nil then
			for _,modifier in pairs(self.beforeInitialGet) do
				self.value = modifier(self.value, self)
			end
		end
	end

	local value

	if self.value == nil and defaultValue ~= nil then --not using "not" because false is a possible value
		value = defaultValue
	else
		value = self.value
	end

	value = clone(value)

	self.value = value

	return value
end

function DataStore:GetAsync(...)
	local args = { ... }
	return Promise.async(function(resolve)
		resolve(self:Get(unpack(args)))
	end)
end

function DataStore:GetTable(default, ...)
	local success, result = self:GetTableAsync(default, ...):await()
	if not success then
		error(result)
	end
	return result
end

function DataStore:GetTableAsync(default, ...)
	assert(default ~= nil, "You must provide a default value.")

	return self:GetAsync(default, ...):andThen(function(result)
		local changed = false
		assert(
			typeof(result) == "table",
			":GetTable/:GetTableAsync was used when the value in the data store isn't a table."
		)

		for defaultKey, defaultValue in pairs(default) do
			if result[defaultKey] == nil then
				result[defaultKey] = defaultValue
				changed = true
			end
		end

		if changed then
			self:Set(result)
		end

		return result
	end)
end

function DataStore:Set(value, _dontCallOnUpdate)
	self.value = clone(value)
	self:_Update(_dontCallOnUpdate)
end

function DataStore:Update(updateFunc)
	self.value = updateFunc(self.value)
	self:_Update()
end

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

function DataStore:IncrementAsync(add, defaultValue)
	self:GetAsync(defaultValue):andThen(function(value)
		self:Set(value + add)
	end)
end

function DataStore:OnUpdate(callback)
	table.insert(self.callbacks, callback)
end

function DataStore:BeforeInitialGet(modifier)
	table.insert(self.beforeInitialGet, modifier)
end

function DataStore:BeforeSave(modifier)
	self.beforeSave = modifier
end

function DataStore:AfterSave(callback)
	table.insert(self.afterSave, callback)
end

--[[**
	<description>
	Adds a backup to the data store if :Get() fails a specified amount of times.
	Will return the value provided (if the value is nil, then the default value of :Get() will be returned)
	and mark the data store as a backup store, and attempts to :Save() will not truly save.
	</description>

	<parameter name = "retries">
	Number of retries before the backup will be used.
	</parameter>

	<parameter name = "value">
	The value to return to :Get() in the case of a failure.
	You can keep this blank and the default value you provided with :Get() will be used instead.
	</parameter>
**--]]
function DataStore:SetBackup(retries, value)
	self.backupRetries = retries
	self.backupValue = value
end

--[[**
	<description>
	Unmark the data store as a backup data store and tell :Get() and reset values to nil.
	</description>
**--]]
function DataStore:ClearBackup()
	self.backup = nil
	self.haveValue = false
	self.value = nil
end

--[[**
	<returns>
	Whether or not the data store is a backup data store and thus won't save during :Save() or call :AfterSave().
	</returns>
**--]]
function DataStore:IsBackup()
	return self.backup ~= nil --some people haven't learned if x then yet, and will do if x == false then.
end

--[[**
	<description>
	Saves the data to the data store. Called when a player leaves.
	</description>
**--]]
function DataStore:Save()
	local success, result = self:SaveAsync():await()

	if success then
		print("saved " .. self.Name)
	else
		error(result)
	end
end

--[[**
	<description>
	Asynchronously saves the data to the data store.
	</description>
**--]]
function DataStore:SaveAsync()
	return Promise.async(function(resolve, reject)
		if not self.valueUpdated then
			warn(("Data store %s was not saved as it was not updated."):format(self.Name))
			resolve(false)
			return
		end

		if RunService:IsStudio() and not SaveInStudio then
			warn(("Data store %s attempted to save in studio while SaveInStudio is false."):format(self.Name))
			if not SaveInStudioObject then
				warn("You can set the value of this by creating a BoolValue named SaveInStudio in ServerStorage.")
			end
			resolve(false)
			return
		end

		if self.backup then
			warn("This data store is a backup store, and thus will not be saved.")
			resolve(false)
			return
		end

		if self.value ~= nil then
			local save = clone(self.value)

			if self.beforeSave then
				local success, result = pcall(self.beforeSave, save, self)

				if success then
					save = result
				else
					reject(result, Constants.SaveFailure.BeforeSaveError)
					return
				end
			end

			local problem = Verifier.testValidity(save)
			if problem then
				reject(problem, Constants.SaveFailure.InvalidData)
				return
			end

			return self.savingMethod:Set(save):andThen(function()
				resolve(true, save)
			end)
		end
	end):andThen(function(saved, save)
		if saved then
			for _, afterSave in pairs(self.afterSave) do
				local success, err = pcall(afterSave, save, self)

				if not success then
					warn("Error on AfterSave: "..err)
				end
			end

			self.valueUpdated = false
		end
	end)
end

function DataStore:BindToClose(callback)
	table.insert(self.bindToClose, callback)
end

function DataStore:GetKeyValue(key)
	return (self.value or {})[key]
end

function DataStore:SetKeyValue(key, newValue)
	if not self.value then
		self.value = self:Get({})
	end

	self.value[key] = newValue
end

local CombinedDataStore = {}

do
	function CombinedDataStore:BeforeInitialGet(modifier)
		self.combinedBeforeInitialGet = modifier
	end

	function CombinedDataStore:BeforeSave(modifier)
		self.combinedBeforeSave = modifier
	end

	function CombinedDataStore:Get(defaultValue, dontAttemptGet)
		local tableResult = self.combinedStore:Get({})
		local tableValue = tableResult[self.combinedName]

		if not dontAttemptGet then
			if tableValue == nil then
				tableValue = defaultValue
			else
				if self.combinedBeforeInitialGet and not self.combinedInitialGot then
					tableValue = self.combinedBeforeInitialGet(tableValue)
				end
			end
		end

		self.combinedInitialGot = true
		tableResult[self.combinedName] = clone(tableValue)
		self.combinedStore:Set(tableResult, true)
		return clone(tableValue)
	end

	function CombinedDataStore:Set(value, dontCallOnUpdate)
		local tableResult = self.combinedStore:GetTable({})
		tableResult[self.combinedName] = value
		self.combinedStore:Set(tableResult, dontCallOnUpdate)
		self:_Update(dontCallOnUpdate)
	end

	function CombinedDataStore:Update(updateFunc)
		self:Set(updateFunc(self:Get()))
		self:_Update()
	end

	function CombinedDataStore:Save()
		self.combinedStore:Save()
	end

	function CombinedDataStore:OnUpdate(callback)
		if not self.onUpdateCallbacks then
			self.onUpdateCallbacks = { callback }
		else
			self.onUpdateCallbacks[#self.onUpdateCallbacks + 1] = callback
		end
	end

	function CombinedDataStore:_Update(dontCallOnUpdate)
		if not dontCallOnUpdate then
			for _, callback in pairs(self.onUpdateCallbacks or {}) do
				callback(self:Get(), self)
			end
		end

		self.combinedStore:_Update(true)
	end

	function CombinedDataStore:SetBackup(retries)
		self.combinedStore:SetBackup(retries)
	end
end

local DataStoreMetatable = {}

DataStoreMetatable.__index = DataStore

--Library
local DataStoreCache = {}

local DataStore2 = {}
local combinedDataStoreInfo = {}

--[[**
	<description>
	Run this once to combine all keys provided into one "main key".
	Internally, this means that data will be stored in a table with the key mainKey.
	This is used to get around the 2-DataStore2 reliability caveat.
	</description>

	<parameter name = "mainKey">
	The key that will be used to house the table.
	</parameter>

	<parameter name = "...">
	All the keys to combine under one table.
	</parameter>
**--]]
function DataStore2.Combine(mainKey, ...)
	for _, name in pairs({...}) do
		combinedDataStoreInfo[name] = mainKey
	end
end

function DataStore2.ClearCache()
	DataStoreCache = {}
end

function DataStore2.SaveAll(player)
	if DataStoreCache[player] then
		for _, dataStore in pairs(DataStoreCache[player]) do
			if dataStore.combinedStore == nil then
				dataStore:Save()
			end
		end
	end
end

function DataStore2.PatchGlobalSettings(patch)
	for key, value in pairs(patch) do
		assert(Settings[key] ~= nil, "No such key exists: " .. key)
		-- TODO: Implement type checking with this when osyris' t is in
		Settings[key] = value
	end
end

function DataStore2.__call(_, dataStoreName, player)
	assert(
		typeof(dataStoreName) == "string" and typeof(player) == "Instance",
		("DataStore2() API call expected {string dataStoreName, Instance player}, got {%s, %s}")
		:format(
			typeof(dataStoreName),
			typeof(player)
		)
	)

	if DataStoreCache[player] and DataStoreCache[player][dataStoreName] then
		return DataStoreCache[player][dataStoreName]
	elseif combinedDataStoreInfo[dataStoreName] then
		local dataStore = DataStore2(combinedDataStoreInfo[dataStoreName], player)

		dataStore:BeforeSave(function(combinedData)
			for key in pairs(combinedData) do
				if combinedDataStoreInfo[key] then
					local combinedStore = DataStore2(key, player)
					local value = combinedStore:Get(nil, true)
					if value ~= nil then
						if combinedStore.combinedBeforeSave then
							value = combinedStore.combinedBeforeSave(clone(value))
						end
						combinedData[key] = value
					end
				end
			end

			return combinedData
		end)

		local combinedStore = setmetatable({
			combinedName = dataStoreName,
			combinedStore = dataStore,
		}, {
			__index = function(_, key)
				return CombinedDataStore[key] or dataStore[key]
			end
		})

		if not DataStoreCache[player] then
			DataStoreCache[player] = {}
		end

		DataStoreCache[player][dataStoreName] = combinedStore
		return combinedStore
	end

	local dataStore = {}

	dataStore.Name = dataStoreName
	dataStore.UserId = player.UserId

	dataStore.callbacks = {}
	dataStore.beforeInitialGet = {}
	dataStore.afterSave = {}
	dataStore.bindToClose = {}
	dataStore.savingMethod = SavingMethods[Settings.SavingMethod].new(dataStore)

	setmetatable(dataStore, DataStoreMetatable)

	local event, fired = Instance.new("BindableEvent"), false

	game:BindToClose(function()
		if not fired then
			spawn(function()
				player.Parent = nil -- Forces AncestryChanged to fire and save the data
			end)

			event.Event:wait()
		end

		local value = dataStore:Get(nil, true)

		for _, bindToClose in pairs(dataStore.bindToClose) do
			bindToClose(player, value)
		end
	end)

	local playerLeavingConnection
	playerLeavingConnection = player.AncestryChanged:Connect(function()
		if player:IsDescendantOf(game) then return end
		playerLeavingConnection:Disconnect()
		dataStore:SaveAsync():andThen(function()
			print("player left, saved " .. dataStoreName)
		end):catch(function(error)
			-- TODO: Something more elegant
			warn("error when player left! " .. error)
		end):finally(function()
			event:Fire()
			fired = true
		end)

		delay(40, function() --Give a long delay for people who haven't figured out the cache :^(
			DataStoreCache[player] = nil
		end)
	end)

	if not DataStoreCache[player] then
		DataStoreCache[player] = {}
	end

	DataStoreCache[player][dataStoreName] = dataStore

	return dataStore
end

DataStore2.Constants = Constants

return setmetatable(DataStore2, DataStore2)

image

image

It’s probably failing on this Part,

Is DataType a string? + You should use DataStore2.Combine

you should checkout this, this is what Kampfkarren told me. :slight_smile:

You have Problem 1 and 2.

Maybe try using tostring(DataType)

i spent like an hour working with this guys plugin it makes no sense to me and i got no were with it, anyone have experience with it?, the video tutorial has no instructions and it creates empty data store folders in server storage and can add values