Skip to content

Instantly share code, notes, and snippets.

@HooferDevelops
Last active July 13, 2025 16:53
Show Gist options
  • Select an option

  • Save HooferDevelops/3f92947da5fde9a94a63353b47bb23a2 to your computer and use it in GitHub Desktop.

Select an option

Save HooferDevelops/3f92947da5fde9a94a63353b47bb23a2 to your computer and use it in GitHub Desktop.
CustomDatastoreService
--[[
CustomDatastoreService
--
This wrapper was made SPECIFICALLY for ProfileService.
What does this mean?
- This will not contain all native DataStore functions, as I
- only added support for the ones needed for ProfileService,
- because that is what I myself am using this for.
Current Functions:
-- Main Initializer --
CustomDatastoreService:GetDataStore(name: string, ) --> PseudoDataStore
-- Custom Functions --
PseudoDataStore:ObtainGetSetBudget() --> table
PseudoDataStore:BudgetWait(budget_type: string) --> bool
-- Pseudo Functions --
PseudoDataStore:GetAsync(key: string, OPTIONAL_custom_main_store: table?, OPTIONAL_custom_main_store_meta: Instance?) --> string | table, Instance
PseudoDataStore:SetAsync(key: string, value: string | table, user_ids: table?, options: Instance?) --> Instance
PseudoDataStore:UpdateAsync(key: string, func: any) --> string | table, Instance
PseudoDataStore:GetVersionAsync(key : string, ver : number) --> string | table, Instance
PseudoDataStore:ListVersionsAsync(key: string, sort_dir: number?, min_date: number?, max_date: number?, page_size: number?) --> Instance
]]--
local DataStoreService = game:GetService("DataStoreService")
local PseudoDataStore = {}
PseudoDataStore.__index = PseudoDataStore
function PseudoDataStore.new(name: string, scope: string?, options: Instance?)
local self = setmetatable({}, PseudoDataStore)
self.Datastore = DataStoreService:GetDataStore(name, scope, options)
return self
end
function PseudoDataStore:ObtainGetSetBudget()
local set = DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.SetIncrementAsync)
local get = DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.GetAsync)
return {
Get = get,
Set = set
}
end
function PseudoDataStore:BudgetWait(budget_type: string)
local budget = self:ObtainGetSetBudget()
if (budget[budget_type] < 60) then
repeat
task.wait(1)
warn("Awaiting budget", budget_type)
budget = self:ObtainGetSetBudget()
until not budget[budget_type] < 60
end
return true
end
function PseudoDataStore:GetAsync(key: string, OPTIONAL_custom_main_store: table?, OPTIONAL_custom_main_store_meta: Instance?)
self:BudgetWait("Get")
local main_store, main_store_meta
if (OPTIONAL_custom_main_store and OPTIONAL_custom_main_store_meta) then
main_store, main_store_meta = OPTIONAL_custom_main_store, OPTIONAL_custom_main_store_meta
else
main_store, main_store_meta = self.Datastore:GetAsync(key.."_root")
end
if (not main_store) then
main_store = {
ver = 0,
partitions = 0
}
else
main_store = game:GetService("HttpService"):JSONDecode(main_store)
end
local resultant_data = nil
local index = 0
repeat
self:BudgetWait("Get")
local index_store, index_store_meta = self.Datastore:GetAsync(
string.format("%s_%s_%s",
key,
tostring(index),
tostring(main_store.ver)
)
)
if (index_store) then
if (not resultant_data) then
resultant_data = ""
end
resultant_data ..= index_store
end
index += 1
task.wait()
until index >= main_store.partitions
pcall(function() -- hah, when in doubt, pcall it
resultant_data = game:GetService("HttpService"):JSONDecode(resultant_data)
end)
return resultant_data, main_store_meta
end
function PseudoDataStore:SetAsync(key: string, value: string | table, user_ids: table?, options: Instance?)
self:BudgetWait("Get")
local main_store, main_store_meta = self.Datastore:GetAsync(key.."_root")
if (not main_store) then
main_store = {
ver = 0,
partitions = 0
}
else
main_store = game:GetService("HttpService"):JSONDecode(main_store)
end
self:BudgetWait("Set")
main_store.ver += 1
if (typeof(value) == "table") then
value = game:GetService("HttpService"):JSONEncode(value)
else
value = tostring(value)
end
local function splitByChunk(text, chunkSize)
local s = {}
for i=1, #text, chunkSize do
s[#s+1] = text:sub(i,i+chunkSize - 1)
end
return s
end
local partitions = splitByChunk(value, 4*10^6)
main_store.partitions = #partitions
local index = 0
repeat
self:BudgetWait("Set")
self.Datastore:SetAsync(
string.format("%s_%s_%s",
key,
tostring(index),
tostring(main_store.ver)
),
partitions[index+1]
)
index += 1
task.wait()
until index >= main_store.partitions
self:BudgetWait("Set")
local variant = self.Datastore:SetAsync(key.."_root", game:GetService("HttpService"):JSONEncode(main_store), user_ids)
return variant
end
function PseudoDataStore:UpdateAsync(key: string, func: any)
local data, meta = self:GetAsync(key)
local new_data, user_ids, options = func(data, meta)
local set = self:SetAsync(key, new_data, user_ids, options)
return new_data, meta
end
function PseudoDataStore:GetVersionAsync(key: string, ver: number)
local value, info = self.Datastore:GetVersionAsync(key.."_root", ver)
local pseudo_value, pseudo_info = self:GetAsync(key, value, info)
return pseudo_value, pseudo_info
end
function PseudoDataStore:ListVersionsAsync(key: string, sort_dir: number?, min_date: number?, max_date: number?, page_size: number?)
return self.Datastore:ListVersionsAsync(key.."_root", sort_dir, min_date, max_date, page_size)
end
local CustomDatastoreService = {}
function CustomDatastoreService:GetDataStore(name: string, scope: string?, options: Instance?)
return PseudoDataStore.new(name, scope, options)
end
return CustomDatastoreService
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment