Would like feedback on my datastore and developer product purchase code!

Hi there! I’m brand new to coding and I’d like to see if anyone could give me some feedback on two of the really important parts in my code.

First is my datastore- So far I haven’t had any issues with it or noticed any dataloss, but I’m hoping that maybe someone with a lot of experience with datastores can look at it and verify that it actually looks 100% good or let me know if there’s perhaps some issue with it that may cause data loss eventually? Thank you!

local players = game:GetService("Players") 
local dataStoreService = game:GetService("DataStoreService")
local database = dataStoreService:GetDataStore("data") 
local sessionData = {}

function playerAdded(player)

	local Coins = Instance.new("IntValue")
	Coins.Name = "Coins"
	Coins.Parent = player
	
	local Trails = {} 
	for i = 1, 24, 1 do 
		Trails[i] = Instance.new("BoolValue")
		Trails[i].Name = "Trails" .. i
		Trails[i].Parent = player
	end

	local success = nil
	local playerData = nil
	local attempt = 1

	-----LOADS DATA OR ASSIGNS DEFAULT DATA-------	
	repeat
		success, playerData = pcall(function()
			return database:GetAsync(player.UserId)
		end)
		attempt += 1
		if not success then
			warn(playerData)
			task.wait(3)
		end

	until success or attempt == 5

	if success then
		print("connected to database")
		if not playerData then
			playerData = {}
		end
		
        --checks player's data. If data is new it's given default value.
		if playerData["Coins"] == nil then
			playerData["Coins"] = 0
			print("adding coins to player's data")
		end
		
		for i = 1, 24 do
			if playerData["Trails" .. i] == nil then
				playerData["Trails" .. i] = false
				print("adding trail ".. i .. " to player's data")
			end
		end

		sessionData[player.UserId] = playerData

	else
		warn("unable to get data for ", player.UserId)
		player:Kick("Unable to load your data. Try again later.")
	end

	--checks for value changes and updates player data
	Coins.Value = sessionData[player.UserId].Coins
	Coins.Changed:Connect(function()
		sessionData[player.UserId].Coins = Coins.Value
	end)

	for i = 1, 24 do 
		Trails[i].Value = sessionData[player.UserId]["Trails" .. i]
		Trails[i].Changed:Connect(function()
			sessionData[player.UserId]["Trails" .. i] = Trails[i].Value
		end)
	end
end

players.PlayerAdded:Connect(playerAdded)

function saveData(player)
	if sessionData[player.UserId] then
		local success = nil
		local errorMsg = nil
		local attempt = 1

		repeat
			success, errorMsg = pcall(function()
				database:SetAsync(player.UserId, sessionData[player.UserId])
			end)
			attempt += 1
			if not success then
				warn(errorMsg)
				task.wait(3)
			end
		until success or attempt == 5

		if success then
			print("Data saved for", player.Name)
		else
			warn("unable to save for ", player.Name)
		end
	end
end

players.PlayerRemoving:Connect(saveData)

function autoSave()
	for _, player in pairs(game.Players:GetPlayers()) do
		saveData(player)
	end
end

game:BindToClose(function()
	autoSave()
end)

while task.wait(300) do
	autoSave()
end

and secondly is my code for handling the purchase of developer products. This also seems to be working as intended but I just want to run it by knowledgeable people just to double check. (“000000000” is just a placeholder, I do use the actual developer product IDs there)

local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local productFunctions = {}

productFunctions[00000000] = function(receipt, player)
	local jewels = player:WaitForChild("Jewels")
	jewels.Value = jewels.Value + 100
	return true
end

productFunctions[000000000] = function(receipt, player)
	local jewels = player:WaitForChild("Jewels")
	jewels.Value = jewels.Value + 300
	return true
end

local function processReceipt(receiptInfo)
	local userId = receiptInfo.PlayerId
	local productId = receiptInfo.ProductId
	print(userId, productId)

	local player = Players:GetPlayerByUserId(userId)
	if player then
		local handler = productFunctions[productId]
		local success, result = pcall(handler, receiptInfo, player)
		if success then
			return Enum.ProductPurchaseDecision.PurchaseGranted
		else
			warn("Failed to process receipt:", receiptInfo, result)
		end
	end

	return Enum.ProductPurchaseDecision.NotProcessedYet
end

--when player buys something this fires
MarketplaceService.ProcessReceipt = processReceipt
1 Like

Looks good! This is a correct basic implementation of DataStores. For more features like session locking and easy handling of data look into using ProfileService (highly recommend).

2 Likes

alright thank you so much for taking a look at it!

1 Like

Also forgot to mention, the developer product code is correct too :+1:

1 Like