What is the best way make a good DataStore?

Hi Developers, iam new with Lua, and iam creating a game like Power Simulator, i made my first DataStore and i want opinion and suggestions to implement, because is my first Time doing that and i dont know what i should implement

    -- services
local Players = game:GetService("Players")
local Version = 1
local DataService = game:GetService("DataStoreService")
local PlayerData = DataService:GetDataStore("SimPowerStore_"..Version)
local MaxAttempts = 3 
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteEvents = ReplicatedStorage.RemoteEvents
local StartTraining = RemoteEvents.Training
local StopTraining = RemoteEvents.StopTraining

-- variables
local ServerData = {}
local Saving = nil

local function DataRetry(DataFunction,...)
        local Tries = 0
        local Data = nil
        local Success,Message = false,nil
        local Args = {...}
        while Tries < MaxAttempts and not Success do
            Tries = Tries + 1
            Success,Message = pcall(function() Data = DataFunction(unpack(Args)) end)
            if not Success then wait(1) warn("DataRetry Failed:",Message) end
        end
        
        if not Success then
	       warn("Data not found")
        else
	       print("Data Found")
        end
          
        return Data
    end


local function newData()
    return {
        ["Stats"] = {
	       ["Strength"] = {
		      ["Value"] = 0;
		      ["Suffix"] = "";
		      ["Multiplier"] = 0;
		      ["Boost"] = 0;
	       };
	       ["Endurance"] = {
		      ["Value"] = 0;
		      ["Suffix"] = "";
		      ["Multiplier"] = 0;
		      ["Boost"] = 0;
	       };
	       ["Mind"] = {
		      ["Value"] = 0;
		      ["Suffix"] = "";
		      ["Multiplier"] = 0;
		      ["Boost"] = 0;
	       };
           ["Jump"] = {
		      ["Value"] = 0;
		      ["Suffix"] = "";
		      ["Multiplier"] = 0;
		      ["Boost"] = 0;
           };	
           ["Speed"] = {
		      ["Value"] = 0;
		      ["Suffix"] = "";
		      ["Multiplier"] = 0;
		      ["Boost"] = 0;
		   }    
        };
        ["Leard"] = {
	          ["Reputation"] = 0;
	          ["Rank"] = "Innocent";
        };
        ["Missions"] = {
	    
        };
        ["Skills"] = {
	
	    };
         
    }
end

local function Get(p)
	local Data = PlayerData:GetAsync("PlayerUserId_"..p.UserId)
	return Data
end

local function loadData(p)
    local data
    local Success,Error = pcall(function()
	    data = DataRetry(Get,p) 
    end)
     
	if Success then
		if not data then
          data = newData()
          print("Generating new data")
		end
	else
		p:Kick("Data not found pls Rejoin")
	end
	 
    ServerData[p.UserId] = data
end

local function teste(p)
	ServerData[p.UserId]["Leard"]["Reputation"] = 30
	print(ServerData[p.UserId]["Leard"]["Reputation"])
end 

local function saveData(p,SaveType)
    
        if ServerData[p.UserId] then
            local Success, Error = pcall(function() 
	           PlayerData:UpdateAsync("PlayerUserId_"..p.UserId, function(oldData)
		          return ServerData[p.UserId]
	           end) 
	
            end)
        
            if Success then
                warn(p.Name.." Data saved ")
            else
	            warn("Data wasn't saved for "..p.Name.." error "..Error)
            end

            if SaveType == "AutoSave" then
	
            else
            ServerData[p.UserId] = nil
            end
        end


            
end

local function loadLeardStats(p)
	local folder = Instance.new("Folder")
	
	local rank = Instance.new("NumberValue")
	rank.Name = "Rank"
	rank.Value = ServerData[p.UserId]["Leard"]["Rank"]
	rank.Parent = folder
	
	local reputation = Instance.new("NumberValue")
	reputation.Name = "Reputation"
	reputation.Value = ServerData[p.UserId]["Leard"]["Reputation"]
	reputation.Parent = folder
	
	folder.Name = "leaderstats"
	folder.Parent = p
end

--Training Events


local AutoTraining = {}

local function increaseStats(Player,Amount,Stat)
	while true do
		wait(1)
		if not AutoTraining[Player.UserId] then return end 
		
		ServerData[Player.UserId]["Stats"][""..Stat]["Value"] = ServerData[Player.UserId]["Stats"][""..Stat]["Value"] + 50
		print(ServerData[Player.UserId]["Stats"][""..Stat]["Value"])
	end
end

local function checkIsTrainingArea(Player,Amount,Part,Stat)
	 print("checkingg")
	 local touching = Part:GetTouchingParts()
	 local ValidPosition = false
	 for i=1,#touching do
		if touching[i] == Player.Character.HumanoidRootPart then
			ValidPosition = true
			break
		end
	end  
	
	if ValidPosition == true then
		AutoTraining[Player.UserId] = true
		increaseStats(Player,Amount,Stat)
	end  
	ValidPosition = nil
	touching = nil
end

local function stopTrainingF(Player,Part)
	local touching = Part:GetTouchingParts()
	 local ValidPosition = false
	 for i=1,#touching do
		if touching[i] == Player.HumanoidRootPart then
			ValidPosition = true
			break
		end
	end
	
	if ValidPosition == true then
		AutoTraining[Player.UserId] = false
	end
	ValidPosition = nil
	touching = nil
end
 
Players.PlayerAdded:connect(function(p)
    loadData(p)
    loadLeardStats(p)
    teste(p)
    StartTraining.OnServerEvent:Connect(checkIsTrainingArea)
	  
    
end)

game:BindToClose(function()	
	Saving = true
	print("Closing")
	for _,p in pairs(Players:GetChildren()) do
         DataRetry(saveData,p,"LeaveSave")
	end
	
end)
 
Players.PlayerRemoving:connect(function(p)
	if Saving == false then
	   DataRetry(saveData,p,"LeaveSave")
	   Saving = nil
	end
	
end)

while true do
	wait(180)
	if not Saving then
		for _,p in pairs(Players:GetChildren()) do
         DataRetry(saveData,p,"AutoSave")
	   end
	end
end

Hello and Welcome, I see that this is your first post on the forum. For starters, please read this post on how to properly post and format code on the forum.

Also, for code that is meant for reviewing, please have it posted in #help-and-feedback:code-review

You can move your post by editing it (clicking the pencil icon at the bottom) and changing the category.

2 Likes

Thanks for helpme Man, also you have Any suggestion for my DataStore?

I personally think the autosaving delay is too long. Perhaps make it 30 - 60 seconds. Kicking the player if there is no data to load is bad UX, you’d might as well add a retry system for that too. Lastly, you should inform the player if the data fails to save, this will make the player stay in the server (hopefully) until the data stores become operational again.

Have DataRetry, only kick the player after try 3 times and only on loadData (that active when player Join), the savedata have DataRetry too but dont kick