Skip to Content
Resourcesqb-lapraces Reference

qb-lapraces

qb-lapraces lets authorized players build lap race tracks with a checkpoint editor, save them to the database, and run races with laps, leaderboards, and per-track records.

It is a persistent track system, which is what distinguishes it from qb-streetraces. Races are created, joined, and managed through the phone, so qb-phone is a practical requirement.

The manifest at the commit linked above declares version 1.5.0.

Dependencies and start order

From the manifest: config.lua as a shared script, client.lua on the client, @oxmysql/lib/MySQL.lua and server.lua on the server, with ui_page 'html/index.html'.

From the implementation: qb-core for the core object, commands and permissions, and qb-phone for qb-phone:client:UpdateLapraces and qb-phone:client:RaceNotify. Without a phone resource listening, races still work but players get no notifications and no list.

Start order: oxmysql, qb-core, qb-phone, then qb-lapraces.

Database

The repository ships qb-lapraces.sql:

CREATE TABLE IF NOT EXISTS `lapraces` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `checkpoints` text DEFAULT NULL, `records` text DEFAULT NULL, `creator` varchar(50) DEFAULT NULL, `distance` int(11) DEFAULT NULL, `raceid` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4;

checkpoints and records hold JSON. creator holds a citizenid, and the server joins against the players table to display the creator’s name.

Tracks are loaded from this table into an in-memory Races table at resource start. Live race state is memory only, so a restart mid race loses the race but not the track.

Configuration

config.lua is two keys:

KeyDefaultMeaning
Config.WhitelistedCreators{ 'PUTCID' }Citizen IDs allowed to create tracks
Config.RaceSetupAllowedtrueGlobal switch for starting a race setup

The shipped 'PUTCID' is a placeholder. Replace it with real citizen IDs or leave the list empty.

Authorization is not whitelist-only. IsWhitelisted also returns true when QBCore.Functions.GetPermission(source) is admin or god, so administrators can always create tracks regardless of the list.

At this commit IsWhitelisted resolves the player with GetPlayerByCitizenId and then indexes Player.PlayerData.source without a nil check, so calling it for an offline citizen ID raises a server-side Lua error.

Exports

Two client exports, both read-only state queries:

ExportReturns
exports['qb-lapraces']:IsInEditor()Whether the local player is in the track editor
exports['qb-lapraces']:IsInRace()Whether the local player is in a race

Integration example

Suppressing your own HUD or interaction prompts while a player is racing:

-- client side, inside your own resource CreateThread(function() local wasBusy = false while true do local isBusy = exports['qb-lapraces']:IsInRace() or exports['qb-lapraces']:IsInEditor() if isBusy ~= wasBusy then wasBusy = isBusy TriggerEvent(isBusy and 'myresource:client:hideInteractions' or 'myresource:client:showInteractions') end Wait(1000) end end)

The exports are plain state reads, so the only thing worth acting on is the transition. Firing the event on every tick would send the same event sixty times a minute for the length of a race, and a version without the showInteractions branch would leave your prompts hidden after the race ends.

Server surface

HandlerKindArguments
qb-lapraces:server:CreateLapRacenet eventRaceName
qb-lapraces:server:SaveRacenet eventRaceData
qb-lapraces:server:SetupRacenet eventRaceId, Laps
qb-lapraces:server:JoinRacenet eventRaceData
qb-lapraces:server:LeaveRacenet eventRaceData
qb-lapraces:server:StartRacenet eventRaceId
qb-lapraces:server:CancelRacenet eventraceId
qb-lapraces:server:UpdateRaceStatenet eventRaceId, Started, Waiting
qb-lapraces:server:UpdateRacerDatanet eventRaceId, Checkpoint, Lap, Finished
qb-lapraces:server:FinishPlayernet eventRaceData, TotalTime, TotalLaps, BestLap
qb-lapraces:server:GetRacescallbacknone
qb-lapraces:server:GetListedRacescallbacknone
qb-lapraces:server:GetRacingDatacallbackRaceId
qb-lapraces:server:GetTrackDatacallbackRaceId
qb-lapraces:server:GetRacingLeaderboardscallbacknone
qb-lapraces:server:HasCreatedRacecallbacknone
qb-lapraces:server:IsAuthorizedToCreateRacescallbackTrackName
qb-lapraces:server:CanRaceSetupcallbacknone

CreateLapRace checks IsWhitelisted before opening the editor, which is the one authorization gate in the set.

Race progress is reported by the client. UpdateRacerData and FinishPlayer accept checkpoint, lap, and lap time values from the client and write them straight into the in-memory race state and, for records, into the lapraces table. Nothing validates that a reported lap time is physically possible or that the checkpoints were passed in order.

That is acceptable for a social race system where the leaderboard is flavour. It is not acceptable if you attach money or items to finishing positions. If you do, validate checkpoint order and minimum lap times on the server first, and only then pay out. See Server functions for the helpers available there.

Commands

/cancelrace is registered through QBCore.Commands.Add with the default user permission and cancels the caller’s ongoing race.

Troubleshooting

SymptomLikely cause
”Not authorized to create races”The citizen ID is not in Config.WhitelistedCreators and the account is not admin or god. The shipped 'PUTCID' is a placeholder
Server Lua error on an authorization checkIsWhitelisted indexes an offline player’s PlayerData
Tracks disappear after a restartThe lapraces table was not created, so saves failed
Races vanish on restart but tracks surviveExpected. Live race state is memory only
No race notificationsqb-phone is not started, or is a fork without those events
Implausible leaderboard timesLap times are reported by the client and stored unvalidated

Proposed manual smoke test

Not yet executed. On staging, with a database backup:

  1. Confirm the lapraces table exists before starting the resource.
  2. Add a real citizen ID to Config.WhitelistedCreators and confirm the editor opens for it.
  3. Build and save a track, restart the server, and confirm the track reloads.
  4. Run a two-player race and confirm finishing order and the records entry.
  5. Cancel a race mid run with /cancelrace and confirm both clients recover.

Sources