Configuration
Carlock uses local config tables in shared, client, and server files, merged by Modules.register('config', config, true). Edit the existing fields in the appropriate file; examples below are focused excerpts, not replacement files. Restart after changing configuration.
Shared: config/shared/config.lua
General and locking
| Option | Default | Purpose |
|---|---|---|
language | 'en' | Locale filename without .json |
debug | false | Resource-scoped ox_lib debug output and effective startup summaries |
useMetadataKeys | true | Use physical inventory keys; falls back to virtual keys without an inventory bridge |
keyGrantTimeout | 15000 | Client self-grant readiness deadline in milliseconds, clamped to 1000–60000 |
locking.command / locking.key | 'togglelock' / 'U' | Lock command and default keybind |
locking.distance | 10.0 | Manual locking range in metres |
locking.preventExitWhenLocked | false | Prevent leaving through locked doors; death handling can be adapted in the client listener |
locking.animation controls the key gesture: enabled = true, dict = 'anim@heists@keycard@', clip = 'exit', duration = 1000, flag = 50, and loadTimeout = 1000. Empty lock and unlock tables inherit these settings; override individual fields for one direction. Gestures are skipped inside a vehicle or while busy and do not delay locking.
locking.sound defaults to enabled, provider = 'native', and distance = 20.0. The native pair is audioName = 'BUTTON' and audioRef = 'MP_PROPERTIES_ELEVATOR_DOORS'. Sound distance is capped at 100 metres; invalid/negative values disable audio. Select custom and implement onVehicleLockSound in bridge/listeners/client.lua for a custom sound.
Vehicle security profiles
vehicleAccessProfiles contains defaultProfile, profiles, and modelOverrides.
| Profile | Classes | Key | Lockpick tool | Hotwire | Tracker |
|---|---|---|---|---|---|
standard | 0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 17, 20 | carkeys | lockpick | wires, no item | false |
highEnd | 5, 6, 7 | carkey_fob | flapper_device | hacking, requires flapper_device | true |
Model overrides take precedence over class mapping, then defaultProfile = 'standard' is used. Keep profile names case-sensitive: highEnd, not highend.
Example: override a vehicle model
Inside the existing vehicleAccessProfiles table:
modelOverrides = {
['sultan'] = 'highEnd',
},
Each profile accepts classes, keyItem, tracker, lockpickItem, lockpickDifficulty, hotwireMode, and hotwireItem. hotwireItem = false removes the item requirement for that profile's hotwire action. Define any custom key/tool items in your inventory as well.
A profile's lockpickDifficulty is a fallback when the model's vehicle class is unavailable. For known classes, lockpick.difficulties controls difficulty; omitting a class blocks lockpicking that class, even if its profile has a fallback. The server resolves profiles from its model catalogue, not a client-selected profile.
Sharing and management
| Option | Default | Purpose |
|---|---|---|
givingKeys.enabled | true | Enables Carlock key transfers |
givingKeys.command | 'givekey' | Virtual-mode give-key menu command |
givingKeys.removeFromGiver | true | Remove the giver's key when transferring |
givingKeys.transferDistance | 10.0 | Transfer range in metres |
manageKeys.enabled | true | Enables key management/self-removal API |
manageKeys.command | 'managekeys' | Virtual-mode management command |
manageKeys.allowSelfRemoval | true | Allows removing your own key |
The give/manage menus and commands are not installed in effective metadata-key mode. Use inventory controls for physical keys. Carlock transfer settings apply to Carlock transfers, not every independent inventory move.
Lockpicking and hotwiring
| Area | Defaults and behavior |
|---|---|
lockpick | enabled = true, fallbackItem = 'lockpick', breakChance = 50, minimumDuration = 500 |
lockpick.game | duration = 20000, stages = 3 (1–6), tolerance easy/medium/hard = 15/10/7, strain rate = 18/26/36 |
lockpick.difficulties | Per-class easy/medium/hard mapping; retain only classes you want to allow |
hotwire | enabled = true, prompt = '[H] - Hotwire', control = 74, maxAttempts = 2, minimumDuration = 500 |
hotwire.wireGame | duration = 12000, wireCount = 4, requiredSparks = 3, sparkMinDuration = 120, holdDuration = 1100 |
hotwire.hackingGame | duration = 15000, previewDuration = 2800, sequenceLength = 5 (3–8) |
Times are milliseconds. Reaching the wire game's maximum live-contact duration fails the attempt; the player must make and release valid spark contacts.
minigames.lockpick, minigames.hotwireHighEnd, and minigames.hotwireStandard each default to { provider = 'builtin', minimumDuration = 500, duration = 20000 }. Select custom and implement the corresponding hook in bridge/minigames/client.lua to use another minigame. Built-in gameplay settings remain in lockpick.game, hotwire.wireGame, and hotwire.hackingGame; custom challenge timings use the minigames entries. See Integrations.
NPC keys and vehicle searching
npcKeyRobbery defaults to enabled with duration = 4000. resistance.initialChance and resistance.afterKeyChance are independent server rolls, both 10 by default; the resistance weapon is WEAPON_PISTOL. blacklistedWeapons contains WEAPON_RADARGUN by default and accepts weapon names or hashes.
npcKeyRobbery.assistance.enabled = true enables walk-up assistance for assistance.jobs = { ['police'] = true }. This uses current ESX/QB/QBX job names, with no grade or duty restriction. Ox/custom bridges without the required job-data contract fail closed.
vehicleSearch defaults to enabled with prompt = '[L] - Search', control = 182, progressLabel = 'Searching Vehicle. . .', and duration = 8000.
Search reward entries
rewards = {
{ chance = 50, type = 'account', name = 'money', quantity = { min = 30, max = 75 } },
{ chance = 30, type = 'key' },
{ chance = 50, type = 'item', name = 'water', label = 'Water', quantity = 1 },
},
One entry is picked randomly, then that entry's chance is rolled. These are not independent rolls for every reward. Use valid account/item names for your framework and inventory. A successful search can still find nothing.
Locksmith
The locksmith requires effective metadata-key mode, locksmith.enabled = true, and at least one location.
| Setting | Default / purpose |
|---|---|
prompt / control | '[E] - Locksmith' / 38 |
interactionDistance / vehicleDistance | 2.0 / 5.0 metres |
ped | Enabled; model = 's_m_y_xmech_02', scenario = 'WORLD_HUMAN_CLIPBOARD', renderDistance = 50.0, zOffset = -1.0 |
locations | Entries with label and coords = vec4(x, y, z, heading) |
duplicate | Enabled, legal (illegal = false), policeChance = 0, duration = 10000 |
duplicate.materials | 2 metalscrap by default |
duplicate.locksmithPayment | { type = 'account', name = 'money', amount = 500 } |
reprogram | Enabled, illegal = true, policeChance = 35, duration = 15000 |
reprogram.payment | { type = 'account', name = 'money', amount = 1000 } |
Duplication offers materials or locksmith payment as alternatives. Account payments use type = 'account'; inventory payments use type = 'item'. Setting illegal controls alert eligibility; it is not an ownership check or a key-revocation policy. Reprogramming changes the selected existing key to a different nearby vehicle's plate; it does not revoke everyone else's keys.
Dispatch and alarms
policeAlerts.enabled = true and provider = 'auto' enable provider selection. Default chances are hotwire = 50, lockpick = 30, and npcKeyRobbery = 70. Locksmith services use their own policeChance.
jobs = { 'police', 'sheriff' }identifies configured police groups for the built-in alert path; external dispatch systems also apply their own recipient rules.vehicleAlarm: enabled,provider = 'native',duration = 20000. Forcustom, implementonVehicleAlarmin the client listener.blip:sprite = 161,color = 1,scale = 0.7,shortRange = false,flashing = true,duration = 20000.
See Integrations for selectors and provider-specific behavior.
Client: config/client/config.lua
| Option | Default | Purpose |
|---|---|---|
interact | 'ox_target' | ox_target, qb-target, sleepless_interact, or textUI |
appearance.interactionAccentColor | false | Hex color for key menus/minigames; false uses Wasabi green |
appearance.progressType | 'circle' | circle or bar for search/NPC progress |
engine.autoStart | true | Automatically start when the driver has access |
engine.keepRunningOnExit | false | Preserve a running engine when the driver gets out |
engine.toggle.enabled | false | Enables the standalone engine command/keybind, not the control-menu engine button |
engine.toggle.command / key | 'toggleengine' / 'G' | Only registered when the toggle is enabled |
vehicleControl.enabled | true | Installs the vehicle control UI and export |
vehicleControl.command / key | 'vehiclecontrol' / 'K' | UI command and default keybind |
vehicleControl.accentColor | '#29b181' | Vehicle control UI color |
vehicleControl.defaultView | 'compact' | compact or full |
ignitionBypass.models, .jobs, and .plates allow driving without a key. Defaults include bicycle models and the police job; plates are full plate values without spaces. Bypass vehicles start automatically regardless of engine.autoStart. Ignition bypass does not grant keys for locks, manual engine controls, or the vehicle control menu.
Server: config/server/config.lua
| Option | Default | Purpose |
|---|---|---|
checkForUpdates | true | Update check at startup and hourly |
lockNPCVehicles | false | Lock parked traffic/NPC vehicles on creation |
adminCommands.giveVehicleKey.enabled | true | Enable admin key command |
adminCommands.giveVehicleKey.command | 'givevehiclekey' | Grant a key for the occupied vehicle |
adminCommands.giveVehicleKey.groups | { 'admin', 'god' } | Framework permission groups |
security.distances.vehicle | 5.0 | Default server vehicle lookup/validation radius; separate from locking range |
security.allowKeysForScriptedVehicles | true | Unowned scripted-vehicle exception for client self-grants; not a switch for trusted server grants |
security.rateLimits.default | interval = 1000, maximum = 10 | Default per-player request counter |
security.rateLimits.policeNotifications | interval = 10000, maximum = 3 | Police alert request limit |
security.rateLimits.locksmith | interval = 60000, maximum = 4 | Locksmith request limit |
security.actionTokens.gracePeriod | 15000 | Extra action completion time in milliseconds |
security.actionTokens.maximumDuration | 120000 | Action-token hard limit; a minimum duration above it prevents starting |
Keep validation server-side. These settings do not replace authorization in resources calling trusted server exports, and not every action uses the same cooldown or distance.
Vehicle names and locales
Add spawn-name labels to config/shared/vehicle_names.lua, for example sultan = 'Karin Sultan' inside its existing table. Restart after editing. These names are presentation data, not ownership evidence.
The shipped locale codes are cn, cs, da, de, en, es, fr, hi, it, jp, ko, nl, pl, pt, and tw. Set language to the filename code. English is loaded first, then the selected locale overlays it; an unavailable locale falls back to English with a warning. Prompts explicitly configured in Lua are edited in config.