Suphi's DataStore Module

Is there a downside to opening 5 datastores?
(“Players”, slot, player.UserId) there will be 5 slots which can hold a lot of data (mmorpg game)

I am needing this to grab the players character information to render their character in the character selection screen and when all is rendered the datastores will close. (And the proper one will open on whatever save slots they select to play on)

Or is there a way to just grab the character data from the datastore without loading all of the unnecessary data such as (inventory, quests, etc)?

I heard it was optimal to have a single datastore per player, have you looked into using tables first?

I have thought about using one data store with tables as save slots however, since this will be a big game with lots of data to save i am afraid i will reach close to the max data storage limit.

2 Likes

After selecting a slot if you only keep 1 session open that’s fine

But if you keep all 5 open and modify all 5 at the same time it possibly that 1 session saves then roblox server go down and another session does not save

When you first load the slots it might be better to not use SDM and just use getasync just to select a slot

Hi! There is one thing currently preventing from migrating from ProfileService to your DataStore module and that is the Queue system
ProfileService currently saves the queue into a normal datastore, allowing it to not expire, in case a gifter gifts an item to a giftee, but the giftee doesn’t log in within the next 45 days
Is there a built in way to fix this, or would I have to write my own system for this?

Thank you!

there is not a built in way but you can make your own

here is a untested quick example

local DataStoreModule = require(11671168253)
local DataStoreService = game:GetService("DataStoreService")
local globalUpdateDataStore = DataStoreService:GetDataStore("GlobalUpdate")

local functions = {
    GiveItem = function(datastore, item, amount)
        datastore.Value.Inventory[item] += amount
    end,
    UpdateType2 = function(datastore, ...)
        print(...)
    end,
}

local function AddGlobal(dataStore, updateType, ...)
    local data = {...}
    table.insert(data, updateType)
    return pcall(globalUpdateDataStore.UpdateAsync, globalUpdateDataStore, dataStore.Key, function(value)
        value = value or {}
        table.insert(value, data)
        return value
    end)
end

local function SyncGlobal(dataStore)
    if dataStore.State ~= true then return false end
    local datas = nil
    local success, returnedValue = pcall(globalUpdateDataStore.UpdateAsync, globalUpdateDataStore, dataStore.Key, function(value)
        if dataStore.State ~= true then return end
        datas = value or {}
        return {}
    end)
    if success == false then return false end
    if returnedValue == nil then return false end
    if dataStore.State ~= true then error("Global datastore emptied but player datastore is not open PANIC!!!") end
    for index, data in datas do
        local updateType = table.remove(data)
        if functions[updateType] then functions[updateType](table.unpack(data)) end
    end
    -- if Roblox servers go down and the players datastore is not saved from here
    -- the global updates will be lost
    return true
end

game.Players.PlayerAdded:Connect(function(player)
    local dataStore = DataStoreModule.new("Player", player.UserId)
    dataStore.AddGlobal = AddGlobal
    dataStore.SyncGlobal = SyncGlobal
end)
 local success = dataStore:AddGlobal("GiveItem", "Coin", 1)
 local success = dataStore:AddGlobal("UpdateType2", "A", "B", 1, 2, 3)
 local success = dataStore:SyncGlobal()

Is it possible to get each value saved in a scope (so you for example can create a search tab of top players and see all their stats)

You can by loading all datastores to workout the top players but this is going to use all your datastore request so the correct way to do this is to use a ordered datastore like this

local DataStoreModule = require(11671168253)
local orderedDataStore = game:GetService("DataStoreService"):GetOrderedDataStore("Coins")

local function Saving(value, dataStore)
    -- everytime the players datastore saves also save there coins amount into the ordered datastore
    orderedDataStore:SetAsync(dataStore.Key, value.Coins)
end

game.Players.ChildAdded:Connect(function(player)
    local dataStore = DataStoreModule.new("Player", player.UserId)
    dataStore.Saving:Connect(Saving)
end)

this will create a ordered datastore and save the coins amount into it
then you can get the top players like this

-- get pages of entries in decending order with 100 items per page
local pages = orderedDataStore:GetSortedAsync(false, 100)
-- get the first 100 entries
local entries = pages:GetCurrentPage()
-- loop the first 100 entries and print the index, player.UserId and Coins that player has
for index, entry in entries do
	print(index, ")  ", entry.key, ":", entry.value)
end

don’t forget to use pcall for SetAsync and GetSortedAsync as they both can error
you can get more information here:

1 Like

(my game isn’t yet released so I don’t need to worry about migrating data around can just start clean)
Chat do you recommend this Datastore module over ProfileService? While I do enjoy using ProfileService some of it is a bit confusing so I haven’t been able to use it to the fullest extent.

Basically the game I’m working on is a Roleplay game so I won’t have a lot of data to save but I will have quite a bit stacked up,

Just basically an idea of the data were saving right now

  • Purchased Vehicles

    • Customization Data
      • License Plate
      • Body Color
      • (Rim Color)
      • (Headlight Color)
      • (Window Tint - Transparency)
      • (Other Body Customization, not added yet but planned for future)
    • Purchase Information
      • OS Date of Purchase
      • Price vehicle purchased at
  • Accessible Roles

    • List of roles user has access to (wont be many in this list)
  • Player History (Expected to store a lot of data)

    • Player Join/Leave History
    • Admin Commands executed on player
    • Money Purchased/Spent
    • Vehicles Purchased/Sold
    • Other Player History
  • Transaction History (Expected to store a lot of data)

    • Vehicle Purchase/Sold
    • Weapons Purchased
    • Items Purchased
    • Withdraws/Deposits/Transfers
    • (Really anything with the cash system)
  • Criminal History (Not expected to store a whole lot of information but I would like to be fetch and add data if the player is in another server or not in the server)

    • (You get the idea)
