✏️Configuration
Detailed configuration information
All config files fields are explained inside the config file in commented code. Some more complicated fields are explained in this page.
Custom mods
You can create custom modifications in the config files that will affect handling fields or assign variables to vehicles when installed.
By default custom mods are installed by using an inventory item. If you'd like your own implementation, you could use Additional Handlings API or edit server_config.lua Items
section.
You can find custom mods data in config.luaunder Custom Mods
section.
Fields
uniqueId:
string
Required id to be used to identify each mod. Must be unique for each mod.
itemData:
table
Contains data for inventory item.
consume:
boolean
Wether or not the item should be removed from inventory after installing the mod.
name:
?string
Item name (not label) that is used to identify your inventory item. If not entered will default to
uniqueId
. Make sure to create the items in your database / items config for your inventory for these to work.
label:
string
Label that will be shown in tablet. If not entered will default to
identifier
.
identifier:
string
Same as
slotName
. This will identify what slot the part will be installed to. Meaning that if two different mods will be created with the sameidentifier
, only one will be available to be installed at a time.
save:
boolean
Wether or not to save the part in database. If the part would not be saved in database, it will disappear after respawning the vehicle.
installedByDefault:
boolean
If set to
true
the part will be installed by default to all vehicles. Might be useful for standalone servers that do not have any inventory item system.
handlingData:
table
Data that will be assigned to the vehicle. If
key
matches any valid handling field it will affect the vehicle's handling field e.g.fInitialDriveForce = {value = 0.01}
will increase thefInitialdriveForce
by0.01
. If key is equal toenableHandlingFields
it will make the fields available for adjusting in tuning tablet. Othewise it will assign the variable to vehicle for any other desired implementation.enableHandlingFields:
string[]
Make the fields available for adjusting in tuning tablet
[
'fTractionCurveMax' | 'fInitialDriveForce' | 'fInitialDriveMaxFlatVel' | <...>
]:{value: ?number, mult: ?number}
Affects the handling value by
value
or adds the multiplier (mult
) when the mod is installed.
[
string
]:any
Assigns the variable to vehicle.
Example
Config.CustomMods = {
-- ...other mods
{
uniqueId = 'car_mod_engine_ecu',
itemData = ,
label = 'ECU',
identifier = ,
save = true,
handlingData = {
enableHandlingFields =
{
},
,
},
},
-- ...other mods
}
Permissions
Permissions will let you set up what is allowed to each group or player. Currently there are a few permissions that will allow or block players from doing certain things with the script.
You can find custom mods data in config.luaunder Permissions
section.
readXml
Allows player to preview XML file output in UI.
writeXml
Allows player to write XML file data to your server files.
writeDefault
Allows player to write default handlings to server database.
tabletCommand
Allows player to use chat command to open tablet. Recommended for servers that do not have inventory system.
driftCommand
Allows player to use command to enable drift mode. This will not require for player to have a small tablet. Recommended for servers that do not have inventory system.
ignoreLimits
Allows player to change values without strict limits. This is useful if player has to adjust handlings to write them to server files.
Config.Permissions
Defines what groups have which permissions. You can find the implementation of permissions in configs/server_config.lua
or adjust the getPlayerPemissions
function to your liking.
type:
'frameworkgroup' | 'identifier' | 'ace'
'frameworkgroup'
- selects the group that is invalue
.'identifier'
- selects specific player by identifier. Identifier is entered invalue
.'ace'
- selects ace permission specified invalue
.
value:
string
permissions:
Permissions
Defines which permissions are assigned to the selected player or group.
Configuration files
Config = {}
---@type 'kmh' | 'mph' Speed unit used in tablet and handling data
Config.SpeedUnit = 'kmh'
---@type number Speed unit multiplier. 3.6 for kmh / 2.236936 for mph
Config.SpeedMultiplier = 3.6
---@type number How often the telemetry page in small tablet data is refreshed. iIn ms
Config.StatsRefreshTimeout = 200
---@type number Base Nitro Consumption rate. Liters per second
Config.BaseNitroConsumption = 0.1
---@type number Base Nitro fInitialDriveForce value.
Config.BaseNitroDriveForceMultiplier = 0.7
---@type number Base Nitro fInitialDragCoeff multiplier.
Config.BaseNitroDragCoefficientMultiplier = -0.5
-- 8888888888 888
-- 888 888
-- 888 888
-- 8888888 888d888 8888b. 88888b.d88b. .d88b. 888 888 888 .d88b. 888d888 888 888
-- 888 888P" "88b 888 "888 "88b d8P Y8b 888 888 888 d88""88b 888P" 888 .88P
-- 888 888 .d888888 888 888 888 88888888 888 888 888 888 888 888 888888K
-- 888 888 888 888 888 888 888 Y8b. Y88b 888 d88P Y88..88P 888 888 "88b
-- 888 888 "Y888888 888 888 888 "Y8888 "Y8888888P" "Y88P" 888 888 888
---@type 'auto' | 'esx' | 'qbcore' | 'custom' If you are using custom framework, set this to 'custom' and configure the framework in client_config.lua and server_config.lua
Config.Framework = 'auto'
Config.FrameworkData = { -- ONLY change these if you have custom names set on your framework
SharedObjectEventNameCL = nil, -- Framework shared object event name for client side
SharedObjectEventNameSV = nil, -- Framework shared object event name for server side
}
---@type 'framework' | 'chat' | 'custom' Notification system used for displaying messages. Can be configured in client_config.lua
Config.Notifications = 'framework'
---@type 'auto' | 'oxmysql' | 'mysql-async' | 'ghmattimysql' | 'NO'
Config.MySQLScript = 'auto'
---@type 'license' | 'steam' | 'discord' | 'ip' Identifier used to save/load player's presets in database
Config.PlayerIdentifier = 'license'
-- Recommended to keep at true. Only use false if you are not using any database.
Config.UseDatabaseHandlingSaving = true
-- Recommended to keep at true. Only use false if you are not using any database.
Config.UseDatabasePresetsSaving = true
-- Recommended to keep at true. Only use false if you are not using any database.
Config.UseDatabaseDefaultHandlingSaving = true
-- Recommended to keep at false. Only use true if you are not using any database.
Config.SaveDefaultsToFile = false
-- If true, will run database checks on server start. Recommended to keep at true.
Config.RunDatabaseChecks = true
-- If true, will automatically save missing default values to database when the vehicle handling is required.
-- WARNING: Recommended to keep at false and save all default values manually by using /admin:cars
Config.AutomaticallySaveMissingDefaultValues = false
AddEventHandler('sb-handlingtuning:frameworkLoaded', function(framework)
if table.has({ 'standalone', 'custom' }, framework) then
Config.SaveDefaultsToFile = true
end
if IsDuplicityVersion() then
-- Owned vehicles table in database information.
-- tableName - Name of table in which all owned vehicles are stored
-- ownerColumn - Owned vehicles owner column in DB table
-- plateColumn - Vehicle plate column in DB table
if SB.FrameworkType == 'qbcore' then
Config.OwnedVehiclesDataTable = {
tableName = 'player_vehicles', ownerColumn = 'citizenid', plateColumn = 'plate' -- QBCore
}
elseif SB.FrameworkType == 'esx' then
Config.OwnedVehiclesDataTable = {
tableName = 'owned_vehicles', ownerColumn = 'owner', plateColumn = 'plate' -- ESX
}
else
Config.OwnedVehiclesDataTable = {}
Config.UseDatabaseHandlingSaving = false
Config.UseDatabasePresetsSaving = false
end
DB_INFO = Config.OwnedVehiclesDataTable
end
end)
if IsDuplicityVersion() then
AddEventHandler('sb-handlingtuning:databaseScriptDetected', function(databaseScript)
if databaseScript == 'NO' then
Config.UseDatabaseHandlingSaving = false
Config.UseDatabasePresetsSaving = false
Config.SaveDefaultsToFile = true
Config.RunDatabaseChecks = false
end
end)
end
-- d8888
-- d88888
-- d88P888
-- d88P 888 .d8888b .d8888b .d88b. .d8888b .d8888b
-- d88P 888 d88P" d88P" d8P Y8b 88K 88K
-- d88P 888 888 888 88888888 "Y8888b. "Y8888b.
-- d8888888888 Y88b. Y88b. Y8b. X88 X88
-- d88P 888 "Y8888P "Y8888P "Y8888 88888P' 88888P'
-- Limited editing allows for all players to use the tablet, but only adjust vehicles handling values for limited number
-- You can adjust how much of each field can be changed by editing configs/config.js Min / max > changeLimit
--
-- For example if vehicle's standard mass is 2000, Min.changeLimit is 200 and max.changeLimit is 500,
-- then player will have 1800 - 2500 to play around with, as 2000 - 200 = 1800, 2000 + 500 = 2500.
-- If value in config.js is not added, this will be used instead
Config.DefaultChangeLimit = 0.1
---@class Permissions
---@field readXml ?boolean allows to view and copy xml file in UI
---@field writeXml ?boolean allows to write xml data to server handling.meta
---@field writeDefault ?boolean allows to write default handlings data to database / file
---@field tabletCommand ?boolean allows to open tablet without having the inventory item
---@field driftCommand ?boolean allows to use drift mode without having the inventory item or required mods on car
---@field ignoreLimits ?boolean allows to ignore change limits
---@type Permissions Defines what permissions are given to each player by default
Config.DefaultPermissions = {
readXml = false,
writeXml = false,
writeDefault = false,
tabletCommand = false,
driftCommand = false,
ignoreLimits = false,
}
--- DO NOT EDIT THE FULL_PERMS
---@type Permissions
FULL_PERMS = {
readXml = true,
writeXml = true,
writeDefault = true,
tabletCommand = true,
driftCommand = true,
ignoreLimits = true,
}
--- Permissions and their conditions
--- type: 'identifier' | 'frameworkgroup' | 'ace' | 'default'
--- value: data for type e.g. framework group name, ace permission name, identifier value
--- permissions: Permissions that are assigned to selector
--- You can see `getPlayerPemissions` function in server_config.lua for implementation of permissions
---@type {type: string, value?: string, permissions: Permissions}[]
Config.Permissions = {
{ type = 'frameworkgroup', value = 'god', permissions = FULL_PERMS },
{ type = 'frameworkgroup', value = 'superadmin', permissions = FULL_PERMS },
{ type = 'frameworkgroup', value = 'admin', permissions = FULL_PERMS },
-- {type = 'identifier', value = 'steam:abc123', permissions = {}},
-- {type = 'ace', value = 'handlingtuning', permissions = {}},
}
-- Name of inventory item
-- Follor the documentation on how to add new items:
-- https://squirrel-bracket.gitbook.io/squirrel-bracket-documentation/v/handling-tuning/installation#create-inventory-items
Config.TabletItemName = 'tunertablet'
Config.SmallTabletItemName = 'smalltunertablet'
-- Command for admins full tablet access
Config.TabletCommand = 'tablet'
Config.SmallTabletCommand = 'smalltablet'
-- These fields will be disabled by default, in order to enable them, you will need to add mods to the car that enable them.
-- For example, if you add 'fSteeringLock' in Config.DisabledFields it will not be shown in tablet, unless you add a mod that enables it and install it on the car:
-- {
-- itemData = {name = 'car_mod_lock_kit', consume = true},
-- identifier = 'lockKit',
-- save = true,
-- handlingData = {enableHandlingFields = {'fSteeringLock'}}
-- }
Config.DisabledFields = {
'nMonetaryValue',
'fPetrolTankVolume',
'fSteeringLock',
'fInitialDriveForce',
'fDownforceModifier',
'fPetrolTankVolume',
'fDriveInertia',
'fRollCentreHeightRear',
'fDriveBiasFront',
'fPercentSubmerged',
'fEngineDamageMult',
'fLowSpeedTractionLossMult',
'fWeaponDamageScaledToVehHealthMult',
'fOilVolume',
'fSeatOffsetDistX',
'fTractionCurveLateral',
'fSuspensionForce',
'fMass',
'fSeatOffsetDistY',
'fSeatOffsetDistZ',
'fTractionSpringDeltaMax',
'nInitialDriveGears',
'fBoostMaxSpeed',
'fCollisionDamageMult',
'fWeaponDamageMult',
'vecInertiaMultiplier',
'vecCentreOfMassOffset',
'fSuspensionCompDamp',
'fRocketBoostCapacity',
}
-- 8888888b. d8b .d888 888 888b d888 888
-- 888 "Y88b Y8P d88P" 888 8888b d8888 888
-- 888 888 888 888 88888b.d88888 888
-- 888 888 888d888 888 888888 888888 888Y88888P888 .d88b. .d88888 .d88b.
-- 888 888 888P" 888 888 888 888 Y888P 888 d88""88b d88" 888 d8P Y8b
-- 888 888 888 888 888 888 888 Y8P 888 888 888 888 888 88888888
-- 888 .d88P 888 888 888 Y88b. 888 " 888 Y88..88P Y88b 888 Y8b.
-- 8888888P" 888 888 888 "Y888 888 888 "Y88P" "Y88888 "Y8888
-- Master switch for drift mode
Config.DriftModeEnabled = true
-- If false the drift mode won't be able to be toggled while moving
Config.AllowMovingDriftToggle = false -- If false the drift mode won't be able to be toggled while moving
---@alias DriftFieldFormula fun(defaultValue: number, speed: number, angle: number, multiplier: number): number
---@class DriftModeField
---@field label string what is displayed in tablet under the Drift group configuration
---@field description string what is displayed in tablet under the Drift group configuration
---@field formula DriftFieldFormula formula used to calculate changes for the field when vehicle is moving sideways
---@field defaultValue ?number
---Defines data for drift mode
---@type table<string, DriftModeField>
Config.DriftModeFields = {
fInitialDriveForce = {
label = 'Drive Force multiplier on slide',
description = 'How much of drive force will be increased when vehicle slides',
formula = function(defaultValue, speed, angle, multiplier)
local result = defaultValue + ((angle / 15) * multiplier)
return result
end,
min = 0.0,
max = 1.0,
defaultValue = 0.5,
},
fSteeringLock = {
label = 'Steering lock multiplier on slide',
formula = function(defaultValue, speed, angle, multiplier)
local result = math.max(defaultValue, math.min(defaultValue + (angle * multiplier), 70.0))
return result
end,
min = 0.0,
max = 1.0,
defaultValue = 0.5,
},
fTractionCurveMin = {
label = 'Traction decrease multiplier on slide',
formula = function(defaultValue, speed, angle, multiplier)
local result = math.max(defaultValue - angle / (17 * multiplier), 1.0)
return result
end,
min = 0.0,
max = 1.0,
defaultValue = 0.5,
},
fDriveBiasFront = {
label = 'Drive bias multiplier on slide',
formula = function(defaultValue, speed, angle, multiplier)
local result = math.max(defaultValue, 0.2)
return result
end,
min = 0.0,
max = 1.0,
defaultValue = 0.5,
},
}
-- .d88888b. 888 888b d888 888
-- d88P Y88b 888 8888b d8888 888
-- 888 888 888 88888b.d88888 888
-- 888 888 888 .d8888b 888888 .d88b. 88888b.d88b. 888Y88888P888 .d88b. .d88888 .d8888b
-- 888 888 888 88K 888 d88""88b 888 "888 "88b 888 Y888P 888 d88""88b d88" 888 88K
-- 888 888 888 888 "Y8888b. 888 888 888 888 888 888 888 Y8P 888 888 888 888 888 "Y8888b.
-- Y88b d88P Y88b 888 X88 Y88b. Y88..88P 888 888 888 888 " 888 Y88..88P Y88b 888 X88
-- "Y8888P" "Y88888 88888P' "Y888 "Y88P" 888 888 888 888 888 "Y88P" "Y88888 88888P'
-- You can add more. Read more on docs:
-- https://squirrel-bracket.gitbook.io/squirrel-bracket-documentation/v/handling-tuning/configuration#custom-mods
-- Read docs on how to add inventory items:
-- https://squirrel-bracket.gitbook.io/squirrel-bracket-documentation/v/handling-tuning/installation#create-inventory-items
Config.CustomMods = {
-- Default custom mods required for drift mode and nitro:
{
uniqueId = 'car_mod_nitro_kit',
itemData = { consume = true },
label = 'Nitro Kit',
identifier = 'nitroKit',
save = true,
installedByDefault = false, -- makes the mod to be installed on all vehicles by default
handlingData = {}, -- leave it empty
},
{
uniqueId = 'car_mod_nitro_tank',
itemData = { consume = true },
label = 'Nitro Tank',
identifier = 'nitroTank',
save = true,
allowReapply = true,
installedByDefault = false,
handlingData = { size = 3.0, level = 3.0 },
},
{
uniqueId = 'car_mod_diff_lsd',
itemData = { consume = true },
label = 'LSD',
identifier = 'differential',
save = true,
handlingData = { type = 'lsd' },
},
{
uniqueId = 'car_mod_diff_welded',
itemData = { consume = true },
label = 'Welded Differential',
identifier = 'differential',
save = true,
handlingData = { type = 'welded' },
},
-- Additional custom mods:
{
uniqueId = 'car_mod_tires',
itemData = { consume = true },
label = 'Sport tires',
identifier = 'tires',
save = true,
handlingData = { fTractionCurveMax = { value = 0.3 }, fTractionCurveMin = { value = 0.3 } },
},
{
uniqueId = 'car_mod_stiff_arb',
itemData = { consume = true },
label = 'Stiff ARB',
identifier = 'arb',
save = true,
handlingData = { fAntiRollBarForce = { value = 0.2 } },
},
{
uniqueId = 'car_mod_ecu',
itemData = { consume = false },
label = 'ECU',
identifier = 'engine_ecu',
save = true,
handlingData = { enableHandlingFields = { 'fInitialDriveForce', 'fInitialDriveMaxFlatVel' }, fInitialDriveForce = { value = 0.01 }, fInitialDriveMaxFlatVel = { value = -2 } },
},
{
uniqueId = 'car_mod_lock_kit',
itemData = { consume = true },
label = 'Steer lock kit',
identifier = 'lockKit',
save = true,
handlingData = { enableHandlingFields = { 'fSteeringLock' } }
}
}
-- 8888888888 d8b .d8888b.
-- 888 Y8P d88P Y88b
-- 888 Y88b.
-- 8888888 88888b. .d88b. 888 88888b. .d88b. "Y888b. 888 888 888 8888b. 88888b.
-- 888 888 "88b d88P"88b 888 888 "88b d8P Y8b "Y88b. 888 888 888 "88b 888 "88b
-- 888 888 888 888 888 888 888 888 88888888 "888 888 888 888 .d888888 888 888
-- 888 888 888 Y88b 888 888 888 888 Y8b. Y88b d88P Y88b 888 d88P 888 888 888 d88P
-- 8888888888 888 888 "Y88888 888 888 888 "Y8888 "Y8888P" "Y8888888P" "Y888888 88888P"
-- 888 888
-- Y8b d88P 888
-- "Y88P" 888
-- Implementation of engine swap feature can be edited in **/*/engine_swap.lua
Config.EngineSwap = {
-- Master switch for engine swap feature
enabled = true,
-- Time it takes to install engine
time = 20000,
-- TODO: Add sound transfer
-- transferSound = false,
-- Engine swap values that will be transferred from donor engine to target vehicle
transferValues = {
'fInitialDragCoeff',
'nInitialDriveGears',
'fInitialDriveForce',
'fDriveInertia',
'fClutchChangeRateScaleUpShift',
'fClutchChangeRateScaleDownShift',
'fInitialDriveMaxFlatVel',
'fLowSpeedTractionLossMult',
},
-- Config for donor engine swap (car from garage is considered to be donor)
donorSwap = {
enabled = true,
swapPrice = 10000, -- Price for donor swapping engine
destroyDonor = true, -- If true, donor vehicle will be destroyed after engine swap
onlyOwned = true, -- If true, only owned vehicles can be used as donor
},
-- Config for engine buy feature
buyEngine = {
enabled = false, -- If true, players can buy engines from NPC
availableEngines = {
{
model = 'nero', -- Model of the engine
label = 'Nero', -- Label that will be displayed in the menu
price = 100000, -- Price of the engine
},
{
model = 'elegy',
label = 'Elegy',
price = 70000,
}
}
},
-- Engine swap NPC cfg
npc = {
coords = vec(472.3840, -1285.2330, 28.5610, 304.0632),
model = 'mp_m_weapwork_01',
spawnDist = 50.0,
showBlip = true,
blip = {
sprite = 1,
color = 1,
label = 'Engine Swap',
}
}
}
-- .d8888b. 888
-- d88P Y88b 888
-- Y88b. 888
-- "Y888b. 888888 8888b. 88888b. .d8888b .d88b.
-- "Y88b. 888 "88b 888 "88b d88P" d8P Y8b
-- "888 888 .d888888 888 888 888 88888888
-- Y88b d88P Y88b. 888 888 888 888 Y88b. Y8b.
-- "Y8888P" "Y888 "Y888888 888 888 "Y8888P "Y8888
-- Enables stance feature. Might be quite performance heavy (adds about 0.04ms for cpu per tick).
Config.EnableStancer = true
-- How many vehicles can be stanced per frame. If you have a lot of vehicles on your server, you might want to lower this number.
Config.MaxStancedVehiclesPerFrame = 3
-- 8888888888 888
-- 888 888
-- 888 888
-- 8888888 888888 .d8888b
-- 888 888 d88P"
-- 888 888 888
-- 888 Y88b. Y88b. d8b
Config.SendClientSideErrorsToServer = false
Config.UI = {
-- Colors used in tablet UI
colors = {
primary = '#f5deb3',
primaryText = '#f5deb3',
},
fullPermsColors = {
primary = '#f5deb3',
primaryText = '#f5deb3',
}
}
-- If set to true, the default handling values will prioritized over handling data read from vehicle.
-- Reading data from vehicle might cause issues if you have any other scripts manipulating handling data.
Config.PrioritizeDefaultHandling = true
Last updated
Was this helpful?