Everything works how I want it to in this script. I do want feedback for shortening and/or optimizing the script, since I know there are parts of the code I can do it to.
-- Variables
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local OmegaNum = require(ReplicatedStorage.Modules.OmegaNum)
local statsTable = require(script.StatsTable)
local DataStore2 = require(ServerScriptService.MainModule)
local statHandler = require(ReplicatedStorage.StatHandler)
local maxRetries = 3
local initialRetryDelay = 1 -- Seconds
local retryDelayMultiplier = 2
local testModeEnabled = false
local kickPlayerOnDataStoreFail = false
DataStore2.Combine("Main1", "Stats")
local function createValueInstance(name: string, valueData: table, parent: Instance)
local valueType = valueData.ValueType
local value = valueData.Value
if valueType == "StringValue" then
local valueInstance = Instance.new("StringValue")
valueInstance.Name = name
valueInstance.Value = value
print(value)
valueInstance.Parent = parent
elseif valueType == "BoolValue" then
local valueInstance = Instance.new("BoolValue")
valueInstance.Name = name
valueInstance.Value = value
print(value)
valueInstance.Parent = parent
else
warn("No value available for type:", valueType)
end
end
local function deepSearch(tbl: {[string]: any}, parentFolder: Instance)
for key, value in pairs(tbl) do
if type(value) == "table" then
if value.ValueType then
print(value)
createValueInstance(key, value, parentFolder)
else
local newFolder = Instance.new("Folder")
newFolder.Name = key
newFolder.Parent = parentFolder
deepSearch(value, newFolder)
end
else
warn("Unexpected value type in deepSearch:", type(value))
end
end
end
local function deepMerge(defaultData, savedData)
for key, defaultValue in pairs(defaultData) do
local savedValue = savedData[key]
if savedValue == nil then
savedData[key] = defaultValue
if type(defaultValue) == "table" and defaultValue.ValueType then
print("Added new stat:", key, defaultValue.Value)
print("Added new stat:", key, defaultValue.ValueType)
else
print("Added new stat:", key, defaultValue)
end
elseif type(defaultValue) == "table" and type(savedValue) == "table" then
if defaultValue.ValueType then
if not savedValue.ValueType or savedValue.ValueType ~= defaultValue.ValueType then
-- Only update the Value if ValueTypes don't match or savedValue doesn't have a ValueType
savedData[key].Value = defaultValue.Value
print("Updated Value due to ValueType mismatch or missing ValueType in saved data:", key)
end
-- No need to do anything if ValueTypes match, as we want to preserve the saved Value
else
savedData[key] = deepMerge(defaultValue, savedValue) -- Correctly return the result of recursion
end
elseif type(savedValue) ~= type(defaultValue) then
savedData[key] = defaultValue
print("Overwritten stat:", key, "with new default:", defaultValue)
end
end
for key in pairs(savedData) do
if defaultData[key] == nil then
savedData[key] = nil
print("Removed obsolete stat:", key)
end
end
return savedData
end
local function logError(context: string, message: string, error: any)
warn(string.format("[%s] %s: %s", context, message, error or "Unknown error"))
end
local function retryDataStoreGet(dataStore, key, defaultValue, plr)
local attempts = 0
local success = false
local result
local err
local dataStoreError
print("test")
local delay = initialRetryDelay
while attempts < maxRetries and not success do
attempts = attempts + 1
print(attempts)
if testModeEnabled then
success = false
err = "Simulated DataStore failure for testing"
dataStoreError = {error = "Simulated Error"}
else
success, result, err = pcall(dataStore.Get, dataStore, key)
if not success then
dataStoreError = err
end
end
if success then
return true, result
else
logError("DataStore2", string.format("Attempt %d failed", attempts), err, dataStoreError)
if attempts < maxRetries then
task.wait(delay)
delay = delay * retryDelayMultiplier
end
end
end
if not success then
logError("DataStore2", "Operation failed after multiple retries", err, dataStoreError)
if kickPlayerOnDataStoreFail then
plr:Kick("Failed to load data after multiple retries")
end
end
return false, defaultValue
end
local function loadData(plr: Player)
local statsFolder = Instance.new("Folder")
statsFolder.Name = "Stats"
statsFolder.Parent = plr
local statsDataStore = DataStore2("Stats", plr)
-- Use the retry mechanism for loading data, passing the player object
local success, savedData = retryDataStoreGet(statsDataStore, "Stats", statsTable, plr)
if not success then
if savedData == "KickPlayer" then
-- player is now kicked within retryDataStoreGet function
return -- Stop further execution of loadData if player is kicked
end
logError("Data Loading", "Failed to load data for player: " .. plr.Name, savedData)
savedData = statsTable -- Fallback to default stats
end
if savedData == nil or next(savedData) == nil then
print("No saved data found. Using default statsTable.")
savedData = statsTable
else
savedData = deepMerge(statsTable, savedData)
end
print("Final Merged Data:")
for key, value in pairs(savedData) do
print(key, value)
end
if type(savedData) == "table" then
deepSearch(savedData, statsFolder)
else
warn("Saved data is not in expected format:", savedData)
end
end
-- Function to save data
local function saveData(plr: Player)
local statsFolder = plr:FindFirstChild("Stats")
if not statsFolder then return end
local function serialize(folder: Instance): {[string]: any}
local tbl: {[string]: any} = {}
for _, child in ipairs(folder:GetChildren()) do
if child:IsA("Folder") then
tbl[child.Name] = serialize(child)
elseif child:IsA("StringValue") or child:IsA("BoolValue") then
tbl[child.Name] = { Value = child.Value, ValueType = child.ClassName }
end
end
return tbl
end
local statsDataStore = DataStore2("Stats", plr)
local dataToSave = serialize(statsFolder)
local success, errorMessage = pcall(function()
statsDataStore:Set(dataToSave)
end)
if not success then
warn("Failed to save data for player:", plr.Name, "Error:", errorMessage)
end
end
-- Player Added Event
Players.PlayerAdded:Connect(function(plr: Player)
print("Player Added:", plr.Name)
loadData(plr)
spawn(function()
statHandler.addTime(plr)
end)
coroutine.wrap(function()
while task.wait(0.1) do
statHandler.addMoney(plr)
end
end)()
end)
-- Player Removing Event
Players.PlayerRemoving:Connect(function(plr: Player)
print("Player Removing:", plr.Name)
saveData(plr)
end)