Suphi's DataStore Module

So I’ve been using this module and it’s been great. Just a question:
We plan on integrating a web app to alter data using the Open Cloud APIs. Would there be any issues with altering data that way? What happens if:

  • Data is altered from the Open Cloud API while the session is locked? (player data while player is in the game)
  • Data is altered from the Open Cloud API while the session is unlocked? (player data while player is NOT in the game)

These cases are what immediately come to mind, would appreciate your feedback on this. Thanks

1 Like

Is there a Wally package for this?

When the server opens the datastore the data is cached onto the server and all changes you make on the server are not instantly saved to the datastore

If you make any changes to the datastore the server will continue to use the catched version so would not be aware of the changes plus will overwrite the data with it’s local cached version in the next save interval

So using open cloud while a session is open will not work but it will work if the session is not open

1 Like

I have not made one but I’m not sure if someone else has made one

just use
wally search YourModuleHere
to check the modules your looking for

I see… I expected as much :sweat_smile: Would there be a way to check from OpenCloud if a player is in the server using SDM?

From my knowledge open cloud cannot access the memorystore

But some options you have are

  1. you could use the messaging service with open cloud then make the server check if the session is open or closed and make any changes it needs the main problem with this option is making sure not all running server reacts but only 1 server reacts

  2. another option is before you open the session save a value into a separate datastore and after the session closes remove that value from the second datastore then check the second datastore with open cloud the problems with this method are that in the small delay between checking the second datastore and updating the main datastore the state might have changed another problem with this method is that if the server crashes or the datastore goes down the players state might not get updated untill they enter your game again this problem could be fixed with a timeout

  3. another option is for open cloud to add a list of actions into a separate datastore using updateasync then when the player logs into the game you updateasync the second datastore do the list of actions and empty out the data

Option 3 is my favourite

There use to be APIs like
https://api.roblox.com/users/(userID)/onlinestatus/
To check if a player is online but not sure if there still working

3 Likes

Option 3 definitely makes the most sense. My initial thoughts were to use option 2, but I wasn’t sure how to handle those cases.

Thanks for the responses!

1 Like

For option 3 you can also use the saving event that takes place every 30 seconds to process the second datastore so that even when the player is online you can still update there main datastore

2 Likes
local Players = game:GetService("Players")
 local DataStoreModule = require(game.ServerStorage.DataStore)
 
 local template = {"Wins"}
 
 local function StateChanged(state, dataStore)
 	if state ~= true then
		return
	end
 	for _, name in template do
 		dataStore.Leaderstats[name].Value = dataStore.Value[name]
 	end
 end
 
 Players.PlayerAdded:Connect(function(player)
 	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
 	leaderstats.Parent = player
 	
 	for _, name in template do
 		local numberValue = Instance.new("NumberValue")
		numberValue.Name = name
 		numberValue.Parent = leaderstats
 	end
 	
	local dataStore = DataStoreModule.new("Player", player.UserId)
 	dataStore.Leaderstats = leaderstats
 	dataStore.StateChanged:Connect(StateChanged)
	print(dataStore.Value)
end)

do you know why it prints me: nil in output? please.

because the datastore has not opened yet

Thats why we use the state changed event to detect that it has opened

 local Players = game:GetService("Players")
 local DataStoreModule = require(game.ServerStorage.DataStore)
 
 local template = {"Wins"}
 
 local function StateChanged(state, dataStore)
 	if state ~= true then return end
	-- datastore is now open we can see the value
 	print(dataStore.Value)
 	for _, name in template do
 		dataStore.Leaderstats[name].Value = dataStore.Value[name]
 	end
 end
 
 Players.PlayerAdded:Connect(function(player)
 	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
 	leaderstats.Parent = player
 	
 	for _, name in template do
 		local numberValue = Instance.new("NumberValue")
		numberValue.Name = name
 		numberValue.Parent = leaderstats
 	end
 	
	local dataStore = DataStoreModule.new("Player", player.UserId)
 	dataStore.Leaderstats = leaderstats
 	dataStore.StateChanged:Connect(StateChanged)
	-- datastore is not open yet value is still nil
end)

I see but I think the problem is that when she says that the dataStore is open her doesn’t load it or I don’t know because in a normal script, I wanted to make a try and I noticed that the dataStore even if we check if it was opened bah it prints nil in the output

--<<my test script>>--
local DataStoreModule = require(game.ServerStorage.DataStore)
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.find("Player", player.UserId)
	if dataStore == nil then return end
	if dataStore.State ~= true then return end
	print(dataStore.Value)
end)

and my playerData

local Players = game:GetService("Players")
local DataStoreModule = require(game.ServerStorage.DataStore)
local Red = require(game.ReplicatedStorage.Red)

