local QBCore = exports['qb-core']:GetCoreObject() local Races = {} local AvailableRaces = {} local LastRaces = {} local NotFinished = {} -- Functions local function SecondsToClock(seconds) seconds = tonumber(seconds) local retval if seconds <= 0 then retval = "00:00:00"; else local hours = string.format("%02.f", math.floor(seconds / 3600)); local mins = string.format("%02.f", math.floor(seconds / 60 - (hours * 60))); local secs = string.format("%02.f", math.floor(seconds - hours * 3600 - mins * 60)); retval = hours .. ":" .. mins .. ":" .. secs end return retval end local function IsWhitelisted(CitizenId) local retval = false for _, cid in pairs(Config.WhitelistedCreators) do if cid == CitizenId then retval = true break end end local Player = QBCore.Functions.GetPlayerByCitizenId(CitizenId) local Perms = QBCore.Functions.GetPermission(Player.PlayerData.source) if Perms == "admin" or Perms == "god" then retval = true end return retval end local function IsNameAvailable(RaceName) local retval = true for RaceId, _ in pairs(Races) do if Races[RaceId].RaceName == RaceName then retval = false break end end return retval end local function HasOpenedRace(CitizenId) local retval = false for _, v in pairs(AvailableRaces) do if v.SetupCitizenId == CitizenId then retval = true end end return retval end local function GetOpenedRaceKey(RaceId) local retval = nil for k, v in pairs(AvailableRaces) do if v.RaceId == RaceId then retval = k break end end return retval end local function GetCurrentRace(MyCitizenId) local retval = nil for RaceId, _ in pairs(Races) do for cid, _ in pairs(Races[RaceId].Racers) do if cid == MyCitizenId then retval = RaceId break end end end return retval end local function GetRaceId(name) local retval = nil for k, v in pairs(Races) do if v.RaceName == name then retval = k break end end return retval end local function GenerateRaceId() local RaceId = "LR-" .. math.random(1111, 9999) while Races[RaceId] ~= nil do RaceId = "LR-" .. math.random(1111, 9999) end return RaceId end -- Events RegisterNetEvent('qb-lapraces:server:FinishPlayer', function(RaceData, TotalTime, TotalLaps, BestLap) local src = source local Player = QBCore.Functions.GetPlayer(src) local AvailableKey = GetOpenedRaceKey(RaceData.RaceId) local PlayersFinished = 0 local AmountOfRacers = 0 for _, v in pairs(Races[RaceData.RaceId].Racers) do if v.Finished then PlayersFinished = PlayersFinished + 1 end AmountOfRacers = AmountOfRacers + 1 end local BLap if TotalLaps < 2 then BLap = TotalTime else BLap = BestLap end if LastRaces[RaceData.RaceId] ~= nil then LastRaces[RaceData.RaceId][#LastRaces[RaceData.RaceId]+1] = { TotalTime = TotalTime, BestLap = BLap, Holder = { [1] = Player.PlayerData.charinfo.firstname, [2] = Player.PlayerData.charinfo.lastname } } else LastRaces[RaceData.RaceId] = {} LastRaces[RaceData.RaceId][#LastRaces[RaceData.RaceId]+1] = { TotalTime = TotalTime, BestLap = BLap, Holder = { [1] = Player.PlayerData.charinfo.firstname, [2] = Player.PlayerData.charinfo.lastname } } end if Races[RaceData.RaceId].Records ~= nil and next(Races[RaceData.RaceId].Records) ~= nil then if BLap < Races[RaceData.RaceId].Records.Time then Races[RaceData.RaceId].Records = { Time = BLap, Holder = { [1] = Player.PlayerData.charinfo.firstname, [2] = Player.PlayerData.charinfo.lastname } } MySQL.update('UPDATE lapraces SET records = ? WHERE raceid = ?', {json.encode(Races[RaceData.RaceId].Records), RaceData.RaceId}) TriggerClientEvent('qb-phone:client:RaceNotify', src, 'You have won the WR from ' .. RaceData.RaceName .. ' disconnected with a time of: ' .. SecondsToClock(BLap) .. '!') end else Races[RaceData.RaceId].Records = { Time = BLap, Holder = { [1] = Player.PlayerData.charinfo.firstname, [2] = Player.PlayerData.charinfo.lastname } } MySQL.update('UPDATE lapraces SET records = ? WHERE raceid = ?', {json.encode(Races[RaceData.RaceId].Records), RaceData.RaceId}) TriggerClientEvent('qb-phone:client:RaceNotify', src, 'You have won the WR from ' .. RaceData.RaceName .. ' put down with a time of: ' .. SecondsToClock(BLap) .. '!') end AvailableRaces[AvailableKey].RaceData = Races[RaceData.RaceId] TriggerClientEvent('qb-lapraces:client:PlayerFinishs', -1, RaceData.RaceId, PlayersFinished, Player) if PlayersFinished == AmountOfRacers then if NotFinished ~= nil and next(NotFinished) ~= nil and NotFinished[RaceData.RaceId] ~= nil and next(NotFinished[RaceData.RaceId]) ~= nil then for _, v in pairs(NotFinished[RaceData.RaceId]) do LastRaces[RaceData.RaceId][#LastRaces[RaceData.RaceId]+1] = { TotalTime = v.TotalTime, BestLap = v.BestLap, Holder = { [1] = v.Holder[1], [2] = v.Holder[2] } } end end Races[RaceData.RaceId].LastLeaderboard = LastRaces[RaceData.RaceId] Races[RaceData.RaceId].Racers = {} Races[RaceData.RaceId].Started = false Races[RaceData.RaceId].Waiting = false table.remove(AvailableRaces, AvailableKey) LastRaces[RaceData.RaceId] = nil NotFinished[RaceData.RaceId] = nil end TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) end) RegisterNetEvent('qb-lapraces:server:CreateLapRace', function(RaceName) local src = source local Player = QBCore.Functions.GetPlayer(src) if IsWhitelisted(Player.PlayerData.citizenid) then if IsNameAvailable(RaceName) then TriggerClientEvent('qb-lapraces:client:StartRaceEditor', source, RaceName) else TriggerClientEvent('QBCore:Notify', source, 'There is already a race with this name.', 'error') end else TriggerClientEvent('QBCore:Notify', source, 'You have not been authorized to race\'s to create.', 'error') end end) RegisterNetEvent('qb-lapraces:server:JoinRace', function(RaceData) local src = source local Player = QBCore.Functions.GetPlayer(src) local RaceId = RaceData.RaceId local AvailableKey = GetOpenedRaceKey(RaceId) local CurrentRace = GetCurrentRace(Player.PlayerData.citizenid) if CurrentRace ~= nil then local AmountOfRacers = 0 local PreviousRaceKey = GetOpenedRaceKey(CurrentRace) for _, _ in pairs(Races[CurrentRace].Racers) do AmountOfRacers = AmountOfRacers + 1 end Races[CurrentRace].Racers[Player.PlayerData.citizenid] = nil if (AmountOfRacers - 1) == 0 then Races[CurrentRace].Racers = {} Races[CurrentRace].Started = false Races[CurrentRace].Waiting = false table.remove(AvailableRaces, PreviousRaceKey) TriggerClientEvent('QBCore:Notify', src, 'You were the only one in the race, the race had ended', 'error') TriggerClientEvent('qb-lapraces:client:LeaveRace', src, Races[CurrentRace]) else AvailableRaces[PreviousRaceKey].RaceData = Races[CurrentRace] TriggerClientEvent('qb-lapraces:client:LeaveRace', src, Races[CurrentRace]) end TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) end Races[RaceId].Waiting = true Races[RaceId].Racers[Player.PlayerData.citizenid] = { Checkpoint = 0, Lap = 1, Finished = false } AvailableRaces[AvailableKey].RaceData = Races[RaceId] TriggerClientEvent('qb-lapraces:client:JoinRace', src, Races[RaceId], AvailableRaces[AvailableKey].Laps) TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) local creatorsource = QBCore.Functions.GetPlayerByCitizenId(AvailableRaces[AvailableKey].SetupCitizenId).PlayerData .source if creatorsource ~= Player.PlayerData.source then TriggerClientEvent('qb-phone:client:RaceNotify', creatorsource, string.sub(Player.PlayerData.charinfo.firstname, 1, 1) .. '. ' .. Player.PlayerData.charinfo.lastname .. ' the race has been joined!') end end) RegisterNetEvent('qb-lapraces:server:LeaveRace', function(RaceData) local src = source local Player = QBCore.Functions.GetPlayer(src) local RaceName if RaceData.RaceData ~= nil then RaceName = RaceData.RaceData.RaceName else RaceName = RaceData.RaceName end local RaceId = GetRaceId(RaceName) local AvailableKey = GetOpenedRaceKey(RaceData.RaceId) local creatorsource = QBCore.Functions.GetPlayerByCitizenId(AvailableRaces[AvailableKey].SetupCitizenId).PlayerData .source if creatorsource ~= Player.PlayerData.source then TriggerClientEvent('qb-phone:client:RaceNotify', creatorsource, string.sub(Player.PlayerData.charinfo.firstname, 1, 1) .. '. ' .. Player.PlayerData.charinfo.lastname .. ' the race has been delivered!') end local AmountOfRacers = 0 for _, _ in pairs(Races[RaceData.RaceId].Racers) do AmountOfRacers = AmountOfRacers + 1 end if NotFinished[RaceData.RaceId] ~= nil then NotFinished[RaceData.RaceId][#NotFinished[RaceData.RaceId]+1] = { TotalTime = "DNF", BestLap = "DNF", Holder = { [1] = Player.PlayerData.charinfo.firstname, [2] = Player.PlayerData.charinfo.lastname } } else NotFinished[RaceData.RaceId] = {} NotFinished[RaceData.RaceId][#NotFinished[RaceData.RaceId]+1] = { TotalTime = "DNF", BestLap = "DNF", Holder = { [1] = Player.PlayerData.charinfo.firstname, [2] = Player.PlayerData.charinfo.lastname } } end Races[RaceId].Racers[Player.PlayerData.citizenid] = nil if (AmountOfRacers - 1) == 0 then if NotFinished ~= nil and next(NotFinished) ~= nil and NotFinished[RaceId] ~= nil and next(NotFinished[RaceId]) ~= nil then for _, v in pairs(NotFinished[RaceId]) do if LastRaces[RaceId] ~= nil then LastRaces[RaceId][#LastRaces[RaceId]+1] = { TotalTime = v.TotalTime, BestLap = v.BestLap, Holder = { [1] = v.Holder[1], [2] = v.Holder[2] } } else LastRaces[RaceId] = {} LastRaces[RaceId][#LastRaces[RaceId]+1] = { TotalTime = v.TotalTime, BestLap = v.BestLap, Holder = { [1] = v.Holder[1], [2] = v.Holder[2] } } end end end Races[RaceId].LastLeaderboard = LastRaces[RaceId] Races[RaceId].Racers = {} Races[RaceId].Started = false Races[RaceId].Waiting = false table.remove(AvailableRaces, AvailableKey) TriggerClientEvent('QBCore:Notify', src, 'You were the only one in the race.The race had ended.', 'error') TriggerClientEvent('qb-lapraces:client:LeaveRace', src, Races[RaceId]) LastRaces[RaceId] = nil NotFinished[RaceId] = nil else AvailableRaces[AvailableKey].RaceData = Races[RaceId] TriggerClientEvent('qb-lapraces:client:LeaveRace', src, Races[RaceId]) end TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) end) RegisterNetEvent('qb-lapraces:server:SetupRace', function(RaceId, Laps) local Player = QBCore.Functions.GetPlayer(source) if Races[RaceId] ~= nil then if not Races[RaceId].Waiting then if not Races[RaceId].Started then Races[RaceId].Waiting = true AvailableRaces[#AvailableRaces+1] = { RaceData = Races[RaceId], Laps = Laps, RaceId = RaceId, SetupCitizenId = Player.PlayerData.citizenid } TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) SetTimeout(5 * 60 * 1000, function() if Races[RaceId].Waiting then local AvailableKey = GetOpenedRaceKey(RaceId) for cid, _ in pairs(Races[RaceId].Racers) do local RacerData = QBCore.Functions.GetPlayerByCitizenId(cid) if RacerData ~= nil then TriggerClientEvent('qb-lapraces:client:LeaveRace', RacerData.PlayerData.source, Races[RaceId]) end end table.remove(AvailableRaces, AvailableKey) Races[RaceId].LastLeaderboard = {} Races[RaceId].Racers = {} Races[RaceId].Started = false Races[RaceId].Waiting = false LastRaces[RaceId] = nil TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) end end) else TriggerClientEvent('QBCore:Notify', source, 'The race is already running', 'error') end else TriggerClientEvent('QBCore:Notify', source, 'The race is already running', 'error') end else TriggerClientEvent('QBCore:Notify', source, 'This race does not exist :(', 'error') end end) RegisterNetEvent('qb-lapraces:server:CancelRace', function(raceId) local src = source local Player = QBCore.Functions.GetPlayer(source) local AvailableKey = GetOpenedRaceKey(raceId) TriggerClientEvent('QBCore:Notify', src, 'Stopping the race: ' .. raceId, 'error') if AvailableKey ~= nil then if AvailableRaces[AvailableKey].SetupCitizenId == Player.PlayerData.citizenid then for cid, _ in pairs(Races[raceId].Racers) do local RacerData = QBCore.Functions.GetPlayerByCitizenId(cid) if RacerData ~= nil then TriggerClientEvent('qb-lapraces:client:LeaveRace', RacerData.PlayerData.source, Races[raceId]) end end table.remove(AvailableRaces, AvailableKey) Races[raceId].LastLeaderboard = {} Races[raceId].Racers = {} Races[raceId].Started = false Races[raceId].Waiting = false LastRaces[raceId] = nil TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) end else TriggerClientEvent('QBCore:Notify', src, 'Race not open: ' .. raceId, 'error') end end) RegisterNetEvent('qb-lapraces:server:UpdateRaceState', function(RaceId, Started, Waiting) Races[RaceId].Waiting = Waiting Races[RaceId].Started = Started end) RegisterNetEvent('qb-lapraces:server:UpdateRacerData', function(RaceId, Checkpoint, Lap, Finished) local src = source local Player = QBCore.Functions.GetPlayer(src) local CitizenId = Player.PlayerData.citizenid Races[RaceId].Racers[CitizenId].Checkpoint = Checkpoint Races[RaceId].Racers[CitizenId].Lap = Lap Races[RaceId].Racers[CitizenId].Finished = Finished TriggerClientEvent('qb-lapraces:client:UpdateRaceRacerData', -1, RaceId, Races[RaceId]) end) RegisterNetEvent('qb-lapraces:server:StartRace', function(RaceId) local src = source local MyPlayer = QBCore.Functions.GetPlayer(src) local AvailableKey = GetOpenedRaceKey(RaceId) if RaceId ~= nil then if AvailableRaces[AvailableKey].SetupCitizenId == MyPlayer.PlayerData.citizenid then AvailableRaces[AvailableKey].RaceData.Started = true AvailableRaces[AvailableKey].RaceData.Waiting = false for CitizenId, _ in pairs(Races[RaceId].Racers) do local Player = QBCore.Functions.GetPlayerByCitizenId(CitizenId) if Player ~= nil then TriggerClientEvent('qb-lapraces:client:RaceCountdown', Player.PlayerData.source) end end TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) else TriggerClientEvent('QBCore:Notify', src, 'You are not the creator of the race..', 'error') end else TriggerClientEvent('QBCore:Notify', src, 'You are not in a race..', 'error') end end) RegisterNetEvent('qb-lapraces:server:SaveRace', function(RaceData) local src = source local Player = QBCore.Functions.GetPlayer(src) local RaceId = GenerateRaceId() local Checkpoints = {} for k, v in pairs(RaceData.Checkpoints) do Checkpoints[k] = { offset = v.offset, coords = v.coords } end Races[RaceId] = { RaceName = RaceData.RaceName, Checkpoints = Checkpoints, Records = {}, Creator = Player.PlayerData.citizenid, RaceId = RaceId, Started = false, Waiting = false, Distance = math.ceil(RaceData.RaceDistance), Racers = {}, LastLeaderboard = {} } MySQL.insert('INSERT INTO lapraces (name, checkpoints, creator, distance, raceid) VALUES (?, ?, ?, ?, ?)', {RaceData.RaceName, json.encode(Checkpoints), Player.PlayerData.citizenid, RaceData.RaceDistance, GenerateRaceId()}) end) -- Callbacks QBCore.Functions.CreateCallback('qb-lapraces:server:GetRacingLeaderboards', function(_, cb) cb(Races) end) QBCore.Functions.CreateCallback('qb-lapraces:server:GetRaces', function(_, cb) cb(AvailableRaces) end) QBCore.Functions.CreateCallback('qb-lapraces:server:GetListedRaces', function(_, cb) cb(Races) end) QBCore.Functions.CreateCallback('qb-lapraces:server:GetRacingData', function(_, cb, RaceId) cb(Races[RaceId]) end) QBCore.Functions.CreateCallback('qb-lapraces:server:HasCreatedRace', function(source, cb) cb(HasOpenedRace(QBCore.Functions.GetPlayer(source).PlayerData.citizenid)) end) QBCore.Functions.CreateCallback('qb-lapraces:server:IsAuthorizedToCreateRaces', function(source, cb, TrackName) cb(IsWhitelisted(QBCore.Functions.GetPlayer(source).PlayerData.citizenid), IsNameAvailable(TrackName)) end) QBCore.Functions.CreateCallback('qb-lapraces:server:CanRaceSetup', function(_, cb) cb(Config.RaceSetupAllowed) end) QBCore.Functions.CreateCallback('qb-lapraces:server:GetTrackData', function(_, cb, RaceId) local result = MySQL.query.await('SELECT * FROM players WHERE citizenid = ?', {Races[RaceId].Creator}) if result[1] ~= nil then result[1].charinfo = json.decode(result[1].charinfo) cb(Races[RaceId], result[1]) else cb(Races[RaceId], { charinfo = { firstname = "Unknown", lastname = "Unknown" } }) end end) -- Commands QBCore.Commands.Add("cancelrace", "Cancel going race..", {}, false, function(source, args) local Player = QBCore.Functions.GetPlayer(source) if IsWhitelisted(Player.PlayerData.citizenid) then local RaceName = table.concat(args, " ") if RaceName ~= nil then local RaceId = GetRaceId(RaceName) if Races[RaceId].Started then local AvailableKey = GetOpenedRaceKey(RaceId) for cid, _ in pairs(Races[RaceId].Racers) do local RacerData = QBCore.Functions.GetPlayerByCitizenId(cid) if RacerData ~= nil then TriggerClientEvent('qb-lapraces:client:LeaveRace', RacerData.PlayerData.source, Races[RaceId]) end end table.remove(AvailableRaces, AvailableKey) Races[RaceId].LastLeaderboard = {} Races[RaceId].Racers = {} Races[RaceId].Started = false Races[RaceId].Waiting = false LastRaces[RaceId] = nil TriggerClientEvent('qb-phone:client:UpdateLapraces', -1) else TriggerClientEvent('QBCore:Notify', source, 'This race has not started yet.', 'error') end end else TriggerClientEvent('QBCore:Notify', source, 'You have not been authorized to do this.', 'error') end end) QBCore.Commands.Add("togglesetup", "Turn on / off racing setup", {}, false, function(source, _) local Player = QBCore.Functions.GetPlayer(source) if IsWhitelisted(Player.PlayerData.citizenid) then Config.RaceSetupAllowed = not Config.RaceSetupAllowed if not Config.RaceSetupAllowed then TriggerClientEvent('QBCore:Notify', source, 'No more races can be created!', 'error') else TriggerClientEvent('QBCore:Notify', source, 'Races can be created again!', 'success') end else TriggerClientEvent('QBCore:Notify', source, 'You have not been authorized to do this.', 'error') end end) -- Threads CreateThread(function() local races = MySQL.query.await('SELECT * FROM lapraces', {}) if races[1] ~= nil then for _, v in pairs(races) do local Records = {} if v.records ~= nil then Records = json.decode(v.records) end Races[v.raceid] = { RaceName = v.name, Checkpoints = json.decode(v.checkpoints), Records = Records, Creator = v.creator, RaceId = v.raceid, Started = false, Waiting = false, Distance = v.distance, LastLeaderboard = {}, Racers = {} } end end end)