Offline Earnings System

Recently, I have been wondering how to create my own an ‘Offline Earnings’ system after seeing many Simulator type games using them, I didn’t want to create it because I have developed a simulator, I just thought it would be helpful for people. Yes, my script may be flawed, I do apologise, I’m not the best, nor am I where I want to be, but it’s the effort that counts! P.S. this is my first forum post!

local DataStoreService = game:GetService("DataStoreService")
local offlineData = DataStoreService:GetDataStore("offlineData")
local dataSave = DataStoreService:GetDataStore("playerData")
local storeService = game:GetService("MarketplaceService")
local maxHours = 24
local maxSeconds = maxHours * 60 * 60

local dataKey = 'whateverkeyyouwant'

local pVals = Instance.new('ArcHandles')
pVals.Name = 'playerVals'
pVals.Parent = game.ServerStorage

function saveData(Player,Cash,Login,Change)
	local playerData = {
		[tostring(Player.UserId)] = {
			savedCash = Cash,
			savedTime = Login,
			savedChange = Change
		}
	}

	dataSave:SetAsync(dataKey,playerData)
end

function dataCheck(passedData)
	if passedData ~= 0 or passedData ~= nil then
		return true
	end
end

Players.PlayerAdded:Connect(function(Player)
	local dataParent = Instance.new('Folder')
	dataParent.Name = 'leaderstats'
	dataParent.Parent = Player
	local cashData = Instance.new('NumberValue')
	cashData.Name = 'Cash'
	cashData.Parent = dataParent
	local pVal = Instance.new('NumberValue')
	pVal.Parent = pVals
	pVal.Name = Player.Name
	local cChange = Instance.new('NumberValue')
	cChange.Parent = Player
	cChange.Name = 'CPS'

	local loadedData
	local success, errormessage = pcall(function()
		loadedData = dataSave:GetAsync(dataKey)
	end)

	if success then
		if loadedData[tostring(Player.UserId)] then
			local personalData = loadedData[tostring(Player.UserId)]
			cashData.Value = personalData.savedCash
			pVal.Value = personalData.savedCash
			if dataCheck(personalData.savedTime) and dataCheck(personalData.savedChange) then
				if personalData.savedTime >= maxSeconds then
					local offlineTime = os.time() - maxSeconds
					local offlineEarnings = (offlineTime * personalData.savedChange) / 4
					cashData.Value += offlineEarnings
				elseif loadedData.savedTime < maxSeconds then
					local offlineTime = os.time() - personalData.savedTime
					local offlineEarnings = (offlineTime * personalData.savedChange) / 4
					cashData.Value += offlineEarnings
				end

				wait()

				saveData(Player,Player.leaderstats.Cash.Value,os.time(),0)
			end
		else
			print(errormessage)
		end

		cashData.Changed:Connect(function()
			if cashData.Value >= pVal.Value then
				local cashDifference = cashData.Value - pVal.Value
				cChange.Value = cashDifference
				wait()
				pVal.Value = cashData.Value
			end
		end)
	end
end)


Players.PlayerRemoving:Connect(function(Player)
	pVals:FindFirstChild(Player.Name):Destroy()
	local leaveTime = os.time()
	local success, errormessage = pcall(function()
		saveData(Player,Player.leaderstats.Cash.Value,leaveTime,Player.CPS.Value)
	end)

	if success then
		print('Successfully saved data!')
	else
		print(errormessage)
	end
end)
18 Likes

looks good but maybe add a function which turns it on so maybe it can be used in a gamepass!

Yeah, I’ll reply to this in a second with it!

1 Like

Sorry, I thought this was scripting support.

Just change the passId Value

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local savedData = DataStoreService:GetDataStore("Cash")
local lastLogin = DataStoreService:GetDataStore("lastLogin")
local cashChange = DataStoreService:GetDataStore("cashChange")
local storeService = game:GetService("MarketplaceService")
local passId = 0

local pVals = Instance.new('ArcHandles')
pVals.Name = 'playerVals'
pVals.Parent = game.ServerStorage

function dataCheck(passedData)
	if passedData ~= 0 or passedData ~= nil then
		return true
	end
end