local remoteEvent = Red.Server("AllEvent")

local template = {
 --[[
 IN LEADERSTATS
 ]]
	Wins = 0,
--[[
IN OTHER
]]
    Grade = "Beginer",
	StoreCoins = 0,
}

local function StateChanged(state, dataStore)
	while dataStore.State == false do
		if dataStore:Open(template) ~= "Success" then
		   task.wait(6) 
		end
	end
end

Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	dataStore.StateChanged:Connect(StateChanged)
	StateChanged(dataStore.State, dataStore)
	
end)

Players.PlayerRemoving:Connect(function(player)
	local dataStore = DataStoreModule.find("Player", player.UserId)
	if dataStore ~= nil then
		dataStore:Destroy()
	end
end)

Your test script won’t work because

1 your using find
2 the state will never be true when the player added event fires

I cover this in my basics video

Here is a demo of how to make it work
But using wait is not recommended you should be doing it the same way I show in my basics video

local DataStoreModule = require(game.ServerStorage.DataStore)
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	local state = dataStore.StateChanged:Wait()
	if state ~= true then return end
	print(dataStore.Value)
end)

The reason you should not be using wait is because the state might change back to closed then back to true while the player is in game and if that happens you will want to setup the player again because when the state changes back to true the values in the datastore might have changed by a different server

Thank you very much being French I had a little trouble on the video, now I know how it works thank you very much for your help you had me a lot help

Hey there seems to be a missspelling here


Unless I’m mistaken…

Thank you for pointing that out I have fixed it :slight_smile:

`local Players = game:GetService("Players")
local DataStoreModule = require(game.ServerStorage.DataStore)
local Red = require(game.ReplicatedStorage.Red)

local remoteEvent = Red.Server("AllEvent")

local template = {
 --[[
 IN LEADERSTATS
 ]]
	Wins = 0,
--[[
IN OTHER
]]
    Grade = "Beginer",
	StoreCoins = 0,
	Codes = {}
}

local function StateChanged(state, dataStore, player)
	while dataStore.State == false do
		if dataStore:Open(template) ~= "Success" then
		   task.wait(6) 
		end
	end
end

Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	dataStore.StateChanged:Connect(StateChanged)
	StateChanged(dataStore.State, dataStore, player)
	remoteEvent:Fire(player, "dataStoreEvent", player, dataStore.Value) --EVENT WITH RED
end)

Players.PlayerRemoving:Connect(function(player)
	local dataStore = DataStoreModule.find("Player", player.UserId)
	if dataStore ~= nil then
		dataStore:Destroy()
	end
end)`

Suphis vairement desoler de te rederanger, but when I create a remote event in playerData it works but the dataStore (in RemoteEvent) since it is under playerAdded it will take the game start data while I want that if there is a data change during the game the event takes it into account. I look for a lot of things but I’m nothing

Thank you for your help I hope not to bother.

Use a metatable.

Code:

local Players = game:GetService("Players")
local DataStoreModule = require(game.ServerStorage.DataStore)
local Red = require(game.ReplicatedStorage.Red)

local remoteEvent = Red.Server("AllEvent")

local template = {
 --[[
 IN LEADERSTATS
 ]]
	Wins = 0,
--[[
IN OTHER
]]
	Grade = "Beginer",
	StoreCoins = 0,
	Codes = {}
}

local function StateChanged(state, dataStore, player)
	while dataStore.State == false do
		if dataStore:Open(template) ~= "Success" then
			task.wait(6) 
		end
	end
end

Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	dataStore.StateChanged:Connect(StateChanged)
	StateChanged(dataStore.State, dataStore, player)
	
	local proxyTable = dataStore.Value
	dataStore.Value = {}

	setmetatable(dataStore.Value, {
		__index = function(self, index)
			return proxyTable[index]
		end,

		__newindex = function(self, index, value)
			proxyTable[index] = value
			remoteEvent:Fire(player, "dataStoreEvent", player, dataStore.Value)
		end,
	})

	remoteEvent:Fire(player, "dataStoreEvent", player, proxyTable)
end)

Players.PlayerRemoving:Connect(function(player)
	local dataStore = DataStoreModule.find("Player", player.UserId)
	if dataStore ~= nil then
		dataStore:Destroy()
	end
end)

Whenever you edit dataStore.Value, it will send the event.

@5uphi, if there is a built in way to do this, please tell me.

One option is to use custom functions

-- Custom functions that we will add to all player datastore objects
local function Set(dataStore, key, value)
    if dataStore.State ~= true then return false end 
    dataStore.Value[key] = value
    remoteEvent:Fire(dataStore.Player, key, value) -- Tell the client there data changed
    return true
