Pulling a stat from this module to use in leaderboard

Hello, I am trying to pull the Lap Time for each player from this module in order to call for it in my leaderboard script, however nothing I do seems to locate the lap time. The module is from one of the Roblox templates (Car Race)… so I know that it not only shows you the lap time every lap, but that the issue is not in the module.

There also is no issue with the leaderboard script, I only need to reference where the stat is so that it can require the module and pull out the stat.

Any help is appreciated, here are the main module and the two sub modules underneath:

Main:

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

-- 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: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

Racer Module:

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: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

Checkpoint Module:

local Checkpoint = {}
Checkpoint.__index = Checkpoint

function Checkpoint.new(collisionBody, manager)
	assert(collisionBody, "Trying to set partless checkpoint")	
	
	local newCheckpoint = {CollisionBody = collisionBody, Manager = manager}
	setmetatable(newCheckpoint, Checkpoint)	
	
	newCheckpoint.PlayersTouched = {}
	
	newCheckpoint.CollisionBody.Touched:connect(function(otherPart)
		if otherPart and otherPart.Parent then
			local player = game.Players:GetPlayerFromCharacter(otherPart.Parent)
			if player then		
				newCheckpoint.Manager:PlayerCrossedCheckpoint(player, newCheckpoint)
			end	
		end
	end)
	
	newCheckpoint.CollisionConnection = nil
	
	return newCheckpoint
end

function Checkpoint:Reset()
	self.PlayersTouched = {}
end

function Checkpoint:SetPlayerPassed(player, lapNumber)
	local alreadyPassed = (self.PlayersTouched[player] == lapNumber)
	self.PlayersTouched[player] = lapNumber
	return alreadyPassed
end

function Checkpoint:PlayerLapPassed(player)
	return self.PlayersTouched[player]
end

return Checkpoint

Inside the function RaceModule:PlayerCrossedCheckpoint() line 163:

local lapTime = racer:FinishLap(currentLap)
1 Like

Yes i had seen that, but when I went to reference it in my leaderboard script, it never pulls up the lap time.

My reference is:

			local RaceManager = require(game.ServerScriptService.MainModule)
			local w = RaceManager:lapTime(plr.UserId) --Get lap time

It’s because you aren’t actually getting the lap data for the userId as no such :lapTime() function exists in RaceManager (Main). You would need to retrieve the data from the racer reference just like :FinishLap() does.

Create a new function under Racer:FinishLap(lapNumber) that would only read the LapTime and not modify it, like so:

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

then you can do: local lapTime = racer:GetLapTime(lapNumber) with racer being a reference to racers[player] inside Main.

Bear with me, this part goes in the main module under the variables? I added the first part into the race module underneath where u said.

Not exactly, “racers” is a table of all the added racers (players) that also contain their racing data. So to get a specific player’s data from it you would need to do racers[player] as seen elsewhere. You could make another function in that module that specifically passes through the specified player object who you know is a racer (otherwise the table wont exist for that player) and also the LapNumber since it also needs to know which lap you want the time for…

function RaceModule:PrintLapTime(player, lapNumber)
	if racer[player] then
		print(racer[player]:GetLapTime(lapNumber))
	else
		print(player, "is not a racer!")
	end
end

Im trying to follow but im a bit lost to be honest… I understood about creating a function to get the lap time in the racer module… where am i putting this line:
local lapTime = racer:GetLapTime(lapNumber)
in the main module and where?

You don’t. I realized you wanted it outside the original function which is why I provided :PrintLapTime() that you can view the time in the output as a test but you would need to edit it slightly to use it elsewhere. I have not tested this out with the actual racing tutorial so I am not 100% sure if these suggestions will actually work, but to the best of my knowledge they should, if not then it might need some trial and error :smile:

1 Like

Im all about the trial and error, thanks for the input so far, its opening up my eyes.

So this script in the template grabs the lap time on line 70-71 (shortDuration) and pushes it through a notification… how can i do the same but without the notification, just adding it to leaderboard? because it is requiring the same main module and grabbing the lap time that way too.

local RaceManager = require(script.Parent.MainModule)
RaceManager:SetDebugEnabled(false)
RaceManager:SetIntermissionDuration(15)

local function pushNotificationForPlayer(player, message, duration)
	local notificationFrameContainer = player.PlayerGui.RaceNotificationScreen:FindFirstChild("NotificationFrameContainer")
	local notificationFrame = Instance.new("TextLabel", notificationFrameContainer)
	notificationFrame.Size = UDim2.new(1, 0, 1, 0)
	notificationFrame.Position = UDim2.new(0, 0, -1, 0)
	notificationFrame.Text = message
	for i, frame in ipairs(notificationFrameContainer:GetChildren()) do
		frame.Position = frame.Position + UDim2.new(0, 0, 1, 0)
	end
	delay(duration, function()
		for i = 1, 60 do
			notificationFrame.BackgroundTransparency = i / 60
			notificationFrame.TextTransparency = i / 60
			wait()
		end		
		notificationFrame:Destroy()
	end)
end

local function pushNotification(message, duration)
	for _, player in ipairs(game.Players:GetPlayers()) do
		pushNotificationForPlayer(player, message, duration)
	end
end

local function onPlayerAdded(player)
	local playerGui = player:WaitForChild("PlayerGui")
	local raceNotificationScreen = Instance.new("ScreenGui", playerGui)
	raceNotificationScreen.Name = "RaceNotificationScreen"
	local notificationFrameContainer = Instance.new("Frame", raceNotificationScreen)
	notificationFrameContainer.Name = "NotificationFrameContainer"
	notificationFrameContainer.BackgroundTransparency = 1
	notificationFrameContainer.BorderSizePixel = 0
	notificationFrameContainer.Size = UDim2.new(0.3, 0, 0.1, 0)
	notificationFrameContainer.Position = UDim2.new(0.35, 0, 0, 0)
end

RaceManager.RaceStarted:connect(function()
	pushNotification("Race started!", 3)
end)

RaceManager.RaceFinished:connect(function(player)
	local message = "Race over. "
	if player then
		message = message .. player.Name .. " won!"
	else
		message = message .. " Ran out of time."
	end
	pushNotification(message, 5)
	wait(5)
	for _, player in ipairs(game.Players:GetPlayers()) do
		player:LoadCharacter()
		if game.StarterGui.ResetPlayerGuiOnSpawn then
			onPlayerAdded(player)
		end
	end
end)

RaceManager.IntermissionStarted:connect(function()
	local intermissionDuration = RaceManager:GetIntermissionDuration()
	wait(intermissionDuration / 2)
	pushNotification("Race starting soon!", intermissionDuration / 4)
end)

RaceManager.LapFinished:connect(function(player, lap, duration)
	local shortDuration = math.floor(tonumber(duration) * 1000) / 1000
	pushNotification(player.Name .. " finished lap " .. lap .. "/" .. RaceManager:GetNumLaps() .. " in " .. shortDuration .. " seconds", 2)
end)

for _, player in ipairs(game.Players:GetChildren()) do
	onPlayerAdded(player)
end
game.Players.PlayerAdded:connect(onPlayerAdded)