Need help in optimizing my code

The data table you’re saving needs to include DataID if you want to prevent re-writing the same data. You could also remove the check if you don’t care…

Btw, can you print out your data when you load in? Also, do the other parts of the code work?

2 Likes

only that part, but it needs to work to save the data
oh and this part too, but it doesnt throw an error
Screenshot 2025-02-20 151519

1 Like

This is such an unproductive font

3 Likes

also how do i print tables (im not familiar with tables sorry)

1 Like

What I did, was just

print(PlayerDataTable)

That prints the table

1 Like

@sonic_848 Hey, quick question—do I need to add anything else to make this work? Like, do I have to manually create the instances myself?
I don’t see any items inside my PlayerDataTable when I check in Explorer. But when I print the table, it does show that the objects and values are there. The weird thing is, I just can’t see them in Explorer, so I can’t update them.
I also have a button that’s supposed to add +1 click when pressed, but it’s not working. Any idea what I might be missing? If you want I can send my script?

1 Like

oh that works?? last time i tried and it printed some random letters

1 Like

I did this:

local function PlayerJoin(Player:Player)
	local PlrId = Player.UserId
	local Success, PlayerData = PlayerDataRequest(PlrId, true, 3, 1)
	if not Success then warn(PlayerData) Player:Kick("Data store issues.") return end
	PlayerDataTable[PlrId] = (PlayerData and PlayerData["DataID"]) and PlayerData or BaseData

	local Folder = CreateInstance("Folder", Player, "PlayerDataFolder")

	for SectionName, Data in PlayerDataTable[PlrId] do
		if typeof(Data) ~= "table" then continue end

		local SubFolder = CreateInstance("Folder", Folder, SectionName)

		for Name, Value in Data do
			local Type = DataTypes[typeof(Value)]
			if not Type then continue end

			local Val = CreateInstance(`{Type}Value`, SubFolder, Name, Value)

			Val.Changed:Connect(function(ChangedValue: number) 
				local OldID = PlayerDataTable[PlrId]["DataID"]
				local NewId = GetRandomID(ChangedValue, OldID)

				PlayerDataTable[PlrId]["DataID"] = NewId
				PlayerDataTable[PlrId][SectionName][Name] = ChangedValue
			end)
		end
	end
	
	print(PlayerDataTable) -- Added table here
end

So exactly like @sonic_848 but just added the print in the end

1 Like

you should use profilestore. it fixes most of the problems that roblox’s datastore service has! tutorial:
https://m.youtube.com/watch?v=evBhoqeYegQ&pp=ygUNcHJvZmlsZSBzdG9yZQ%3D%3D

1 Like

If you removed the data id part then just delete everything related to it. So the changed event should only have that last line

2 Likes

With the folder i made for @H0angg it’s under the player instance. I explain how it works you should read the posts above if you haven’t yet.

2 Likes
			local Val = CreateInstance(`{Type}Value`, SubFolder, Name, Value)
			Val.Changed:Connect(function(ChangedValue: number) 
				--local OldID = PlayerDataTable[PlrId]["DataID"]
				--local NewId = GetRandomID(ChangedValue, OldID)
				--PlayerDataTable[PlrId]["DataID"] = NewId
				PlayerDataTable[PlrId][SectionName][Name] = ChangedValue
			end)

like this?

but my data arent saved, its just the base data

1 Like

hey @sonic_848 i actually got ur code to work after changing something

local dss = game:GetService("DataStoreService")

local MainStore = dss:GetDataStore("PlayerData")

local PlayerDataTable = {}
local BaseData = { --Base data for when a player joins for the first time
	Settings = {Music = true, CustomCursor = true},
	Inventory = {Cake = 10, ChimBall = 10, Slingshot = 10},
	Misc = {Coins = 0, SaveCoins = 0},
	Slots = {Slot1 = "Cake", Slot2 = "Slingshot"},
	DataID = 0
}
local Idk = {
	number = "Number",
	boolean = "Bool",
	string = "String"
}

