Most efficient way to save game data

Hi Guys,

I am just curious what the best way to save data such as money, my current set up when selling an item for money is to save it to local data then to server data then onto the datastore.

Would it be best to send a money value from a local script straight over to server data then to datastore skipping local data saving completely and when writing to server data does it write to local data?

2 Likes

NEVER send money (as an example for data) through a localscript. Exploiters can very very easily edit this. One of the most secure ways to save game data is below.

-- MAKE A SCRIPT AND PLACE THAT SCRIPT IN SERVERSCRIPTSERVICE, USE THE CODE BELOW FOR IT
local dataStoreService = game:GetService("DataStoreService");
local gameData = dataStoreService:GetDataStore("GameData1");

game.Players.PlayerAdded:Connect(function(player)
local folder = Instance.new("Folder", player);
folder.Name = "Leaderstats";
local cash = Instance.new("IntValue", folder);
cash.Name = "Cash"
cash.Value = 50
local playerData = gameData:GetAsync(player.UserId) or false;
spawn(function() -- this function is optional to have. this function saves data automatically every 1 minute.
while player do
wait(60) -- waits 1 minute before autosaving data.
local success, err = pcall(function()
gameData:SetAsync(player.UserId, player:FindFirstChild("Leaderstats").Cash.Value)
end)
if not success then
warn("Failed to autosave data for " .. player.Name .. ".")
else return;
end
end)
if playerData then
cash.Value = playerData
end
end)

game.Players.PlayerRemoving:Connect(function(player)
if player:FindFirstChild("Leaderstats").Cash then
local success, err = pcall(function()
gameData:SetAsync(player.UserId, player:FindFirstChild("Leaderstats").Cash.Value)
end)
if not success then
warn("Failed to save data for " .. player.Name .. ".")
else return;
end
end)

Whats the rules on manipulating cash??? I have a local gui that when selling an item it gives you money then sends it to a server script which from there saves it to you Server side player data

Saving GPU Entity which sends the local information to server script to save to server

indent preformatted text by 4 spaces
--Initialize Bindable Event
local saveGPUEquipment = game.GUI.Main.GlobalEvents:WaitForChild("SaveGPUEquipment")

--Initializing Player Services
local Players = game:GetService("Players")
 local currentPlayer = Players.LocalPlayer

 --Initializing Replicated Storage
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local isSaving = ReplicatedStorage:WaitForChild("IsSaving")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local saveData = ReplicatedStorage:WaitForChild("SaveDataToServer")
local isSavingBoolean = false

--Initializing Variables
local globalGPUEntity

saveGPUEquipment.Event:Connect(function(entityN, cashV, powerUsedV, cryptoV)

--Initializing Function Variables
local i = 1
local g = 1
--Pausing Data Save Till Saving Has Finished
isSavingBoolean = isSaving:InvokeServer()

while isSavingBoolean == true do
	if isSavingBoolean == false then
		print("Saving Finalised Continuing With Save")
		break
	else
		isSavingBoolean = isSaving:InvokeServer()
		print("Waiting For Saving To Finalise")
		wait(1)
	end
end

if isSavingBoolean == false then
	print("Saving Hasnt Started")
end

while true do
	--Finding Power Slot Object
	local gpuSlot = "slot"..g
	gpuSlot = currentPlayer.GPUEntities:FindFirstChild(gpuSlot)
	
	if gpuSlot.Value == nil or gpuSlot.Value == "nil" or gpuSlot.Value == "Deleted" then
		--Setting Power Equipment Entity Value
		gpuSlot.Value = entityN
		globalGPUEntity = entityN
		--Saving Updated Player Stats
		currentPlayer.stats.Money.Value = currentPlayer.stats.Money.Value - cashV
		currentPlayer.stats.ElectricityUsed.Value = currentPlayer.stats.ElectricityUsed.Value + 
powerUsedV
		currentPlayer.stats.CryptoGen.Value = currentPlayer.stats.CryptoGen.Value + cryptoV
		currentPlayer.stats.TotalGPUS.Value = currentPlayer.stats.TotalGPUS.Value + 1
		local playerData = {
			["Money"] = currentPlayer.stats.Money.Value,
			["ElectricityUsed"] = currentPlayer.stats.ElectricityUsed.Value,
			["CryptoGen"] = currentPlayer.stats.CryptoGen.Value,
			["TotalGPUs"] = currentPlayer.stats.TotalGPUS.Value
		}
		saveData:FireServer(g,i,globalGPUEntity, "Nil", playerData)
		while true do
			local invSlot = "slot"..i
			invSlot = currentPlayer.Inventory:FindFirstChild(invSlot)
			if invSlot.Value == nil or invSlot.Value == "nil" or invSlot.Value == "Deleted" then
				invSlot.Value = gpuSlot.Value
				local playerData = {}
				saveData:FireServer(i,i,globalGPUEntity, "Inv", playerData)
				break
			else if i >= 200 then
					break
				end
			end
			i = i + 1
		end
		break
	else
		print("GPU Equiptment Slot Full")
		g = g + 1
		if g >= 100 then
			break
		end
	end
end
end)

