Fetching Data from a Module to save to DataStore

I have been going nuts trying to grab the lap time from my module to save to my datastore. I have included my main module script (which has two sub modules I didnt include here), and the datastore script. Where am I going wrong? It just wont put the lap time up on the board.

Also am including my error output.

Main Module Script:

local RaceModule = {}
local Checkpoint = require(script.Checkpoint)
local Racer = require(script.Racer)

-- Local variable defaults
local startCheckpoint = nil
local finishCheckpoint = nil
local numLaps = 3
local autoStart = true
local raceDuration = 220
local intermissionDuration = 15
local debugEnabled = false
local lapTime = {}

-- Other local variables
local numCheckpoints = 0
local checkpoints = {}
local racers = {}
local winner = nil


-- Private functions
local function getCheckpointFromPart(part)
	for _, checkpoint in pairs(checkpoints) do
		if checkpoint.CollisionBody == part then
			return checkpoint
		end
	end
	return nil
end

local function hasPlayerFinishedLap(player, lap)
	for _, checkpoint in pairs(checkpoints) do
		if checkpoint:PlayerLapPassed(player, lap) ~= lap then
			return false
		end
	end
	return true
end

local function addRacer(player)
	racers[player] = Racer.new()
end

local function checkAllFinished()
	for _, racer in pairs(racers) do
		if not racer:HasFinishedLap(numLaps) then
			return false
		end
	end
	return true
end

-- Getters/Setters
function RaceModule:SetStartCheckpoint(checkpoint)
	startCheckpoint = getCheckpointFromPart(checkpoint)
end

function RaceModule:GetStartCheckpoint()
	return startCheckpoint
end

function RaceModule:SetFinishCheckpoint(checkpoint)
	finishCheckpoint = getCheckpointFromPart(checkpoint)
end

function RaceModule:GetFinishCheckpoint()
	return finishCheckpoint
end

function RaceModule:SetNumLaps(number)
	numLaps = number
end

function RaceModule:GetNumLaps()
	return numLaps
end

function RaceModule:GetLapTime()
	return lapTime
end

function RaceModule:SetAutoStart(autostart)
	autoStart = autostart
end

function RaceModule:GetAutoStart()
	return autoStart
end

function RaceModule:SetRaceDuration(duration)
	raceDuration = duration
end

function RaceModule:GetRaceDuration()
	return raceDuration
end

function RaceModule:SetIntermissionDuration(duration)
	intermissionDuration = duration
end

function RaceModule:GetIntermissionDuration()
	return intermissionDuration
end

function RaceModule:SetDebugEnabled(enabled)
	debugEnabled = enabled
end

function RaceModule:GetDebugEnabled()
	return debugEnabled
end

-- Events
local onRaceStartedEvent = Instance.new("BindableEvent")
RaceModule.RaceStarted = onRaceStartedEvent.Event 

local onRaceFinishedEvent = Instance.new("BindableEvent")
RaceModule.RaceFinished = onRaceFinishedEvent.Event

local onLapStartedEvent = Instance.new("BindableEvent")
RaceModule.LapStarted = onLapStartedEvent.Event

local onLapFinishedEvent = Instance.new("BindableEvent")
RaceModule.LapFinished = onLapFinishedEvent.Event

local onIntermissionStartedEvent = Instance.new("BindableEvent")
RaceModule.IntermissionStarted = onIntermissionStartedEvent.Event

local onCheckpointPassedEvent = Instance.new("BindableEvent")
RaceModule.CheckpointPassed = onCheckpointPassedEvent.Event

-- Public functions
function RaceModule:SetRacers(racerList)
	for i = 1, #racerList do
		addRacer(racerList[i])
	end
end

function RaceModule:RegisterCheckpoint(checkpoint)
	if not checkpoints[checkpoint] then
		local newCheckpoint = Checkpoint.new(checkpoint, RaceModule)
		numCheckpoints = numCheckpoints + 1
		checkpoints[checkpoint] = newCheckpoint
	end
end

function RaceModule:PlayerCrossedCheckpoint(player, checkpoint)
	local racer = racers[player]	
	
	if not racer or racer.FinishedRace then return end
	
	local currentLap = racer.CurrentLap
	
	if checkpoint == startCheckpoint and not racer:HasStartedLap(currentLap) then
		racer:StartLap(currentLap)
		onLapStartedEvent:Fire(player, currentLap)
	end
	
	if racer:HasStartedLap(currentLap) then
		local alreadyPassed = checkpoint:SetPlayerPassed(player, currentLap)
		if not alreadyPassed then		
			onCheckpointPassedEvent:Fire(checkpoint.CollisionBody)
		end
	end
	
	if checkpoint == finishCheckpoint and hasPlayerFinishedLap(player, currentLap) then
		local lapTime = racer:FinishLap(currentLap)
		onLapFinishedEvent:Fire(player, currentLap, lapTime)
		if currentLap == numLaps then
			if not winner then
				winner = player
			end
			racer.FinishedRace = true
			if checkAllFinished() then
				onRaceFinishedEvent:Fire(winner)
			end
		else
			racer.CurrentLap = racer.CurrentLap + 1
		end
	end
end