end

local function Increment(dataStore, key, delta)
    if dataStore.State ~= true then return false end 
    dataStore.Value[key] += delta
    remoteEvent:Fire(dataStore.Player, key, dataStore.Value[key]) -- Tell the client there data changed
    return true
end

game.Players.PlayerAdded:Connect(function(player)
    local dataStore = DataStoreModule.new("Player", player.UserId)
    dataStore.Player = player -- Add what player owns this datastore object
    dataStore.Set = Set -- Add the set function to the datastore object
    dataStore.Increment = Increment -- Add the increment function to the datastore object
end)

once you have added the function inside the datastore object you can call it from any script like this

local dataStore = DataStoreModule.find("Player", player.UserId)
if dataStore == nil then error("DataStore not found") end

-- lets use are custom functions
local success = dataStore:Set("StoreCoins", 100)
local success = dataStore:Increment("Wins", 1)

and by calling the set function it will also fire the remote event

2 Likes

@5uphi, I tried your method but I still can’t I’ll show you my scripts

--PlayerData (ServerScriptService)
local Players = game:GetService("Players")
local DataStoreModule = require(game.ServerStorage.DataStore)
local Red = require(game.ReplicatedStorage.Red)

local remoteEvent = Red.Server("AllEvent")

local template = {
 --[[
 IN LEADERSTATS
 ]]
	Wins = 0,
--[[
IN OTHER
]]
	Grade = "Beginer",
	StoreCoins = 0,
	Codes = {}
}

local function StateChanged(state, dataStore, player)
	while dataStore.State == false do
		if dataStore:Open(template) ~= "Success" then
			task.wait(6) 
		end
	end
end

Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	dataStore.StateChanged:Connect(StateChanged)
	StateChanged(dataStore.State, dataStore, player)
end)

Players.PlayerRemoving:Connect(function(player)
	local dataStore = DataStoreModule.find("Player", player.UserId)
	if dataStore ~= nil then
		dataStore:Destroy()
	end
end)
--the script you sent me (ServerScriptService)
local DataStoreModule = require(game.ServerStorage.DataStore)
local Red = require(game.ReplicatedStorage.Red)

local remoteEvent = Red.Server("AllEvent")
local function Set(dataStore, name, value)
	if dataStore.State ~= true then return false end 
	dataStore.Value[name] = value
	remoteEvent:Fire(dataStore.Player, name, value)
	return name
end

local function Increment(dataStore, name, delta)
	if dataStore.State ~= true then return false end 
	dataStore.Value[name] += delta
	remoteEvent:Fire(dataStore.Player, name, dataStore.Value[name])
	return true
end

game.Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	dataStore.Player = player
	dataStore.Set = Set
	dataStore.Increment = Increment
end)
-- Custom Function (ServerScriptService)
local DataStoreModule = require(game.ServerStorage.DataStore)

game.Players.PlayerAdded:Connect(function(player)
	local dataStore = DataStoreModule.new("Player", player.UserId)
	local state = dataStore.StateChanged:Wait()
	if state ~= true then return end

	local success = dataStore:Set("Grade", dataStore.Value.Grade)
	local success = dataStore:Increment("Codes", dataStore.Value.Codes)
end)
--ManageCode (ReplicatedStorage
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = game.Players.LocalPlayer
local Red = require(ReplicatedStorage.Red)
local rewardModule = require(ReplicatedStorage.InformationModule)

local remoteEvent = Red.Client("AllEvent")
local codeFrame = player.PlayerGui.CenterGui.CodeFrame.InfoFrame

local code = {}
code.__index = code

function code.new(text: string?, storeCoins: number?, Expired)
	local newCode = {}
	setmetatable(newCode, code)
	newCode.Code = text
	newCode.StoreCoins = storeCoins
	newCode.Expired = Expired
	newCode.Redeem = false
	return newCode
end

function code:CodeEnter(codesValue)
	if self.Expired == true then
		rewardModule:cloneVotingText(codeFrame, "This code Expired")
	else
		if table.find(codesValue, self.Code) then
			rewardModule:cloneVotingText(codeFrame, "Code already Redeem")
		else
			remoteEvent:Fire("codeRedeem", self.Code, self.StoreCoins)
			rewardModule:cloneVotingText(codeFrame, "Code Redeem")
		end
	end
end
return code


--MainScript (Client)
--CodeFrame:
remoteEvent:On("Codes", function(codesValue)
	enterCode.FocusLost:Connect(function()
		local code = orderedCode[enterCode.Text]
		if code then
			code:CodeEnter(codesValue)
		else
			informationModule:cloneVotingText(codeGui, "This Code Doesnt exist")
		end
	end)
end)
1 Like