Saving to Server Script is a server script that save data to server side
indent preformatted text by 4 spaces

 local ReplicatedStorage = game:GetService("ReplicatedStorage")
 local saveData = ReplicatedStorage:WaitForChild("SaveDataToServer")

local function SaveDataServer(player, i, secondI, entity, InvIdentifier, playerData)
if string.find(entity, "gpu") and string.find(InvIdentifier, "Inv") then
	local invSlot = "slot"..i
	invSlot = player.Inventory:FindFirstChild(invSlot)
	invSlot = entity
else if string.find(entity, "gpu") then
		player.stats.Money.Value = playerData["Money"]
		player.stats.ElectricityUsed.Value = playerData["ElectricityUsed"]
		player.stats.CryptoGen.Value = playerData["CryptoGen"]
		player.stats.TotalGPUS.Value = playerData["TotalGPUs"]
		local gpuSlot = "slot"..i
		gpuSlot = player.GPUEntities:FindFirstChild(gpuSlot)
		gpuSlot.Value = entity
	end
end

if string.find(entity, "power") and string.find(InvIdentifier, "Inv") then
	local invSlot = "slot"..i
	invSlot = player.Inventory:FindFirstChild(invSlot)
	invSlot = entity
else if string.find(entity, "power") then
		player.stats.Money.Value = playerData["Money"]
		player.stats.ElectricityMade.Value = playerData["ElectricityMade"]
		local powerSlot = "slot"..i
		powerSlot = player.PowerEntities:FindFirstChild(powerSlot)
		powerSlot.Value = entity
	end
end

if string.find(entity, "pc") then
	player.stats.Money.Value = playerData["Money"]
	player.stats.GPUCapacity.Value = playerData["GPUCapacity"]
	local pcSlot = "slot"..i
	pcSlot = player.PCEquipmentEntities:FindFirstChild(pcSlot)
	pcSlot.Value = entity
end

if string.find(entity, "server") then
	player.stats.Money.Value = playerData["Money"]
	player.stats.Multiplication.Value = playerData["Multiplication"]
	local serverSlot = "slot"..i
	serverSlot = player.ServerEquipmentEntities:FindFirstChild(serverSlot)
	serverSlot.Value = entity
end

if string.find(InvIdentifier, "InDelete") then
	player.stats.Money.Value = playerData["Money"]
	player.stats.ElectricityMade.Value = playerData["ElectricityMade"]
	player.stats.ElectricityUsed.Value = playerData["ElectricityUsed"]
	player.stats.CryptoGen.Value = playerData["CryptoGen"]
	player.stats.TotalGPUS.Value = playerData["TotalGPUs"]
	local invSlot = "slot"..i
	invSlot = player.Inventory:FindFirstChild(invSlot)
	invSlot.Value = "Deleted"
	if string.find(entity, "gpu") then
		local gpuSlot = "slot"..secondI
		gpuSlot = player.GPUEntities:FindFirstChild(gpuSlot)
		gpuSlot.Value = "Deleted"
	else if string.find(entity, "power") then
			local  powerSlot = "slot"..secondI
			powerSlot = player.PowerEntities:FindFirstChild(powerSlot)
			powerSlot.Value = "Deleted"
		end
	end
end

if string.find(InvIdentifier, "Business") then
	player.Business.BName.Value = playerData["Name"]
end
end

saveData.OnServerEvent:Connect(SaveDataServer)

Curious if this is bad practice

The proper way to manipulate any currency is for the client to send a request to the server, such as purchase or sell, then have the server validate that request to make sure the client can actually have that action done. If the request is valid, continue on with your function. Otherwise, break out of the function

1 Like

Cheers excellent answer, thanks mate