Help creating VIP subscription system

Recently, I decided to make a game with one of my friends, and as I was coding, I came across an idea that I wanted to use in my game: A VIP system, such as the one in world//zero where when you pay “x” amount of robux, you earn vip for 1 or 2 days.

I attempted to do that along side a daily rewards system, but , being the fairly new coder myself, I easily got overwhelmed by all the numbers and times involved.

I followed ( to an extent) a tutorial on daily rewards and attempted to edit it in order to have a vip count down as well but it just isnt working out for me.

I’m getting no errors, and its printing my stats, but for simplicities sake, I tried to set the daily reward timer to 1 minute in order to earn jewels at a faster rate, but even after waiting one minute in studio, I got nothing.

Here is the pastebin to my code since it is pretty lengthy, I added some notes inside as well. any help given would be a huge help because currently I’m so lost :stuck_out_tongue:

Im not too good with the math aspect of os.time() so that could be one of my errors.

Thanks!

1 Like

First thing, datastores aren’t available in studio by default. Does it work out of studio?

Havent tested it outside of studio, however, inside studio I turned on
image

which should make it compatible with studio tests shouldnt it?

EDIT: just tried waiting a minute in that actual roblox player, still doesnt work.
image
after waiting a minute, jewels should be 20 and reward day should be 2

Making timed memberships is quite easy, since all you have to do is

local expiration = os.time() + 86400
-- This would be the current time + 1 Day

Now you can check in your script if the user is a vip by comparing expiration with the current time.

if expiration > os.time() then
    print("user is vip")
end

You can do this as well with daily rewards


By the way, I fixed the script a bit so it should work. I also commented every change I did, if you don't understand a change, a comment or I forgot to comment a change feel free to ask. (Errors may appear as I can not guarantee this will work since I did not test this, so there is a possibility that you will need to fix some errors yourself)
Code
local DatastoreService = game:GetService("DataStoreService")
local Datastore = DatastoreService:GetDataStore("PlayerInfo")

-- This allows the PlayerRemoving event to save the players data
local ServerUserData = {}
 
local TimeScale = {
	SECOND = 0,
	MINUTE = 60,
	HOUR = 3600,
	DAY = 86400
}

-- DailyRewardTime is now in seconds
local DailyRewardTime = TimeScale.DAY * 1 -- One day
local DailyRewards = {
	DailyReward1 = 20,
	DailyReward2 = 40,
	DailyReward3 = 80,
	DailyReward4 = 160,
	DailyReward5 = 320,
	DailyReward6 = 640,
}

-- I removed HasVip and changed RewardWait to LastReward
local Stats = {
    VipTime = 0;
   
    RewardWait = os.time();
    RewardDay = 0;
   
    HasBoughtPremium = false;
   
    Jewels = 0
}

 
game.Players.PlayerAdded:Connect(function(plr)
	-- We get the userdata within the event, and if it is nil, we set the userdata to Stats
	local userdata = Datastore:GetAsync(plr.UserId)
	userdata = userdata ~= nil and userdata or Stats
   
	-- We moved the VIP check to the bottom to ensure we are using the updated value in case the user has a 7 day streak

	-- Check if the last reward was within the DailyRewardTime span
	-- Note: We are changing the LastReward to os.time()
	local LastReward = userdata.LastReward - os.time()
	if LastReward >= DailyRewardTime and LastReward < DailyRewardTime * 2 then
		-- Increase the daily count since it is 1 day since the last reward
		userdata.RewardDay = userdata.RewardDay + 1
	elseif LastReward >= DailyRewardTime * 2 then
		-- Reset the daily count since it is 2 days since the last reward
		userdata.RewardDay = 0
	end
	-- In your old script, you checked if the RewardDay is 7 after you set it back to 0, so I moved it outside after the RewardDay check
	if userdata.RewardDay == 7 then
		-- They played 7 days in a row
		userdata.VipTime = os.time() + TimeScale.HOUR * 1 -- Give them one hour of VipTime
	else
		userdata.Jewels = userdata.Jewels + DailyReward["DailyReward" .. userdata.RewardDay] -- No need to use tostring here
	end

	-- Check if the user is still VIP
	-- Note: We are changing the VipTime to os.time() + seconds you want to add
	if userdata.VipTime > os.time() then
		print("user is vip")
	end

	-- Put userdata into the ServerUserData table we created above so we can save when the player leaves
	ServerUserData[plr.Name] = userdata

	-- Now print all values :)
    for i,v in pairs(userdata) do
        print(i,v)
    end
end)

-- Utility function for saving
function nilAndDefault(value, default)
	return value ~= nil and value or default
end
 