function RaceModule:Start()
	if debugEnabled then print("RACEMODULE: Starting race") end	
	
	winner = nil
	
	assert(numCheckpoints > 0, "No checkpoints registered")
	assert(startCheckpoint, "Start Checkpoint not set")
	assert(finishCheckpoint, "Finish Checkpoint not set")
	
	if debugEnabled then print("RACEMODULE: Checking if players set") end
	if #racers == 0 then
		RaceModule:SetRacers(game.Players:GetPlayers())
	end	
	
	for _, checkpoint in pairs(checkpoints) do
		checkpoint:Reset()
	end

	onRaceStartedEvent:Fire()
	
	local raceOver = false
	local finishedConnection
	finishedConnection = RaceModule.RaceFinished:connect(function()
		raceOver = true
		racers = {}
		finishedConnection:disconnect()
	end)
	spawn(function()
		wait(raceDuration)
		if not raceOver then
			onRaceFinishedEvent:Fire(winner)
		end
	end)
end



-- Autostart code
spawn(function()
	wait(5)
	
	repeat
		onIntermissionStartedEvent:Fire()
		wait(intermissionDuration)
		
		RaceModule:Start()
		
		RaceModule.RaceFinished:wait()
	until autoStart == false	
end)

return RaceModule

DataStore Script:


-- // Assigning variables //
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("MyDataStore") -- This can be changed to whatever you want
local RaceManager = require(script.Parent.MainModule)
local Laptime = RaceManager:GetLapTime()

local function saveData(player) -- The functions that saves data

	local tableToSave = {
		Laptime.Value; -- First value from the table
	}

	local success, err = pcall(function()
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
	end)

	if success then -- If the data has been saved
		print("Data has been saved!")
	else -- Else if the save failed
		print("Data hasn't been saved!")
		warn(err)		
	end
end

game.Players.PlayerAdded:Connect(function(player) -- When a player joins the game

	-- // Assigning player stats //
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local Lap = Instance.new("IntValue")
	Laptime.Name = "Lap time"
	Laptime.Parent = leaderstats

	local data -- We will define the data here so we can use it later, this data is the table we saved
	local success, err = pcall(function()

		data = dataStore:GetAsync(player.UserId) -- Get the data from the datastore

	end)

	if success and data then -- If there were no errors and player loaded the data

		Laptime.Value = data[1] -- Set the lap to the first value of the table (data)


	else -- The player didn't load in the data, and probably is a new player
		print("The player has no data!") -- The default will be set to 0
	end

end)

game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
	local success, err  = pcall(function()
		saveData(player) -- Save the data
	end)

	if success then
		print("Data has been saved")
	else
		print("Data has not been saved!")
	end
end)

game:BindToClose(function() -- When the server shuts down
	for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
		local success, err  = pcall(function()
			saveData(player) -- Save the data
		end)

		if success then
			print("Data has been saved")
		else
			print("Data has not been saved!")
		end
	end
end)

Output window:

  10:06:22.467  DataStore request was added to queue. If request queue fills, further requests will be dropped. Try sending fewer requests.Key = 1809649908  -  Studio
  10:06:22.884  Data has been saved!  -  Server
  10:06:22.884  Data has been saved  -  Server

I know that the issue is right here:

But how to correct it i am not sure.

Im bumping this up in hopes that someone knows something about this.

Am I correct in saying that LapTime is a table?
If so, replace this:

local tableToSave = {
		Laptime.Value; -- First value from the table
	}

to

local tableToSave = {
		Laptime[1].Value; -- First value from the table
	}

your description doesnt make too much sense, but this is probably an issue:
setting .Name and .Parent of Laptime rather than Lap

and you might be using Laptime instead if Lap afterwards a few times too

Yes sorry i had caught that shortly after posting, forgot to correct it… that wasn’t the issue.

Here is the Racer submodule where I am pulling the LapTime from:

local Lap = {}
Lap.__index = Lap

function Lap.new()
	local newLap = {}
	setmetatable(newLap, Lap)
	
	newLap.Started = true
	newLap.StartTime = tick()
	newLap.LapTime = 0
	newLap.Finished = false		
	
	return newLap
end

local Racer = {}
Racer.__index = Racer

function Racer.new()
	local newRacer = {}
	setmetatable(newRacer, Racer)
	
	newRacer.CurrentLap = 1
	newRacer.Laps = {}
	newRacer.FinishedRace = false
	
	return newRacer
end

function Racer:StartLap(lapNumber)
	self.Laps[lapNumber] = Lap.new()
end

function Racer:FinishLap(lapNumber)
	local lap = self.Laps[lapNumber]
	lap.LapTime = tick() - lap.StartTime
	lap.Finished = true
	return lap.LapTime
end

function Racer:GetLapTime(lapNumber)
	local lap = self.Laps[lapNumber]
	if lap.Finished then
		return lap.LapTime
	else
		return "not finished!"
	end
end

function Racer:HasStartedLap(lapNumber)
	if lapNumber <= #self.Laps then
		return self.Laps[lapNumber].Started
	end 
	return false
end

function Racer:HasFinishedLap(lapNumber)
	if lapNumber <= #self.Laps then
		return self.Laps[lapNumber].Finished
	end
	return false
end

return Racer

The issue could also be from this line then:

local Laptime = RaceManager:GetLapTime()

Instead of calling it once at the start, you may have better success calling it whenever it is needed

1 Like