Players.PlayerAdded:Connect(function(Player)
	local dataParent = Instance.new('Folder')
	dataParent.Name = 'leaderstats'
	dataParent.Parent = Player
	local cashData = Instance.new('NumberValue')
	cashData.Name = 'Cash'
	cashData.Parent = dataParent
	local pVal = Instance.new('NumberValue')
	pVal.Parent = pVals
	local cChange = Instance.new('NumberValue')
	cChange.Parent = Player
	cChange.Name = 'CPS'

	local loadedCash
	local loginData
	local changeData
	local success, errormessage = pcall(function()
		loadedCash = savedData:GetAsync(Player.UserId)
		loginData = lastLogin:GetAsync(Player.UserId)
		changeData = cashChange:GetAsync(Player.UserId)
	end)

	if success then
		cashData.Value = loadedCash
		pVal.Value = loadedCash
		if storeService:UserOwnsGamePassAsync(Player.UserId,passId) then
			if dataCheck(loginData) and dataCheck(changeData) then
				local offlineTime = os.time() - loginData
				local offlineEarnings = (offlineTime * changeData) / 4
				cashData.Value += offlineEarnings
				
				wait()
				
				cashChange:SetAsync(Player.UserId,0)
			end
		end
	else
		print(errormessage)
	end

	cashData.Changed:Connect(function()
		if cashData.Value >= pVal.Value then
			local cashDifference = cashData.Value - pVal.Value
			cChange.Value = cashDifference
			wait()
			pVal.Value = cashData.Value
		end
	end)
end)


Players.PlayerRemoving:Connect(function(Player)
	local leaveTime = os.time()
	local success, errormessage = pcall(function()
		savedData:SetAsync(Player.UserId,Player.leaderstats.Cash.Value)
		lastLogin:SetAsync(Player.UserId,leaveTime)
		cashChange:SetAsync(Player.UserId,Player.CPS.Value)
	end)

	if success then
		print('Successfully saved data!')
	else
		print(errormessage)
	end
end)
1 Like

Wow looks great maybe try changing it to a boolean value instead of a num one
:+1:

for what exactly? there are multiple values…

oops my mistake I hadn’t fully read the script

It was the pass id

ahhh, no problem! it happens lmao

Avoid using more DataStores than you have to. You can put all this related data into one DataStore with the key as the player’s UserId and the value as a dictionary containing all this. Having a DataStore per value of data causes an anti-pattern of too much DataStore use. In your case, you’re hitting a 3n rate limit where per player, 3 Get/Set requests are ran. You’re sure to get throttles very quickly.

I know, I should really use a table for storing data, let me do that rq

done it and edited the original post, thanks for brining this to light!

This is cool, cant use it due to having an already existing cash creating script, and don’t know how to edit this.

just change the part where is has leaderstats.cash.value!

your code seems to create new stats

this bit

then edit it… not too hard to do is it?

1 Like

Hey can you swoop in real quick send me the gamepass code but without the part that ads new stats please :slight_smile: Im really busy to do it myself sorry

1 Like
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local savedData = DataStoreService:GetDataStore("Cash")
local lastLogin = DataStoreService:GetDataStore("lastLogin")
local cashChange = DataStoreService:GetDataStore("cashChange")
local storeService = game:GetService("MarketplaceService")
local passId = 0

local pVals = Instance.new('ArcHandles')
pVals.Name = 'playerVals'
pVals.Parent = game.ServerStorage

function dataCheck(passedData)
	if passedData ~= 0 or passedData ~= nil then
		return true
	end
end

Players.PlayerAdded:Connect(function(Player)
	if success then
		cashData.Value = loadedCash
		pVal.Value = loadedCash
		if storeService:UserOwnsGamePassAsync(Player.UserId,passId) then
			if dataCheck(loginData) and dataCheck(changeData) then
				local offlineTime = os.time() - loginData
				local offlineEarnings = (offlineTime * changeData) / 4
				cashData.Value += offlineEarnings
				
				wait()
				
				cashChange:SetAsync(Player.UserId,0)
			end
		end
	else
		print(errormessage)
	end

	cashData.Changed:Connect(function()
		if cashData.Value >= pVal.Value then
			local cashDifference = cashData.Value - pVal.Value
			cChange.Value = cashDifference
			wait()
			pVal.Value = cashData.Value
		end
	end)
end)


Players.PlayerRemoving:Connect(function(Player)
	local leaveTime = os.time()
	local success, errormessage = pcall(function()
		savedData:SetAsync(Player.UserId,Player.leaderstats.Cash.Value)
		lastLogin:SetAsync(Player.UserId,leaveTime)
		cashChange:SetAsync(Player.UserId,Player.CPS.Value)
	end)

	if success then
		print('Successfully saved data!')
	else
		print(errormessage)
	end
end)

Im going to need it work it out later

1 Like

huh? what do you mean by

Im going to need it work it out later

I feel like something may be off, with it getting the existing leaderboard, but cant fix it right now.