game.Players.PlayerRemoving:Connect(function(plr)
	local userdata = ServerUserData[plr.Name]
	Datastore:UpdateAsync(plr.UserId, function(oldData)
		-- Check if oldData exists, if not use Stats as base
		local newData = nilAndDefault(oldData, Stats)

		-- Set each value
		for i,v in pairs(userdata) do
			newData[i] = v
		end

		-- Return newData so the Datastore saves
		return newData
	end)
end)
5 Likes

This helps so much!!!

I just have a couple questions:

  1. For line 47, when you defined “local LastReward” I’m getting an error that i’m subtracting nil from number, even after I changed “RewardWait” to “LastReward”

  2. Say if I wanted to display a countdown for VIP, could I do this by firing over the number remaining for VIP, subtracting os.time and dividing it by roughly 3600 to get the amount in hours, then just have a repeat function count it down in game?

for now those are my only question, but again dude, seriously this helps me a lot

EDIT: Solved #1, i was using the old data store key which had nil values that correspond with this one.

  1. Good to know it’s fixed :slight_smile:
  2. To find out the time remaining in seconds, just do this userdata.VipTime - os.time(). If it is above > 0 then the user still has VIP and you have the time in seconds remaining. If it is below 0 then the user is not VIP and you don’t need the value.

Also, it doesnt seem to be saving the jewels / not giving the daily reward, I changed the daily reward so that the day goes up by 1 every minute and then i get my reward but I havent got anything and my rewards day is still printing 0.

Heres the edited code:

Code
local DatastoreService = game:GetService("DataStoreService")
local Datastore = DatastoreService:GetDataStore("PlayerInformation")

-- This allows the PlayerRemoving event to save the players data
local ServerUserData = {}
 
local TimeScale = {
	SECOND = 0,
	MINUTE = 60,
	HOUR = 3600,
	DAY = 86400
}

-- DailyRewardTime is now in seconds
local DailyRewardTime = TimeScale.MINUTE * 1 -- changed to one minute
local DailyRewards = {
	DailyReward0 = 0, -- added this because without a day 0 it errors.
	DailyReward1 = 20,
	DailyReward2 = 40,
	DailyReward3 = 80,
	DailyReward4 = 160,
	DailyReward5 = 320,
	DailyReward6 = 640,
}

-- I removed HasVip and changed RewardWait to LastReward
local Stats = {
VipTime = 0;
   
LastReward = os.time();
RewardDay = 0;
   
HasBoughtPremium = false;
   
Jewels = 0
}

 
game.Players.PlayerAdded:Connect(function(plr)
	-- We get the userdata within the event, and if it is nil, we set the userdata to Stats
	local userdata = Datastore:GetAsync(plr.UserId)
	userdata = userdata ~= nil and userdata or Stats
   
	-- We moved the VIP check to the bottom to ensure we are using the updated value in case the user has a 7 day streak

	-- Check if the last reward was within the DailyRewardTime span
	-- Note: We are changing the LastReward to os.time()
	local LastReward = userdata.LastReward - os.time()
	if LastReward >= DailyRewardTime and LastReward < DailyRewardTime * 2 then
		-- Increase the daily count since it is 1 day since the last reward
		
		userdata.RewardDay = userdata.RewardDay + 1
	elseif LastReward >= DailyRewardTime * 2 then
		-- Reset the daily count since it is 2 days since the last reward
		userdata.RewardDay = 0
	end
	-- In your old script, you checked if the RewardDay is 7 after you set it back to 0, so I moved it outside after the RewardDay check
	if userdata.RewardDay == 7 then
		-- They played 7 days in a row
		userdata.VipTime = os.time() + TimeScale.HOUR * 1 -- Give them one hour of VipTime
	else
		userdata.Jewels = userdata.Jewels + DailyRewards["DailyReward" .. userdata.RewardDay] -- No need to use tostring here
	end

	-- Check if the user is still VIP
	-- Note: We are changing the VipTime to os.time() + seconds you want to add
	if userdata.VipTime > os.time() then
		print("user is vip")
	end

	-- Put userdata into the ServerUserData table we created above so we can save when the player leaves
	ServerUserData[plr.Name] = userdata

	-- Now print all values :)
for i,v in pairs(userdata) do
    print(i,v)
end
end)

-- Utility function for saving
function nilAndDefault(value, default)
	return value ~= nil and value or default
end
 
game.Players.PlayerRemoving:Connect(function(plr)
	local userdata = ServerUserData[plr.Name]
	Datastore:UpdateAsync(plr.UserId, function(oldData)
		-- Check if oldData exists, if not use Stats as base
		local newData = nilAndDefault(oldData, Stats)

		-- Set each value
		for i,v in pairs(userdata) do
			newData[i] = v
		end

		-- Return newData so the Datastore saves
		return newData
	end)
end)

I know its not wise, but do you think instead of :UpdateAsync() that I should use :SetAsync()?

1 Like

After a lot of looking at it myself I figured out all the issues! thanks again @SimplyData!