What is Johhny's Custom DataStore?
It is a wrapper that's designed to make using DataStores more convenient, by:
- Removing the need to use pcall or xpcall, as the module uses them internally
- Calls DataStore methods in a new thread, so that they no longer yield the code below
- Adds the ability to set a default value, so that you'll no longer have to worry about data for first time players being nil
- Forces Studio to wait 1 second before closing the server
How does it work?
It does so by creating a custom DataStore object, whose methods handle the inconveniences for you. When requiring the module, it returns a frozen table which contains a fetcher and a constructor for the custom DataStore:
.new()
Arguments:
- name - The DataStore's name - Required - Value Type = string
- default_value - The default data/value that new players will get - Optional - Value Type = any
- options - The DataStoreOptions Instance - Optional - Value Type = DataStoreOptions or nil
Note: The default value of "default_value" is nil, to match the original behavior of DataStores
This function returns a CustomDataStore object
.waitFor()
Arguments:
- name - The DataStore's name - Required - Value Type = string
Note: "waitFor" does yield the code below it until a custom DataStore with the same name is first created using the "new" constructor
If a custom DataStore with the same name isn't created within 5 seconds, a warning will show up in the output
TLDR: Please do not use this function as a constructor, only to fetch a custom DataStore that was already created using "new"
This function returns a CustomDataStore object
How do the custom DataStore's methods work?
Please note that unlike the original DataStore methods with are indexed using a colon :, the custom DataStore's methods will need to be indexed using a dot .
.Get()
Arguments:
- key - The DataStore key, which you'd like to retrieve data from - Required - Value Type = string
- onError - The function to be called if Get fails - Required - Value Type = function
- onSuccess - The function to be called if Get works successfully - Required - Value Type = function
- options - The DataStoreGetOptions Instance - Optional - Value Type = DataStoreGetOptions or nil
This method returns nothing
.Set()
Arguments:
- key - The DataStore key, which you'd like to save data to - Required - Value Type = string
- data - The data to save - Required - Value Type = any but not nil
- onError - The function to be called if Set fails - Required - Value Type = function
- onSuccess - The function to be called if Set works successfully - Optional - Value Type = function or nil
- options - The DataStoreSetOptions Instance - Optional - Value Type = DataStoreSetOptions or nil
This method returns nothing
.Remove()
Arguments:
- key - The DataStore key, which you'd like to remove its data - Required - Value Type = string
- onError - The function to be called if Remove fails - Required - Value Type = function
- onSuccess - The function to be called if Remove works successfully - Optional - Value Type = function or nil
This method returns nothing
Usage Examples
Leaderstats:
--!strict
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local DataStore = require(ServerStorage.Modules.DataStore)
local data = {
Coins = 100,
Gems = 10
}
local dataStore = DataStore.new("ExampleDataStore", data)
Players.PlayerAdded:Connect(function(player: Player)
dataStore.Get(""..player.UserId, warn, function(data: typeof(data))
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Value = data.Coins
coins.Parent = leaderstats
local gems = Instance.new("IntValue")
gems.Name = "Gems"
gems.Value = data.Gems
gems.Parent = leaderstats
end)
end)
--!strict
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local DataStore = require(ServerStorage.Modules.DataStore)
local dataStore = DataStore.waitFor("ExampleDataStore")
local debounce = false
script.Parent.Touched:Connect(function(otherPart)
if debounce then return end
local model = otherPart:FindFirstAncestorOfClass("Model")
if model then
local player = Players:GetPlayerFromCharacter(model)
if player then
debounce = true
dataStore.Get(""..player.UserId, warn, function(data: typeof(dataStore.Default))
data.Coins += 10
dataStore.Set(""..player.UserId, data, warn, function()
player.leaderstats.Coins.Value = data.Coins
end)
end)
task.wait(1)
debounce = false
end
end
end)
CustomDataStoreDemoArea.rbxl (55.5 KB)
Module source and link
Source code
--!strict
local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
local EMPTY_ARRAY = {}
export type func = (...any) -> ...any
export type CustomDataStore = {
DataStore: DataStore,
Default: any,
Get: (key: string, onError: func, onSuccess: func, options: DataStoreGetOptions?) -> (),
Remove: (key: string, onError: func, onSuccess: func?) -> (),
Set: (key: string, data: any, onError: func, onSuccess: func?, options: DataStoreSetOptions?) -> ()
}
local dataStores: {[string]: CustomDataStore} = {}
local function get(dataStore: DataStore, key: string, onError: func, onSuccess: func, default: any, options: DataStoreGetOptions?)
local success, data = pcall(dataStore.GetAsync, dataStore, key, options)
if success then
onSuccess(data or default)
else
onError(data)
end
end
local function remove(dataStore: DataStore, key: string, onError: func, onSuccess: func?)
local success, result = pcall(dataStore.RemoveAsync, dataStore, key)
if success then
if onSuccess then onSuccess(result) end
else
onError(result)
end
end
local function set(dataStore: DataStore, key: string, data: any, onError: func, onSuccess: func?, options: DataStoreSetOptions?)
local success, result = pcall(dataStore.SetAsync, dataStore, key, data, EMPTY_ARRAY, options)
if success then
if onSuccess then onSuccess(result) end
else
onError(result)
end
end
if RunService:IsStudio() then
game:BindToClose(function()
task.wait(1)
end)
end
return table.freeze{
new = function(name: string, default_value: any, options: DataStoreOptions?): CustomDataStore
if typeof(name) ~= "string" then error("DataStore.new - Name must be a string value", 2) end
if dataStores[name] then return dataStores[name] end
local CustomDataStore = {
DataStore = DataStoreService:GetDataStore(name, options),
Default = default_value
}
function CustomDataStore.Get(key: string, onError: func, onSuccess: func, options: DataStoreGetOptions?)
if typeof(key) ~= "string" then error("DataStore.Get - Key must be a string value", 2) end
if typeof(onError) ~= "function" then error("DataStore.Get - OnError must be a function", 2) end
if typeof(onSuccess) ~= "function" then error("DataStore.Get - OnSuccess must be a function", 2) end
task.spawn(get, CustomDataStore.DataStore, key, onError, onSuccess, CustomDataStore.Default, options)
end
function CustomDataStore.Remove(key: string, onError: func, onSuccess: func?)
if typeof(key) ~= "string" then error("DataStore.Remove - Key must be a string value", 2) end
if typeof(onError) ~= "function" then error("DataStore.Remove - OnError must be a function", 2) end
if typeof(onSuccess) ~= "nil" and typeof(onSuccess) ~= "function" then error("DataStore.Remove - OnSuccess must be nil or a function", 2) end
task.spawn(remove, CustomDataStore.DataStore, key, onError, onSuccess)
end
function CustomDataStore.Set(key: string, data: any, onError: func, onSuccess: func?, options: DataStoreSetOptions?)
if typeof(key) ~= "string" then error("DataStore.Set - Key must be a string value", 2) end
if typeof(data) == "nil" then error('DataStore.Set - The value of "data" cannot be nil', 2) end
if typeof(onError) ~= "function" then error("DataStore.Set - OnError must be a function", 2) end
if typeof(onSuccess) ~= "nil" and typeof(onSuccess) ~= "function" then error("DataStore.Set - OnSuccess must be nil or a function", 2) end
task.spawn(set, CustomDataStore.DataStore, key, data, onError, onSuccess, options)
end
dataStores[name] = table.freeze(CustomDataStore)
return dataStores[name]
end,
waitFor = function(name: string): CustomDataStore
if typeof(name) ~= "string" then error("DataStore.waitFor - Name must be a string value", 2) end
local x = 0
local warned
while true do
x += task.wait()
if dataStores[name] then return dataStores[name] end
if warned or x < 5 then continue end
warned = true
warn(`DataStore.waitFor - DataStore "{name}" is taking longer than 5 seconds to be found`)
end
end
}
I hope you enjoy using this module