--Repeat delay is for how long the function waits before retrying to get/set data
local function PlayerDataRequest(UserID : number, Get : boolean, Tries : number, RepeatDelay : number, SetData : {}?)
	if not (Tries and RepeatDelay and UserID) then
		warn("Check params")
		return nil
	end
	if Get == false and not SetData then
		warn("Set data not provided.")
		return nil
	end
	local CurrentTries = 0
	local Success, Error, Data = false, nil, nil
	repeat
		CurrentTries += 1
		if Get then 
			Success, Error = pcall(function()
				Data = MainStore:GetAsync(UserID)
			end)
		else
			Success, Error = pcall(function()
				--if OldData["DataID"] == SetData["DataID"] then
				--	return nil
				--else
				--	return SetData
				--end
				Data = MainStore:SetAsync(UserID, SetData)
			end)
		end
		if Success then
			break
		else
			task.wait(RepeatDelay)
		end
	until (CurrentTries == Tries) or Success
	return Success, Error, Data
end

local function CreateInstance(InstanceName : string, Parent : Instance, Name : string,  Value:any?) : NumberValue|BoolValue|StringValue
	local X = Instance.new(InstanceName)
	X.Name = Name
	if Value then X.Value = Value end
	X.Parent = Parent
	return X
end

local function GetRandomID(Last:number) : number
	local Rand = Random.new(os.clock() + math.random(0, 1000))

	local Num = Rand:NextNumber(0,100)
	if Num == Last then
		Num = GetRandomID(Last)
		task.wait()
	end
	return Num
end

local function PlayerJoin(Player)
	local PlrId = Player.UserId
	local Success, Error, PlayerData = PlayerDataRequest(PlrId, true, 3, 1)
	if not Success then
		warn(Error)
		Player:Kick("Data store issues.")
		return
	end
	PlayerDataTable[PlrId] = (PlayerData and PlayerData["DataID"]) and PlayerData or BaseData
	local Folder = CreateInstance("Folder", Player, "PlayerDataFolder")
	for SectionName, Data in PlayerDataTable[PlrId] do
		if typeof(Data) ~= "table" then
			continue
		end
		local SubFolder = CreateInstance("Folder", Folder, SectionName)
		for Name, Value in Data do
			local Type = Idk[typeof(Value)]
			if not Type then
				continue
			end
			local Val = CreateInstance(`{Type}Value`, SubFolder, Name, Value)
			Val.Changed:Connect(function(ChangedValue: number) 
				--local OldID = PlayerDataTable[PlrId]["DataID"]
				--local NewId = GetRandomID(ChangedValue, OldID)
				--PlayerDataTable[PlrId]["DataID"] = NewId
				PlayerDataTable[PlrId][SectionName][Name] = ChangedValue
				print(PlayerDataTable)
			end)
		end
	end
end

local function PlayerLeave(Player:Player)
	local PlayerData = PlayerDataTable[Player.UserId]
	if not PlayerData then
		return
	end
	local Success, Err = PlayerDataRequest(Player.UserId, false, 3, 1, PlayerData)
	if Success then
		print("saved")
	end
	if Err then
		warn(Err)
	end
	PlayerDataTable[Player.UserId] = nil
end

is it okay to use setasync instead of updateasync

1 Like

Why not RunService:IsClient() & RunService:IsServer()?

1 Like

Yea you can use set async.
Original save function with set async here:

2 Likes

This is a server script not a module

1 Like

That doesn’t disallow you from using IsClient() instead of C = or some hacky way?

1 Like

It’s a function connected to both a OnServerInvoke and a OnInvoke event so you cant use that

2 Likes

is there a way to save more instance in a table, like more settings and inventory, idk how to explain atp

local BaseData = { --Base data for when a player joins for the first time
	Settings = {Music = true, CustomCursor = true, test = false},
	Inventory = {Cake = 10, ChimBall = 10, Slingshot = 10},
	Coins = {Coins = 0, SaveCoins = 0},
	Slots = {Slot1 = "Cake", Slot2 = "Slingshot"},
	DataID = 0
}

when i add the test in, it doesnt appears in the players data

I just updated the entire code to make it work because there was stuff I had to add. So, the new way it will work is if you update the base data, make sure to change the version number. Version number HAS to be in both tables and don’t remove it. So basically if you have saved data and your version is like 0 and the base data one is like 1 then the player’s data will change too (you can test and see). Also, the data store name had to change because we couldn’t do it with the previous one but everything should work now. DataID and updateasync shenanigans were removed btw and they follow what you’ve been wanting. To make the players’ data switch just change the version number

