Why is my datastore saving in client tests?

Im running into an issue where my game’s DataStore is saving data on cleint tests. I’ve got a system that handles baton (my in game product) ownership using DataStores, and I’m seeing that the data ends up saving locally on the client.

I’m not sure if this is a problem or if it’s a good practice in certain situations. I thought DataStores should be saving on the server for proper persistence, but it seems like it’s not working the way I expected

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local ownershipDataStore = DataStoreService:GetDataStore("OwnershipDataStore56")
local OtherStuff = DataStoreService:GetDataStore("OtherStuff")
local currencyName = "Coins"

local Remotes = ReplicatedStorage:WaitForChild("Remotes")

local BatonNames = {
	"Blue Baton",
	"Electric Baton",
	"Golden Baton",
	"Hammer Baton",
	"Lightsaber Baton",
	"Magic Baton",
	"Red Baton",
	"Scythe Baton",
	"Robux Baton",
	"White Baton",
	"Blade Baton",
}

local prices = {
	["Blue Baton"] = 100,
	["Electric Baton"] = 200,
	["Golden Baton"] = 300,
	["Hammer Baton"] = 400,
	["Lightsaber Baton"] = 500,
	["Magic Baton"] = 600,
	["Red Baton"] = 700,
	["Scythe Baton"] = 800,
	["Vip Baton"] = 900,
	["White Baton"] = 1000,
	["Blade Baton"] = 1100,
}

local toolsFolder = game:GetService("ServerStorage"):FindFirstChild("Baton")
local tools = {}

for _, batonName in ipairs(BatonNames) do
	tools[batonName] = toolsFolder and toolsFolder:FindFirstChild(batonName)
end

game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Value = 5555
	coins.Parent = leaderstats
	local folder = Instance.new("Folder")
	folder.Name = "Ownership"
	folder.Parent = player
	
	local BatonSelected = Instance.new("StringValue") -- // Hey Next person to work on this, you can use this to find what the player currently has equipped!
	BatonSelected.Parent = player
	BatonSelected.Name = "BatonSelected"
	BatonSelected.Value = "Blue Baton"

	for _, batonName in ipairs(BatonNames) do
		local batonValue = Instance.new("BoolValue")
		batonValue.Name = batonName
		batonValue.Parent = folder
	end
	
	local d
	
	local success, data = pcall(function()
		d = OtherStuff:GetAsync(player.UserId)
		return ownershipDataStore:GetAsync(player.UserId)
	end)

	if success and data then
		--warn("DATA: ", data)
		for _, batonName in ipairs(BatonNames) do
			local batonValue = folder:FindFirstChild(batonName)
			if batonValue then
				batonValue.Value = data[batonName] or false
			end
		end
		BatonSelected.Value = d["Selected"]
	else
		for _, batonName in ipairs(BatonNames) do
			local batonValue = folder:FindFirstChild(batonName)
			if batonValue then
				batonValue.Value = false
			end
		end
		if not success then
			warn("Failed to load data for " .. player.Name)
		end
	end
	
	task.wait(5)
	Remotes.ShopReady:FireClient(player)
end)

game.Players.PlayerRemoving:Connect(function(player)
	local folder = player:FindFirstChild("Ownership")
	if folder then
		local data = {}
		for _, batonName in ipairs(BatonNames) do
			local batonValue = folder:FindFirstChild(batonName)
			if batonValue then
				data[batonName] = batonValue.Value
			end
		end
		
		local S = {
			["Selected"] = player.BatonSelected.Value
		}
		
		local success, err = pcall(function()
			OtherStuff:SetAsync(player.UserId, S)
			ownershipDataStore:SetAsync(player.UserId, data)
		end)

		if not success then
			warn("Failed to save data for " .. player.Name .. ": " .. err)
		end
	end
end)

game.ReplicatedStorage.PurchaseEquip.OnServerEvent:Connect(function(player, batonName)
	local playerCoins = player:FindFirstChild("leaderstats"):FindFirstChild("Coins")
	local ownershipBaton = player:FindFirstChild("Ownership"):FindFirstChild(batonName)

	if ownershipBaton and ownershipBaton.Value then
		local char = player.Character
		local Hum: Humanoid = char.Humanoid
		
		Hum:UnequipTools()
		
		for i, v in player.Backpack:GetChildren() do
			v:Destroy()
		end

		local tool = tools[batonName]
		if tool then
			print("should equip")
			--local clonedTool = tool:Clone()
			--clonedTool.Parent = player.Backpack
			player.BatonSelected.Value = batonName
		end
	else
		if prices[batonName] then
			if playerCoins and playerCoins.Value >= prices[batonName] then
				playerCoins.Value = playerCoins.Value - prices[batonName]
				
				task.wait(0.5)
				
				local char = player.Character
				local Hum: Humanoid = char.Humanoid
				
				Hum:UnequipTools()
				for i, v in player.Backpack:GetChildren() do
					v:Destroy()
				end

				local tool = tools[batonName]
				if tool then
					local clonedTool = tool:Clone()
					clonedTool.Parent = player.Backpack
					ownershipBaton.Value = true
				end
			end
		end
	end
end)

game.ReplicatedStorage.Equip.OnServerEvent:Connect(function(ply, batonName)
	print("Fired")
	--local isAlreadyEquipped = ply.Character:FindFirstChild(batonName) or ply.Backpack:FindFirstChild(batonName)
	--if isAlreadyEquipped then print("equipedidkwhy") return end
	
	local char = ply.Character
	local Hum: Humanoid = char.Humanoid
	
	Hum:UnequipTools()
	
	for i, v in ply.Backpack:GetChildren() do
		v:Destroy()
	end
	
	local tool = ReplicatedStorage.Batons:FindFirstChild(batonName)
	if tool then
		--print(batonName)
		--print("Tool found: " .. batonName)
		--local clonedTool = tool:Clone()
		--print("Cloned tool: " .. clonedTool.Name)
		--clonedTool.Parent = ply.Backpack
		--print("Tool parented to Backpack")
		ply.BatonSelected.Value = batonName
	else
		print("Tool not found: " .. batonName)
	end
end)

If anyone can find a solution for this it would help alot I dont script so im honestly not sure what to do

What do you mean by these? DataStores nor DataStoreService are not replicated to the clients if that’s what you mean

Everything in that script points to it being a Script (server script) and not a LocalScript (client only script).
What is making you think this is running on the client?

Sorry if I sound a bit stupid I dont script but the reason I think this is because whenever I go into a cilent test anything I buy saves and when i go into the cilent test again its still there that doesnt normally happen with datastores right?

I understand but for some reason whatever I buy is saving in client tests and im not too sure why as its not supposed to

Client Tests? If you mean hitting the Play button in Roblox Studio then that is running the server script along with the client scripts.

Oh so its normal to be saving between tests? Somebody told me its not

Yes it is, Roblox Studio does connect to the same datasore api which normal servers do. Whatever data you save on Studio saves globally, meaning you should use different keys for testing in studio.