ns-bus
by NullSect · Qbox / QBCore / ESX
A transit driver career for FiveM: admin-built stops and lines, NPC passengers, ticket checks, stop requests, police inspections and a long-term career.
Version 1.1.2. All 16 phases are done and the job was field-tested on a Qbox server; 1.1 adds the ticket machine on board for real players. See
CHANGELOG.mdfor what changed.
Features
- Bus depot with desk NPC: start and end shifts, optional job requirement
- In-game stop manager (
/busadmin): positions are captured where the admin stands or parks, stored in the database - Route builder: order stops, express skips, waiting times, return routes, GPS map preview
- Driving routes (F6 in the bus): GPS to the next stop, server-checked stop arrivals, schedule, missed stops, pay and XP paid exactly once
- NPC passengers: they wait at the stops, queue, board into free seats, ride to their own stop and get off; bus capacity, doors, pay per passenger
- Tickets at the door: every passenger holds up their ticket and ID card; compare the names (someone else's card reads valid at the validator), then let them in (Y) or refuse them (N); expired tickets, no ticket, silent fare dodgers; reputation and XP for good ticket control
- Ticket machine on board (K): real players riding a bus buy a single ticket, a discount ticket (this ride only) or a day pass, printed in their character's name and kept in the database; the driver sees the players on board, offers them a ticket (Y pays, N declines) and earns a commission on every sale; police inspections check player riders too, who pay their own fine
- Stop requests: riders press the button before their stop (chime with adjustable volume, bell in the HUD); empty stops can be driven past without penalty; passengers on board and their stops in the F6 menu
- Passenger personalities (13 types), passengers who talk to you (answer with 1 / 2 / 3), random events (arguments, lost items, wrong line, emergency stop, a sick passenger with EMS call, blocked door, suspicious passenger), passenger satisfaction in the route report
- Police inspections: the officer waits at the coming stop, or a police car with its siren pulls the bus over on the road (not stopping counts as fleeing); the officer talks to you like a passenger (polite answers lower the fine); real police players can inspect too ("Inspect bus"); fare dodgers are taken off, the driver is fined only for dodgers they let on, inspection report
- City Transit interface: driver dashboard (routes, career, assignments, vehicle, statistics, achievements, inspections), bus garage with a parking bay per bus, service bay screen, driver console while driving, conversation card, door check, route and inspection reports; every panel can fall back to ox_lib menus (
Config.UI) - Admin dashboard (
/busadmin): overview, drivers, stop editor, route editor with drag and drop and a schematic map, vehicles, inspections, test drives - More XP sources (long routes, requested stops, clean and perfect routes, friendly conversations, passed inspections) and reputation loss for missed stops; XP never goes down
- Daily and weekly assignments (data-driven, picked by level and route type, money / XP / reputation / unlock rewards) and 12 achievements
- Admin test drive of any route, without rewards (passengers included)
- 5 bus models unlocked by career level, server-side spawn, plates and vehicle keys
- Your own bus: fuel, engine, body, cleanliness and interior carry over between shifts; service bay at the depot (refuel, clean, repair); safe driving (collisions cost score and reputation, a clean route gives XP); upgrades bought at the desk
- Return point, automatic cleanup, destroyed and abandoned bus handling
- Driver career profile per character: XP, derived level, 9 ranks, reputation (0-100), lifetime statistics
- Automatic, non-destructive database migrations
- Server-authoritative request router: one entry point, payload rules (no NaN or infinity, size limits), rate limiting, per-player busy lock, suspicious-behaviour logging; stop arrivals faster than any bus could drive (teleports) are refused; covered by fuzz, exploit and race tests
- Framework bridge: Qbox, QBCore, ESX (auto-detected)
- Integration bridges: ox_target / qb-target, ox_inventory / qb / esx, ox_fuel / LegacyFuel / cdn-fuel / ps-fuel, qs / ps / cd dispatch with
autoandcustom - English and Turkish locales
- Admin-protected developer commands with audit log
Dependencies
| Resource | Required |
|---|---|
| ox_lib | yes |
| oxmysql | yes |
| qbx_core, qb-core or es_extended | one of them |
| ox_target or qb-target | yes (depot desk) |
| qbx_vehiclekeys or qb-vehiclekeys | optional (bus keys) |
Installation
See INSTALLATION.md.
Troubleshooting
| What you see | Why, and what to do |
|---|---|
| The bus has no keys | The ready line in the server console names the keys resource it found (vehicle keys: ...). none: no supported keys resource runs; set Config.VehicleKeys or use 'custom' (see CONFIGURATION) |
| No stop request chime | Check Config.StopRequest.Sound and Volume; the chime is nui/sounds/stop.ogg |
| Pressing N to talk refuses the passenger at the door | pma-voice uses N for push-to-talk. Rebind "Bus: refuse the passenger at the door" in GTA settings (Key Bindings > FiveM) or change Config.Tickets.Keys |
| 1 / 2 / 3 used an item instead of answering | Only while no conversation card is up; with the card up they answer. With an inventory other than ox_inventory, rebind the answer keys |
| "You cannot have got here that fast" | The stop was reached faster than Config.Security.MaxAverageSpeed allows from the last stop (a teleport). Admin test drives are not checked |
| Collisions are not counted | Set Config.Debug.Enabled = true: every damage report is printed with the body drop and the impact speed; tune Config.Condition.SafeDriving.ImpactKmh |
| K does nothing on a bus | The machine works only on a bus driven on a real route (not an admin test drive) and with Config.TicketMachine.Enabled. Another resource may use K: rebind "Bus: ticket machine" in GTA settings |
| Pressing N declines a ticket offer | Only while the offer card is up; rebind "Bus: decline the offered ticket" or change Config.TicketMachine.Keys |
| Developer commands do nothing | Config.Debug.Commands = true and the ns-bus.dev ACE; check with busperms <id> in the server console |
Commands
| Command | Who | Effect |
|---|---|---|
/busadmin | ns-bus.admin or framework admin | Admin dashboard: overview, drivers, stops, routes, vehicles, inspections, test drives |
| F6 (keybind) | driver on shift | Between routes: dashboard on the Routes tab. On a route: skip a stop, cancel, passengers (select one to talk) |
| 1 / 2 / 3 (keybinds) | driver, while a passenger talks to you | Positive answer / neutral or refusing answer / no answer (the card with the line and the answers shows above the console) |
| Y / N (keybinds) | driver, while a passenger is at the door | Let in / refuse (the passenger's ticket and ID card open by themselves; compare the names) |
/busprofile | every player with a driver profile | Driver dashboard (Config.Career.Command) |
Developer commands. They need Config.Debug.Commands = true and the ns-bus.dev ACE.
| Command | Effect |
|---|---|
/busdebug [id] | Shows state, level, XP, reputation |
/buscareer [level] [id] | Sets a player's level |
/busxp [amount] [id] | Adds or removes XP |
/busreputation [amount] [id] | Adds or removes reputation |
/buscoords | Copies your position as vector4 (for placing depots) |
/buspassenger [id] | Passenger summary of the active route (full list in the server console) |
/busticket [id] | Ticket summary of the active route (tickets in the server console) |
/busstoprequest [id] | A rider presses the stop button for the coming stop (for testing) |
/busevent [event] [id] | Starts a passenger event now; without an event it lists them |
/bustalk [id] | A seated rider starts a conversation now |
/businspection [pullover] [id] | Starts a routine police inspection now; with pullover a police car pulls the bus over |
/busassignments [id] | Picks new daily and weekly assignments for the current periods (progress starts over) |
Server console only: busperms [id] shows a player's ns-bus permissions and identifiers.
All of them can be used from the server console with a player id. Every use is logged in ns_bus_audit.
Permissions
add_ace group.admin ns-bus.admin allow # /busadmin
add_ace group.admin ns-bus.dev allow # developer commands
Police players need no ACE: anyone with a job in Config.Inspection.Police.Jobs (default: Config.Dispatch.PoliceJobs) and on duty gets "Inspect bus" on buses that are on a route.
Exports (server)
exports['ns-bus']:GetDriverState(playerId) -- state name or nil
exports['ns-bus']:GetActiveRoute(playerId) -- { id, code, label } or nil
exports['ns-bus']:GetPassengerCount(playerId) -- number
exports['ns-bus']:GetCareerLevel(playerId) -- level or nil
exports['ns-bus']:GetReputation(playerId) -- 0 - 100 or nil
exports['ns-bus']:HasAchievement(playerId, id) -- boolean
exports['ns-bus']:HasUnlock(playerId, id) -- boolean (reward unlocks)
exports['ns-bus']:GetBusCondition(playerId) -- { fuel, engine, body, cleanliness, interior, distance, ... } or nil
exports['ns-bus']:RepairBus(playerId, parts?) -- boolean, for mechanic scripts (parts: { engine, body, interior })
Server events for other resources (listen with AddEventHandler, never RegisterNetEvent):
AddEventHandler('ns-bus:sv:achievementUnlocked', function(playerId, achievementId) end)
AddEventHandler('ns-bus:sv:assignmentCompleted', function(playerId, assignmentId, period) end)
AddEventHandler('ns-bus:sv:rewardUnlocked', function(playerId, unlockId, reason) end)
Database
Tables: ns_bus_migrations, ns_bus_meta, ns_bus_profiles, ns_bus_achievements, ns_bus_audit, ns_bus_stops, ns_bus_routes, ns_bus_route_stops, ns_bus_runs, ns_bus_inspections, ns_bus_assignments, ns_bus_counters, ns_bus_fleet, ns_bus_tickets. Created automatically on first start (or import sql/bus.sql).