3 Likes

Some people might recommend SDM and some might recommend PS the only way to know for sure would be to just try both and pick the one you like more. Looking at the source code might also help you decide

When it comes to the data your going to save, no datastore module is going to restrict you. The only restrictions are set by Roblox not by the module, so it does not really mater what data your going to save all modules will have no problem saving it as long as Roblox accepts it.

I want to open a players DS so should I use find or new? I’m not 100℅ sure of the difference even though i watched your videos on it. It’s a gang/crew system and when the owner deletes their crew I want to edit the members data so they’re not in the crew anymore.

Hello, sorry for bumping on this topic. I have a question regarding this module in itself:

  1. Does it support typechecking? (i.e: I have a module that’s based on typechecking; meaning I’d have to define the type for it to work.) Previously, I’m using ProfileService, however; it doesn’t seem to have any typecheck support unfortunately.
type ModuleObject = {
   Name: string,
   Data: CustomDataStore? -- like this...

} & Methods;

type Methods = {
   . . .
}

  1. Is there a proper way to determine if a data is loaded or not (i.e: Signal) (and also a way to handle data that failed to load?) I have a custom player loading system, where the client will send an event to the server and the server will verify that player’s custom class object. I wanna make sure that their data is loaded properly before they are verified (AKA prevent exploiters from bypassing their load before their data is even fully loaded if that makes sense.)
Event.Load:Connect(function(player: Player): boolean
	local playerObject = PlayerService:GetPlayer(player) -- custom PlayerService module; returns player object

	if playerObject then
        -- data is a BasicState (a public resource module)
		if not playerObject.Data:Get("IsLoaded") then
			-- Mark player data as loaded and signal player loading.
			playerObject.Data:Set("IsLoaded", true)
			PlayerService.Signal.PlayerLoaded:Fire(playerObject)

			return true
		end
	end

	return false
end)

  1. Does this module support the use of ReplicaService? My game heavily rely on the use of Replicas to communicate between client/server.

new will give you a existing datastore object but if one does not exist it will create a new one

find will give you a existing datastore object but if one does not exist it will return nil and not make a new one


normally you use new inside the playeradded event and find everywhere else

  1. yes it does have typechecking

  2. there is the StateChanged signal that will tell you when the datastore loads

dataStore.StateChanged:Connect(function(state, datastore)
    if state == true then
        print("Datastore is ready to use")
    end
end)

or you can just check the state using the datastore object directly no need make your own “IsLoaded” property

if dataStore.State == true then
    print("Datastore is ready to use")
end

you can check if there datastore fails to load like this

-- try to open the session
local response, responseData = dataStore:Open()

-- If the session fails to open lets warn
if response ~= "Success" then
    warn(dataStore.Id, response, responseData)
end
  1. iv never used ReplicaService but datastore.Value is just a simple variable that can be set to anything in most cases its set to a table so if ReplicaService is capable of replicating a standard table then it should work

I would recommend watching the basics video as it covers questions 1 and 2

Does find not work in the command bar? I have a module that handles something and uses find within the module. When required in a regular script it works but when the module is used on the command bar, find doesn’t work

1 Like

The command bar works in a different VM/Actor this means that when you require the module your not requiring the same module all your scripts are but a brand new module and in that brand new module the datastore object does not exist so find is not able to find it

2 Likes

I had a studio crash. This seemingly locked the datastore for roughly 5-10 minutes.

  • Is this intended behavior on server crash? (the session didn’t close)
  • Any way to change this time?

If I understand correctly, the advantage of MemoryStores is that they’re more precise in timing compared to os.time. So this time could maybe be reduced?

LockInterval(60 seconds) * LockAttempts(5 attempts) = LockTime(5 min)

you could reduce LockInterval to 30 and LockAttempts to 3 but I would not really recommend going any lower

LockInterval(30 seconds) * LockAttempts(3 attempts) = LockTime(1 min 30 seconds)

the lower you go the higher the chance the session might close if Roblox servers have micro outages but the higher you go the more resilient it will be to micro outages

2 Likes

Thanks for your reply.

Could you explain what you mean with the micro outages?

The way I understand it is that any server crash will cause the sessions to remain locked for 5 mins.

Case A
if you set LockInterval to 60 and LockAttempts to 5 then the memorystore will need to fail 15 times over a time span of 5 minutes before the session will close


Case B
if you set LockInterval to 30 and LockAttempts to 3 then the memorystore will need to fail 9 times over a time span of 1 minutes and 30 seconds before the session will close


lets say for example Roblox memorystore servers go down for 3 minutes then comes back online

in Case A the session will stay open and you wont notice anything

in Case B the session will automatically close after 1 minutes and 30 seconds and the state of the datastore object will change back to false while the state is false you wont be able to use the datastore until you reopen the datastore using the Open() function

(if your using my player example, that example will keep trying to reopen the session every 6 seconds until Roblox servers start working again)

but if Roblox memorystore servers go down for longer then 5 minutes then both Case A and Case B will close

so this means that Case A will allow Roblox servers to go down for up to 5 minutes and it wont be effected

and this means that Case B will allow Roblox servers to go down for up to 1 minutes and 30 seconds and it wont be effected

if you look here you can see that sometimes there are internal errors and having a longer lock time will prevent these errors from effecting the status of your datastore

2 Likes