Hi. I have recently created this simple datastore class:
-- Data Class
-- UntitledProtocol
-- July 24, 2020
local DataClass = {}
DataClass.__index = DataClass
local FETCH_CHANCES = 3
local SAVE_CHANCES = 3
local COOLDOWN = 6
local DataStoreService = game:GetService("DataStoreService")
--[[
MODULE
]]
function DataClass.create(name, player, defaultData)
local self = setmetatable({}, DataClass)
self._data = defaultData
self._dataStore = DataStoreService:GetDataStore(name)
self._player = player
self._fetchRawDataQueue = Instance.new("BindableEvent") -- The event which will fire once a call of the fetch raw data has been ran
self._fetchRawDataWasRan = false -- Indicates if _fetchRawData function has been ran for the first time
self._fetchingRawData = false
self._savingQueue = Instance.new("BindableEvent")
self._savingEnabled = false
self._saving = false
self.unableToSave = Instance.new("BindableEvent") -- Fires if a save call was unsuccessfull
self.unableToFetch = Instance.new("BindableEvent") -- Fires if a _fetchRawData call was unsuccessfull
return self
end
--[[
PRIVATE API
]]
function DataClass:_fetchRawData()
if (self._fetchingRawData) then
warn("Another _fetchRawData call is already occurring. Returning current call when finished.")
self._fetchRawDataQueue.Event:Wait()
return
end
self._fetchingRawData = true
local success
local err
for _ = 1, FETCH_CHANCES, 1 do
local data
success, err = pcall(function()
data = self._dataStore:GetAsync(self._player.UserId)
end)
if (success) then
self._data = data or self._data -- If the player is new, we don't want nil to overwrite our default value
break
end
wait(COOLDOWN)
end
if not success then
warn("_fetchRawData called failed:", err)
self.unableToFetch:Fire()
end
self._savingEnabled = success
self._fetchRawDataWasRan = true
self._fetchingRawData = false
self._fetchRawDataQueue:Fire()
end
--[[
PUBLIC API
]]
function DataClass:Save()
if (not self._savingEnabled) then warn("Saving disabled.") return end
if (self._saving) then
warn("A save call is already in progress, waiting until completed.")
self._savingQueue.Event:Wait()
return
end
self._data.ARB = (self._data.ARB or 0) + 1 -- Increment the anti rollback value
self._saving = true
local success
local err
for _ = 1, SAVE_CHANCES, 1 do
success, err = pcall(self._dataStore.UpdateAsync, self._dataStore, self._player.UserId, function(oldData)
if (not self.oldData) or (self._data.ARB > oldData.ARB) then
return self._data
else
self._savingEnabled = false
self._player = self._player:Kick("Anti rollback value is lesser than or equal to old antirollback value. Please rejoin after a few minutes.")
return oldData
end
end)
if success then
break
end
wait(COOLDOWN)
end
if not success then
self.unableToSave:Fire(err)
end
self._saving = false
self._savingQueue:Fire()
end
function DataClass:GetAllData()
if (not self._fetchRawDataWasRan) then
self:_fetchRawData()
end
return self.data
end
function DataClass:GetDataComponent(name)
if (not self._fetchRawDataWasRan) then
self:_fetchRawData()
end
return self.data[name]
end
function DataClass:CacheDataComponent(name, value)
if (not self._fetchRawDataWasRan) then
self:_fetchRawData()
end
self._data[name] = value
end
return DataClass
Although it works, I feel like the code is starting to become difficult to navigate through and change. Therefore, future edits of this may cause unnecessary amounts of time figuring out what is happening. Any suggestions on how I can simplify are very appreciated. Thanks.