Help on Making a Daily, Self-Updating Shop?

Hey there, thanks for coming to check this out.

Anyways, straight to my point. What would be the most efficient way to code a self-updating, daily shop? (Like Fortnite)

What I’m trying to do is a daily shop system (that I’m using a module to get an external time for, so the times should be synced on all servers) to update every 24 hours (I’ve got that figured out already, and I’ve already offset it to 5 PM PST). But, for the items, I’m using a function from a module with weighted randomization, so using a seed wouldn’t generate the same set of items, probably meaning that items across servers would be different.

If you want more info on the function I’m using, it’s a “roll” function I’m using in a module that returns a random rarity. Obviously, I could just make this run in a for loop and then pick random items based on the returned rarity, and boom, that’s how I am planning to get my set of items.

To sync and update, my idea is to use MessagingService; but the problem is that I don’t know how to fully make sure it would only publish the Async once.

Really, in my idea, what I’m trying to do is generate the list of items on one server, and then publish the list to the MessagingService topic and then have servers update on the callback function.

The problem with this is, if there’s no way for me to “debounce” the PublishAsync, wouldn’t the function I’m using that generates items just keep running over and over and republish again until every server has republished their own set of items? (servers would just keep overwriting each other)

The only thing I’ve come up with so far to counter this is to maybe make a bool value in a datastore and then have it act as a debounce. Is this possible?

Basically, I have the daily part figured out. I just need help with the self-updating + server syncing part. How would I debounce a :PublishAsync()?

Also, I want to see if anyone else can come up with another way to do this, so feel free to leave any different ways to do this.

Thanks :pray:

EDIT:

I’ve shifted my idea from MessagingService to this:

I could generate a set of items, and then save it to a data store WITH the seed generated for that day. Then, I could add a conditional where it does :GetAsync and then check to see if the seed value in the data store is equal to the seed generated for the current day (which would be the same for every server, as long as its the same day). That way, if the value isn’t the same, it would generate a new set and update the data store with the new seed value. Only thing is wouldn’t all of the servers end up doing :GetAsync at the same time? So, they would all detect an old value because there’s no way for a server to “wait” for another server to update it? Or would it still work because all the servers are still updating the Async regardless?

All I need to know how to do to solve this problem is one of these options:

  • Find a way to generate new items with a seed and keep weighted randomization (to keep different rarities)
  • Update a data store or a global value somewhere + make sure servers aren’t overwriting each other

Or, any other way someone can offer.

Here’s my code so far for it to update every 24 hours at 5:00 PM PST. I still need to add a seed, of course.

6 Likes

Not all of us have played this game, so maybe provide a picture for references?

2 Likes

Sure, I guess. But I’m not trying to make my shop LOOK like Fortnite, I’m just trying to figure out how to do a shop function like Fortnite’s, where it randomnly generates a set of items and updates it daily, and in sync, across all servers.

I know how to generate the items, but I don’t know how to pass that SAME SET of items across all the servers and have them update in real-time.

1 Like

I had a similar issue when I was building the daily quest system for my game.

You can seed the math.random function with the specific day (since it is synced between servers). Then you get a random number that is synced between all server and you never have to run publishAsync or other messaging functions.

Afterwards you can do your item generation routines and stuff.

1 Like

You could use a table like this:

local Items = {"Item1", "Item2", "Item3", "Item4",}
local Daily_Items = {

            ["Daily_Item1"] = math.random(1, #Items),
            ["Daily_Item2"] = math.random(1, #Items),
            ["Daily_Item3"] = math.random(1, #Items),
            ["Daily_Item4"] = math.random(1, #Items)

}

--[[ Then make them just like if Daily_Item1 is a Emote called test for example, make it so it will show on the spot a gui with the emote called test and others.
]]--
1 Like

Yeah, I get this concept. Okay, the problem is that my function to pick items is completely random.

So, if a new day passed, I would get my function to roll and pick 10 different items, for example. But, this wouldn’t sync the items across all servers because they’re rolling 10 times in a completely random function.

Do you know how I can generate items with a seed, and still keep weighted randomization (like Common, Rare, Legendary)? If there is, I can switch to that.

1 Like

This wouldn’t sync across all servers, though, right? Because math.random isn’t always going to be the same set of numbers.

How would I generate by seed, and still keep weight of the rarities?

1 Like

In order to get the same randomness on all the servers just seed the randomness with the day. How you handle the randomness to generate items is a different story.

You can have the function that picks items have “seed” as an argument so when you call it you can also pass it the seed for the math.random functions.

1 Like

The easiest thing to do is have a external server handle the random items. And use httpService to get it in each server. And update it every hour or something

I recommend using glitch (because its free) and the server should have an endpoint that returns the item.

This fixes the problem because its only ONE server.

1 Like

To get it to sync, I recommend you try this tutorial:

Then you update the spreadsheet manually. I would make the key in the spread sheet the date and make the value equal to what item are in the shop.

1 Like

I agree with this, this would fix all my problems. Is Glitch’s language also Lua? Because I wouldn’t know how to code it if it wasn’t. Otherwise, I guess I can learn its language.

Right now though, I’m still trying to see if I can do this in game, so I’ll use this as a last resort option.

1 Like

Its node.js (Javascript). its pretty simple to learn tho

1 Like

This would work, except I’m not trying to update the shop manually, because I know for sure that some times I won’t be available/ won’t have the strength to update every single item in ALL of my future shops every single day. I could look into this and see if I can randomize the keys and values by weight. I’ll also use this as one of my “last resort” options as well.

1 Like

Good idea, I can try to test this out later and look at some results.

1 Like

If you make the key the date, you would only have to update it once a week or something, so if you miss a day it’s not terrible.

1 Like

Yeah, you’re right, but right now the highest priority for me is to find a way to get this done all by code.

1 Like

To get time, use os.time or os.date, called from a server script of course.

For the random numbers, use math.randomseed to make the seed the same in all server instances, perhaps by using the current year and day of year. This was pointed out by another person here as well. For those who don’t know, math.random is pseudorandom, so if the seed is the same across servers, the random numbers will be the same across all servers. Or maybe you can use a non-random rotation.

You may also be able to use datastores for some things, but be aware of its limits.

If you want to get crazy, use httpservice and set up your own server or something.

2 Likes

Maybe try adding a script that uses data stores to save a new random code for the shop every 2 minutes. Sort of like this:

local dss = game:GetService("DataStoreService")
local ShopDS = dss:GetDataStore("ShopDataStore")

local TodaysShop = ShopDS:GetAsync(ShopDS:GetAsync("CurrentDate"))

while wait(120) do
    local totalDates = ShopDS:GetAsync("TotalDates")
    ShopDS:SetAsync(totalDates, RandomNumberHere) -- Replace with your random number thingy
    ShopDS:SetAsync("TotalDates", totalDates + 1)
    if ReplaceWithCurrentHour == 24 and ReplaceWithCurrentMinute > 58 then -- Replace the variables with current hour/minute
        --Shop needs to be updated!
        local yesterday = ShopDS:GetAsync("CurrentDate")
        wait(120)
        ShopDS:SetAsync(yesterday + 1)
        TodaysShop = yesterday + 1
end

That may seem confusing, but if it works it should update the list every 2 minutes. The only limitations I see are the data store limitation, and the fact that you would need to have at least on server running every day at midnight (or whenever you switch shops) and you would need to reset everything every 99999999999999999999 days (so you wouldn’t break the max length of a datastore’s key).

This is probably over-complicated though.

2 Likes

This just gave me an idea. Maybe, I could generate a set of items, and then save it to a data store WITH the seed generated for that day. Then, I could add a conditional where it does :GetAsync and then check to see if the seed value in the data store is equal to the seed generated for the current day (which would be the same for every server, as long as its the same day). That way, if the value isn’t the same, it would generate a new set and update the data store with the new seed value. Only thing is wouldn’t all of the servers end up doing :GetAsync at the same time? So, they would all detect an old value because there’s no way for a server to “wait” for another server to update it? Or would it still work because all the servers are still updating the Async regardless?

All I need to know how to do to solve this problem is one of these options:

  • Find a way to generate new items with a seed and keep weighted randomization (to keep different rarities)

  • Update a data store or a global value somewhere + make sure servers aren’t overwriting each other

Or, any other way someone can offer.

Here’s my code so far for it to update every 24 hours at 5:00 PM PST. I still need to add a seed, of course.

local syncedtime = require(game.ReplicatedStorage.SyncedTime) -- returns a synced time, because it's external. I'm not using os.time() because there have been a lot of reports of it not being synced
 
local lt = require(game.ServerScriptService.LootTable) -- The module I'm using for rolling and picking items
syncedtime.init()

local offset = (60 * 60 * 17)  -- 17 hours, or 61,200 seconds

while wait(1) do
	local t = (math.floor(syncedtime.time())) + offset -- Sets the date to Thursday 5PM PST
	local origtime = t - offset
	
	local daypass = origtime % 86400 -- basically, this would output how far into the day we are in seconds
	print(daypass)
	
	if daypass == 0 then --- we know exactly one day has passed, I'd do all of my shop functions under this
		print("It is 5:00 PM PST.")  

	else
		local timeleft = 86400 - daypass  -- finds out how much time is left until the next day
		
		local timeleftstring = toHMS(timeleft)  -  just formatting to get it to HH:MM:SS 
		
		--print("The time left until 5:00 PM PST is ".. timeleftstring)  <-- Obviously I would just use "timeleftstring" for a timer in a shop
	end
end

Keep in mind this may be too complicated, as you are probably able to arrange for each server to select the items on their own. The items would all be the same, as the random number seeds would all be the same.

Otherwise, to have different weights, just use different random number ranges. You may use
a = math.random(1, 10) for regular items and b = math.random(1, 100) for rare items.

To make sure severs don’t overwrite each other, use the year and day of year as a key (such as 2020150) for each new real life day and use UpdateAsync to check if the data store has been set up for that day. If not, write to the data store. Be sure to keep in the mind the limits of data stores.