Code:

local players = game:GetService("Players")
local dss = game:GetService("DataStoreService")

local MainStore = dss:GetDataStore("PlayerData_1")

local PlayerDataTable = {}

local BaseData = { --Base data for when a player joins for the first time
	Settings = {Music = true, CustomCursor = true, test = false},
	Inventory = {Cake = 10, ChimBall = 10, Slingshot = 10},
	Coins = {Coins = 0, SaveCoins = 0},
	Slots = {Slot1 = "Cake", Slot2 = "Slingshot"},
	Version = 0
}

local Idk = {
	number = "Number",
	boolean = "Bool",
	string = "String"
}

--Repeat delay is for how long the function waits before retrying to get/set data
local function PlayerDataRequest(UserID:number, Get:boolean, Tries:number, RepeatDelay:number, SetData:{}?)
	if not (Tries and RepeatDelay and UserID) then warn("Check params") return nil end
	if Get == false and not SetData then warn("Set data not provided.") return nil end

	local CurrentTries = 0
	local Success, Data = false, nil

	repeat
		CurrentTries += 1

		if Get then 
			Success, Data = pcall(MainStore.GetAsync, MainStore, UserID)
		else	
			Success, Data = pcall(MainStore.SetAsync, MainStore, UserID, SetData)
		end

		if Success then
			break
		else
			task.wait(RepeatDelay)
		end

	until (CurrentTries == Tries) or Success

	return Success, Data
end

local function CreateInstance(InstanceName:string, Parent:Instance, Name:string,  Value:any?) : NumberValue|BoolValue|StringValue
	local X = Instance.new(InstanceName)
	X.Name = Name
	if Value then X.Value = Value end
	X.Parent = Parent
	return X
end

local function MatchTableKeys(T1:{}, T2:{})
	-- Add missing keys from T2 to T1
	for key, value in pairs(T2) do
		if type(value) == "table" then
			T1[key] = T1[key] or {}
			MatchTableKeys(T1[key], T2[key])
		else
			if T1[key] == nil then
				T1[key] = value
			end
		end
	end
	
	for key in pairs(T1) do
		if T2[key] == nil then
			T1[key] = nil
		end
	end
end

local function PlayerJoin(Player:Player)
	local PlrId = Player.UserId
	
	local Success, PlayerData = PlayerDataRequest(PlrId, true, 3, 1)
	if not Success then warn(PlayerData) Player:Kick("Data store issues.") return end
	
	PlayerDataTable[PlrId] = (PlayerData and PlayerData["Version"]) and PlayerData or BaseData

	if PlayerDataTable[PlrId]["Version"] ~= BaseData["Version"] then 
		MatchTableKeys(PlayerDataTable[PlrId], BaseData)
		PlayerDataTable[PlrId]["Version"] = BaseData["Version"]
	end
	
	local Folder = CreateInstance("Folder", Player, "PlayerDataFolder")

	for SectionName, Data in PlayerDataTable[PlrId] do
		if typeof(Data) ~= "table" then continue end

		local SubFolder = CreateInstance("Folder", Folder, SectionName)

		for Name, Value in Data do
			local Type = Idk[typeof(Value)]
			if not Type then continue end

			local Val = CreateInstance(`{Type}Value`, SubFolder, Name, Value)

			Val.Changed:Connect(function(ChangedValue: number) 
				PlayerDataTable[PlrId][SectionName][Name] = ChangedValue
			end)
		end
	end
end

local function PlayerLeave(Player:Player)
	local PlayerData = PlayerDataTable[Player.UserId]
	if not PlayerData then return end

	local Success, Err = PlayerDataRequest(Player.UserId, false, 3, 1, PlayerData)
	if not Success then warn(Err) end
	PlayerDataTable[Player.UserId] = nil
end

players.PlayerAdded:Connect(PlayerJoin)
players.PlayerRemoving:Connect(PlayerLeave)

Edit: Don’t increment down on the version number when updating your base data only go up. Also, you can use decimals for the version number so like 1.2 and 1.002.